Deleting file from FTP server using PowerShell - c#

I wrote a PowerShell script to download files using FTPto my local machine.
After the file is downloaded, I want to delete it from the FTP server. I wrote this code too. But unfortunately it's not working.
Can anyone help me to point out what is wrong with my code? Any clues will be helpful ...
Here is my code
function Delete-File($Source,$Target,$UserName,$Password)
{
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Source)
{
"ABCDEF File exists on ftp server."
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse()
"ABCDEF File deleted."
}
}
function Get-FTPFile ($Source,$Target,$UserName,$Password)
{
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
# set the request's network credentials for an authenticated connection
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($username,$password)
if(Test-Path $targetpath)
{
"ABCDEF File exists"
}
else
{
"ABCDEF File downloaded"
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftprequest.UseBinary = $true
$ftprequest.KeepAlive = $false
Delete-File $sourceuri $targetpath $user $pass
}
# send the ftp request to the server
$ftpresponse = $ftprequest.GetResponse()
# get a download stream from the server response
$responsestream = $ftpresponse.GetResponseStream()
# create the target file on the local system and the download buffer
$targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
[byte[]]$readbuffer = New-Object byte[] 1024
# loop through the download stream and send the data to the target
file
do{
$readlength = $responsestream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$targetfile.close()
}
$sourceuri = "ftp://ftpxyz.com/vit/ABCDEF.XML"
$targetpath = "C:\Temp\M\NEWFOLDER\ABCDEF.XML"
$user = "*******"
$pass = "*******"
Get-FTPFile $sourceuri $targetpath $user $pass
Delete-File $sourceuri $targetpath $user $pass
Every time I execute this script, the only statement I get
ABCDEF file downloaded
or
ABCDEF file exists
I guess Delete-File is not executing at all... any type of clue will be helpful.

You cannot use Test-Path with an FTP URL. So your code for deleting the file will never execute.
Just remove the Test-Path condition and try to delete the file unconditionally. Then check for error and treat "file not exist" error as you like.
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($UserName, $Password)
try
{
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse() | Out-Null
Write-Host ("File {0} deleted." -f $Source)
}
catch
{
if ($_.Exception.InnerException.Response.StatusCode -eq 550)
{
Write-Host ("File {0} does not exist." -f $Source)
}
else
{
Write-Host $_.Exception.Message
}
}
Though as you try to delete the file only after you successfully download it, it's actually unlikely that the file won't exist.
So you may consider to give up on any specific error handling.

I ran your script locally to try it out and found a few issues. I refactored also a few things just to make it a bit more readable (at least in my opinion :) ).
Issues
Line 13. $Source parameter there is a ftp://... path. Test-Path will always return $false here and the delete request will never be executed.
In Get-FTPFile you were not referencing the input parameter of the function, instead the variables defined outside of it. I don't know if this was just a copy & paste bug or on purpose. In my opinion you should use the parameters you sent to the function. Lines 38, 39 and 50 at least in my code below.
Code
function Delete-File
{
param(
[string]$Source,
[string]$Target,
[string]$UserName,
[string]$Password
)
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Source)
{
"ABCDEF File exists on ftp server."
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile
$ftprequest.GetResponse()
"ABCDEF File deleted."
}
}
function Get-FTPFile
{
param(
[string]$Source,
[string]$Target,
[string]$UserName,
[string]$Password
)
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftprequest = [System.Net.FtpWebRequest]::create($Source)
# set the request's network credentials for an authenticated connection
$ftprequest.Credentials =
New-Object System.Net.NetworkCredential($UserName,$Password)
if(Test-Path $Target)
{
"ABCDEF File exists"
}
else
{
"ABCDEF File downloaded"
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftprequest.UseBinary = $true
$ftprequest.KeepAlive = $false
Delete-File $Source $Target $UserName $Password
}
# send the ftp request to the server
$ftpresponse = $ftprequest.GetResponse()
# get a download stream from the server response
$responsestream = $ftpresponse.GetResponseStream()
# create the target file on the local system and the download buffer
$targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create)
[byte[]]$readbuffer = New-Object byte[] 1024
# loop through the download stream and send the data to the target
file
do{
$readlength = $responsestream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$targetfile.close()
}
$sourceuri = "ftp://ftpxyz.com/vit/ABCDEF.XML"
$targetpath = "C:\Temp\M\NEWFOLDER\ABCDEF.XML"
$user = "*******"
$pass = "*******"
Get-FTPFile $sourceuri $targetpath $user $pass
#Delete-File $sourceuri $targetpath $user $pass
There are also ready made PowerShell cmdlets for talking to FTP/SFTP, no need to create everything from scratch, unless you are required to.
Anyway, for reference, check out e.g.
https://www.powershellgallery.com/packages/PSFTP
https://www.powershellgallery.com/packages/WinSCP

