Unable to execute some commands in ps1 file from powershell - powershell

I have a windows service running on one of the Azure VMs.
So whenever a deployment has to be done, we copy the binaries manually. So now, I'm writing a script to do that.
Besically the binaries are in the form of a zip folder in MachineA. That zip folder is copied to MachineB (where windows service is running).After copying, the files are extracted and then zip folder is deleted. Then after the service is started.
To do this I have the below script.
#get session details
$UserName = "$IPAddress\$adminUsername"
$Password = ConvertTo-SecureString $adminPassword -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($UserName, $Password)
$s = New-PSSession -ComputerName $IPAddress -Credential $psCred
#stop the service
Invoke-Command -Session $s -ScriptBlock {Stop-Service -Name "ServiceName" -Force}
#delete existing binaries in destination machine
$tempDestPath = $destinationPath + "\*"
Invoke-Command -Session $s -ScriptBlock {param($tempDestPath)Remove-Item $tempDestPath -Recurse} -ArgumentList $tempDestPath
#copy binaries zip folder in destination machine
Copy-Item -Path $sourcePath -Destination $destinationPath -ToSession $s -Recurse
#extract zipfolder in destination machine
$zipFilePath = $destinationPath + "\" + $fileName
Invoke-Command -Session $s -ScriptBlock {param($zipFilePath,$destinationPath) Expand-Archive $zipFilePath -DestinationPath $destinationPath}-ArgumentList $zipFilePath,$destinationPath
#delete zipfolder in destination machine after extraction
Invoke-Command -Session $s -ScriptBlock {param($zipFilePath)Remove-Item –path $zipFilePath}-ArgumentList $zipFilePath
#start the service
Invoke-Command -Session $s -ScriptBlock {Start-Service -Name "ServiceName"}
This is working fine when I open Windows powershell in MachineA and execute these commands one by one.
But when I put the exact same commands in a ps1 file and execute that file, I'm getting the below error:
At C:\ScriptTest\test.ps1:13 char:95
+ ... -ScriptBlock {Start-Service -Name "ServiceName"}
+ ~~
The string is missing the terminator: ".
At C:\ScriptTest\test.ps1:11 char:42
+ Invoke-Command -Session $s -ScriptBlock {param($zipFilePath)Remov ...
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
Where am I missing this terminator. I'm not able to figure out. Any help is highly appreciated.

Turns out a - in one of the commands is wrong.
I have replaced this line
Invoke-Command -Session $s -ScriptBlock {param($zipFilePath)Remove-Item –path $zipFilePath}-ArgumentList $zipFilePath
with this line
Invoke-Command -Session $s -ScriptBlock {param($zipFilePath)Remove-Item -path $zipFilePath}-ArgumentList $zipFilePath
The hyphen in from of the path is slightly different.I was able to figure out from this answer

Related

How to pass variable in Scriipt Block for use with Invoke-Command that will call an executable that takes the variable as switch param

I am attempting to create a generic script that will process registry files on a list of remote servers. The script will take the input of the path/filename to process and a list of servers to process it on. Copies the .reg file to the remote server and then attempts to process the .reg file on the remote server. In order to make it generic for use with any .reg file I want to pass the path\filename to process in a variable in the script block. I have seen examples of passing named variables and positional parameters but that doesn't seem to meet my requirements. I tried creating the script block contents as a Scriptblock type and calling it but it does not work. Most of the errors I have been getting are related to invalid parsing, ambiguous parameter, etc. I have seen a couple of working examples in which the .reg file path/filename is hard-coded but that defeats the purpose of what I am attempting to accomplish. I have tried the Invoke-Command with a session and with the -ComputerName variable in order to try the :using:" command with no success. Is there any way to pass a variable that is basically the command line values (switch params & filepath/name) for an executable within the scriptblock or am I going about this in the wrong manner? In code below I am crossing domains so having to open session for those servers in order to create directory if it doesn't exist.
while ($true)
{
$regFile = Read-Host -Prompt "Enter the path & name of registry file to be processed"
If(Test-Path -Path $regFile) { break }
Write-Host "You must enter valid path & filename of a registry file to process."
}
$servers = Get-Content D:\MLB\Scripts\servers.txt
$fileNm = [System.IO.Path]::GetFileName($regFile)
$pass = ConvertTo-SecureString "blahblah" -AsPlainText -Force
$Creds = new-object -typename System.Management.Automation.PSCredential( "domain\username", $pass)
foreach ($server in $servers)
{
$dirPath = ''
$newfile = '\\' + $server + '\d$\MLB\RegFiles\' + $fileNm
if($server.ToLower().Contains("web"))
{
$Session = New-PSSession -ComputerName $server -Credential $Creds
Invoke-Command -Session $Session -ScriptBlock { New-Item -ErrorAction SilentlyContinue -ItemType directory -Path D:\MLB\RegFiles }
$newfile = "d:\MLB\RegFiles\" + $fileNm
Copy-Item $regFile -Destination $newfile -ToSession $Session -Force
Remove-PSSession -Session $Session
}
else
{
$dirPath = "\\$server\d`$\MLB\RegFiles"
New-Item -ErrorAction SilentlyContinue -ItemType directory -Path $dirPath
$newfile = "\\$server\d`$\MLB\RegFiles\$fileNm"
Copy-Item $regFile -Destination $newfile -Force
}
Invoke-Command -ComputerName $server -Credential $Creds -ScriptBlock {
$args = "s/ $newfile"
Start-Process -filepath "C:\Windows\regedit.exe" -Argumentlist $args
}

PSWindowsUpdate gets Acces Denied on Remote Machienes while Domain Admin

I want to deploy Updates to Windows Servers in Our Domain.
To achieve this i want to use the Module "PSWindowsUpdate" Here is the Official Release.
I use this Module in combination with PSSessions and import it locally on all Servers outside of the default Module Path.
It should accept the updates and install them without rebooting. This Script is run using an Domain Administrator
After it Accepts the Updates it should start downloading where this happens: The Error of the Job
I started getting this error after the 2018 July Security Patch installed.
As I can't share all of the code because of Company reasons, here is the part that matters:
function invokeUpdate{
param(
$session
)
if($Script:My.Reboot.isChecked){
$job = Invoke-Command -Session $session -ScriptBlock {Import-Module "C:\Scripts\updateModule\$($Using:My.ModuleVersion)\PSWindowsUpdate"; get-windowsupdate -install -AcceptAll} -AsJob
}else {
$job = Invoke-Command -Session $session -ScriptBlock {Import-Module "C:\Scripts\updateModule\$($Using:My.ModuleVersion)\PSWindowsUpdate"; get-windowsupdate -install -ignoreReboot -AcceptAll} -AsJob
}
return $job
}
function initSession{
param(
$serverHostname
)
$ses = New-PSSession -Computername $serverHostname
if(!(Invoke-Command -Session $ses -ScriptBlock {Test-Path "C:\Scripts\updateModule\" })){
Copy-Item "$modpath\$($Script:My.ModuleVersion)" -Destination "C:\Scripts\updateModule\$($Script:My.ModuleVersion)" -ToSession $ses -Recurse
}
Invoke-Command -Session $ses -ScriptBlock {
if((Get-ChildItem -Path "C:\Scripts\updateModule\").count -gt 1){
Get-ChildItem | Where-Object Name -NotLike "$($Using:My.ModuleVersion)" | Remove-Item -Recurse -Force
}
}
return $ses
}
$sessions = [System.Collections.ArrayList]#()
$Script:My.ModuleVersion = "2.1.1.2"
foreach ( $server in $Script:My.ServerActive.Items){
$sessions.Add( (initSession -serverHostname $server) )
}
foreach ($ses in $sessions){
invokeUpdate -session $ses
}
$Script:My.ServerActive.Items :
contains a list of server fqdns
Any Ideas or Solutions would save me,
thanks!
Nik
Edit 1:
Here is the Error Message:
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
+ CategoryInfo : NotSpecified: (:) [Get-WindowsUpdate], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,PSWindowsUpdate.GetWindowsUpdate
+ PSComputerName : fs02.azubi.netz
This will break my Sessions, but the output is $true
([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
Invoke-Command : Cannot bind parameter 'Session'. Cannot convert value "True" to type "System.Management.Automation.Runspaces.PSSession". ...
To Fix This Problem I had to change the way of Copying to the other System and the Actual call of get-windowsupdate.
The Mooudle has to be in $env:PSModPath, so to fix it you have to copy into one of those folders.
Copy-Item "$modpath\$($Script:My.ModuleVersion)\" -Destination "$psmod\PSWindowsUpdate\$($Script:My.ModuleVersion)" -ToSession $ses -Recurse -ErrorAction Stop
the Update doesnt need to run Over Invoke Command.
Get-WindowsUpdate -AcceptAll -Install -IgnoreReboot -ComputerName $session.ComputerName
Hope this will Help if you get a similar Problem!

Rename Vm Computer name using Bulk

I have Text file with number of local VM names in Hyper-v :
newname
IL-SRV
IL-TST
IL-MGN
IL-BBT
This is the names in my hyper-V environment , And i would like to change their computer name by using Powershell and bulk
I'm using this script
$computers = import-csv -Path C:\Users\Itay\Desktop\Servers.txt
foreach ($newname in $computers){
Invoke-Command -VMName $computers.newname -Credential administrator -ScriptBlock {Rename-Computer -NewName $computers.newname -Restart -Force}
}
But i receive this error
"
Invoke-Command : The input VMName IL-SRV does not resolve to a single virtual machine.
At line:11 char:1
+ Invoke-Command -VMName $computers.newname -Credential administrator - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Command], ArgumentException
+ FullyQualifiedErrorId : InvalidVMNameNotSingle,Microsoft.PowerShell.Commands.InvokeCommandCommad
"
Each iteration you are processing the entire list of $computers.newname
In your loop you create variable $newname without ever using it. You should be very careful when running commands that make changes especially if you aren't familiar with how powershell works. The other glaring issue is you are using Invoke-Command on a remote computer but using a local variable. I am taking a guess that you're trying to do is this.
$CSVData = import-csv -Path C:\Users\Itay\Desktop\Servers.txt
foreach ($line in $CSVData){
Invoke-Command -VMName $line.newname -Credential administrator -ScriptBlock {Rename-Computer -NewName $using:line.newname -Restart -whatif}
}
Note the $using:line variable. This provides the contents of the local variable to the remote computer. Another way to handle it would be use the -Argumentlist. I recommend using named parameters when doing so, like this.
$CSVData = import-csv -Path C:\Users\Itay\Desktop\Servers.txt
foreach ($line in $CSVData){
Invoke-Command -VMName $line.newname -Credential administrator -ScriptBlock {
Param($incomingname)
Rename-Computer -NewName $incomingname -Restart -whatif
} -ArgumentList $line.newname
}
The last thing you need to do AFTER confirming it's going to do what you desire is to remove the -WhatIf parameter from Rename-Computer. Rename-Computer has a -ComputerName parameter as well, fyi.
Either of these could also be written like this since there is only one property on $CSVData that we care about
$CSVData = import-csv -Path C:\Users\Itay\Desktop\Servers.txt
foreach ($name in $CSVData.newname){
Invoke-Command -VMName $name-Credential administrator -ScriptBlock {Rename-Computer -NewName $using:name -Restart -whatif}
}
or
$CSVData = import-csv -Path C:\Users\Itay\Desktop\Servers.txt
foreach ($name in $CSVData.newname){
Invoke-Command -VMName $name -Credential administrator -ScriptBlock {
Param($incomingname)
Rename-Computer -NewName $incomingname -Restart -whatif
} -ArgumentList $name
}

Trouble using Invoke-Command with ScriptBlock and -ArgumentList

I'm new to powershell and can't figure why I get the following error
Invoke-Command : A positional parameter cannot be found that accepts argument
'D:\Deploy\file.zip'.
At D:\source\Scripts\Build-Deploy\Build-Deploy\ServersDeploy.ps1:105 char:5
Invoke-Command -ComputerName $servers -ScriptBlock {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidArgument: (:) [Invoke-Command], ParameterBindingException
FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.InvokeCommandCommand
This is the script being run
params([string[[]]$servers, [string]$dest_package_path, [string]$src_package_path,[string]$deploy_script)
Invoke-Command -ComputerName $servers -ScriptBlock {
param($dest_package_path,$src_package_path,$deploy_script)
Write-Output "Destination path = $dest_package_path"
Write-Output "Copying zip $src_package_path to the destination host"
New-Item -ItemType Directory -Force -Path $dest_package_path
Write-Output "Directory Created"
Copy-Item -Path $src_package_path -Destination $dest_package_path -Force
Write-Host "Copying remote deploy scripts to the destination host"
Copy-Item -Path $deploy_script -Destination $dest_package_path -Force
} -ArgumentList $dest_package_path $src_package_path $deploy_script
Because you separated the arguments with spaces instead of a comma. That makes them new arguments to Invoke-Command.
-ArgumentList a single parameter that takes an array:
Invoke-Command -ComputerName $servers -ScriptBlock {
# Stuff
} -ArgumentList $dest_package_path,$src_package_path,$deploy_script

using PsFtp to upload a file to FTP Powershell

I've been wracking my brain on this issue and can't seem to fix it. I'm trying to upload a file to FTP using PSFTP.
The script I'm using:
#------------------------------------------------------
#local variables
$ftp_server = "SERVERNAME"
$ftp_path = "/FTPPATH/PATH"
$local = "C:\ftp\"
$local_in = Join-Path $local "In"
$local_out = Join-Path $local "Out"
$session = "my_ftp_session"
# set up credentials object
$username = "FFandP"
$password = Get-Content "$local_out\Credentials.txt" | ConvertTo-SecureString -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
Set-FTPConnection -Server $ftp_server -Credentials $cred -Session $session -KeepAlive -confirm -UseBinary
Get-ChildItem -Path $local_out |
% {
$ftp_file = "$ftp_path/$($_.Name)" # determine item fullname
Add-FTPItem -Path $ftp_file -LocalPath $_.FullName -Session $session -
}
# -------------------------------------------------
And the error I receive:
Add-FTPItem : Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (550) File
unavailable (e.g., file not found, no access)."
At line:22 char:1
+ Add-FTPItem -Path $ftp_file -LocalPath $_.FullName -Session $session
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Add-FTPItem
I've tried running the Add-FTPitem command by itself, but I get the same error.
I can upload to the FTP using FileZilla. I have also tried removing the variables and using hard-coded paths; I get the same error.
Any ideas?
The answer in #Josh's comment solved it for me. Run Add-FTPItem with the -Overwrite parameter.
Add-FTPItem -Path $remotePath -LocaPath $myPath -Overwrite
It took me a moment to figure out this problem, but here is my solution (I had the same problem).
When using Add-FTPItem the -Path parameter must not include the filename itself.
Add-FTPItem
-Path "ftp://SomeServer/SomeDir/"
-LocalPath "C:\SomeFilename.ext"
-Session $session
So in your example it should be:
Add-FTPItem -Path $ftp_path -LocalPath $_.FullName -Session $session
The filename will be added to the remote FTP path. In case you don't want to have the same name you must either rename the file locally first or remotely after.
Change block
Get-ChildItem -Path $local_out | %{ .... }
to one line
Get-ChildItem -Path $local_out | Add-FTPItem -Path $ftp_path