Powershell background threads and return values - multithreading

I'm having trouble getting background threads to work in Powershell. I'd like to use runspaces where I can plug a script into it and get updates when there is one and allow my main thread to do with that data what it needs to. I'm not sure why doesn't this code return the proper values? All of the servers I tested against return true when not used in a runspace, but the return value here is 0.
I'm not interested in using powershell jobs.
$servers = #("x1","x2","x3");
$results = New-Object Collections.Generic.List[String];
$input = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$output = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$results.Clear();
foreach ($server in $servers)
{
Write-Host $server;
$powerShell = [Management.Automation.PowerShell]::Create();
[Void]$PowerShell.AddScript({
$result = Test-Connection $server -Count 1 -Quiet;
return ($server - $result)
})
$handle = $powerShell.BeginInvoke($input,$output);
$results.Add($output);
}
$results;
or
$results = New-Object Collections.Generic.List[String];
$input = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$output = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$results.Clear();
foreach ($server in $servers)
{
Write-Host $server;
$powerShell = [Management.Automation.PowerShell]::Create();
[Void]$PowerShell.AddScript({
param ($server)
$result = Test-Connection $server -Count 1 -Quiet;
return ($server - $result)
})
$handle = $powerShell.BeginInvoke($server,$output);
#$handle = $powerShell.BeginInvoke($input,$output);
$results.Add($output);
}
$results;

