Created
January 30, 2017 16:10
-
-
Save dazfuller/41f56214d8ea56c19de6089d35e296b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.DESCRIPTION | |
Finds and removes VMs which have not been started within a defined period of time | |
.NOTES | |
AUTHOR: @dazfuller | |
LASTEDIT: Jan 26, 2017 | |
#> | |
# Define the number of days after which a VM is considered dead | |
$NumberDays = 7 | |
# Get all of the VMs in the subscription which are in an deallocated state | |
$VMs = Get-AzureRmVM -Status | Where-Object -FilterScript { $_.PowerState -like "*deallocated*" } | |
ForEach ($VM in $VMs) | |
{ | |
# Find the most recent successful start event for the VM | |
$StartActivity = Get-AzureRmLog -ResourceId $VM.Id -StartTime $StopActivity.EventTimestamp | | |
Where-Object -FilterScript { $_.OperationName -like "*start*" } | | |
Sort-Object -Property EventTimeStamp -Descending | | |
Select-Object -First 1 | |
$RemoveVM = $false | |
if ($StartActivity -eq $null) | |
{ | |
# If there is no recent start activity for the VM then flag it for removal | |
Write-Output "$($VM.Id) has no recent start event" | |
$RemoveVM = $true | |
} | |
else | |
{ | |
# Get the first successful deallocate event after the previous start event | |
$StopActivity = Get-AzureRmLog -ResourceId $VM.Id -StartTime $StartActivity.EventTimestamp | | |
Where-Object -FilterScript { $_.OperationName -like "*deallocate*" -and $_.Status -eq "succeeded" } | | |
Sort-Object -Property EventTimeStamp | | |
Select-Object -First 1 | |
if ($StopActivity -ne $null -and (Get-Date).Subtract($StopActivity.EventTimestamp).Days -ge $NumberDays) | |
{ | |
# If the date of the stop event is greater or equal to 14 days then flag the VM for removal. | |
# If there is no stop event (i.e. the VM has only just been provisioned) then it will be ignored | |
# by this | |
Write-Output "$($VM.Id) was stopped $NumberDays or more days ago and has not been restarted" | |
$RemoveVM = $true | |
} | |
} | |
# If the VM has been flagged for removal then remove it | |
if ($RemoveVM) | |
{ | |
# If the resource group was created to host the VM then remove the whole resource group | |
if ($VM.Tags["displayName"] -eq "VM Resource Group") | |
{ | |
Remove-AzureRmResourceGroup -Name $VM.ResourceGroupName -WhatIf | |
} | |
else | |
{ | |
# Remove associated resources | |
# | |
# Get the VMs disks | |
$Disks = Get-VMDisks -VM $VM | |
# Get the network interface cards | |
$Nics = Get-VMNics -VM $VM | |
# - Public IP | |
$PublicIPs = $VM | Get-AzureRmPublicIpAddress | |
#Remove the VM and it's resources | |
Remove-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -WhatIf | |
# Remove all disks | |
ForEach ($Disk in $Disks) | |
{ | |
Delete-Disk $VM -DiskUri $Disk | |
} | |
# Remove the NICs | |
ForEach ($Nic in $Nics) | |
{ | |
Remove-AzureRmNetworkInterface -ResourceGroupName $VM.ResourceGroupName -Name $Nic -WhatIf | |
} | |
# Remove the Public IP addresses | |
ForEach ($PublicIP in $PublicIPs) | |
{ | |
Remove-AzureRmPublicIpAddress -ResourceGroupName $VM.ResourceGroupName -Name $PublicIP.Name -WhatIf | |
} | |
} | |
} | |
} | |
# Get the network interface resources for a given VM | |
Function Get-VMNics | |
{ | |
Param | |
( | |
[Parameter(Mandatory=$true)] | |
$VM | |
) | |
# Get each NIC ID and return a list of just the name component | |
$VM.NetworkInterfaceIDs | ForEach-Object -Process { $_ -split "/" | Select-Object -Last 1 } | |
} | |
# Gets all disks attached to a given VM (both OS and data) | |
Function Get-VMDisks | |
{ | |
Param | |
( | |
[Parameter(Mandatory=$true)] | |
$VM | |
) | |
$Disks = @() | |
# Get the OS disk as there can only be one | |
$Disks += $VM.StorageProfile.OsDisk.Vhd.Uri | |
# Add each data disk to the collection | |
ForEach ($Disk in $VM.StorageProfile.DataDisks) | |
{ | |
$Disks += $Disk.Vhd.Uri | |
} | |
$Disks | |
} | |
Function Delete-Disk | |
{ | |
Param | |
( | |
[Parameter(Mandatory=$true)] | |
$VM, | |
[Parameter(Mandatory=$true)] | |
[string] $DiskUri | |
) | |
# Get the parts needed from the URI | |
$SAParts = $DiskUri -split "/" | |
$SAName = ($SAParts[2] -split "\.")[0] | |
$Container = $SAParts[-2] | |
$Blob = $SAParts[-1] | |
# Get the storage account information and a context | |
$SA = Get-AzureRmStorageAccount -ResourceGroupName $VM.ResourceGroupName -Name $SAName | |
$SAKey = ($SA | Get-AzureRmStorageAccountKey)[0].Value | |
$SAContext = New-AzureStorageContext -StorageAccountName $SA.StorageAccountName -StorageAccountKey $SAKey -Protocol Https | |
# Remove the storage blob | |
Get-AzureStorageBlob -Container $Container -Blob $Blob -Context $SAContext | Remove-AzureStorageBlob -WhatIf | |
# Clean up the container | |
$RemainingBlobs = Get-AzureStorageContainer -Name $Container -Context $SAContext | Get-AzureStorageBlob | |
if ($RemainingBlobs -eq $null) | |
{ | |
Remove-AzureStorageContainer -Name $Container -Context $SAContext -WhatIf | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment