mirror of
https://github.com/NetSPI/MicroBurst
synced 2026-06-08 12:01:29 +00:00
152 lines
6.5 KiB
PowerShell
152 lines
6.5 KiB
PowerShell
<#
|
|
File: Invoke-AzVMBulkCMD.ps1
|
|
Author: Karl Fosaaen (@kfosaaen), NetSPI - 2020
|
|
Description: PowerShell function for running PowerShell scripts against multiple Azure VMs.
|
|
#>
|
|
|
|
|
|
|
|
Function Invoke-AzVMBulkCMD
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Runs a Powershell script against all (or select) VMs in a subscription/resource group/etc.
|
|
.DESCRIPTION
|
|
This function will run a PowerShell script on all (or a list of) VMs in a subscription/resource group/etc. This can be handy for creating reverse shells, running tools, or doing practical automation of work.
|
|
.PARAMETER Subscription
|
|
Subscription to use.
|
|
.PARAMETER ResourceGroup
|
|
Restrict the script to a specific Resource Group.
|
|
.EXAMPLE
|
|
PS C:\MicroBurst> Invoke-AzVMBulkCMD -Verbose -Script .\test.ps1
|
|
Executing C:\MicroBurst\test.ps1 against all (1) VMs in the Testing-Resources Subscription
|
|
Are you Sure You Want To Proceed: (Y/n):
|
|
VERBOSE: Running .\test.ps1 on the Remote-West - (10.2.0.5 : 40.112.160.13) virtual machine (1 of 1)
|
|
VERBOSE: Script Status: Succeeded
|
|
Script Output:
|
|
# exit
|
|
Bye!
|
|
|
|
VERBOSE: Script Execution Completed on Remote-West - (10.2.0.5 : 40.112.160.13)
|
|
VERBOSE: Script Execution Completed in 37 seconds
|
|
|
|
.LINK
|
|
https://blog.netspi.com/running-powershell-scripts-on-azure-vms
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
Param(
|
|
[Parameter(Mandatory=$false,
|
|
ValueFromPipeline = $true,
|
|
ValueFromPipelineByPropertyName = $true,
|
|
HelpMessage="Subscription to use.")]
|
|
[string[]]$Subscription,
|
|
|
|
[Parameter(Mandatory=$false,
|
|
ValueFromPipeline = $true,
|
|
ValueFromPipelineByPropertyName = $true,
|
|
HelpMessage="Resource Group to use.")]
|
|
[string[]]$ResourceGroupName,
|
|
|
|
[Parameter(Mandatory=$false,
|
|
ValueFromPipeline = $true,
|
|
ValueFromPipelineByPropertyName = $true,
|
|
HelpMessage="Individual VM Name(s) to use.")]
|
|
[string[]]$Name = $null,
|
|
|
|
[Parameter(Mandatory=$true,
|
|
HelpMessage="Script to run.")]
|
|
[string]$Script = "",
|
|
|
|
[Parameter(Mandatory=$false,
|
|
HelpMessage="File to use for output.")]
|
|
[string]$output = ""
|
|
|
|
)
|
|
|
|
# If Subscription, then grab all the VMs in each sub
|
|
if ($Subscription){
|
|
foreach ($sub in $Subscription){
|
|
Select-AzSubscription -SubscriptionName $sub | Out-Null
|
|
# Get a list of the running VMs for the Subscription, run the script on each one
|
|
$vms += Get-AzVM -Status | where {$_.PowerState -EQ "VM running"}
|
|
$VMCount = $vms.Count
|
|
Write-Verbose "Executing $Script against $VMCount VMs in the $sub Subscription"
|
|
}
|
|
}
|
|
|
|
# If Resource Group, then grab all the VMs in each RG
|
|
if ($ResourceGroupName){
|
|
$vms = $null
|
|
# Iterate the RG list and add the VMs to the array
|
|
foreach($rg in $ResourceGroupName){
|
|
$vms = Get-AzVM -Status -ResourceGroupName $rg | where {$_.PowerState -EQ "VM running"}
|
|
$VMCount = $vms.Count
|
|
Write-Verbose "Executing $Script against $VMCount VMs in the $rg Resource Group"
|
|
}
|
|
}
|
|
|
|
# If names, run against the names listed
|
|
if($Name){
|
|
$vms = $null
|
|
# Iterate the name list and add the VMs (that are running) to the array
|
|
foreach($listName in $Name){
|
|
$vms += Get-AzVM -Status | where Name -EQ $listName | where {$_.PowerState -EQ "VM running"}
|
|
}
|
|
$VMCount = $vms.Count
|
|
Write-Verbose "Executing $Script against $VMCount VMs"
|
|
}
|
|
|
|
# If no RG or Names, then get all VMs for the current Sub
|
|
if (($ResourceGroupName -eq $null) -and ($Name -eq $null)){
|
|
# Get a list of the running VMs for the Subscription, run the script on each one
|
|
$vms = Get-AzVM -Status | where {$_.PowerState -EQ "VM running"}
|
|
$subName = (Get-AzSubscription -SubscriptionId ((Get-AzContext).Subscription.Id)).Name
|
|
$VMCount = $vms.Count
|
|
Write-Host "Executing $Script against all ($VMCount) VMs in the $subName Subscription"
|
|
$confirmation = Read-Host "Are you Sure You Want To Proceed: (Y/n)"
|
|
if (($confirmation -eq 'n') -or ( $confirmation -eq 'N')) {
|
|
Break
|
|
}
|
|
else{}
|
|
}
|
|
|
|
|
|
if($vms){
|
|
$VMcounter = 0
|
|
foreach ($vm in $vms){
|
|
$VMcounter++
|
|
# Measure Execution Time
|
|
$commandTime = Measure-Command {
|
|
# Get IP Information for better host tracking
|
|
$NICid = Get-AzNetworkInterface | select Name,VirtualMachine -ExpandProperty VirtualMachine | where Id -EQ $vm.Id
|
|
$VMInterface = Get-AzNetworkInterface -ResourceGroupName $vm.ResourceGroupName -Name $NICid.Name
|
|
$privIP = $VMInterface.IpConfigurations[0].PrivateIpAddress
|
|
$pubIP = (Get-AzPublicIpAddress | where Id -EQ $VMInterface.IpConfigurations[0].PublicIpAddress.Id | select IpAddress).IpAddress
|
|
|
|
# Run the PS1 file
|
|
$VMName = $vm.Name
|
|
Write-Verbose "Running $Script on the $VMName - ($privIP : $pubIP) virtual machine ($VMcounter of $VMCount)"
|
|
Try{
|
|
$scriptOutput = Invoke-AzVMRunCommand -ResourceGroupName $vm.ResourceGroupName -VMName $VMName -CommandId RunPowerShellScript -ScriptPath $Script -ErrorAction SilentlyContinue
|
|
|
|
#write verbose the return status and write the output from the script
|
|
$scriptStatus = $scriptOutput.Status
|
|
Write-Verbose "Script Status: $scriptStatus"
|
|
$cmdOut = $scriptOutput.Value[0].Message
|
|
if ($output){
|
|
"$Script on the $VMName - ($privIP : $pubIP) virtual machine" | Out-File -Append -FilePath $output
|
|
$cmdOut | Out-File -Append -FilePath $output
|
|
Write-Verbose "Script output written to $output"
|
|
}
|
|
else{Write-Host "Script Output: `n$cmdOut"}
|
|
Write-Verbose "Script Execution Completed on $VMName - ($privIP : $pubIP)"
|
|
}
|
|
Catch{Write-Verbose "`tError in command excution. Check the Azure Activity Log for more details."}
|
|
} | select TotalSeconds
|
|
$outputTime = [int]$commandTime.TotalSeconds
|
|
Write-Verbose "Script Execution Completed in $outputTime seconds"
|
|
}
|
|
}
|
|
else{Write-Host "No VMs selected for code execution"}
|
|
} |