Have fun.
$servers = #("x1","x2","x3");
$input = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$output = New-Object 'System.Management.Automation.PSDataCollection[psobject]'
$GH = [hashtable]::Synchronized(#{})
[System.Collections.Generic.List[PSObject]]$GH.results = #()
[System.Collections.Generic.List[PSObject]]$jobs = #()
$initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, ([int]$env:NUMBER_OF_PROCESSORS + 1), $initialSessionState, $host )
$runspacePool.ApartmentState = 'MTA'
$runspacePool.ThreadOptions = "ReuseThread"
[void]$runspacePool.Open()
$jobCounter = 1
foreach ($server in $servers)
{
Write-Host $server;
$job = [System.Management.Automation.PowerShell]::Create($initialSessionState)
$job.RunspacePool = $runspacePool
$scriptBlock = { param ( [hashtable]$GH, [string]$server ); $result = Test-Connection $server -Count 1 -Quiet; $GH.results.Add( [PSObject]#{ 'Server' = $server; 'Result' = $result } ) }
[void]$job.AddScript( $scriptBlock ).AddArgument( $GH ).AddArgument( $server )
$jobs += New-Object PSObject -Property #{
RunNum = $jobCounter++
JobObj = $job
Result = $job.BeginInvoke() }
do {
Sleep -Seconds 1
} while( $runspacePool.GetAvailableRunspaces() -lt 1 )
}
Do {
Sleep -Seconds 1
} While( $jobs.Result.IsCompleted -contains $false)
$GH.results;
$runspaces = Get-Runspace | Where { $_.Id -gt 1 }
foreach( $runspace in $runspaces ) {
try{
[void]$runspace.Close()
[void]$runspace.Dispose()
}
catch {
}
}
[void]$runspacePool.Close()
[void]$runspacePool.Dispose()

Related

System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter 'DisplayName' because it is an empty string

I am working on a PowerShell script that needs to access Azure subscriptions of a particular tenant. The goal is for the script to retrieve info about the managed entities. The current errors I am facing are due to getting $subscriptions and using DisplayName as a parameter as seen in the screenshot below. What is the best way to get subscriptions info from a Tenant for managed identities?
param (
[bool]$SendAllMessage = $true,
[switch]$LocalDebug
)
#region Variables
$azfNo = ""
#endregion
#region Connection to Azure
$WarningPreference = 'SilentlyContinue'
$SecurityIssuesData = #()
if ($LocalDebug) {
$RunCommand = $LocalHelperLocation + "helper-Connect.ps1"
$subscriptions = & $RunCommand -LocalDebug $LocalDebug
$RunCommand = $LocalHelperLocation + "helper-waiver.ps1"
$Waivers = & $RunCommand -azfNo $azfNo -LocalDebug $LocalDebug
}
else {
$subscriptions = .\helper-Connect.ps1 -LocalDebug $LocalDebug
$Waivers = .\helper-waiver.ps1 -azfNo $azfNo -LocalDebug $LocalDebug
}
#endregion
$TenantIDs = $subscriptions | select TenantID | sort-object -Property TenantID -Unique
$SubscriptionGuid = $subscriptions.Id
foreach ($TenantID in $TenantIDs) {
$TenantName = $subscriptions | select TenantName | where { $_.TenantID -eq $TenantID }
# https://learn.microsoft.com/en-us/powershell/module/az.resources/get-azadserviceprincipal?view=azps-8.2.0
$Resources = Get-AzADServicePrincipal -DisplayName $TenantName
$ResourceType = "Managed Identities"
ForEach ($Resource in $Resources) {
$ResourceID = $SubscriptionGuid + "|" + $Resource.ResourceGroup + "|" + $Resource.DisplayName
$ResourceName = $Resource.DisplayName
# https://github.com/Azure/azure-powershell/blob/main/src/Resources/Resources/help/Get-AzTag.md
$AssetTags = Get-AzTag -ResourceId $ResourceID
# https://learn.microsoft.com/en-us/powershell/module/az.resources/get-azresource?view=azps-8.2.0
$SubscriptionTags = Get-AzResource -ResourceId $ResourceID
$Status = "compliant"
$Message = #()
if ($LocalDebug) {
$RunCommand = $LocalHelperLocation + "helper-GetAssetID.ps1"
$ResourceAssetID = & $RunCommand -SubscriptionTags $null -ResourceGroupName $null -ResourceTags $Resource.Tags
}
else {
$ResourceAssetID = .\helper-GetAssetID.ps1 -SubscriptionTags $null -ResourceGroupName $null -ResourceTags $Resource.Tags
}
#Region Logic
# Identies must have a valid owner and AssetID tag and supported subscription tag.
if ($null -ne $ResourceAssetID -And $Resource.AppRole -contains "Owner" -And $AssetTags -ge 1 -And $SubscriptionTags -ge 1) {
# Compliant
}
else {
$Status = "noncompliant"
}
# End Logic
$SecurityIssuesOther = [PSCustomObject]#{
managedEntities = $Resource
}
$SecurityIssuesData += [PSCustomObject]#{
Status = $Status
SubscriptionName = $null
SubscriptionGuid = $null
TenantID = $TenantId
TenantName = $TenantName
ResourceID = $ResourceID
ResourceGroup = $null
ResourceType = $ResourceType
ResourceName = $ResourceName
AssetID = $ResourceAssetID
message = $Message
Other = $SecurityIssuesOther
}
}
}
#region Send Message
if ($LocalDebug) {
$RunCommand = $LocalHelperLocation + "helper-sendmsg.ps1"
& $RunCommand -azfNo $azfNo -SendAllMessage $SendAllMessage -TotalCompliant $TotalCompliant -TotalNoncompliant $TotalNoncompliant -TotalWaiver $TotalWaiver -SecurityIssues $SecurityIssuesData -SecurityIssuesSubscriptions $SecurityIssuesSubscriptions -LocalDebug $LocalDebug
}
else {
.\helper-sendmsg.ps1 -azfNo $azfNo -SendAllMessage $SendAllMessage -TotalCompliant $TotalCompliant -TotalNoncompliant $TotalNoncompliant -TotalWaiver $TotalWaiver -SecurityIssues $SecurityIssuesData -SecurityIssuesSubscriptions $SecurityIssuesSubscriptions -LocalDebug $LocalDebug
}
#endregion

Trouble with column output

