Using invoke-parallel in Powershell, I'm trying to get a list of hosts where a certain command works vs. does not. How can I write to a global variable inside of invoke-parallel?
$creds = Get-Credential -UserName $username -Message 'Password?'
$servers = get-content .\hosts.txt
$success = #()
$failure = #()
Invoke-Parallel -InputObject $servers -throttle 20 -runspaceTimeout 30 -ImportVariables -ScriptBlock {
try
{
$result = Invoke-Command $_ -Credential $creds -Authentication "Negotiate" -ErrorAction Stop {hostname}
$success += $result
}
catch
{
$failure += $_
}
}
write-host $success
write-host $failure
Try this:
$Results = Invoke-Parallel -InputObject $servers -throttle 20 -runspaceTimeout 30 -ImportVariables -ScriptBlock {
try
{
$Output = Invoke-Command $_ -Credential $creds -Authentication "Negotiate" -ErrorAction Stop {hostname}
}
catch
{
$Output = $_
}
#($Output)
}
Final code sample for what I ended up using.
$username = "myuser"
$creds = Get-Credential -UserName $username -Message 'Password?'
$servers = get-content .\hosts.txt
$Results = Invoke-Parallel -InputObject $servers -throttle 20 -runspaceTimeout 30 -ImportVariables -ScriptBlock {
$arr = #{}
try
{
$Output = Invoke-Command $_ -Credential $creds -Authentication "Negotiate" {hostname} -ErrorAction Stop
$arr[$_] = "successful"
}
catch
{
$arr[$_] = "failed"
}
$arr
}
$Results
Related
I'm trying to use Invoke-Command inside an advanced function to return an object with the workspaces.
This works:
$g = Invoke-Command -ComputerName server1,server2,server3 -ScriptBlock {
$AgentCfg = New-Object -ComObject AgentConfigManager.MgmtSvcCfg
$AgentCfg.GetCloudWorkspaces()
}
$g| Measure-Object
This does not, Measure-Object says there are 0 objects.
function Get-WorkspaceInfo {
[CmdletBinding()]
param(
[string[]]$ComputerName,
[string]$ErrorLog
)
BEGIN {}
PROCESS {
foreach ($Computer in $ComputerName) {
write-verbose "$Computer Iteration"
$RemoteWorkspaces = Invoke-Command -ComputerName $Computer -ScriptBlock {
$AgentCfg = New-Object -ComObject AgentConfigManager.MgmtSvcCfg
$OMSWorkspaces = $AgentCfg.GetCloudWorkspaces() }
$RemoteWorkspaces | Measure-Object
}
}
END {}
}
Get-Workspaceinfo -ErrorLog x.txt -ComputerName server1,server2,server3 -Verbose
I'm tying to work out how I pass a variable from an if statement into a nested if statement starting at:
If ($server -eq $env:COMPUTERNAME)
Foreach ($comp in $computer) {
$server = split-FQDN $comp -part H
$domain = Split-FQDN $comp -part D
#Set the domain based parameters
If ($domain -eq '1.fqdn.com') {
set-domparams $domain $userprompt $userpass
}
Elseif ($domain -eq '2.fqdn.com') {
set-domparams $domain $userprompt $userpass
}
Elseif ($domain -eq '3.fqdn.com') {
set-domparams $domain $userprompt $userpass
}
ElseIf ($domain -eq '4.fqdn.com') {
set-domparams $domain $userprompt $userpass
}
ElseIf ($domain -eq '5.fqdn.com') {
set-domparams $domain $userprompt $userpass
}
ElseIf ($domain -eq '6.fqdn.com') {
set-domparams $domain $userprompt $userpass
}
If ($server -eq $env:COMPUTERNAME) {
$server = $_
#clear Kerberos ticket cache
Invoke-Expression -Command:'cmd.exe /c klist purge' | Out-Null
$buildlogsuccess = check-buildlog $domain
$chocologstatus = Invoke-Command -ScriptBlock {check-choco}
$KMSvalues = Invoke-Command -ScriptBlock {get-winlicense}
$parentOU = get-ParentOU (Get-ADComputer -Server $dc -SearchBase $searchbase -Filter {name -eq $server} -Credential $fetchCreds) | select -expand parentou
$SCCMcheck = Invoke-Command -ScriptBlock {get-sccmstatus}
$scomcheck = Invoke-Command -ScriptBlock {get-scomstatus} -argumentlist $scom
$AV = Invoke-Command -ScriptBlock {get-avstatus}
$wfirewall = Invoke-Command -ScriptBlock {(get-service MpsSvc).status}
$net35 = Invoke-Command -ScriptBlock {(Get-WindowsFeature NET-Framework-Core).installed}
$admins = Invoke-Command -ScriptBlock {check-admins}
$DomainComms = Invoke-Command -ScriptBlock {get-domaininfo}
$bigfix = Invoke-Command -ScriptBlock {get-bigfix}
}
Else {
$server = $_
#clear Kerberos ticket cache
Invoke-Expression -Command:'cmd.exe /c klist purge' | Out-Null
#start running functions against target server(s) to get required info
$PSSession = New-PSSession -ComputerName $comp -Credential $fetchCreds -Name $server
$buildlogsuccess = check-buildlog $domain
$chocologstatus = Invoke-Command -Session $pssession -ScriptBlock ${function:check-choco}
$KMSvalues = Invoke-Command -Session $PSSession -ScriptBlock ${Function:get-winlicense}
$parentOU = get-ParentOU (Get-ADComputer -Server $dc -SearchBase $searchbase -Filter {name -eq $server} -Credential $fetchCreds) | select -expand parentou
$SCCMcheck = Invoke-Command -Session $PSSession -ScriptBlock ${Function:get-sccmstatus}
$scomcheck = Invoke-Command -Session $PSSession -ScriptBlock ${function:get-scomstatus} -argumentlist $scom
$AV = Invoke-Command -Session $PSSession -ScriptBlock ${Function:get-avstatus}
$wfirewall = Invoke-Command -Session $PSSession -ScriptBlock {(get-service MpsSvc).status}
$net35 = Invoke-Command -Session $PSSession -ScriptBlock {(Get-WindowsFeature NET-Framework-Core).installed}
$admins = Invoke-Command -Session $PSSession -ScriptBlock ${Function:check-admins}
$DomainComms = Invoke-Command -Session $PSSession -ScriptBlock ${Function:get-domaininfo}
$bigfix = Invoke-Command -Session $PSSession -ScriptBlock ${Function:get-bigfix}
#clean up the remote session
Remove-PSSession -Name $server
}
I already have some global variables in use that are passed by a loaded function, but I'm trying to avoid do the same for this bit of code and understand how to pass my remaining variables ($comp, $computer and $domain) correctly to the next inner loop.
I've tried getting $server to work doing the following but haven't had any success as of yet:
$server = $_
This is more code review opinion instead of an answer, but anyway.
Instead of long if...elseif constructs, why not use table driven programming? If there are lots of domains and they require different credentials, maintaining the if...elseif is tedious. Store the credentials in custom Powershell objects and those in a hashtable. Then lookup can be done with the domain name only. Like so,
# Declare a hashtable and add custom credential objects
$creds=#{}
$creds.Add('foo.fqdn', $(New-Object –TypeName PSObject –Prop #{'Domain'='foo.fqdn'; 'User'='foo.user'; 'Pass'='foo.pass'}))
$creds.Add('bar.fqdn', $(New-Object –TypeName PSObject –Prop #{'Domain'='bar.fqdn'; 'User'='bar.user'; 'Pass'='bar.pass'}))
$creds.Add('zof.fqdn', $(New-Object –TypeName PSObject –Prop #{'Domain'='zof.fqdn'; 'User'='zof.user'; 'Pass'='zof.pass'}))
# Later when credentials are needed, check if the domain is present
# and get the values from the hashtable.
if($creds.ContainsKey($domain) {
set-domparams $domain $creds[$domain].User $creds[$domain].Pass
} else {
write-warning "Domain $domain not found!"
}
I think this is a non-question as your variables are valid within the loops/statements they are created.
Foreach ($comp in $computer) {
$server = split-FQDN $comp -part H
$domain = Split-FQDN $comp -part D
if($true){
# $comp, $server and $domain are all in scope
Write-host "$comp $server $domain"
if($true){
# also true in nested if statements
Write-host "$comp $server $domain"
}
}
Else{
# $comp, $server and $domain are all in scope
Write-host "$comp $server $domain"
}
}
# outside, $comp is not available, only $computer
# $server and $domain will only contain the values from the last run of the foreach loop.
I am getting below error while installing the packages in Sitecore through Powershell.
We had a similar question reported on the community https://community.sitecore.net/developers/f/7/t/1173
The issue is that the web service we built encounters a timeout with the long running process of the installation wizard. We provide a solution in which you can create a background ScriptSession job that you can later retrieve.
In our gitbook we provide this example.
Import-Module -Name SPE
$session = New-ScriptSession -Username admin -Password b -ConnectionUri http://remotesitecore
$job = Invoke-RemoteScript -Session $session -ScriptBlock {
Start-ScriptSession -ScriptBlock {
# Replace the contents of this scriptblock with your installation steps.
# I just put $true there so something would come back.
Start-Sleep -Seconds 10
[PSCustomObject]#{"IsComplete"=$true}
}
}
$keepRunning = $true
while($keepRunning) {
$done = Invoke-RemoteScript -Session $session -ScriptBlock {
$scriptSession = Get-ScriptSession -Id $params.JobId
$scriptSession.State -ne "Busy"
} -Arguments #{"JobId" = $job.ID}
if($done) {
$keepRunning = $false
Invoke-RemoteScript -Session $session -ScriptBlock {
$scriptSession = Get-ScriptSession -Id $params.JobId
if($scriptSession.State -ne "Busy") {
$scriptSession | Receive-ScriptSession
}
} -Arguments #{"JobId" = $job.ID}
} else {
Start-Sleep -Milliseconds 500
}
}
Expect to see updates to the book for simplified a usage with the release of SPE 4.
This is sample code
Import-Module -Name "C:\Scripts_31DEC\SPE Remoting-3.3\SPE"
$session = New-ScriptSession -Username admin -Password b -ConnectionUri http://quuintranet
$job = Invoke-RemoteScript -Session $session -ScriptBlock {
Start-ScriptSession -ScriptBlock {
# Replace the contents of this scriptblock with your installation steps.
# I just put $true there so something would come back.
Install-Package -Path "C:\Scripts_31DEC\quutest_ow.zip" -InstallMode Overwrite
[PSCustomObject]#{"IsComplete"=$true}
}
}
$keepRunning = $true
while($keepRunning) {
$done = Invoke-RemoteScript -Session $session -ScriptBlock {
$scriptSession = Get-ScriptSession -Id $params.JobId
$scriptSession.State -ne "Busy"
} -Arguments #{"JobId" = $job.ID}
if($done) {
$keepRunning = $false
Invoke-RemoteScript -Session $session -ScriptBlock {
$scriptSession = Get-ScriptSession -Id $params.JobId
if($scriptSession.State -ne "Busy") {
$scriptSession | Receive-ScriptSession
}
} -Arguments #{"JobId" = $job.ID}
} else {
Start-Sleep -Milliseconds 500
}
}
I am supplying 3 servers to loop however the $mdtable.table.count is only 1. I must be missing a simple thing here. Can anyone please help me resolve this?
Get-Content 'C:\test\computers.txt' | ? { $_.Trim() -ne "" } | ForEach-Object {
$value = Invoke-Command -Computer $_ -ScriptBlock {
Param($computer)
# Connect to SQL and query data, extract data to SQL Adapter
$SqlQuery = "xp_fixeddrives"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Data Source=$computer;Initial Catalog='Secaudit';Integrated Security = True";
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($SqlQuery, $Sqlconnection)
$mdtable = New-Object System.Data.Dataset
$nRecs = $SqlAdapter.Fill($mdtable) | Out-Null
$nRecs | Out-Null
$res = $mdtable.Tables.Count
$res
} -ArgumentList $_ -Credential $cred
}
$value
The thing you're missing is that
... | ForEach-object {
$value = Invoke-Command -Computer $_ -ScriptBlock {
...
} -ArgumentList $_ -Credential $cred
}
replaces the value of $value with each iteration when you actually want to accumulate the values.
You can achieve this for instance like this:
... | ForEach-object {
$value += Invoke-Command -Computer $_ -ScriptBlock {
...
} -ArgumentList $_ -Credential $cred
}
or like this:
$value = ... | ForEach-object {
Invoke-Command -Computer $_ -ScriptBlock {
...
} -ArgumentList $_ -Credential $cred
} | Measure-Object -Sum | Select-Object -Expand Sum
I am writing a PowerShell script for multithreading NUnit tests. My problem is that I take test categories from the file category.txt, and I have to write which categories have been done into my output file file 1.txt. I also need to output an XML report after all tests have been performed. How can I do this in NUnit?
$Groups=Get-Content d:\test\category.txt | Select-Object -Last 10
Write-Host $Groups
if ($Groups -ne $null)
{Write-Host "true"}
else
{write-host "false"}
###Multithreading###
$ThreadNumber =$Groups.Count
$ScriptBlock = {
function Nunit {
$Connection = #{"server" = ""; "username" = ""; "password" = ""}
write-verbose $Connection
$serv = $connection.Get_Item("server")
$user = $connection.Get_Item("username")
$pass = $connection.Get_Item("password")
$securePassword = ConvertTo-SecureString -AsPlainText $pass -Force
#Create connection credentials object for Invoke-Command
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user, $securePassword
$Output = "C:\"
$scriptBlock = {
CMD.EXE /C "C:\testNunit\bin\nunit-console.exe /xml:c:\console-test.xml C:\testNunit\dll\Tests.dll /include:TestTypeSmoke> c:\1.txt"
}
Invoke-Command -ComputerName $serv -ScriptBlock $scriptBlock -credential $cred
}
Nunit
}
I will try to get back to you on NUnit after trying some things at my end, but as a suggestion you could also try PowerShell Workflow for Multi-Threading. They work like functions.
Workflow NUnit
{
Param
(
$server,
$username,
$password,
)
foreach -parallel -throttlelimit 2($group in $groups)
try{
//NUnit Code here
}
}
Catch
{
$_.Exception.Message
}
}
}
NUnit-server "" -username "" -password ""