Related

How to expand Variables inside a scriptblock prior to serialization

I have a custom function that accepts a [scriptblock] parameter, The scriptblock is serailzed using [System.Management.Automation.PSSerializer]::Serialize() before it is sent to a remote process to be deserialized and invoked. The remote process does not have access to the local variables. I would like to allow the variables to be placed into the scriptblock on function call, however i would settle with a second parameter $ArgumentList to pass the arguments\parameters to the scriptblock. I browsed through System.Management.Automation.dll at Microsoft.PowerShell.Commands.InvokeCommandCommand to see if i could determine how Invoke-command adds this functionality but i'm a beginner in C# and could't figure it out.
How can I expand the variables within the scriptblock before they are sent along their way?
An example of the local serialize function:
Function Send-Command
{
param(
[scriptblock]$Scriptblock
)
[String]$Serialized = [System.Management.Automation.PSSerializer]::Serialize($Scriptblock)
[byte[]]$MessageBytes = [System.Text.Encoding]::UTF8.GetBytes($Serialized)
return $MessageBytes
}
And on the other end:
[byte[]]$Serialized = [System.Text.Encoding]::UTF8.GetString($MessageBytes)
[String]$Deserialized = [System.Management.Automation.PSSerializer]::Deserialize($Serialized.ToString())
return $Deserialized
I call the function with local variables:
$String = "This is a Test"
Send-Command -Scriptblock {Write-Output $String }
And on the other end:
Command: Write-Output $String
Should Output:
Command: Write-Output "This is a Test"
Using PowerShell I would suggest te following:
Why don't you use Invoke-Command with the scriptblock to run the scriptblock on the remote computer? You can combine that both with the $using:LocalVariableName statement to access local variables from the remote host or with local variable expansion as described above, or even combine both.
I guess something like this should do the job:
$ScriptBlock = {
Write-Output $using:Text
}
$Text = 'This is a test'
function Send-Command {
param (
[scriptblock] $ScriptBlock,
[string] $ComputerName,
[System.Management.Automation.PSCredential] $Credential
)
Invoke-Command -ComputerName $Computername -ScriptBlock $ScriptBlock -Credential $Credential
}
$ComputerName = 'Server0123'
$Credential = Get-Credential -Message "Enter credential for computer $($ComputerName)"
Send-Command $ScriptBlock $ComputerName $Credential
PowerShell: Invoke-Command
PowerShell: about_remote_variables
There's a way to deserialize a ScriptBlock.
You have to first create a scriptblock from the string.
Then you have to serialize all the variables that are bounded to the scope.
After it we use a regular expression to replace the variables with a payload to deserialize their values.
When the code is deserialized, it will have in the ScriptBlock an injected code to deserialize the value of the variables, so they retain the contents.
Then you need to call GetNewClosure() to unbound variables from the deserializer scope (any parameters for example), then they will be free to be bounded in the next call.
This solution is based on what the powershell DSC infraestructure does.
Function Serialize-Command
{
param(
[scriptblock]$Scriptblock
)
$rxp = '\$using:(?<var>\w+)'
$ssb = $Scriptblock.ToString()
$cb = {
$v = (Get-Variable -Name $args[0].Groups['var'] -ValueOnly)
$ser = [System.Management.Automation.PSSerializer]::Serialize($v)
"`$([System.Management.Automation.PSSerializer]::Deserialize('{0}'))" -f $ser
}
$sb = [RegEx]::Replace($ssb, $rxp, $cb, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
$Serialized = [System.Management.Automation.PSSerializer]::Serialize($sb)
[System.Text.Encoding]::UTF8.GetBytes($Serialized)
}
Function Deserialize-Command
{
param(
[byte[]]$ScriptblockString
)
$Serialized = [System.Text.Encoding]::UTF8.GetString($ScriptblockString)
$sb = [System.Management.Automation.PSSerializer]::Deserialize($Serialized.ToString())
[Scriptblock]::Create($sb).GetNewClosure()
}
$a = "is this what you wanted?"
$b = Serialize-Command { "test $using:a" }
$a = "okay"
$c = Deserialize-Command $b
$a = "right"
PS> & $c
is this what you wanted?
sources:
https://www.briantist.com/how-to/use-duplicate-dsc-script-resources-in-loop/
https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.scriptblock.getnewclosure?view=powershellsdk-1.1.0#System_Management_Automation_ScriptBlock_GetNewClosure
https://devblogs.microsoft.com/powershell/get-closure-with-getnewclosure/
The three common ways to expands variables are the below:
$VariableName
"$VariableName"
$($VariableName)
... but there are other considerations depending on what you are doing.

Create a new file in ftp using powershell

I need to upload a file to ftp and following is what I am trying
# Config
$Username = "XXX"
$Password = "XXX"
$LocalFile = "E:\shell\File1.xml"
$RemoteFile = "ftp://XXX/Folder1/File1.xml"
# Create a FTPWebRequest
$FTPRequest = [System.Net.FtpWebRequest]::Create($RemoteFile)
$FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username,$Password)
$FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$FTPRequest.UseBinary = $true
$FTPRequest.KeepAlive = $false
# Send the ftp request
$FTPResponse = $FTPRequest.GetResponse()
# Get a download stream from the server response
$ResponseStream = $FTPResponse.GetResponseStream()
# Create the target file on the local system and the download buffer
$LocalFileFile = New-Object IO.FileStream ($LocalFile,[IO.FileMode]::Create)
[byte[]]$ReadBuffer = New-Object byte[] 1024
# Loop through the download
do {
$ReadLength = $ResponseStream.Read($ReadBuffer,0,1024)
$LocalFileFile.Write($ReadBuffer,0,$ReadLength)
}
while ($ReadLength -ne 0)
Now I am getting an error stating
Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
I have not created any file in ftp by default. I want teh script to create a new file from the local file contents.
What am I doing wrong ?