Trouble with IPAddress column. Output with PowerShell to the excel csv.
shows Error System String []. All other columns show as need. Actual ip in the column wont output on the excel csv output.
List item I'm trying to correct on excel csv.
IPAddress column,
Etc
$computers = Get-Content "C:\EXAMPLE\Computers.txt"
$osColumns = 'PSComputerName', 'Caption', 'OSArchitecture', 'Version', 'BuildNumber'
$biosColumns = 'PSComputerName', 'Manufacturer', 'SerialNumber', 'SMBIOSBIOSVersion'
$lastbootColumns = 'csname', 'lastbootuptime'
$IPColumns = 'DNSHostName', 'IPAddress', 'MACAddress'
$OSinfo = Get-WmiObject Win32_OperatingSystem -ComputerName $computers | Select-Object $osColumns
$BIOSinfo = Get-WmiObject -Class Win32_BIOS -ComputerName $computers | Select-Object $biosColumns
$lastboot = Get-CimInstance win32_operatingsystem -ComputerName $computers | Select-Object $lastbootColumns
$IPinfo = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $computers | Select-Object $IPColumns
$computerInfo = foreach( $computer in $computers ) {
$thisOSInfo = $OSInfo | Where-Object { $_.PSComputerName -eq $computer }
$thisBIOSInfo = $BIOSinfo | Where-Object { $_.PSComputerName -eq $computer }
$thisLastboot = $lastboot | Where-Object { $_.csname -eq $computer }
$thisIPInfo = $IPinfo | Where-Object { $_.DNSHostName -eq $computer }
$row = #{}
foreach( $prop in #( $thisOSInfo.psobject.Properties.Name ) ) {
if( $prop -in $osColumns ) {
$row[$prop] = $thisOSInfo.$prop
}
}
foreach( $prop in #( $thisBIOSInfo.psobject.Properties.Name ) ) {
if( $prop -in $biosColumns ) {
$row[$prop] = $thisBIOSInfo.$prop
}
}
foreach( $prop in #( $thisLastboot.psobject.Properties.Name ) ) {
if( $prop -in $lastbootColumns ) {
$row[$prop] = $thisLastboot.$prop
}
}
foreach( $prop in #( $thisIPInfo.psobject.Properties.Name ) ) {
if( $prop -in $IPColumns ) {
$row[$prop] = $thisIPInfo.$prop
}
}
[PSCustomObject]$row
}
$computerInfo | Export-Csv -NoTypeInformation C:\EXAMPLE\report.csv

Get\List all azure subnets

I tried creating a script to get all azure subnets and their details.
How to get all azure subnets including their address and associated NSG?
$list = $null
$list = #()
$vnets = Get-AzVirtualNetwork
foreach ($vnet in $vnets) {
$subnets = $vnet.Subnets.name
foreach ($subnet in $subnets){
$SubnetDetails = Get-AzVirtualNetworkSubnetConfig -Name $subnet -VirtualNetwork $vnet
$list += [PSCustomObject]#{
VNETName = $vnet.Name
VNETAddressSpaces = $vnet.AddressSpace.AddressPrefixes -join ', '
SubnetName = $SubnetDetails.name
SubnetsPrefix = $SubnetDetails.AddressPrefix -join ''
SubnetNSG = if($SubnetDetails.NetworkSecurityGroup -eq $Null) {"No NSG"} else{$SubnetDetails.NetworkSecurityGroup.id.split('/')[8]}
SubnetNSGID = if($SubnetDetails.NetworkSecurityGroup -eq $Null) {"No NSG"} else{$SubnetDetails.NetworkSecurityGroup.id}
}
}
}
$list | ogv

PowerShell Workflow not running as intended

