PowerShell pass a switch to a function with Invoke-Command - powershell

I'm having some difficulties passing the switch -CleanFolders to a function by using Invoke-Command. I found this, but I don't really know how to implement it as it's not targeted to a function.
Calling my function like this works fine:
Delete-OldFiles $Target $OlderThanDays $Server -CleanFolders
Invoke-Command -ComputerName "$Server" -Authentication Credssp -Credential $Credentials -ScriptBlock ${Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server)
But this doesn't work at all:
Invoke-Command -ComputerName "$Server" -Authentication Credssp -Credential $Credentials -ScriptBlock ${Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server, -CleanFolders)
Full script:
#__________________________________________________________________________________________________________________________________
$ImportFile = "S:\Input\Scheduled Task\Auto_Clean.csv"
$Password = cat "S:\Input\pwd.txt" | ConvertTo-SecureString -Force
$UserName = "domain\me"
#__________________________________________________________________________________________________________________________________
# Scriptblock for running the function in a job
$JobFunc = {
# Function that removes files older than x days in all subfolders
Function Delete-OldFiles {
<#
.SYNOPSIS
Script to delete files and folders older than x days
.DESCRIPTION
Remove files older than x days in all subfolders and write success and failure actions to the logfile in "\\DEUSTHEIDIT02\Log\Scheduled Task\Auto_Clean\".
By default, empty foldes will be left behind and not deleted.
.PARAMETER Target
The path that will be recusively scanned for old files
.PARAMETER OlderThanDays
Filter for age of file, entered in days. Use 0 for all files to be removed
.PARAMETER CleanFolders
If this switch is specified folders that are older than 'OlderThanDays' and are empty will be removed. Default behaviour of this script is to leave empty folders behind.
.EXAMPLE
Delete-OldFiles -Target "\\grouphc.net\bnl\DEPARTMENTS\Brussels\CBR\SHARE\Target" -OlderThanDays "10"
Delete-OldFiles "\\grouphc.net\bnl\DEPARTMENTS\Brussels\CBR\SHARE\Target" "10"
Delete-OldFiles "E:\DEPARTMENTS\CBR\SHARE\Target" "10"
Description:
Deletes all files older than 10 days in the Target folder and all of its subfolders and write success and failure to the log.
.EXAMPLE
Delete-OldFiles "\\grouphc.net\bnl\DEPARTMENTS\Brussels\CBR\SHARE\Target" "10" -CleanFolders
Description:
Deletes all files older than 10 days and all empty folders in the Target folder and all of its subfolders and write success and failure to the log.
.NOTES
REQUIREMENTS Remote server:
PowerShell 2.0
As admin run 'Set-ExecutionPolicy RemoteSigned'
As amdin run 'Enable-WSManCredSSP -Role Server -Force'
REQUIREMENTS Script server:
Enable-WSManCredSSP -Role Client -DelegateComputer *.grouphc.net -Force
CHANGELOG
2014/05/06 Script born
2014/05/07 Added total runtime of the script to the logfile
2014/05/12 Added timestamps, fail and success messages to the logfile
2014/05/13 Added PowerShell 2.0 compatibility for Windows Server 2003
2014/05/13 Added remove empty directories
2014/05/14 Added comment section on top
2014/05/15 Added parameters to pass variables $Target and $OlderThanDays
2014/05/19 Added CSV input file for easy management
2014/05/20 Created function Delete-OldFiles and Delete-OldFolders
2014/05/22 Rewrote script for calling functions
2014/05/23 Added switch -CleanFolders
2014/06/02 Added password authentication for double hop
2014/06/03 Added loging capability to different files with timestamps
.AUTHOR
Me #>
[CmdletBinding(SupportsShouldProcess=$True)] # Add -WhatIf support for dry run
Param(
[Parameter(Mandatory=$True,Position=1)]
[ValidateScript({Test-Path $_})]
[String]$Target,
[Parameter(Mandatory=$True,Position=2)]
[Int]$OlderThanDays,
[Parameter(Mandatory=$False,Position=3)]
[String]$Server,
[switch]$CleanFolders
)
#__________________________________________________________________________________________________________________________________
# Create logfile with the correct name
# Gets date and reformats to be used in log filename
$TempDate = (get-date).ToString("dd-MM-yyyy")
# Reformats $Target so it can be used in the log filename
$TempFolderPath = $Target -replace '\\','_'
$TempFolderPath = $TempFolderPath -replace ':',''
$TempFolderPath = $TempFolderPath -replace ' ',''
# Combines the date and the path scanned into the log filename
$script:LogFile = "\\DEUSTHEIDIT02\Log\Scheduled Task\Auto_Clean\$Server - $TempFolderPath - $TempDate.log"
#__________________________________________________________________________________________________________________________________
# Check the version of PowerShell
if ($PSVersionTable.PSVersion.Major -ge "3") {
# PowerShell 3+ Remove files older than (FASTER)
Get-ChildItem -Path $Target -Recurse -File |
Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-$OlderThanDays) } |
ForEach {
$Item = $_.FullName
Remove-Item $Item -Recurse -Force -ErrorAction SilentlyContinue
# Log succes/failure
$Timestamp = (Get-Date).ToShortDateString()+" | "+(Get-Date).ToLongTimeString()
if (Test-Path $Item) {
"$Timestamp | FAILLED: $Server $Item (IN USE)"
}
else {
"$Timestamp | REMOVED: $Server $Item"
}
} | Tee-Object $LogFile -Append }
Else {
# PowerShell 2 Remove files older than
Get-ChildItem -Path $Target -Recurse |
Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt (Get-Date).AddDays(-$OlderThanDays) } |
ForEach {
$Item = $_.FullName
Remove-Item $Item -Recurse -Force -ErrorAction SilentlyContinue
# Log succes/failure
$Timestamp = (Get-Date).ToShortDateString()+" | "+(Get-Date).ToLongTimeString()
if (Test-Path $Item) {
Write-Host "$Timestamp | FAILLED: $Server $Item (IN USE)"
"$Timestamp | FAILLED: $Server $Item (IN USE)"
}
else {
Write-Host "$Timestamp | REMOVED: $Server $Item"
"$Timestamp | REMOVED: $Server $Item"
}
} | Out-File $LogFile -Append }
#__________________________________________________________________________________________________________________________________
# Switch -CleanFolders deletes empty folders older than x days
if ($CleanFolders) {
# Check the version of PowerShell
if ($PSVersionTable.PSVersion.Major -ge "3") {
# PowerShell 3+ Remove empty folders older than (FASTER)
Get-ChildItem -Path $Target -Recurse -Force -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-$OlderThanDays) } |
Where-Object { (Get-ChildItem -Path $_.FullName -Recurse -Force -File) -eq $null } |
ForEach {
$Item = $_.FullName
Remove-Item $Item -Recurse -Force -ErrorAction SilentlyContinue
# Log succes/failure
$Timestamp = (Get-Date).ToShortDateString()+" | "+(Get-Date).ToLongTimeString()
if (Test-Path $Item) {
"$Timestamp | FAILLED: $Server $Item (IN USE)"
}
else {
"$Timestamp | REMOVED: $Server $Item"
}
} | Tee-Object $LogFile -Append
}
else {
# PowerShell 2 Remove empty folders older than
Get-ChildItem -Path $Target -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } |
Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-$OlderThanDays) } |
ForEach {
$Item = $_.FullName
Remove-Item $Item -Recurse -Force -ErrorAction SilentlyContinue
# Log succes/failure
$Timestamp = (Get-Date).ToShortDateString()+" | "+(Get-Date).ToLongTimeString()
if (Test-Path $Item) {
Write-Host "$Timestamp | FAILLED: $Server $Item (IN USE)"
"$Timestamp | FAILLED: $Server $Item (IN USE)"
}
else {
Write-Host "$Timestamp | REMOVED: $Server $Item"
"$Timestamp | REMOVED: $Server $Item"
}
} | Out-File $LogFile -Append
}
}
}
}
#__________________________________________________________________________________________________________________________________
# Read input file and ignore all lines starting with #
$File = (Import-Csv -Path $ImportFile -Header "Server", "Target", "OlderThanDays" | Where { $_.Server -NotMatch "#" } )
#__________________________________________________________________________________________________________________________________
# If the UNC Path is provided we will run the script locally else it wil be run on the remote server as a job
Foreach ($_ in $File) {
# Set easy names
$Server = $_.Server
$Target = $_.Target
$OlderThanDays = $_.OlderThanDays
if ($Server -eq "UNC")
{
Write-Host "UNC Path detected: $Target, $OlderThanDays" -ForegroundColor Yellow
Start-Job -ScriptBlock {Param($Target, $OlderThanDays, $Server) Delete-OldFiles $Target $OlderThanDays $Server} -InitializationScript $JobFunc -ArgumentList ($Target, $OlderThanDays, $Server) -Name DelFiles
# Remove empty folders to: Delete-OldFiles $Target $OlderThanDays $Server -CleanFolders
}
else
{
Write-Host "Local path detected: $Server, $Target, $OlderThanDays" -ForegroundColor Cyan
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $UserName,$Password
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $UserName,$Password
Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock {$JobFunc; Delete-OldFiles $Target $OlderThanDays $Server} -ArgumentList ($Target, $OlderThanDays, $Server) #-AsJob -JobName DelFiles
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock {Delete-OldFiles $args[0] $args[1] $args[2] -CleanFolders:$args[3]} -ArgumentList ($Target, $OlderThanDays, $Server, $true) -AsJob -JobName DelFiles
# Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock ${Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server)
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock ${Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server) -AsJob -JobName DelFiles
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock {Delete-OldFiles $Target, $OlderThanDays, $Server} -ArgumentList ($Target, $OlderThanDays, $Server) #-AsJob -JobName DelFiles
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock {Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server) #-AsJob -JobName DelFiles
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock ${Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server)
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock {$JobFunc} -ArgumentList ($Target, $OlderThanDays, $Server) -AsJob -JobName DelFiles
#Invoke-Command -ComputerName "$Server.grouphc.net" -Authentication Credssp -Credential $Credentials -ScriptBlock ${Function:Delete-OldFiles} -ArgumentList ($Target, $OlderThanDays, $Server)
}
}
Thank you for your help.