Powershell + EWS script to move all text messages to Inbox/SMS subfolder

I'm hoping someone more experienced than me can post a Powershell script example (using the simplest possible method) that will...
Identify all messages that use "Text Message" OR "Multimedia Message" forms... then, move them to the Inbox/SMS subfolder.
Basically, I'm just trying to reproduce what my Outlook 2010 Rule does, except via command line.
If there's a pre-made cmdlet that does this already; with an example of what Im trying to do, that would be ideal.
You should be able to do that with something like this
$MailboxName = $args[0]
## Load Managed API dll
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
function FolderIdFromPath{
param (
$FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )"
)
process{
## Find and Bind to Folder based on Path
#Define the path to search should be seperated with \
#Bind to the MSGFolder Root
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
#Split the Search path into an array
$fldArray = $FolderPath.Split("\")
#Loop through the Split Array and do a Search for each level of folder
for ($lint = 1; $lint -lt $fldArray.Length; $lint++) {
#Perform search based on the displayname of each folder level
$fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint])
$findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)
if ($findFolderResults.TotalCount -gt 0){
foreach($folder in $findFolderResults.Folders){
$tfTargetFolder = $folder
}
}
else{
"Error Folder Not Found"
$tfTargetFolder = $null
break
}
}
if($tfTargetFolder -ne $null){
return $tfTargetFolder.Id.UniqueId.ToString()
}
}
}
$TextMessageClass = "IPM.Note.Mobile.SMS"
$MMSMessageClass = "IPM.Note.Mobile.MMS"
$sfItemSearchFilter1 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$TextMessageClass)
$sfItemSearchFilter2 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$MMSMessageClass)
#SearchFilter collection Or
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or);
$sfCollection.Add($sfItemSearchFilter1)
$sfCollection.Add($sfItemSearchFilter2)
# Bind to the Inbox Folder
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
# Get Folder to Move Item to
$fldId = FolderIdFromPath -FolderPath "\Inbox\SMS"
$SubFolderId = new-object Microsoft.Exchange.WebServices.Data.FolderId($fldId)
$SubFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SubFolderId)
$Itemids = #()
if($SubFolder -ne $null){
#Define ItemView to retrive just 1000 Items
$ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$fiItems = $null
do{
$fiItems = $service.FindItems($Inbox.Id,$sfCollection,$ivItemView)
#[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
Write-Host ("Moving " + $Item.Subject)
$Itemids += $Item
}
$ivItemView.Offset += $fiItems.Items.Count
}while($fiItems.MoreAvailable -eq $true)
#Total Items Processed Varible
$nmbProcessed = 0
if($Itemids.Count -gt 0){
write-host ("Move " + $Itemids.Count + " Items")
#Create Collection for Move Batch
$type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
$type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.ItemId" -as "Type")
$BatchItemids = [Activator]::CreateInstance($type)
#Varible to Track BatchSize
$batchSize = 0
foreach($iiID in $Itemids){
$nmbProcessed++
$BatchItemids.Add($iiID.Id)
if($iiID.Size -ne $null){
$batchSize += $iiID.Size
}
#if BatchCount greator then 50 or larger the 10 MB Move Batch
if($BatchItemids.Count -eq 50 -bor $batchSize -gt (10*1MB)){
$Result = $null
$Result = $service.MoveItems($BatchItemids,$SubFolder.Id)
[INT]$collectionCount = 0
[INT]$Rcount = 0
[INT]$Errcount = 0
if($Result -ne $null){
foreach ($res in $Result){
if ($res.Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success){
$Rcount++
}
else{
$Errcount++
}
$collectionCount++
}
}
else{
Write-Host -foregroundcolor red ("Move Result Null Exception")
}
Write-host ($Rcount.ToString() + " Items Moved Successfully " + "Total Processed " + $nmbProcessed + " Total Folder Items " + $Itemids.Count)
if($Errcount -gt 0){
Write-Host -foregroundcolor red ($Errcount.ToString() + " Error failed Moved")
}
$BatchItemids.Clear()
$batchSize = 0
}
}
if($BatchItemids.Count -gt 0){
$Result = $service.MoveItems($BatchItemids,$SubFolder.Id)
[INT]$Rcount = 0
[INT]$Errcount = 0
foreach ($res in $Result){
if ($res.Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success){
$Rcount++
}
else{
$Errcount++
}
}
Write-host ($Rcount.ToString() + " Items Moved Successfully")
if($Errcount -gt 0){
Write-Host -foregroundcolor red ($Errcount.ToString() + " Error failed Moved")
}
}
}
}
Cheers
Glen