I have a PowerShell script which I've converted to a Workflow. The script is near identical, except for the few lines that need to be altered in order for it to be a Workflow.
However, for some reason it doesn't output like I think it should, and I am absolutely clueless as to why this would be the case.
Original PS script:
$DynatraceTenantID = "asdf"
$DynatraceToken = "asdf"
$DynatraceServer = "asdf"
$TagName = "env"
$TagValue = "dynatrace"
$result_array_altered = #()
$result_array_installed = #()
$result_array_notaltered = #()
$vms = Get-AzureRmResource -TagName $TagName -TagValue $TagValue | Where-Object {$_.ResourceType -like "Microsoft.Compute/virtualMachines"} | Where-Object {$_.Name -inotlike "MyVMx0"}
Foreach ($vm in $vms) {
$vm0 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status
$vm1 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name
$vm2 = $vm1.Extensions
if ($vm2.Publisher -notcontains "dynatrace.ruxit"){
if ($vm0.Statuses.DisplayStatus -eq "VM running") {
Set-AzureRmVmExtension `
-Name "Dynatrace" `
-Publisher "dynatrace.ruxit" `
-ResourceGroupName "$($vm1.ResourceGroupName)" `
-Location "$($vm1.Location)" `
-VMName "$($vm1.Name)" `
-ExtensionType "oneAgentWindows" `
-TypeHandlerVersion "2.3" `
-Settings #{ "tenantId"=$DynatraceTenantID; "token"=$DynatraceToken;"server"=$DynatraceServer; }
$objAltered = New-Object -TypeName PSObject -Property #{
VMaltered = $($vm1.Name)
IDaltered = $($vm1.Id)
}
$result_array_altered += $objAltered
} else {
$objNotAltered = New-Object -TypeName PSObject -Property #{
VMnotAltered = $($vm1.Name)
IDnotAltered = $($vm1.Id)
}
$result_array_notaltered += $objNotAltered
}
} else {
$objInstalled = New-Object -TypeName PSObject -Property #{
VMinstalled = $($vm1.Name)
IDinstalled = $($vm1.Id)
}
$result_array_installed += $objInstalled
}
}
$result_array_altered | ConvertTo-Json
$result_array_installed | ConvertTo-Json
$result_array_notaltered | ConvertTo-Json
Outputs the following:
[
{
"IDinstalled": "/subscriptions/MY-SUBSCRIPTION/resourceGroups/virtual-machines/providers/Microsoft.Compute/virtualMachines/MyVMw0",
"VMinstalled": "MyVMw0"
},
{
"IDinstalled": "/subscriptions/MY-SUBSCRIPTION/resourceGroups/virtual-machines/providers/Microsoft.Compute/virtualMachines/MyVMw1",
"VMinstalled": "MyVMw1"
}
]
The converted PowerShell to PS Workflow:
workflow InstallDynatrace {
$DynatraceTenantID = "asdf"
$DynatraceToken = "asdf"
$DynatraceServer = "asdf"
$TagName = "env"
$TagValue = "dynatrace"
$result_array_altered = #()
$result_array_installed = #()
$result_array_notaltered = #()
$vms = Get-AzureRmResource -TagName $TagName -TagValue $TagValue | Where-Object {$_.ResourceType -like "Microsoft.Compute/virtualMachines"} | Where-Object {$_.Name -inotlike "MyVMx0"}
Foreach -parallel ($vm in $vms) {
$vm0 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status
$vm1 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name
$vm2 = $vm1.Extensions
if ($vm2.Publisher -notcontains "dynatrace.ruxit"){
if ($vm0.Statuses.DisplayStatus -eq "VM running") {
Set-AzureRmVmExtension `
-Name "Dynatrace" `
-Publisher "dynatrace.ruxit" `
-ResourceGroupName "$($vm1.ResourceGroupName)" `
-Location "$($vm1.Location)" `
-VMName "$($vm1.Name)" `
-ExtensionType "oneAgentWindows" `
-TypeHandlerVersion "2.3" `
-Settings #{ "tenantId"=$DynatraceTenantID; "token"=$DynatraceToken;"server"=$DynatraceServer; }
$objAltered = New-Object -TypeName PSObject -Property #{
VMaltered = $($vm1.Name)
IDaltered = $($vm1.Id)
}
} else {
$objNotAltered = New-Object -TypeName PSObject -Property #{
VMnotAltered = $($vm1.Name)
IDnotAltered = $($vm1.Id)
}
}
} else {
$objInstalled = New-Object -TypeName PSObject -Property #{
VMinstalled = $($vm1.Name)
IDinstalled = $($vm1.Id)
}
}
$workflow:result_array_altered += $objAltered
$workflow:result_array_notaltered += $objNotAltered
$workflow:result_array_installed += $objInstalled
}
$result_array_altered | ConvertTo-Json
$result_array_installed | ConvertTo-Json
$result_array_notaltered | ConvertTo-Json
}
Outputs the following:
PS C:\WINDOWS\system32> InstallDynatrace
[
{
"VMnotAltered": "MyVMw1",
"IDnotAltered": "/subscriptions/MY-SUBSCRIPTION/resourceGroups/virtual-machines/providers/Microsoft.Compute/virtualMachines/MyVMw1",
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "36c288df-41c3-4b61-9a6c-445707f76bea"
},
{
"VMnotAltered": "MyVMw0",
"IDnotAltered": "/subscriptions/MY-SUBSCRIPTION/resourceGroups/virtual-machines/providers/Microsoft.Compute/virtualMachines/MyVMw0",
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "36c288df-41c3-4b61-9a6c-445707f76bea"
}
]
Note how the original script shows that results array to be VMinstalled and IDinstalled, whereas the workflow shows as VMnotAltered and IDnotAltered, meaning that it saw the VMs as NOT having Dynatrace installed (line 19), didn't see it Running (line 20) and just added it to the Not Altered array.
Any ideas where I'm going wrong?
UPDATED FOR THEO ANSWER
PS C:\WINDOWS\system32>
workflow InstallDynatrace {
$DynatraceTenantID = "asdf"
$DynatraceToken = "asdf"
$DynatraceServer = "asdf"
$TagName = "env"
$TagValue = "dynatrace"
$result_array_altered = #()
$result_array_installed = #()
$result_array_notaltered = #()
$vms = Get-AzureRmResource -TagName $TagName -TagValue $TagValue | Where-Object {$_.ResourceType -like "Microsoft.Compute/virtualMachines"} | Where-Object {$_.Name -inotlike "MyVMx0"}
Foreach -parallel ($vm in $vms) {
$vm0 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status
$vm1 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name
$vm2 = $vm1.Extensions
if ($vm2.Publisher -ne "dynatrace.ruxit"){
if ($vm0.Statuses.DisplayStatus -eq "VM running") {
Set-AzureRmVmExtension `
-Name "Dynatrace" `
-Publisher "dynatrace.ruxit" `
-ResourceGroupName "$($vm1.ResourceGroupName)" `
-Location "$($vm1.Location)" `
-VMName "$($vm1.Name)" `
-ExtensionType "oneAgentWindows" `
-TypeHandlerVersion "2.3" `
-Settings #{ "tenantId"=$DynatraceTenantID; "token"=$DynatraceToken;"server"=$DynatraceServer; }
$objAltered = New-Object -TypeName PSObject -Property #{
VMaltered = $($vm1.Name)
IDaltered = $($vm1.Id)
}
$workflow:result_array_altered += $objAltered
} else {
$objNotAltered = New-Object -TypeName PSObject -Property #{
VMnotAltered = $($vm1.Name)
IDnotAltered = $($vm1.Id)
}
$workflow:result_array_notaltered += $objNotAltered
}
} else {
$objInstalled = New-Object -TypeName PSObject -Property #{
VMinstalled = $($vm1.Name)
IDinstalled = $($vm1.Id)
}
$workflow:result_array_installed += $objInstalled
}
}
$workflow:result_array_altered | ConvertTo-Json
$workflow:result_array_installed | ConvertTo-Json
$workflow:result_array_notaltered | ConvertTo-Json
}
InstallDynatrace
[
{
"VMnotAltered": "MyVMw0",
"IDnotAltered": "/subscriptions/asdf/resourceGroups/virtual-machines/providers/Microsoft.Compute/virtualMachines/MyVMw0",
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "d23d8e5b-dc3c-475f-82fe-968a3eeef946"
},
{
"VMnotAltered": "MyVMw1",
"IDnotAltered": "/subscriptions/asdf/resourceGroups/virtual-machines/providers/Microsoft.Compute/virtualMachines/MyVMw1",
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "d23d8e5b-dc3c-475f-82fe-968a3eeef946"
}
]
PS C:\WINDOWS\system32>
You have made the same error you did earlier I'm afraid.
Where in the script you add the object to the corresponding array where it should be added, namely right after creating it inside the if or else block, in the Workflow you add them all at the end of the loop..
This means that regardless of the outcome of the if {...} else {...} tests, you add them anyway.
This should do things in the right place:
Foreach -parallel ($vm in $vms) {
$vm0 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status
$vm1 = Get-AzureRmVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name
$vm2 = $vm1.Extensions
if ($vm2.Publisher -notcontains "dynatrace.ruxit"){ # should be -ne I think
if ($vm0.Statuses.DisplayStatus -eq "VM running") {
Set-AzureRmVmExtension `
-Name "Dynatrace" `
-Publisher "dynatrace.ruxit" `
-ResourceGroupName "$($vm1.ResourceGroupName)" `
-Location "$($vm1.Location)" `
-VMName "$($vm1.Name)" `
-ExtensionType "oneAgentWindows" `
-TypeHandlerVersion "2.3" `
-Settings #{ "tenantId"=$DynatraceTenantID; "token"=$DynatraceToken;"server"=$DynatraceServer; }
$objAltered = New-Object -TypeName PSObject -Property #{
VMaltered = $($vm1.Name)
IDaltered = $($vm1.Id)
}
# this is where you add the object to the $workflow:result_array_altered array
$workflow:result_array_altered += $objAltered
}
else {
$objNotAltered = New-Object -TypeName PSObject -Property #{
VMnotAltered = $($vm1.Name)
IDnotAltered = $($vm1.Id)
}
# this is where you add the object to the $workflow:result_array_notaltered array
$workflow:result_array_notaltered += $objNotAltered
}
}
else {
$objInstalled = New-Object -TypeName PSObject -Property #{
VMinstalled = $($vm1.Name)
IDinstalled = $($vm1.Id)
}
# this is where you add the object to the $workflow:result_array_installed array
$workflow:result_array_installed += $objInstalled
}
}
$workflow:result_array_altered | ConvertTo-Json
$workflow:result_array_notaltered | ConvertTo-Json
$workflow:result_array_installed | ConvertTo-Json
Then, at the end of the workflow, you outputted the arrays you still had in memory from the earlier script version ($result_array_altered | ConvertTo-Json etc. I have changed that to match the new arrays for you.
Also, isn't $vm2.Publisher just a single string? In that case, change the lines
if ($vm2.Publisher -notcontains "dynatrace.ruxit")
into
if ($vm2.Publisher -ne "dynatrace.ruxit")
(-notcontains checks a value in an array where -ne tests for the 'not-equalness' of a single object, so a string in this case)
UPDATE
I MAY have found an answer for the unwanted extra information here
It may look ugly, but try this at the end of the workflow:
$workflow:result_array_altered | Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, PSSourceJobInstanceId | ConvertTo-Json
$workflow:result_array_installed | Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, PSSourceJobInstanceId | ConvertTo-Json
$workflow:result_array_notaltered | Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, PSSourceJobInstanceId | ConvertTo-Json

Why is my powershell multi runspace code taking the same amount of time as my synchronous code?

To test this code you'll have to fill in the top 2 variables. What I can't understand is why the top code set (psuedo multithreaded) takes the same amount of time to process as the bottom code set (standard synchronous processing)? Using start-jobs I experienced the same thing.
What am I doing wrong with runspaces here?
cls
$dagName = "XXXXXXX";
$connectionUri = "XXXXXXX";
Write-Host ([DateTime]::Now).ToString();
Write-Host "";
$time = New-Object system.Diagnostics.Stopwatch
Start-Sleep 1
$time.Start()
$system = $time.Elapsed.TotalMilliseconds
$session = new-pssession -ConfigurationName Microsoft.Exchange -ConnectionUri $connectionUri;
import-pssession $session -AllowClobber;
set-adserversettings -viewentireforest $true;
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null;
if (($dagName -eq $null) -or ($dagName -eq ""))
{
$dagName = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a dag name", "Dag");
}
$dagObject = Get-DatabaseAvailabilityGroup -Identity $dagName;
$servers = New-Object System.Collections.ArrayList;
foreach($server in $dagObject.Servers)
{
$servers.Add($server.ToString()) | Out-Null;
}
$getExchangeServers = #();
$getMailboxDatabases = #();
$getMailboxDatabasesWithStatus = #();
$getWinEvents = #();
$maxThreads = 7
$runspacePool = [RunspaceFactory ]::CreateRunspacePool(1, $maxThreads)
$runspacePool.Open()
$scriptBlock1 = { param($p) $session1 = new-pssession -ConfigurationName Microsoft.Exchange -ConnectionUri $connectionUri; import-pssession $session1 -AllowClobber; $getExchangeServers = #(); foreach ($server in $p) { $getExchangeServers += get-exchangeserver $server; } return $getExchangeServers; }
$scriptBlock2 = { param($p) $session2 = new-pssession -ConfigurationName Microsoft.Exchange -ConnectionUri $connectionUri; import-pssession $session2 -AllowClobber; $getMailboxDatabases = #(); foreach ($server in $p) { $getMailboxDatabases += Get-MailboxDatabase -Server $server; } return $getMailboxDatabases; }
$scriptBlock3 = { param($p) $session3 = new-pssession -ConfigurationName Microsoft.Exchange -ConnectionUri $connectionUri; import-pssession $session3 -AllowClobber; $getMailboxDatabasesWithStatus = #(); foreach ($server in $p) { $getMailboxDatabasesWithStatus += Get-MailboxDatabase -Server $server -Status; } return $getMailboxDatabasesWithStatus; }
$jobs = #()
$job1 = [powershell ]::Create().AddScript($scriptBlock1 ).AddArgument($servers)
$job1.RunspacePool = $runspacePool
$jobs += New-Object PSObject -Property #{
Pipe = $job1
Result = $job1.BeginInvoke()
}
$job2 = [powershell ]::Create().AddScript($scriptBlock2 ).AddArgument($servers)
$job2.RunspacePool = $runspacePool
$jobs += New-Object PSObject -Property #{
Pipe = $job2
Result = $job2.BeginInvoke()
}
$job3 = [powershell ]::Create().AddScript($scriptBlock3 ).AddArgument($servers)
$job3.RunspacePool = $runspacePool
$jobs += New-Object PSObject -Property #{
Pipe = $job3
Result = $job3.BeginInvoke()
}
Do {
Write-Host $Jobs.Result.IsCompleted;
Start-Sleep -Seconds 7
} While ( $Jobs.Result.IsCompleted -contains $false )
Write-Host "All jobs completed!"
Write-Host $Jobs.Result.IsCompleted;
Foreach ($Job in $Jobs)
{
Write-Host $job.Pipe.EndInvoke($job.Result);
}
$time.Stop();
$totalTime = $time.Elapsed.TotalMilliseconds - $system;
[float]$runTimeSeconds = [math]::Round($totalTime/1000, 2);
[float]$runTimeMinutes = [math]::Round($totalTime/60000, 2);
Write-Host
Write-Host "This function took " $runTimeSeconds.ToString() " seconds (or " $runTimeMinutes.ToString() " minutes) to run";
<# Code set 2#>
Write-Host ([DateTime]::Now).ToString();
Write-Host "";
$time = New-Object system.Diagnostics.Stopwatch
Start-Sleep 1
$time.Start()
$system = $time.Elapsed.TotalMilliseconds
$session = new-pssession -ConfigurationName Microsoft.Exchange -ConnectionUri $connectionUri;
import-pssession $session -AllowClobber;
set-adserversettings -viewentireforest $true;
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null;
if (($dagName -eq $null) -or ($dagName -eq ""))
{
$dagName = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a dag name", "Dag");
}
$dagObject = Get-DatabaseAvailabilityGroup -Identity $dagName;
$servers = New-Object System.Collections.ArrayList;
foreach($server in $dagObject.Servers)
{
$servers.Add($server.ToString()) | Out-Null;
}
$getExchangeServers = #();
$getMailboxDatabases = #();
$getMailboxDatabasesWithStatus = #();
$getWinEvents = #();
foreach ($server in $servers) { $getExchangeServers += get-exchangeserver $server; } Write-Host $getExchangeServers;
foreach ($server in $servers) { $getMailboxDatabases += Get-MailboxDatabase -Server $server; } Write-Host $getMailboxDatabases;
foreach ($server in $servers) { $getMailboxDatabasesWithStatus += Get-MailboxDatabase -Server $server -Status; } Write-Host $getMailboxDatabasesWithStatus;
$time.Stop();
$totalTime = $time.Elapsed.TotalMilliseconds - $system;
[float]$runTimeSeconds = [math]::Round($totalTime/1000, 2);
[float]$runTimeMinutes = [math]::Round($totalTime/60000, 2);
Write-Host
Write-Host "This second function took " $runTimeSeconds.ToString() " seconds (or " $runTimeMinutes.ToString() " minutes) to run";

Resources