Try like this:
$sb = {
function Delete-OldFiles {
#...
}
Delete-OldFiles $args[0] $args[1] $args[2] -CleanFolders:$args[3]
}
Invoke-Command -ComputerName $Server -Authentication Credssp `
-Credential $Credentials -ScriptBlock $sb `
-ArgumentList ($Target, $OlderThanDays, $Server, $true)

Related

Powershell - run script on multiple computers simultaneously

I'm working on a script that cleanup old user account and some data from computers.
I would like to run the script on 5 computers at one time from the attached list of PCs.
Is it possible? If so, how can it be done?
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$host_path = 'Host path'
)
$computer = Get-Content "$host_path"
foreach ($computer in $computer){
Invoke-Command -ComputerName $computer -ScriptBlock { Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-30))}| Remove-WmiObject }
Invoke-Command -ComputerName $computer -ScriptBlock { Remove-Item -Path C:\Windows\ccmcache\* -Confirm:$false -Force -Recurse -Debug }
Invoke-Command -ComputerName $computer -ScriptBlock { Remove-Item -Path C:\ProgramData\1E\NomadBranch\* -Confirm:$false -Force -Recurse -Debug }
}
You can pass multiple computer names to Invoke-Command at once to achieve this:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$host_path = 'Host path'
)
$computerNames = Get-Content $host_path
Invoke-Command -ComputerName $computerNames -ScriptBlock {
Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-30))}| Remove-WmiObject
Remove-Item -Path C:\Windows\ccmcache\* -Confirm:$false -Force -Recurse -Debug
Remove-Item -Path C:\ProgramData\1E\NomadBranch\* -Confirm:$false -Force -Recurse -Debug
}
If you want to "chunk" the list of computer names into batches on N machines at a time, you can do it like this:
$computerNames = Get-Content $host_path
$batchSize = 5
while($computerNames.Count -gt 0){
# Pull the first N names from the list
$nextBatch = #($computerNames |Select -First $batchSize)
# Then overwrite the list with any elements _after_ the first N names
$computerNames = #($computerNames |Select -Skip $batchSize)
Write-Host "Executing remote command against $($nextBatch.Count) computers: [$($nextBatch.ForEach({"'$_'"}) -join ', ')]"
# Invoke remoting command against the batch of computer names
Invoke-Command -ComputerName $nextBatch -ScriptBlock {
Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-30))}| Remove-WmiObject
Remove-Item -Path C:\Windows\ccmcache\* -Confirm:$false -Force -Recurse -Debug
Remove-Item -Path C:\ProgramData\1E\NomadBranch\* -Confirm:$false -Force -Recurse -Debug
}
}
If you are using PowerShell 7.x, you can do the following.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$host_path = 'Host path'
)
# The default value for ThrottleLimit is 5, but I put it here to show syntax.
# Throttle is the number of concurrent runspaces to use. (ex: do 5 objects at a time)
Get-Content $host_path | Foreach-Object -ThrottleLimit 5 -Parallel -ScriptBlock {
Invoke-Command -ComputerName $_ -ScriptBlock {
Get-WMIObject -class Win32_UserProfile | Where-Object {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-30))}| Remove-WmiObject
Remove-Item -Path C:\Windows\ccmcache\* -Confirm:$false -Force -Recurse -Debug
Remove-Item -Path C:\ProgramData\1E\NomadBranch\* -Confirm:$false -Force -Recurse -Debug
}
}
This will run X loops at a time, X being your -ThrottleLimit value, which defaults to 5.
Again, this is only available in PowerShell 7, and not backwards compatible with Windows PowerShell.

