I have the below code to check daily if all the VMs that are meant to auto-start have started successfully.
$Date = (Get-Date -Format "dd-MM-yyyy HH-mm").toString()
$Results = "C:\temp\DailyChecks_$($Date).txt"
$Cred = get-credential -UserName 'AZ-User'
Connect-AzAccount -Tenant 'TenantID' -credential $Cred | out-null
$fCount = (Get-AzVM -Status | Where-Object { $_.tags['Managed By'] -like 'Manager' }).count
do {
$rCount = (Get-AzVM -Status | Where-Object { $_.tags['Managed By'] -like 'Manager' -and $_.PowerState -eq 'VM Running' }).count
if($rCount -lt $fCount)
{
write-host "There are $rCount VMs running, checking again"
}
elseif($rCount -eq $fCount){write-host "There are $rCount VMs running, exiting loop"}
}until($fCount -eq $rCount)
Get-AzVM -Status | Where-Object { $_.tags['Managed By'] -like 'Manager' } |
Select-Object Name, PowerState | Format-Table | out-file $Results
I have a couple of questions:
Is there a better way to write this code?
How can I check if the code has been running for longer than 30 minutes and yet not all VMs are running?
You can determine for how much the time the script is running by using a StopWatch object.
$sw = [System.Diagnostics.Stopwatch]::new()
$sw.Start()
Whenever you want, just check the elapsed time by checking the elapsed property
$sw.Elapsed
$sw.Elapsed.TotalSeconds
Write-Host "Time elapsed: $([Math]::Round($sw.Elapsed.TotalMinutes,0)) min $($sw.Elapsed.Seconds) seconds"
Related
My Powershell script is not explicitly calling for any specific resources but I am getting "ErrorCode: TargetResourceNotFound" error. I have attached the error in the image. What am I missing?
$subs = Get-AzSubscription | Where-Object {$_.Name -like "*-NonProd"}
foreach ($sub in $subs)
{
Select-AzSubscription -SubscriptionId $sub.Id
$RGs = Get-AzResourceGroup | Where-Object {$_.ResourceGroupName -like "*Infra"}
foreach ($RG in $RGs)
{
$NetworkWatchers = Get-AzNetworkWatcher
$NSGs = (Get-AzNetworkSecurityGroup).Id
foreach ($NSG in $NSGs)
{
foreach ($NetworkWatcher in $NetworkWatchers)
{
$Status = Get-AzNetworkWatcherFlowLogStatus -NetworkWatcherName $NetworkWatcher.Name
ResourceGroupName $RG.ResourceGroupName -TargetResourceId $NSG -Verbose
}
if (($Status).Enabled -eq $true)
{
Write-Output "$NSG in $(($sub).Name) has FlowLogs Enabled" | Tee-Object -FilePath 'C:\Users\user1\downloads\Output.txt' -Verbose -Append
}
if (($Status).Enabled -ne $true)
{
Write-Output "$NSG in $(($sub).Name) does not have FlowLogs Enabled" | Tee-Object -FilePath 'C:\Users\user1\downloads\Output.txt' -Verbose -Append
}
}
}
}
enter code here
Error Attached
Network Watchers are usually in a hidden resource group, and perhaps you are trying to find one in one of the available RGs. Try omitting the RG factor and use
$subs = Get-AzSubscription | Where-Object { $_.Name -like "*-NonProd" }
foreach ($sub in $subs) {
Select-AzSubscription -SubscriptionId $sub.Id
$NetworkWatchers = Get-AzNetworkWatcher
$NSGs = (Get-AzNetworkSecurityGroup).Id
foreach ($NSG in $NSGs) {
foreach ($NetworkWatcher in $NetworkWatchers) {
$Status = Get-AzNetworkWatcherFlowLogStatus -NetworkWatcher $NetworkWatcher -TargetResourceId $NSG -Verbose
}
if (($Status).Enabled -eq $true) {
Write-Output "$NSG in $(($sub).Name) has FlowLogs Enabled" | Tee-Object -FilePath 'C:\Users\user1\downloads\Output.txt' -Verbose -Append
}
if (($Status).Enabled -ne $true) {
Write-Output "$NSG in $(($sub).Name) does not have FlowLogs Enabled" | Tee-Object -FilePath 'C:\Users\user1\downloads\Output.txt' -Verbose -Append
}
}
}
I am able to get all flow logs configuration status.
Here is another approach:
I tried to reproduce the same in my environment and got the same error as below:
The error TargetResourceNotFound usually occurs if you are passing invalid resource group name or subscription name.
To confirm whether the resource group or subscription exists, execute the below code lines separately like below:
$subs = Get-AzSubscription | Where-Object {$_.Name -like "*-name"}
$subs
$RGs = Get-AzResourceGroup | Where-Object {$_.ResourceGroupName -like "*name"}
$RGs
The error states that Target resource identifier /subscriptions/subid/resourceGroups/RG/providers/Microsoft.Network/networkWatchers/*** not found in the region westeurope. Cross-verify whether the Network Watcher exists.
I am able to get the status of the Network Watcher successfully when I passed valid subscription and Resource group like below:
If the error still persists, try excluding the $RGs = Get-AzResourceGroup | Where-Object {$_.ResourceGroupName -like "*name"} and execute.
I appreciate your assistance.
I got it working by changing -NetworkWatcherName $NetworkWatcher.Name to -NetworkWatcherName $NetworkWatcher.ResourceGroupName
foreach ($NSG in $NSGs)
{
# $NSG.Id
# $NSGid = $NSG.Id
foreach ($NetworkWatcher in $NetworkWatchers)
{
$Status = Get-AzNetworkWatcherFlowLogStatus -NetworkWatcherName $NetworkWatcher.ResourceGroupName -ResourceGroupName $RG.ResourceGroupName -TargetResourceId $NSG -Verbose -ErrorAction SilentlyContinue
if (($Status).Enabled -eq $true)
{
Write-Output "$NSG in $(($sub).Name) has FlowLogs Enabled" | Tee-Object -FilePath 'C:\Users\A240379\downloads\OutEnabled.csv' -Verbose -Append
}
if (($Status).Enabled -ne $true)
{
Write-Output "$NSG in $(($sub).Name) does not have FlowLogs Enabled" | Tee-Object -FilePath 'C:\Users\A240379\downloads\OutNotEnabled.csv' -Verbose -Append
}
}
}
I have a Powershell script that checks on a set of VM status before starting them. If the VM'S are in deallocating mode there should be a sleep and retry on 30 seconds. The code does not do a a retry. The code does a vm start in batches on 2 for vm's with wilcards as mentioned below in an order.
Need help if possible
$ResName= "resvmtest"
$action="start"
if($action -eq "start"){
$vnames=#('*dom*','*DBs*','*')
foreach($vname in $vnames) {
Write-Host "Starting VM with "$vname
$vmList = Get-AzVM -ResourceGroupName $ResName -Name $vname -Status | Select-Object Name, PowerState, ResourceGroupName
do{
$batch = #{
Skip = 0
First = 2
}
do{
foreach($vm in ($vmList | Select-Object #batch)){
$Stoploop = $false
[int]$Retrycount = "0"
do {
try {
if($vm.PowerState -eq "VM Deallocated"){
Write-Host "Job completed"
$Stoploop = $true
}
}
catch {
if ($vm.PowerState -eq "VM Deallocatting") {
Write-Host "VM Still not Deallocated"
Start-Sleep -Seconds 10
$Retrycount = $Retrycount + 1
}
}
}
While ($Stoploop -eq $false)
$params = #($vm.Name, $vm.ResourceGroupName,$vm.PowerState)
$job = Start-Job -ScriptBlock {
param($ComputerName,$serviceName,$statuses)
Start-AzVM -Name $ComputerName -ResourceGroupName $serviceName
} -ArgumentList $params
}
Wait-Job -Job $job
Get-Job | Receive-Job
$batch.Skip += 2
}
until($batch.skip -ge $vmList.count)
}
while($job.state -ne "Completed")
}
}
If you just want to wait for a VM's PowerState turns to VM Deallocated from VM Deallocatting, try the code below:
$vms = Get-AzVm -Status| Select-Object Name, PowerState,ResourceGroupName
#pick certain vm for demo here.
$vm = $vms[1]
$retryCount = 0
while($vm.PowerState -eq 'VM deallocating'){
Write-Host "waiting for VM deallocated..."
Start-Sleep -Seconds 5
$retryCount +=1
Write-Host "check count:$retryCount"
#get latest vm status
$vm.PowerState = (Get-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Status).Statuses[1].DisplayStatus
Write-Host "vm current state:"$vm.PowerState
}
Write-Host "vm new state:" + $vm.PowerState
Result:
I am using PowerShell to automate VM start/stop in Azure. However, when I set the $OPTION parameter it doesn't do anything. I have if/elseif/else statements.
See code below:
Add-AzureRmAccount -Credential $psCred -TenantId <removed> -ServicePrincipal
Get-AzureRmSubscription -SubscriptionName "<removed>"
#Get VMs using Tags
$vms = (Find-AzureRmResource | Where-Object {($_.tags.Project -eq "DevOps") -And ($_.tags.Test -eq "ernest")} | Select Name, ResourceGroupName)
$vms
Write-Output "Number of Virtual Machines: $($vms.Name.Count)"
foreach($VM in $vms)
{
if ($OPTION -eq "start" -And $VM.ResourceType -eq "Microsoft.Compute/virtualMachines")
{
Write-Output "Starting :- $VM.Name in $VM.ResourceGroupName"
Start-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -Verbose
Write-Output $VM.Name "has started successfully"
Write-Output "Writing output to workspace"
Get-AzureRmVM -Status | Where-Object {($_.tags.Project -eq "DevOps") -And ($_.tags.Test -eq "ernest")} | Select Name, ResourceGroupName, PowerState > VM_Start_Info-$(get-date -f yyyy-MM-dd).tsv
}
elseif ($OPTION -eq "stop" -And $VM.ResourceType -eq "Microsoft.Compute/virtualMachines")
{
Write-Output "Deallocating :- $VM.Name in $VM.ResourceGroupName"
Stop-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -Force -Verbose
Write-Output $VM.Name "has been deallocated successfully"
Write-Output "Writing output to workspace"
Get-AzureRmVM -Status | Where-Object {($_.tags.Project -eq "DevOps") -And ($_.tags.Test -eq "ernest")} | Select Name, ResourceGroupName, PowerState > VM_Stopped_Info-$(get-date -f yyyy-MM-dd).tsv
}
else
{
Write-Output "No option selected, select an option"
}
}
Write-Output "Script complete"
I had to add the $env:OPTION parameter to the Execute Powershell window. It works now
I would wrap everything inside foreach($computer in $computers) in a Start-Job to make them run simultaneously. The only problem is, I need to wait for all the jobs to complete before I do the ConvertTo-Json at the bottom.
$sb = "OU=some,OU=ou,DC=some,DC=domain"
$computers = Get-ADComputer -Filter {(Enabled -eq $true)} -SearchBase "$sb" -Properties *
$hasmanufacturer = New-Object System.Collections.Generic.List[System.Object]
foreach($computer in $computers)
{
$drives = try{#(Get-WMIObject -Class Win32_CDROMDrive -Property * -ComputerName $computer.Name -ErrorAction Stop)} catch {$null}
foreach($drive in $drives)
{
if($drive.Manufacturer)
{
$hasmanufacturer.Add($computer)
continue
}
} # inner foreach
}
ConvertTo-Json $hasmanufacturer
Use a Get-Job | Wait-Job before executing the ConvertTo-Json
How about using the array of computer names as a parameter to Invoke-Command. It will run, by default, 32 concurrent remote sessions. The number can be changed with the -Throttle parameter.
$computers = Get-ADComputer -Filter {(Enabled -eq $true)} -SearchBase "OU=Servers,DC=xxx,DC=com" -Properties Name |
Where-Object { $_.Name -match 'LAX_*' } |
ForEach-Object { $_.Name }
$computers
$j = Invoke-Command `
-ComputerName $computers `
-ScriptBlock { Get-WMIObject -Class Win32_CDROMDrive -Property * -ErrorAction Stop } `
-AsJob
while ( (Get-Job -Id $j.Id).Status -eq 'Running') {}
Get-Job -Id $j.Id | Wait-Job
$results = Receive-Job -Id $j.Id
$results
Get-AzureRmVM -ResourceGroupName RG-VNETS |
ForEach-Object {
Get-AzureRmVM -ResourceGroupName RG-VNETS -Name $_.Name -Status
} |
ForEach-Object {
if (-Not ($_.Statuses[1].DisplayStatus -like "*deallocated*")) {
Stop-AzureRmVM -ResourceGroupName RG-VNETS -Name $_.Name -Force
}
}
I've got this script that stops all my Azure VMs, the catch here is that this script shuts down one VM at a time.
i.e. if I have three VMs: VM1, VM2, VM3
The script doesn't shut down VM2 until VM1 is fully shutdown and so on. I don't know if there's a way to tell PowerShell not to wait for each VM to be fully shutdown to proceed with the following one.
There is already a feature request on GitHub for doing such operations asynchronously which should be implemented in the near future.
In the meantime you could do a workaround like the following using the PoshRSJob module - just replace temp4so with your resource group name
# Install PoshRSJob if necessary
#
# Install-Module PoshRSJob
Login-AzureRmAccount
$start = Get-Date
$jobs = Get-AzureRmVM -ResourceGroupName temp4so |
% {
Get-AzureRmVM -ResourceGroupName temp4so -Name $_.Name -Status
} |
% {
if (-Not ($_.Statuses[1].DisplayStatus -like "*deallocated*")) {
$vm = $_
Start-RSJob {
Stop-AzureRmVM -ResourceGroupName temp4so -Name ($using:vm).Name -Force
}
}
}
$jobs | Wait-RSJob | Receive-RSJob
$jobs | Remove-RSJob
$end = Get-Date
Write-Host ("Stopping took {0}" -f ($end - $start))
which in my test case with 3 VMs resulted in output similar to the following which shows that the operations where done in parallel
OperationId :
Status : Succeeded
StartTime : 24.09.2016 18:49:10
EndTime : 24.09.2016 18:51:32
Error :
OperationId :
Status : Succeeded
StartTime : 24.09.2016 18:49:11
EndTime : 24.09.2016 18:51:22
Error :
OperationId :
Status : Succeeded
StartTime : 24.09.2016 18:49:11
EndTime : 24.09.2016 18:51:22
Error :
Stopping took 00:02:32.9115538
Note: You cannot simply use the standard Start-Job to offload the sync. operations to a background job as the newly created PowerShell instances in the background do not share the context with your initial session and therefore would require you to authenticate again for each of those sessions. As PoshRSJob uses PowerShell runspaces within the initial PowerShell instance it does not require to authenticate again.
They implemented -AsJob per the GitHub feature request DAXaholic linked. Here's an example taken from the GitHub comments section.
$VMList = Get-AzureRmVM -ResourceGroupName $resourceGroupName
$JobList = #()
foreach ($vm in $VMList) {
$JobList += Stop-AzureRmVm -ResourceGroupName $resourceGroupName -Name $vm -Force -AsJob | Add-Member -MemberType NoteProperty -Name VMName -Value $vm.Name -PassThru
}
and if you want to wait for the jobs to finish with cleanup:
$JobList | Wait-Job | Receive-Job
$JobList | Remove-Job