I wants to run a multiple instance of one powershell script for multiple service with different parameter. I have written below powershell script , but not sure what i am doing its not taking parameter.
[string[]] $ScriptstrArray = ‘-command Invoke-Expression ".\Smoke-Test.ps1 -Environment QA -servicename rpms"' ,
‘-command Invoke-Expression ".\Smoke-Test.ps1 -Environment QA - servicename rsms"' ,
‘-command Invoke-Expression ".\Smoke-Test.ps1 -Environment pvs - servicename spsms"'
workflow RunSmokeTests {
########################################################################
param([string[]]$sList)
ForEach -Parallel ($s in $sList) {
start-process powershell.exe -argument $s
}
}
RunSmokeTests -sList $ScriptstrArray
Can someone please me to identify the issue. Thanks in advance.
I have used the external helper Invoke-Parallel for such scenarios in the past. It has a -InputObject parameter that allows you to pass in an array with e.g. the servers/services and their corresponding details. See here or here for source and examples.
Related
I'm running PowerShell commands on a remote machine by the use of Invoke-Command -ComputerName. I'm trying to obtain the path of the temporary directory of the remote machine.
Depending on where I call [System.IO.Path]::GetTempPath() it either outputs the expected remote directory C:\Users\…\AppData\Local\Temp or my local temporary directory C:\temp.
This command is not working as expected:
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
Write-Output ([System.IO.Path]::GetTempPath())
}
# Outputs local directory 'C:\temp'
# Expected remote directory 'C:\Users\…\AppData\Local\Temp'
The problem can be reproduced with other commands than Write-Output, e. g. Join-Path.
Contrary, the following code samples all give the expected output of C:\Users\…\AppData\Local\Temp.
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
[System.IO.Path]::GetTempPath()
}
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
$tmp = [System.IO.Path]::GetTempPath(); Write-Output $tmp
}
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
Start-Sleep 1
Write-Output ([System.IO.Path]::GetTempPath())
}
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
Write-Output ([System.IO.Path]::GetTempPath())
Start-Sleep 1
}
Obviously Start-Sleep isn't a solution, but it seems to indicate some kind of timing problem.
Suspecting that the problem isn't limited to GetTempPath() I tried another user-related .NET API, which also unexpectedly outputs my local folder instead of the remote one:
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
Write-Output ([System.Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments))
}
How can I use [System.IO.Path]::GetTempPath() and other .NET API in a PowerShell remote session in a predictable way?
Santiago Squarzon has found the relevant bug report:
GitHub issue #14511
The issue equally affects Enter-PSSession.
While a decision was made to fix the problem, that fix hasn't yet been made as of PowerShell 7.3.1 - and given that the legacy PowerShell edition, Windows PowerShell (versions up to v5.1, the latest and final version) will see security-critical fixes only, the fix will likely never be implemented there.
While the linked bug report talks about the behavior originally having been by (questionable) design, the fact that it only surfaces in very narrow circumstances (see below) implies that at the very least that original design intent's implementation was faulty.
The problem seems to be specific to a script block with the following characteristics:
containing a single statement
that is a cmdlet call (possibly with additional pipeline segments)
whose arguments involve .NET method calls, which are then unexpectedly performed on the caller's side.
Workaround:
Make sure that your remotely executing script block contains more than one statement.
A simple way to add a no-op dummy statement is to use $null++:
# This makes [System.IO.Path]::GetTempPath() *locally* report
# 'C:\temp\'
# *Remotely*, the *original* value should be in effect, even when targeting the
# same machine (given that the env. var. modification is process-local).
$env:TMP = 'C:\temp'
Invoke-Command -ComputerName MyRemoteMachine -ScriptBlock {
Write-Output ([System.IO.Path]::GetTempPath()); $null++ # <- dummy statement.
}
Other workarounds are possible too, such as enclosing the cmdlet call in (...) or inserting a dummy variable assignment
(Write-Output ($unused = [System.IO.Path]::GetTempPath()))
Your Start-Sleep workaround happened to work because by definition it too added another statement; but what that statement is doesn't matter, and there's no timing component to the bug.
I have currently had to take a huge leap from my unix scripting to the MS side of things and found myself overwhelmed with PowerShell.
My situation is as follows:
I have a script script.ps1 which can be only run under specific windows account. In order to facilitate the use, it was decided that if user runs the script from a different account, it will pop up a query for credentials and restart itself from within (similarly to recursion), but importantly - maintaining the input parameters.
I have found out, that the Invoke-Command is probably what I am looking for, but I cannot seem to be able to build the PS query for this.
my code snippet looks like
if(!([System.Environment]::UserName -eq $user)){
$Credential = Get-Credential -credential INTRANET\$user
Invoke-Command -FilePath $script -Credential $Credential -ArgumentList $arguments
}
where $user contains the desired user, $script contains filepath to the script.ps1 and $arguments contain command line arguments that were passed to the script as a String, i. e. -order 66 -location UAT
but currently I get an error
Parameter set cannot be resolved using the specified named parameters.
...
FullyQualifiedErrorId : AmbiguousParameterSet
I tried shuffling the parameters around, I tried using Start-Process instead of Invoke-Command, but everything resulted in same or similar errors.
Also, because I am really new to the powershell, please do not hesitate to offer different solution, if it is viable. I do not know the capabilities of the language well.
Lastly, please note that the starting point is always powershell prompt running with non-elevated user account. Unfortunately, the option to start up powershell under a different account in the first place is not available to us.
The problem probably is that you specify the parameters stored in the variable $arguments as string in the regular format like you said: -order 66 -location UAT
The parameter -ArgumentList works differently, its an array used for array splatting. So you can't pass the values by the parameter name. You have to pass the values by parameter order, e.g.:
$Arguments = #(66,'uat')
Invoke-Command -FilePath $script -Credential $Credential -ArgumentList $Arguments
See Parameter Argumentlist.
See Array Splatting.
The value 66 is passed to the first parameter, the value uat to the 2nd... So you must know the order of the parameters and insert the related values into the array at the right position.
To control the position of the parameters, the param specification in the other script should at least have:
param (
[parameter(Position=1)]
[int]$order,
[parameter(Position=2)]
[string]$location
)
I have a job setup to run/call a script that outputs a file. The code works fine when run on its own. I have verified the job is not being blocked and completes successfully. However, no file is generated when the script is called from the job. I'm even doing something very similar elsewhere without an issue, in fact it is from another similar set up that I pulled this code to begin with. Anyway, here is what I have:
When run as job, no file output:
$McShieldCheckerJob = Start-Job -ScriptBlock {
E:\ICSScoreCardUtilityPack\ServiceAndProcessCheckerV1.0.ps1 -ServicesAndOrProcesses 'McShield' -Comps 'COMP15' `
-OutputMode 1 -OutputFile $McShieldCheckOutputFileName -GroupByMachine "N" -AsJob "Y"
} -Name McShieldCheckerJob
When run not as a job, file outputs as expected.
E:\ICSScoreCardUtilityPack\ServiceAndProcessCheckerV1.0.ps1 -ServicesAndOrProcesses 'McShield' -Comps 'COMP15' `
-OutputMode 1 -OutputFile $McShieldCheckOutputFileName -GroupByMachine "N" -AsJob "Y"
Stumped as to why this won't work exactly the same as a job vs. not as a job. Again, I have verified via Get-Job and Receive-job that the job is not being blocked and is completing successfully.
EDIT:
It seems the variable $McShieldCheckOutputFileName cannot be resolved within the scriptblock for the job. If I use a string literal [for $McShieldCheckOutputFileName] I do not have the issue. (I was able to determine after a bit more troubleshooting that the lack of an output file when running as a job was due to a null value for my output file name.) So, I can probably work around this easily enough, but still this seems curious that a variable cannot be resolved within the scriptblock? Must be a scope thing...
Figured it out... here is how it is done:
$McShieldCheckerJob = Start-Job -ScriptBlock {
E:\ICSScoreCardUtilityPack\ServiceAndProcessCheckerV1.0.ps1 -ServicesAndOrProcesses 'McShield' -Comps 'COMP15' `
-OutputMode 1 -OutputFile "$($args[0])" -GroupByMachine "N"
} -Name McShieldCheckerJob -ArgumentList $McShieldCheckOutputFileName
As we can see, we need to specify an -Argumentlist to the script block, then reference said arguments within the scriptblock via the $args variable.
Reference (see example 10)
I am building a script to automate computer build and configuration: The idea is that from WDS it comes as clean as possible, automatically runs this script which will check the serial number, query our Workday database of assets and configure the OS according to what the user assigned to that system needs.
Right now I am focusing on 3 big groups: Laptop, Desktop, and Lab. All 3 will have some SW that will be the same and some that will be specific for each. My issue is with msiexec: Initially, I hard-coded all the installations for each group. but this means that I will have to change the script each time something is updated (say a new app is rolled out as default). which is not ideal.
function Install-Desktop {
#Write-Output "Here will be the install Desktop computer script"
$IPATH="<Path To root sw folder>"
#Software List
<# SOFTWARE LIST #>
$office="$IPATH\script\o365"
$webex="$IPATH\script\webex"
$chrome="$IPATH\script\chrome"
#install Ofice:
Invoke-Expression "$office\setup.exe /configure $office\O365.xml"
$params = '/i', "$webex\webexapp.msi",'/qb!','/norestart'
Start-Process msiexec -ArgumentList "$params" -Wait -PassThru
$params = '/i', "$chrome\GoogleChromeStandaloneEnterprise64.msi",'/qb!','/norestart'
Start-Process msiexec -ArgumentList $params -Wait -PassThru
}
This piece of code works well.
Now my idea was to import from a list the software to be installed (it is easier to maintain a list than to modify the script every time). something like:
function install-software {
param (
[String]$Type
)
$IPATH=<ROOT SW Folder>
$SoftWares=Import-Csv -Path "$IPath\script\$Type`.csv" #there will be a Laptop.csv in that path
foreach ($Software in $SoftWares) {
#detect if it is msiexect or other:
# (this has to do with how the csv is built, the first parameter is '/i' if it is an msi installer)
if ($Software.param1 -eq "'/i'") {
Start-Process msiexec -ArgumentList $Software -Wait -PassThru
}
else {
$Params=[string]::Join(" ",$Software.param1,$Software.param2,$Software.param3,$Software.param4)
Invoke-Expression "$Params"
}
}
}
This only works on the else part. However on the msiexec side of the if, the MSI opens as without arguments. I tried a lot of ways to pass the args, none worked. I am not a PowerShell guru in any way, so there is probably something that I am missing to see here.
Well, it looks like you have to pass the full path, it doesn't even let you use mounted net drive: so the answer was on the csv. instead of S:\<path to installer> it had to be \<Full path to installer> and i had to get rid of all the quotes and double quotes as well.
I was wondering if it was possible to delete queues remotely via PowerShell? I have the following script:
cls
[Reflection.Assembly]::LoadWithPartialName("System.Messaging")
$computers = #("comp1","comp2","comp3");
foreach($computer in $computers) {
$messageQueues = [System.Messaging.MessageQueue]::GetPrivateQueuesByMachine($computer);
foreach ($queue in $messageQueues) {
$endpoint = [string]::Format("FormatName:DIRECT=OS:{0}\{1}", $computer, $queue.QueueName);
Write-Host $endpoint
[System.Messaging.MessageQueue]::Delete($endpoint);
}
}
This works fine, if I was running it on the machine whose queues I want to delete however when I run this remotely I get the error:
The specified format name does not support the requested operation. For example, a direct queue format name cannot be deleted.
Any ideas if this can be done?
EDIT
Oddly, I have figured I can remote onto the machine via PowerShell and execute a script block. However, I don't understand the difference between doing:
THIS:
$endpoint = [string]::Format("FormatName:DIRECT=OS:{0}\{1}", $computer, $queue.QueueName);
Invoke-Command -ComputerName $computer -ScriptBlock { [Reflection.Assembly]::LoadWithPartialName("System.Messaging"); [System.Messaging.MessageQueue]::Delete($endpoint) };
AND THIS:
Invoke-Command -ComputerName $computer -ScriptBlock { [Reflection.Assembly]::LoadWithPartialName("System.Messaging"); [System.Messaging.MessageQueue]::Delete("FormatName:DIRECT=OS:MY_SERVER\some.endpoint") };
The value of $endpoint is the same however, for some odd reason it doesn't like the variable approach though both values are identical. I tested this by setting $endpoint then calling delete. I get the error:
Exception calling "Delete" with "1" argument(s): "Invalid value for parameter path."
What I'm trying to say is if I hard code the value as part of the argument it works but assign it to a variable then invoke the method I get an error
For historic purposes if anyone else is experiencing this issue or is wondering how to delete queues remotely then please see below.
How do I delete private queues on a remote computer? It is possible to delete queues remotely. This can be achieved using the command Enable-PSRemoting -Force. Without this, you encounter the issue #JohnBreakWell indicated (see his link to MSDN).
The scope of variables when using Invoke-Command? The problem I found was the variables I declared were simply out of scope (script block was unable to see it). To rectify this, I simply did the following:
The important bit being the argument list and the use of param.
$computers = #("comp1","comp2");
foreach($computer in $computers) {
[Reflection.Assembly]::LoadWithPartialName("System.Messaging");
$messageQueues = [System.Messaging.MessageQueue]::GetPrivateQueuesByMachine($computer);
foreach ($queue in $messageQueues) {
$endpoint = [string]::Format("FormatName:DIRECT=OS:{0}\{1}", $computer, $queue.QueueName);
Enable-PSRemoting -Force
Invoke-Command -ComputerName $computer -ScriptBlock {
param ($computer, $endpoint)
[Reflection.Assembly]::LoadWithPartialName("System.Messaging");
[System.Messaging.MessageQueue]::Delete($endpoint)
}
} -ArgumentList $computer, $endpoint
}
You cannot delete a remote private queue.
You need to perform the operation locally to the queue.
From MQDeleteQueue:
Remarks
(2nd paragraph)
"Private queues registered on a remote computer ... cannot be deleted."
As Dr. Schizo mentioned, you'll need to execute
Enable-PSRemoting -Force
on the remote machine, but then, assuming you're using Server 2012 r2, it's as simple as:
Invoke-Command -ComputerName COMPUTERNAME { Get-MsmqQueue -Name QUEUENAME | Remove-MsmqQueue }