New-PSDrive "The network path was not found"

i have trouble with "New-PSDrive -Root"
When i try to map a New-PSDrive -Root $patch with a $path using an array, the cmd does not map the drive but give me an error : "The network Path was not found".
The path is working if i use an explorer in my windows.
How can i fix that ?
Thanks a lot
exemple :
foreach ($s in $serverlist)
{
$path = "\\$s\e$\Updates\file\
New-PSDrive -Name "S" -Root $path -Persist -PSProvider "FileSystem" -Credential $cred
}
Problem
This is the entire script :
Get-Date -Format "dddd MM/dd/yyyy HH:mm K">> "C:\file\results.txt"
$cred = Get-Credential -Credential domain\name
$serverlist = #(get-content -Path "C:\file\serverlist.txt")
foreach ($s in $serverlist)
{
$path = "\\$s\e$\Updates\file\"
New-PSDrive -Name "S" -Root $path -Persist -PSProvider "FileSystem" -Credential $cred
$path2 = "\\$s\e$\Updates\file\errors.txt"
$file = Get-Content $path2
$containsWord = $file | %{$_ -match "0"}
if ($containsWord -contains $true) {
Out-File -FilePath "C:\file\results.txt" -Append -InputObject "$s : ok"
} else {
Out-File -FilePath "C:\file\results.txt" -Append -InputObject "$s : nok"
}
Remove-PSDrive -Name "S"
}
EDIT 1 : If i try to access to the file directly by an windows explorer with the same credential and I, after that, run the script, it works
As commented, the user in $cred may have permissions to access the file in the path on the server, but you as it seems do not.
Try using Invoke-Command where you can execute a scriptblock using different credentials than your own:
$cred = Get-Credential -Credential domain\name
$serverlist = Get-Content -Path "C:\file\serverlist.txt"
# loop through the list of servers and have these perform the action in the scriptblock
$result = foreach ($s in $serverlist) {
Invoke-Command -ComputerName $s -Credential $cred -ScriptBlock {
# you're running this on the server itself, so now use the LOCAL path
$msg = if ((Get-Content 'E:\Updates\file\errors.txt' -Raw) -match '0') { 'ok' } else { 'nok' }
# output 'ok' or 'nok'
'{0} : {1}' -f $env:COMPUTERNAME, $msg
}
}
# write to the results.txt file
# change 'Add-Content' in the next line to 'Set-Content' if you want to create a new, blank file
Get-Date -Format "dddd MM/dd/yyyy HH:mm K" | Add-Content -Path 'C:\file\results.txt'
$result | Add-Content -Path 'C:\file\results.txt'
In fact, you don't even need a foreach loop because parameter -ComputerName can receive an array of server names:
$result = Invoke-Command -ComputerName $serverlist -Credential $cred -ScriptBlock {
# you're running this on the server itself, so now use the LOCAL path
$msg = if ((Get-Content 'E:\Updates\file\errors.txt' -Raw) -match '0') { 'ok' } else { 'nok' }
# output 'ok' or 'nok'
'{0} : {1}' -f $env:COMPUTERNAME, $msg
}

