I like to work with REST APIs in the past, so I decided to head over to the Azure REST API and create an Azure VM with Powershell and Azure Rest API. I thought I would do it in a quick session between two appointments but failed miserably. It took me a few hours to figure out how to deal with Azure REST API, especially with the correct JSON, to provide the Image I need.
So, this is a walk-through post because some steps need to be prepared.
In the end, you will get a PowerShell Script which will create an Azure VM using Azure REST API without any Credentials Prompts. We need an Azure App Registration and assign the correct Roles, Ressource Group, existing NEtwork Interface, and other Properties to achieve this.
I have written a GRAPH API Series, where i explained how to create an Azure App Registration, so if there is something not clear, read that: MS Graph API
Azure App Regsitration for Azure REST API
Of course, we want to have a Script that will run without user interaction, so we need to create an Azure App Registration for this.
So let’s create an Azure App Regsitration and note the Application and Tenant ID because we need that for our Script.
Also, create a Client Secret and copy that Secret to use it for the PowerShell Script.
Use those Values in the Script at that Place
Now let’s assign a Role to that App Registration and remember the Name you gave your App.
Navigate to your Subscription, where the VM is created, and navigate to “Access control (IAM).”
Click “Add” and “Add role assignment.”
As Role select “Contributor” and enter the App Name in “Select” and choose the App
Click “Save” to make sure the App has the Role assigned.
As we are right now in the Subscription Section, navigate back to “Overview,” and note the Subscription ID for the PowerShell Script
and enter that ID to the PowerShell Script.
Script Variables
Now let’s go through our Variables in the Script and how to use them.
VM Details
Those three should be easy. Make sure the RessourceGroup and NetworkInterface exist.
The VMName can be anything and will be created during the Script.
Location
Location Value can be a little tricky to get the correct value.
The Easiest Way to get a list with all Locations in your Subscription, run the following CMDlet from the AZ Module
Get-AzLocation
that will result in a list of Locations
To help, here is a Table from 18.08.2021; use the “Location” Value for the Script.
Location | DisplayName | Providers |
---|---|---|
eastasia | East Asia | System.Collections.Generic.List`1[System.String] |
southeastasia | Southeast Asia | System.Collections.Generic.List`1[System.String] |
centralus | Central US | System.Collections.Generic.List`1[System.String] |
eastus | East US | System.Collections.Generic.List`1[System.String] |
eastus2 | East US 2 | System.Collections.Generic.List`1[System.String] |
westus | West US | System.Collections.Generic.List`1[System.String] |
northcentralus | North Central US | System.Collections.Generic.List`1[System.String] |
southcentralus | South Central US | System.Collections.Generic.List`1[System.String] |
northeurope | North Europe | System.Collections.Generic.List`1[System.String] |
westeurope | West Europe | System.Collections.Generic.List`1[System.String] |
japanwest | Japan West | System.Collections.Generic.List`1[System.String] |
japaneast | Japan East | System.Collections.Generic.List`1[System.String] |
brazilsouth | Brazil South | System.Collections.Generic.List`1[System.String] |
australiaeast | Australia East | System.Collections.Generic.List`1[System.String] |
australiasoutheast | Australia Southeast | System.Collections.Generic.List`1[System.String] |
southindia | South India | System.Collections.Generic.List`1[System.String] |
centralindia | Central India | System.Collections.Generic.List`1[System.String] |
westindia | West India | System.Collections.Generic.List`1[System.String] |
canadacentral | Canada Central | System.Collections.Generic.List`1[System.String] |
canadaeast | Canada East | System.Collections.Generic.List`1[System.String] |
uksouth | UK South | System.Collections.Generic.List`1[System.String] |
ukwest | UK West | System.Collections.Generic.List`1[System.String] |
westcentralus | West Central US | System.Collections.Generic.List`1[System.String] |
westus2 | West US 2 | System.Collections.Generic.List`1[System.String] |
koreacentral | Korea Central | System.Collections.Generic.List`1[System.String] |
koreasouth | Korea South | System.Collections.Generic.List`1[System.String] |
francecentral | France Central | System.Collections.Generic.List`1[System.String] |
francesouth | France South | System.Collections.Generic.List`1[System.String] |
australiacentral | Australia Central | System.Collections.Generic.List`1[System.String] |
australiacentral2 | Australia Central 2 | System.Collections.Generic.List`1[System.String] |
uaecentral | UAE Central | System.Collections.Generic.List`1[System.String] |
uaenorth | UAE North | System.Collections.Generic.List`1[System.String] |
southafricanorth | South Africa North | System.Collections.Generic.List`1[System.String] |
southafricawest | South Africa West | System.Collections.Generic.List`1[System.String] |
switzerlandnorth | Switzerland North | System.Collections.Generic.List`1[System.String] |
switzerlandwest | Switzerland West | System.Collections.Generic.List`1[System.String] |
germanynorth | Germany North | System.Collections.Generic.List`1[System.String] |
germanywestcentral | Germany West Central | System.Collections.Generic.List`1[System.String] |
norwaywest | Norway West | System.Collections.Generic.List`1[System.String] |
norwayeast | Norway East | System.Collections.Generic.List`1[System.String] |
brazilsoutheast | Brazil Southeast | System.Collections.Generic.List`1[System.String] |
westus3 | West US 3 | System.Collections.Generic.List`1[System.String] |
swedencentral | Sweden Central | System.Collections.Generic.List`1[System.String] |
Image Settings
Now there is the most tricky part, at least for me in that example. To Find the correct Server Image to install. In our example, I use an existing Image from the Marketplace and install the latest Server 2019.
So, how to get that Values?
Run that command to get a list of Publisher
Get-AzVMImagePublisher -Location $Location | Select PublisherName
$publisher = "MicrosoftWindowsServer"
Now, run the following to get the Offer Names
Get-AzVMImageOffer -Location $Location -PublisherName $publisher | select Offer
$offer = "WindowsServer"
Now its time to get the SKU with that command
Get-AzVMImageSku -Location $Location -PublisherName $publisher -Offer $offer | select SKUS
$sku = "2019-Datacenter"
You can use the “Latest” Value to get the newest Image, but you can also use one of the values below.
Get-AzVMImage -Location $Location -PublisherName $publisher -Offer $offer -Sku $sku | Select Version
The rest is now the whole Script where we connect to Azure Rest API with the Azure App Registration Details and send a JSON to create the VM.
In the end, we will wait until the Deployment is successful with a do/until the command.
The Script to create an Azure VM with PowerShell and Azure REST API
$applicationId = 'your Application ID'
$tenantId = 'your Tenant ID'
$secret = 'your Secret'
$subscriptionId = 'your Subscription ID'
#VM Details
$RessourceGroupName = "RG_TEST_TechguyNewVM"
$VMname = "TESTVM"
$NetworkInterfaceName = "TESTVM-NIC"
$vmSize="Standard_D1_v2"
#Location
$Location = "northeurope"
#Image Settings
$publisher = "MicrosoftWindowsServer"
$offer = "WindowsServer"
$sku = "2019-Datacenter"
$version = "latest"
#VM Login
$User="User"
$PW"superSa3PW!"
#API Version
$apiversion="2021-03-01"
$param = @{
Uri = "https://login.microsoftonline.com/$tenantId/oauth2/token?api-version=$apiversion";
Method = 'Post';
Body = @{
grant_type = 'client_credentials';
resource = 'https://management.core.windows.net/';
client_id = $applicationId;
client_secret = $secret
}
}
$result = Invoke-RestMethod @param
$token = $result.access_token
$headers = @{
"Authorization" = "Bearer $($token)"
"Content-type" = "application/json"
}
$URL = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$RessourceGroupName/providers/Microsoft.Compute/virtualMachines/$($VMname)?api-version=$apiversion"
$bodyNewVMexistingNetwork = @"
{
"location": "$location",
"properties": {
"hardwareProfile": {
"vmSize": "$VMsize"
},
"storageProfile": {
"imageReference": {
"sku": "$sku",
"publisher": "$publisher",
"version": "$version",
"offer": "$offer"
},
"osDisk": {
"caching": "ReadWrite",
"managedDisk": {
"storageAccountType": "Standard_LRS"
},
"name": "myVMosdisk",
"createOption": "FromImage"
}
},
"osProfile": {
"adminUsername": "$User",
"computerName": "$VMname",
"adminPassword": "$PW"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "/subscriptions/$subscriptionId/resourceGroups/$RessourceGroupName/providers/Microsoft.Network/networkInterfaces/$NetworkInterfaceName",
"properties": {
"primary": true
}
}
]
}
}
}
}
}
"@
Invoke-RestMethod -Method PUT -URI $URL -headers $headers -body $bodyNewVMexistingNetwork
### Get Status
do {
$URLtoGet = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$RessourceGroupName/providers/Microsoft.Compute/virtualMachines/$($VMname)?api-version=$apiversion"
$Result = Invoke-RestMethod -Method GET -URI $URLtoGet -headers $headers
$result.properties.provisioningState
Start-Sleep -Seconds 5
} until ($result.properties.provisioningState -ne "Creating")
##Connect-AzAccount
#Get-AzVMImagePublisher -Location $Location | Select PublisherName
#Get-AzVMImageOffer -Location $Location -PublisherName $publisher | select Offer
#Get-AzVMImageSku -Location $Location -PublisherName $publisher -Offer $offer | select SKUS
#Get-AzVMImage -Location $Location -PublisherName $publisher -Offer $offer -Sku $sku | Select Version
##
#
#Get-AzVMImage -Location $Location -PublisherName $publisher -Offer $offer -Skus $sku -Version $version
Self Service Azure VM Creation with au2mator Self Service Portal
GitHub Repo
Here is the Azure REST API GitHub Repo with other examples: Seidlm/Microsoft-Azure Examples (github.com)
External Docs
Virtual Machines – REST API (Azure Compute) | Microsoft Docs
Michael Seidl aka Techguy
au2mate everything