mbourgon
mbourgon

Reputation: 1336

Powershell workflow with "foreach -parallel" and Invoke-AZVMRunCommand- how to get results out?

I have a bunch of VMs in Azure I want to run the same script against. Because of reasons, I can't use powershell 7, so I have to use powershell 5. Which means I can use "foreach -parallel", but NOT "foreach-parallel". So it can work, but requires a workflow.

I have the following workflow that will grab the list of servers in a specific tenant/environment, and then run a foreach -parallel to run a script against each of them at the same time.

The problem I'm running into is that it appears to work, but the result I get out is:

Microsoft.Azure.Commands.Compute.Automation.Models.PSRunCommandResult
blah 

Which is not the actual result (in this it would be the time). Because it's within a workflow, getting the details out is... painful. How do I get the results? It appears to run, there's no errors, but I can't figure out the results. Thanks.

The get-member on $out gives:

Name                  MemberType   Definition                                                                                                                                       
----                  ----------   ----------                                                                                                                                       
GetType               Method       type GetType()                                                                                                                                   
ToString              Method       string ToString(), string ToString(string format, System.IFormatProvider formatProvider), string IFormattable.ToString(string format, System.I...
PSComputerName        NoteProperty string PSComputerName=localhost                                                                                                                  
PSShowComputerName    NoteProperty bool PSShowComputerName=True                                                                                                                     
PSSourceJobInstanceId NoteProperty guid PSSourceJobInstanceId=0870d1ff-1234-5678-014e-2e123456c7d8                                                                                  
Capacity              Property     System.Int32 {get;set;}                                                                                                                          
Count                 Property     System.Int32 {get;set;}                                                                                                                          
EndTime               Property      {get;set;}                                                                                                                                      
Error                 Property      {get;set;}                                                                                                                                      
Item                  Property      {get;set;}                                                                                                                                      
Name                  Property      {get;set;}                                                                                                                                      
Output                Property      {get;set;}                                                                                                                                      
StartTime             Property      {get;set;}                                                                                                                                      
Status                Property     System.String {get;set;}                                                                                                                         
Value                 Property     Deserialized.System.Collections.Generic.List`1[[Microsoft.Azure.Management.Compute.Models.InstanceViewStatus, Microsoft.Azure.Management.Compu...
Microsoft.Azure.Commands.Compute.Automation.Models.PSRunCommandResult

And here's the script I'm running:

#for some reason you need to disconnect from one and then connect to the other, can't run two at once.
disconnect-azaccount

connect-azaccount -Tenant "mytenantid" 

#get all the dev Windows VM servers in the current tenant that are turned on.
$serverinfo = @()
Get-AzContext -ListAvailable |where-object {$_.Name -like "*Dev*"}| %{
$_|select-azcontext
$serverinfo += get-azvm -status | Where-Object {$_.PowerState -eq "VM running" -and $_.StorageProfile.OSDisk.OSType -eq "Windows"}
}

#honestly, for the test I filtered down to the jumpboxes.
$serverinfo2 = $serverinfo|where {$_.Name -like "*jumpbox*"}

#workflows are needed; can't run foreach -parallel otherwise.
Workflow TestParallel{
#need to pass in the details, it can't reach the $serverinfo2 otherwise.
   param($listofservers) #take serverinfo2 and make it accessible from within the workflow.
   $test=@()

   #need to set the proper context
   Get-AzContext -ListAvailable |where-object {$_.Name -like "*Dev*"}| select-azcontext

   #now we run each of these at the same time. Foreach-parallel requires 7, but we can do foreach -parallel within powershell 4 workflows.
   Foreach -parallel($server in $listofservers){
    #now that the script is on there, run the command locally.
    $out = Invoke-AzVMRunCommand `
        -ResourceGroupName $server.ResourceGroupName `
        -Name $server.name `
        -CommandId 'RunPowerShellScript' `
        -ScriptString "$dt = gwmi win32_localtime; New-Object DateTime $dt.year,$dt.month,$dt.day,$dt.hour,$dt.minute, $dt.second"
        #scriptstring is a newer command, added in july 2022

    #Formating the Output with the VM name. Value[0].Message contains the results from running the script.
    #export it to a variable that will survive the foreach
    #$WORKFLOW:test += $server.Name + " " + $out.Value[0].Message
    $WORKFLOW:test += $out.Value[1].Message #appears to be 1 now, using " -scriptstring"

    #writing it locally to see if it shows up
    "$out"

   }
    [array]$resultsArray = @($test)
    write-output ("blah " + $test[0])
    $resultsArray
}

TestParallel $serverinfo2

Upvotes: 0

Views: 911

Answers (1)

KrishnaG
KrishnaG

Reputation: 3484

I would avoid using "foreach -parallel" and "workflow" because of current limitations w.r.t parallel processing and workflows that are mentioned here so I would recommend to try foreach loop for all VMs using first approach mentioned in this answer i.e., to store the script in storage account and then run that script remotely in all VMs using Invoke-AzVMRunCommand cmdlet with the help of foreach loop which basically runs the script in all VMs.

Upvotes: 0

Related Questions