Check if process is running on multiple remote computers and copy a file if process is NOT running, keep list of those which succeeded

I need to copy a file to multiple computers, but can only do so if a particular app (process) is not running.
I know I can use Invoke-Command to run a script (scriptblock) on a list of machines.
But how can I check if process is running on the machine and then only copy file if it is not running.
So that at the end of running against a load of computers I can easily see those which succeeded e.g. process was not running and file was copied
Thanks
UPDATE:
I am assuming something like this will do the first bits of what I am asking, but how to visually show or log success or failure so I know which computers have been done - doesn't need to be anything fancy, even if simply a variable that holds computername of those where process wasn't running and file was copied okay
Invoke-Command -ComputerName PC1, PC2, PC3 -ScriptBlock {
If ((Get-process -Name notepad -ea SilentlyContinue) -eq $Null){
Copy-Item -Path "\\server01\c$\test\file.txt" -Destination "C:\test\file.txt" -Force
}
}
$Procs = invoke-command -ComputerName PC1 { get-process | Select Name }
If($Procs -notmatch "Notepad"){ Copy-Item -Path "\\server01\c$\test\file.txt" -Destination "\\$PC1\c$\test\" -Force}
edited:
$computers = #("PC1","PC2","PC3")
Foreach($computer in $computers){
$Procs = invoke-command -ComputerName $computer { Get-Process Notepad -ErrorAction SilentlyContinue}
If(!$Procs){"$Computer - not running Notepad"; Copy-Item -Path "\\server01\c$\test\file.txt" -Destination "\\$computer\c$\test\" -Force}
elseif($Procs){"$Computer - is running Notepad"}
}
Edit2(for clean output):
$computers = #("PC1","PC2","PC3")
$RNote = #()
$NNote = #()
$off = #()
Foreach($computer in $computers){
$TestC = Test-Connection -ComputerName $computer -Count 1
If(!($TestC)){$off += $computer} Else{
$Procs = invoke-command -ComputerName $computer { Get-Process Notepad -ErrorAction SilentlyContinue}
If(!$Procs){$NNote +=$computer; Copy-Item -Path "\\server01\c$\test\file.txt" -Destination "\\$computer\c$\test\" -Force}
elseif($Procs){$RNote +=$computer}
}
}
$leng =[array]$RNote.count,$NNote.Count,$off.count
[int]$max = ($leng | measure -Maximum).Maximum
for($i=0; $i -lt $max;$i++){
[pscustomobject]#{
"Notepad On" = $(if ($RNote[$i]){$RNote[$i]})
"Notepad Off" = $(if ($NNote[$i]){$NNote[$i]})
"Offline " = $(if ($off[$i]){$off[$i]})
}
}
I think this is what you're looking for or at least close:
$Results = #()
$Results +=
Invoke-Command -ComputerName DellXPS137000, DellXPS8920 -ScriptBlock {
$GPArgs = #{Name = "Notepad++"
ErrorAction = "SilentlyContinue"}
If ( $Null -ne (get-process #GPArgs )) {
#Process your copy here
$Status = "Success"
}
Else {$Status = "Failed"}
$Machine =
(Get-CimInstance -ClassName 'Win32_OperatingSystem').CSName
Return ,"$Machine : $Status"
}
Value of $Results:
PS> $results
DELLXPS137000 : Success
DELLXPS8920 : Failed
HTH

Set-Content using variables

I'm trying to generate PowerShell scripts using a PowerShell script. How do I get the variables' values inside the content of the newly generated script?
foreach ($server in $testServers) {
New-Item -ItemType File -Name "$($server)_wu.ps1" -Path "D:\tools\windows updates\uscripts"
Set-Content -Path "D:\tools\windows updates\uscripts\$($server)_wu.ps1" -Value {
$computer = $server
Invoke-Command -ComputerName $computer -ScriptBlock {
Set-ExecutionPolicy Bypass
Find-Module PSWindowsUpdate | Install-Module -Force | Import-Module -Force
}
$updatesCount = (Get-WindowsUpdate -ComputerName $computer).Count
while ($updatesCount -gt "0") {
psexec \\$computer -s "powershell.exe "Install-WindowsUpdate -Confirm:`$false -IgnoreReboot""
Restart-Computer -ComputerName $computer -Force -Wait
$updatesCount = (Get-WindowsUpdate -ComputerName $computer).Count
}
}
}
The result here is the following:
$computer = $server
Invoke-Command -ComputerName $computer -ScriptBlock {
Set-ExecutionPolicy Bypass
Find-Module PSWindowsUpdate | Install-Module -Force | Import-Module -Force
}
$updatesCount = (Get-WindowsUpdate -ComputerName $computer).Count
while ($updatesCount -gt "0") {
psexec \\$computer -s "powershell.exe "Install-WindowsUpdate -Confirm:`$false -IgnoreReboot""
Restart-Computer -ComputerName $computer -Force -Wait
$updatesCount = (Get-WindowsUpdate -ComputerName $computer).Count
}
But, in the new script, I'd like to have $server replaced by the name of the server for which the script is generated.
Alright - so I couldn't figure out how to manipulate my string and ended up solving it like this... Any shorter, simpler answer is absolutely welcome (note that the rest of the script changed a bit as well)
foreach ($server in $testServers) {
New-Item -ItemType File -Name "$($server)_wu.ps1" -Path "\\VILV12ICTSCRIPT\d$\windowsupdates\uscripts"
$value = '
$computer = $PSCommandPath
$computer = $computer.Split("`\")[3]
$computer = $computer.Split("_")[0]
Invoke-Command -ComputerName $computer -ScriptBlock {
Set-ExecutionPolicy Bypass
Find-Module PSWindowsUpdate | Install-Module -Force | Import-Module -Force
}
$updatesCount = (PsExec.exe \\$computer -s "powershell.exe "Get-WindowsUpdate"").Count
while ($updatesCount -gt "0") {
psexec \\$computer -s "powershell.exe "Install-WindowsUpdate -Confirm:`$false -IgnoreReboot""
Restart-Computer -ComputerName $computer -Force -Wait
$updatesCount = (PsExec.exe \\$computer -s "powershell.exe "Get-WindowsUpdate"").Count
}
Write-Host "No more updates available for $($computer)" -BackgroundColor DarkGreen
'
Set-Content -Path "\\VILV12ICTSCRIPT\d$\windowsupdates\uscripts\$($server)_wu.ps1" -Value $value
}
And both the script to generate and the generated scripts work like charms (^^,)
Cheers
The canonical way to do what you're asking is probably to use a here-string and insert particular values via the format operator (-f):
foreach ($server in $testServers) {
#'
$computer = '{0}'
Invoke-Command -ComputerName $computer -ScriptBlock {
Set-ExecutionPolicy Bypass
Find-Module PSWindowsUpdate | Install-Module -Force | Import-Module -Force
}
$updatesCount = (Get-WindowsUpdate -ComputerName $computer).Count
while ($updatesCount -gt "0") {
psexec \\$computer -s 'powershell.exe "Install-WindowsUpdate -Confirm:$false -IgnoreReboot"'
Restart-Computer -ComputerName $computer -Force -Wait
$updatesCount = (Get-WindowsUpdate -ComputerName $computer).Count
}
'# -f $server | Set-Content -Path "D:\tools\windows updates\uscripts\${server}_wu.ps1"
}
However, I'd argue that writing one parameterized script and invoking that with the respective computer name might be a more appropriate solution than creating one script per computer:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$Computer
)
Invoke-Command -ComputerName $Computer -ScriptBlock {
Set-ExecutionPolicy Bypass
Find-Module PSWindowsUpdate | Install-Module -Force | Import-Module -Force
}
$updatesCount = (Get-WindowsUpdate -ComputerName $Computer).Count
while ($updatesCount -gt "0") {
psexec \\$Computer -s 'powershell.exe "Install-WindowsUpdate -Confirm:$false -IgnoreReboot"'
Restart-Computer -ComputerName $Computer -Force -Wait
$updatesCount = (Get-WindowsUpdate -ComputerName $Computer).Count
}
Invocation:
PS> script.ps1 -Computer FOO

Output from invoke-command not returning

I have the following scriptblock:
$scriptblock = {
$regpath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$securitylayer = Get-ItemProperty -Path $regpath -Name SecurityLayer -ErrorAction SilentlyContinue
If (!($securitylayer) -or ($securitylayer.securitylayer -ne '0')) {
Write-Host -ForegroundColor Yellow "Regkey not present or value not 0. Creating/setting to 0"
#Commented out for testing purposes
#Set-ItemProperty -Path $regpath -Name SecurityLayer -Value 0
}
Else {Write-Host -ForegroundColor green "Regkey present and set to 0. Skipping."}
}
that I pass to a PSSession on a remote machine running Server 2003 SP2:
$computername = 'computer'
$pssession = New-PSSession -ComputerName $computername -Name $computername
Invoke-Command -Session $pssession -ScriptBlock {$scriptblock}
But i don't see any output.
I've also tried
write-output
but still don't see any output.
I saw another post that suggested doing the following, however nothing was returned:
$scriptblock = {
$regpath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$securitylayer = Get-ItemProperty -Path $regpath -Name SecurityLayer -ErrorAction SilentlyContinue
If (!($securitylayer) -or ($securitylayer.securitylayer -ne '0')) {
new-object pscustomobject –property #{Result = "Regkey not present or value not 0. Creating/setting to 0"}
#Commented out for testing purposes
#Set-ItemProperty -Path $regpath -Name SecurityLayer -Value 0
}
Else {new-object pscustomobject –property #{Result = "Regkey present and set to 0. Skipping."}}
}
$computername = 'computer'
$pssession = New-PSSession -ComputerName $computername -Name $computername
$results = Invoke-Command -Session $pssession -ScriptBlock {$scriptblock}
$results.result
Code runs as expected when run on machine.
You are wrapping your scriptblock in a scriptblock, so it's not actually executing the script block. Just remove the {} from around the {$scriptblock}:
Invoke-Command -Session $pssession -ScriptBlock $scriptblock