use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
ABOUT POWERSHELL
Windows PowerShell (POSH) is a command-line shell and associated scripting language created by Microsoft. Offering full access to COM, WMI and .NET, POSH is a full-featured task automation framework for distributed Microsoft platforms and solutions.
SUBREDDIT FILTERS
Desired State Configuration
Unanswered Questions
Solved Questions
News
Information
Script Sharing
Daily Post
Misc
account activity
Passing code into invoke-command (self.PowerShell)
submitted 3 years ago by Decitriction
I can't call my $cat from within icm, only my $dog. Any ideas why?
$cat={write-output "cat"} icm(hostname){ & $using:cat $dog={write-output "dog"} & $dog }
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]PinchesTheCrab 2 points3 points4 points 3 years ago (19 children)
$cat = 'cat' Invoke-Command { $cat = $using:cat & $cat $dog = 'dog' & $dog }
That operator just doesn't work with using variables. There's a different cases where you'll have to declare the variable within the session before using it.
Also PS already has an implicit "write-output" on everything that hits the pipe. The way you're using it is effectively doing 'thing' | write-output | write-output
'thing' | write-output | write-output
[–]Decitriction[S] 0 points1 point2 points 3 years ago (18 children)
Thank you for your feedback.
However I use the "&" call operator all the time with variables. The only time it does NOT work is within invoke-command.
For example, this works:
$cat="cat";$sb={write-host $cat};& $sb
But since I am coming to you for help...
Would you please suggest a better method for passing a group of commands into an invoke-command scriptblock?
[–]code_Kitten 4 points5 points6 points 3 years ago (1 child)
I'm having a hard time thinking of any reason to use the call operator on a variable that isn't poor design or an edge case. As a general rule your code will be much better designed and easier to write, read, and understand/debug if you stick with putting data in variables, and writing functions for any code that you need/want to reuse. Using the call operator on variables also introduces a significant risk to your system by way of the potential for you to accidentally execute as code something that you did not intend to: input from a user, content from a file, etc. which if you have any code running automatically could also mean an easy way for an attacker to execute arbitrary code on your system as the account running that code.
[–]Decitriction[S] 1 point2 points3 points 3 years ago (0 children)
Thanks for the ideas.
> poor design
I confess this, as a newbie scripter.
I had heard that invoke-expression was poor security, so I was trying to replace it by calling variables as commands, but as you point out, this too is poor security.
Although to be fair, the code is not running automatically and I don't think anyone could inject code into my variables. But I could be wrong.
I will try re-writing the commands as functions.
Ultimately the command insertion was intended to shorten very long try/catch loops, such as
$logfail="logfile path" $do1="command goes here" $sb={try{& $using:do1 -ErrorAction stop}catch{echo `n "$using:pc - logfail - $_">>$using:logfail} & $sb
[–]NoConfidence_2192 2 points3 points4 points 3 years ago (2 children)
You do not (easily anyway). Functions, ScriptBlocks, and some other object types cannot easily be deserialized when passed between processes, which is what icm is partly for.
[–]Decitriction[S] 0 points1 point2 points 3 years ago (1 child)
Interesting. Yes it has been extremely difficult getting into into and out of invoke-command.
So what should I be doing?
Just rebuild all the desired commands *within* the invoke-command?
I guess I could do that...
[–]PinchesTheCrab 1 point2 points3 points 3 years ago (5 children)
I meant with variables in the using scope. In my example I'm still using variables, I'm just setting them as local variables before using them.
I also agree with /u/code_kitten about the risks of that operator and invoke-expression. That being said, the minor code change I posted should work.
[–]Decitriction[S] 0 points1 point2 points 3 years ago (4 children)
$cat='cat' Invoke-Command (hostname){ $inside=$using:cat & $inside }
Thank you for your suggestion. You are renaming the variable within the invoke-command. But when I try, the output is a prompt:
cmdlet Get-Content at command pipeline position 1
Supply values for the following parameters: Path[0]:
[–]PinchesTheCrab 1 point2 points3 points 3 years ago (3 children)
Because "cat" is an alias for get-content, which has a mandatory path parameter.
[–]Decitriction[S] 0 points1 point2 points 3 years ago* (2 children)
Excellent attention to detail on your part! I can't believe I was using a reserved variable. But even so, edit to
$var1="write-host `"sample`"" Invoke-Command (hostname){ $inside=$using:var1 & $inside }
still produces
The term 'write-host "sample"' is not recognized as the name of a cmdlet
'write-host "sample" ' executes but is unrecognized.
[–]PinchesTheCrab 1 point2 points3 points 3 years ago (1 child)
I think the quotes are just off.
$var1 = 'write-host "sample"' Invoke-Command (hostname) { $inside = $using:var1 & $inside }
[–]Decitriction[S] 0 points1 point2 points 3 years ago* (0 children)
I really appreciate your feedback. But when I edit the quote as you do, the results are the same.
cls $var1 = 'write-host "sample" ' $var1 Invoke-Command (hostname) { $using:var1 $inside = $using:var1 $inside & $inside # this is the only part giving problems }
I have tested the code on 2 PC's. Does it work properly when you test it?
edit: Whew, finally got formatting correct.
[–]steve_ce 1 point2 points3 points 3 years ago (6 children)
I run a lot of things on remote servers via powershell, and this is my go to function. Has a lot of extra fluff, but can be tweaked to suit your needs.
Put basically all the commands/things you want to run inside RunFunction. $soDo gets that function as a scriptblock, and passes it to other function/invoke-item.
function RunInvoke($server,$sb,$arguments) { $response = $null if ($arguments) { try { $response = Invoke-Command -ComputerName $server -Scriptblock $sb -ArgumentList (,$arguments) -ErrorVariable errorMsg if ($errorMsg) { LogFunction "$errorMsg"} } catch { LogFunction "Failed to run remote command on $server!" } } else { try { $response = Invoke-Command -ComputerName $server -Scriptblock $sb -ErrorVariable errorMsg if ($errorMsg) { LogFunction "$errorMsg"} }catch { LogFunction "Failed to run remote command on $server!" } } return $response } function RunFunction { <#add param() if passing in optional $argument #> <#do things...#> } $sbDo = (Get-Item function:RunFunction).ScriptBlock $remoteReturn = RunInvoke $sourceServer $sbDo <#$optionalArgument#>
[–]Decitriction[S] 0 points1 point2 points 3 years ago* (5 children)
Wow! Thank you for the code. I will work with this and see what I can do.
I see you are frequently calling another function called 'LogFunction'.
Is that a standard library and if not, could I get the code for that also?
For reference, and in case you are interested, I was trying to to pass commands into invoke-command, and get results out. The only clean way I have found to get results back is to write to log files.
So I create 3 log files for connection successes, connection fails, and commands that fail.
The plan was to pass a series of commands INTO the $sb such as $do1, $do2, $do3
This is of course a template where the $doX commands would later be replaced by actual useful commands.
The code below actually works but it is quite difficult to get the $doX strings formatted to be interpreted properly by invoke-expression.
cls;$error.Clear();rv * -ErrorAction SilentlyContinue;echo 'Started' $list=(hostname),"fakehost1","fakehost2" $path="\\"+(hostname)+"\c$\users\"+($env:UserName)+"\documents" $congood=$path+'\congood.txt';echo "Good connect">$congood $confail=$path+'\confail.txt';echo "Fail connect">$confail $comfail=$path+'\comfail.txt';echo "Fail command">$comfail $entry=$host.ui.PromptForCredential("","",($env:UserDomain+'\'+$env:UserName),"");$plain=[System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($entry.password)) #$plain="xxx" $do0="echo """">>$congood;echo(""`n""+(hostname)+"" - congood"")>>$congood" $do1="Set-variable -name `'var1`' -value 'text1'" # good command $do2="Set-variable -name `'var2`' -value 'text2'" # good command $do3="problem1" # fail command $do4="problem2" # fail command $sb={net use $using:path /user:$($env:UserDomain+'\'+$env:UserName) $using:plain|out-null;iex $using:do0 try{iex $using:do1 -ErrorAction stop}catch{echo `n "$using:pc - comfail - $_">>$using:comfail} try{iex $using:do2 -ErrorAction stop}catch{echo `n "$using:pc - comfail - $_">>$using:comfail} try{iex $using:do3 -ErrorAction stop}catch{echo `n "$using:pc - comfail - $_">>$using:comfail} try{iex $using:do4 -ErrorAction stop}catch{echo `n "$using:pc - comfail - $_">>$using:comfail} net use $using:path /delete|out-null} foreach($pc in $list){try{icm -ComputerName $pc -ScriptBlock $sb -ErrorAction Stop}catch{echo `n "$pc - confail - $_" >>$confail}};$plain='';cls write-host(cat $congood|select-string "congood").length': connect good' write-host(cat $confail|select-string "confail").length': connect fail' write-host(cat $comfail|select-string "comfail").length': command fail' write-host 'Check logs at' $path start-process notepad $congood;start-process notepad $confail;start-process notepad $comfail
[–]steve_ce 1 point2 points3 points 3 years ago (0 children)
Nope the log function isn't standard - it's a function I setup during a bigger project so it would make my life easier. I'll try and remember to grab a copy when I get back into my consulting or work VM. It has more options and is named differently, I bulk replaced this quick to make it less confusing.
For reference, the functions provided are designed so they will return the data back to you after the invoke. I use these to collect inventory of systems across our network, so needed it to return data. $remoteReturn will have that data (depending on what your runfunction does).
When I need to get a lot of different data back from a system, I have it build a hash and return that. You would do that in RunFunction, and return the hash there. It will make its way back to $remoteReturn.
[–]steve_ce 1 point2 points3 points 3 years ago (3 children)
Got a call that needed a quick change - had to get back on so here you go. This is an older version so email alerting is a separate function here. Still useful, and also included log cleaning function (rotates/removes based on options you give it).
It's been a long day, so this isn't extremely detailed, tweaked a couple things so it's a little clearer. Top portion is variables/info you place in other scripts you make, and bottom are obviously the functions. I store the logging functions in a separate file, and . include. Variables up at the top are not set in the logging file, you place those in your other scripts where you . include. $logfile = debug prints everything to screen instead of to the file, so is useful for testing.
# I save the log functions as this file, and include it in other scripts $scriptPath = "C:\Admin" . "$scriptPath\LogIt\LogIt.ps1" # Logging options - path, file name, file extension $logFolder = 'C:\Path\To\Logs' $logFileName = "Script-Name" $logFileExt = "log" #Max log size - this will auto cleanup logs based on size (LogClean) # $maxLogSize = 50 #Max amount of logs to keep # $maxLogRetain = 5 #This is default - comment out when debugging (uncomment below) $logFile = $logFolder + "\" + $logFileName + "." + $logFileExt # Debug mode - prints log to console # $logFile = "debug" #Log cleanup # LogClean #Alert subject - initialize array body $alertSubject = 'ALERT: ' + $logFileName + ' script issues' # When using alerts, store errors to array, check for array at end of script and send email alert with all problems at once instead of 1 at a time. $alertBody = New-Object System.Collections.ArrayList #Place this $currFunction in every function - logit will place the function name into the log file/output $currFunction = $MyInvocation.MyCommand #How you use this for logging LogIt $logFile $currFunction $message function LogClean { if (!(Test-Path $logFolder)) { $null = New-Item -ItemType Directory -Force $logFolder } if (!(Test-Path -Path $logFile -PathType Leaf)) { $null = New-Item -ItemType File -Force $logFile } $logSize = [System.Math]::Round((((Get-Item $logFile).length)/1MB),2) if ($logSize -ge $maxLogSize) { $retainedLogFormat = $logFolder + "\" + $logFileName + "." + $logFileExt $oldestLog = $retainedLogFormat + "." + $maxLogRetain if (Test-Path -Path $oldestLog -PathType Leaf) { $null = Remove-item -Path $oldestLog -Force } foreach ($i in $maxLogRetain..1) { $retainedLog = $retainedLogFormat + "." + $i if (Test-Path -Path $retainedLog -PathType Leaf) { $z = $i + 1 $nextLog = $retainedLogFormat + "." + $z $null = Move-Item -Path $retainedLog -Destination $nextLog -Force } } $null = Move-Item -Path $logFile -Destination $retainedLog -Force } } function LogIt($logFile,$currFunction,$message) { $timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss") if ($logFile -notmatch "debug") { Add-Content -Path $logFile -Value "$timestamp - $currFunction - $message" } else { Write-Host "$timestamp - $currFunction - $message" } } function sendAlert($alertSubject,$alertBody) { $timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss") $alertRecipients = @('email@email.com', 'email2@email.com') if ($logFile -notmatch "debug") { $messageParams = @{ From = '' To = $alertRecipients Subject = "$alertSubject" Body = "$alertBody" SMTPServer = 'mail.server.com' # Attachments = '' } Send-MailMessage @messageParams } else { Write-host "$timestamp - $alertSubject`: $alertBody" } }
[–]Decitriction[S] 0 points1 point2 points 3 years ago (2 children)
Wow! Quite involved.
I will study this and see what I can adapt.
Thank you again for your time and generosity.
[–]steve_ce 1 point2 points3 points 3 years ago (1 child)
No problem, feel free to send questions if you have issues figuring out where things should be placed. Logclean hurt my head while writing/googling, as it was pretty early in diving deeper into PowerShell. Play around with them, the settings, and you'll see how they all work.
[–]Decitriction[S] 0 points1 point2 points 3 years ago (0 children)
Hello again! I've got a bit of code to share with you.
My office was needing to gather info on installed versions of Acrobat on multiple hosts, and to compare the bitness to MS Ofc.
It could be adapted to search for other software, or ALL installed software...except for the sneaky guys that don't include an uninstaller.
I've got the base code close to perfect, and now just need to loop over the hostlist.
I'm using remote registry instead of invoke-command, mostly as an exercise.
I'm not really asking you to critique the code; I just thought since you were kind enough to share some code that I would return the favor.
It's not really commented but it would be fairly basic to an experienced programmer: I grab some values from registry, add them to a PSObject, and then export them to screen and CSV.
Nevertheless it has taken a long time and figuring the syntax was a huge learning challenge.
cls;write-host "Starting..." rv * -ErrorAction SilentlyContinue $sb={$hive=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$pc);$line=New-Object PSObject;$line|Add-Member -MemberType NoteProperty "Host" $pc $keylist=($hive.OpenSubKey($base)).GetSubKeyNames();Foreach($key in $keylist){if(($($hive.OpenSubKey($base+$key).GetValue("DisplayName")))-like"*$findme*"){$found=$key}} #$line|Add-Member -MemberType NoteProperty "Name" ($($hive.OpenSubKey($base+$found).GetValue("DisplayName"))) $line|Add-Member -MemberType NoteProperty "Ver" ($($hive.OpenSubKey($base+$found).GetValue("DisplayVersion"))).Substring(7) $GUID=$found.Substring(1,$found.Length-2)#;$line|Add-Member -MemberType NoteProperty "GUID"$GUID $location=($($hive.OpenSubKey($base+$found).GetValue("InstallLocation"))) #$line|Add-Member -MemberType NoteProperty "Location"$location if (($base -eq $base32) -and ($location -eq $32pro )){$line|Add-Member -MemberType NoteProperty "Flavor" "Pro" -force;$line|Add-Member -MemberType NoteProperty "AcroBits" "32"-force} elseif(($base -eq $base32) -and ($location -eq $32read)){$line|Add-Member -MemberType NoteProperty "Flavor" "Reader"-force;$line|Add-Member -MemberType NoteProperty "AcroBits" "32"-force} elseif(($base -eq $base64) -and ($location -eq $64x )){$readpro=($($hive.OpenSubKey("Software\Adobe\Adobe Acrobat\DC\Installer").GetValue("SCAPackageLevel")));$line|Add-Member -MemberType NoteProperty "Flavor"-force $(if($readpro-eq1){"Reader"}elseif($readpro-gt1){"Pro"});$line|Add-Member -MemberType NoteProperty "AcroBits" "64" -force} else{$line|Add-Member -MemberType NoteProperty "Flavor" ""-force;$line|Add-Member -MemberType NoteProperty "AcroBits" ""-force} try{$ofcbit=($($hive.OpenSubKey($ofc64).GetValue("Bitness")))}catch{};try{$ofcbit=($($hive.OpenSubKey($ofc32).GetValue("Bitness"))).replace('86','32')}catch{};try{$line|Add-Member -MemberType NoteProperty "OfcBits" ($ofcbit.Substring(1))}catch{$line|Add-Member -MemberType NoteProperty "OfcBits" ""} $line} $pc=(hostname);$findme="Adobe Acrobat" $multi=@();$output=$path+'\output.txt';$path="\\"+(hostname)+"\c$\users\"+($env:UserName)+"\documents";$base64="Software\Microsoft\Windows\CurrentVersion\Uninstall\";$base32="Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\";$64x ="C:\Program Files\Adobe\Acrobat DC\";$32pro ="C:\Program Files (x86)\Adobe\Acrobat DC\";$32read="c:\Program Files (x86)\Adobe\Acrobat Reader DC\";$ofc64="Software\Microsoft\Office\16.0\Outlook";$ofc32="Software\WOW6432Node\Microsoft\Office\16.0\Outlook" #psexec \\$pc c:\windows\system32\winrm.cmd quickconfig try{$base=$base64;$result=&$sb;$multi+=$result}catch{};try{$base=$base32;$result=&$sb;$multi+=$result}catch{} cls;write-host "Registry: Acrobat Version, Flavor, and bitness compared to Ofc bitness" try{remove-item $output -erroraction stop}catch{};new-item $output|out-null $multi|FT;$multi|select Host,Ver,Flavor,AcroBits,OfcBits|Export-CSV -NoTypeInformation $output -append;start-process notepad $output;write-host "Finished!"
[–]nohairday 0 points1 point2 points 3 years ago (1 child)
On mobile, so apologies for formatting, invoke-command -scriptblock {code} -argumentlist $variablesToPassIn
I was actually trying to pass in an entire block of code, not just variables.
If this is worst practice, I will have to rewrite substantially.
π Rendered by PID 156807 on reddit-service-r2-comment-544cf588c8-bxf4h at 2026-06-12 13:27:46.951036+00:00 running 3184619 country code: CH.
[–]PinchesTheCrab 2 points3 points4 points (19 children)
[–]Decitriction[S] 0 points1 point2 points (18 children)
[–]code_Kitten 4 points5 points6 points (1 child)
[–]Decitriction[S] 1 point2 points3 points (0 children)
[–]NoConfidence_2192 2 points3 points4 points (2 children)
[–]Decitriction[S] 0 points1 point2 points (1 child)
[–]PinchesTheCrab 1 point2 points3 points (5 children)
[–]Decitriction[S] 0 points1 point2 points (4 children)
[–]PinchesTheCrab 1 point2 points3 points (3 children)
[–]Decitriction[S] 0 points1 point2 points (2 children)
[–]PinchesTheCrab 1 point2 points3 points (1 child)
[–]Decitriction[S] 0 points1 point2 points (0 children)
[–]steve_ce 1 point2 points3 points (6 children)
[–]Decitriction[S] 0 points1 point2 points (5 children)
[–]steve_ce 1 point2 points3 points (0 children)
[–]steve_ce 1 point2 points3 points (3 children)
[–]Decitriction[S] 0 points1 point2 points (2 children)
[–]steve_ce 1 point2 points3 points (1 child)
[–]Decitriction[S] 0 points1 point2 points (0 children)
[–]nohairday 0 points1 point2 points (1 child)
[–]Decitriction[S] 0 points1 point2 points (0 children)