Accessing Sharepoint document library list using Windows PowerShell script

Task: I need to loop thru all files on Sharepoint site and download them to local folder.
Script:
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$s = Get-SPSite “https://abc.abctools.consumer.abc.net/sites/rtc/report/SitePages/Forms/AllPages.aspx”
$files = $s.RootWeb.GetFolder("Shared Documents").Files
foreach ($file in $files) {
Write-host $file.Name
$b = $file.OpenBinary()
$fs = New-Object System.IO.FileStream(("C:\SP Document Library files\"+$file.Name), [System.IO.FileMode]::Create)
$bw = New-Object System.IO.BinaryWriter($fs)
$bw.Write($b)
$bw.Close()
}
Errors: I get when i try to run/execute above script.
1. "You cannot call a method on a null-valued expression."
New-Object: Exception calling ".ctor" with "2" agrument(s): "Could not find a part of the path 'C:\SP Document Library files\'
New-Object: Constructor not found. Cannot find an appropriate constructor for the type system.IO.BinaryWrite.
The term 'Get-SPSite' is not recognized as a cmdlet, function, operable program or script file. verify the term and try again.
Response on Error #2: I have created the folder & named "SP Document Library files" so that path is correct C:\SP Document Library files not sure why i see that msg.
Library files (.csv,.xls) exists in a folder.
Folder name : 2014-01-31.
1. What to do to in order resolve above error message(s).
2. I'm not sure if i need to use whole sharepoint url or part of it.Educate me on that.
Thanks!!
Try by giving ReadWrite FileAccess.
And you can get the root web directly if you know the Url instead of using SPSite.
Here's my script I use and has always worked
$siteUrl = '“https://abc.abctools.consumer.abc.net/sites/rtc”'
$listUrl = '“https://abc.abctools.consumer.abc.net/sites/rtc/Shared Documents”'
$folderPath = 'C:\\....'
$web = Get-SPWeb -Identity $siteUrl
$list = $web.GetList($listUrl)
$items = $list.Items
ForEach ($item in $items)
{
$binary = $item.File.OpenBinary();
$folderPathToSave = $folderPath + "\\" + $item.Name;
if ($binary -ne $null)
{
$stream = New-Object System.IO.FileStream($folderPathToSave,[System.IO.FileMode]::Create,[System.IO.FileAccess]::ReadWrite);
$writer = New-Object System.IO.BinaryWriter($stream);
$writer.Write($binary);
$writer.Close();
}
}
$web.Dispose()
The original post:
http://naimmurati.wordpress.com/2012/06/07/backup-documents-from-document-library-with-powershell-script/

Call WinRT Async method from Powershell to set account picture in win8

I'm trying to put something together that will use the AD Thumbnail photo to set a user's account picture on Windows 8. It seems like I should be able to use the API from WinRT to do this. I've pieced something together from various sources about calling the API from powershell, but I can't get it working. Here's an example of what I've tried to do:
$photo = ([ADSISEARCHER]“samaccountname=$($username)”).findone().properties.thumbnailphoto
$path = "C:\temp\Photo.jpg"
$photo | set-content $path -encoding byte
[Windows.System.UserProfile.UserInformation,Windows.System.UserProfile,ContentType=WindowsRuntime] > $null
[Windows.System.UserProfile.UserInformation]::SetAccountPictureAsync($photo)
I've tried a couple of other variations, but no matter what I do, I end up with an error like this:
Cannot convert argument "image", with value: "System.Object[]", for "setAccountPictureAsync" to type "Windows.Storage.IStorageFile" . . .
Is there something simple that I'm missing here to make this work?
I found this blog post by Keith Hill which seems like it might be helpful, but I am not sure if it directly translates to the issue I'm having.
Thanks!
Aurock
https://fleexlab.blogspot.com/2018/02/using-winrts-iasyncoperation-in.html has a pure-PowerShell solution.
Add-Type -AssemblyName System.Runtime.WindowsRuntime
$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
function Await($WinRtTask, $ResultType) {
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
$netTask = $asTask.Invoke($null, #($WinRtTask))
$netTask.Wait(-1) | Out-Null
$netTask.Result
}
This could then be used as:
$photoPath = "$home\Pictures\Photo.jpg"
$file = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($photoPath)) ([Windows.Storage.StorageFile])
$result = Await ([Windows.System.UserProfile.UserInformation]::SetAccountPictureAsync($file)) ([Windows.System.UserProfile.SetAccountPictureResult])
SetAccountPicture expects an object implementing IStorageFile and not a byte array. I would save the picture to your Pictures folder then load that into a StorageFile as shown below. You should be able to pass that object into the SetAccountPicture() method e.g.
$photoPath = "$home\Pictures\Photo.jpg"
$asyncOp = [Windows.Storage.StorageFile]::GetFileFromPathAsync($photoPath)
$typeName = 'PoshWinRT.AsyncOperationWrapper[Windows.Storage.StorageFile]'
$wrapper = new-object $typeName -Arg $asyncOp
$file = $wrapper.AwaitResult()
$asyncOp = [Windows.System.UserProfile.UserInformation]::SetAccountPictureAsync($file)
$typeName = 'PoshWinRT.AsyncOperationWrapper[Windows.System.UserProfile.SetAccountPictureResult]'
$wrapper = new-object $typeName -Arg $asyncOp
$result = $wrapper.AwaitResult()
$wrapper.Dispose()

Categories