Deallocate an Azure VM from itself

Bild des Benutzers Marcel Meurer

In these days I’m dealing with the automation of starting and stopping Azure virtual machines. I do this to avoid unnecessary costs for customers running Citrix or RDS workers on Azure. I translated a piece of my work into a PowerShell script to de-allocate the VM on which it is running.

Azure Instance Metadata Service

To get information about the running VM I use Azure Instance Metadata Service (https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service). This information contains the public ip address, vm size, os type and a lot more. To identify this Azure VM for later deallocation later, I need some specific information: the vmId. You can get meta data using PowerShell:

$md=Invoke-RestMethod -Headers @{"Metadata"="true"} -URI http://169.254.169.254/metadata/instance?api-version=2017-08-01

 $md.compute.vmId contains the unique identifier for the running VM. I will use this id later to match the correct VM for deallocating.

Service Principal Account

I want to deallocate the VM automatically without logging-in myself. Therefore I have to use a service principal account. It is pretty easy to create a service principal account:

Go to the Azure portal, open the Azure Active Directory of your subscription(s) and choose „App registration“ – “New application registration”:

 

Give your app a name (e.g.: PowerShell-Services) and enter a sign-on URL. User http://localhost for this url and click “Create”.

 

Select the previously created app, select “Keys” and go on: Enter a name, select an expiration time and save the configuration: Important: copy your key directly after saving it:

 

Also copy the application id for later use:

 

And last: copy the tenant id from your Azure Active Directory by selecting it and click on “Properties”:

 

You have all data you need to logon unattended now:

App-id:

21acad78-9006-4f22-9156-xxxxxxxxxxxxx

App-key:

sjsgUk7a5hgaTkZGuOGeLxxxxxxxxxxxxxxxJ0lXs2/o=

Tenant-id:

06522f94-0d15-4fba-aac8-xxxxxxxxxxxxxxx

Give the right permissions

In my case I will use this single app/service principal to shut down and deallocate every VM in my Azure subscription (this can be differing in your case and can be a security breach if another user gets the logon data from your PowerShell script). To give the app the right to work with VM I added in on subscription level: Select Subscriptions -> subscription -> Access Control (IAM) -> Add

 

Role: Virtual Machine Contributor*

Assign to: Azure AD user, group, or application

Select: You application/service principal

Save

 

* The app/service principal has the permission to start/stop/modify/… all VM’s in the subscription. If you need more granularity you can create custom roles (https://www.sepago.de/blog/2017/07/13/preventing-administrative-users-to-change-critical-network-settings-in-an-azure-hub)

The script

To find from your VM the corresponding one in Azure use this PowerShell script with the service principal credentials and deallocate it:

$AppId="21acad78-9006-4f22-9156-xxxxxxxxxxxxx"

$AppKey="sjsgUk7a5hgaTkZGuOGeLxxxxxxxxxxxxxxxJ0lXs2/o="

$TenantId="06522f94-0d15-4fba-aac8-xxxxxxxxxxxxxxx"



$md=Invoke-RestMethod -Headers @{"Metadata"="true"} -URI http://169.254.169.254/metadata/instance?api-version=2017-08-01

$Cred = New-Object System.Management.Automation.PSCredential ($AppId, (ConvertTo-SecureString $AppKey -AsPlainText -Force))

Login-AzureRmAccount -Credential $cred -ServicePrincipal -TenantId $TenantId



# enumerate subscriptions

$subscritions=Get-AzureRmSubscription

foreach ($subscription in $subscritions)

{

    Write-Host("Working on subscription: $($subscription.name)")

    Get-AzureRmSubscription -SubscriptionId $subscription.Id |Out-Null

    $vms=Get-AzureRmVM

    Write-Host("Number of VMs: $($vms.Count)")

    $vm=@($vms | where vmId -EQ $md.compute.vmId)

    if ($vm -ne 0)

    {

        Write-Host("Deallocating $($vm.Name)")

        Stop-AzureRmVM -Id $vm[0].Id -Name $vm[0].Name -Force

    } else

    {

        Write-Host("VM not found in this subscription")

    }
}

Hint:

@skillriver wrote a blog shutting to Shutdown and Deallocate an Azure VM using Managed Service Identity. This avoids to create an Azure AD application: 

https://gotoguy.blog/2018/01/17/shutdown-and-deallocate-an-azure-vm-using-managed-service-identity-and-instance-metadata-service/

Neuen Kommentar schreiben
Durch Absenden dieses Formulars akzeptieren Sie die Mollom Privatsphärenrichtlinie.