Skip to content

Instantly share code, notes, and snippets.

@dazfuller
Created January 30, 2017 16:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dazfuller/41f56214d8ea56c19de6089d35e296b1 to your computer and use it in GitHub Desktop.
Save dazfuller/41f56214d8ea56c19de6089d35e296b1 to your computer and use it in GitHub Desktop.
<#
.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