Background
Most of the services in Azure such as Storage Accounts, Key Vaults or AppService Websites must have globally unique names, where the fully qualified domain name (aka FQDN) for the service uses the name you selected and the suffix for the specific service. For example, for Key Vaults its vault.azure.net and for WebApps its azurewebsites.net
The Azure portal can help you determine the name availability during the service creation, but there’s no built-in PowerShell cmdlet or azure cli command to do so for ARM services (in the old ASM days, we had the Test-AzureName PowerShell cmdlet we could use to check for a classic cloud services name availability).
For scenarios where you have an automated deployment and don’t want the deployment failing because of the name availability, you’d want to have a simple command that returns a true/false boolean value that determines if the name is already taken or not.
Proposed solution
Several of the Azure providers have an API that exposes a checkNameAvailability action that you can use the test the name’s availability. Each provider requires and accepts a different set of parameters, where the most important ones are obviously the name you want to check and the service type.
To get a list of the providers that support the checkNameAvailability action, you can use the following PowerShell command:
Get-AzResourceProvider | Where-Object { $_.ResourceTypes.ResourceTypeName -eq 'checkNameAvailability' } | Select-Object ProviderNamespace
That outputs the following:
ProviderNamespace ----------------- Microsoft.Sql Microsoft.Web Microsoft.DBforMySQL Microsoft.Media Microsoft.Cdn Microsoft.ApiManagement Microsoft.BotService Microsoft.Storage Microsoft.KeyVault Microsoft.Management microsoft.support
Drilling down to one of the providers, we can see the list of API versions that support the action, and we can build the URI string to invoke
Get-AzResourceProvider -ProviderNamespace Microsoft.Web | Where-Object { $_.ResourceTypes.ResourceTypeName -eq 'checkNameAvailability' } | Select-Object -ExpandProperty ResourceTypes | Select-Object -ExpandProperty ApiVersions
Invoking Azure APIs using PowerShell is simple enough, you just need the bearer token, the URI to the API action and the needed parameters for the action. For some of the APIs we need a subscription ID to work with.
The important and main function is Test-AzNameAvailability:
function Test-AzNameAvailability { param( [Parameter(Mandatory = $true)] [string] $AuthorizationToken, [Parameter(Mandatory = $true)] [string] $SubscriptionId, [Parameter(Mandatory = $true)] [string] $Name, [Parameter(Mandatory = $true)] [ValidateSet( 'ApiManagement', 'KeyVault', 'ManagementGroup', 'Sql', 'StorageAccount', 'WebApp')] $ServiceType ) $uriByServiceType = @{ ApiManagement = 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.ApiManagement/checkNameAvailability?api-version=2019-01-01' KeyVault = 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.KeyVault/checkNameAvailability?api-version=2019-09-01' ManagementGroup = 'https://management.azure.com/providers/Microsoft.Management/checkNameAvailability?api-version=2018-03-01-preview' Sql = 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Sql/checkNameAvailability?api-version=2018-06-01-preview' StorageAccount = 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Storage/checkNameAvailability?api-version=2019-06-01' WebApp = 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Web/checkNameAvailability?api-version=2019-08-01' } $typeByServiceType = @{ ApiManagement = 'Microsoft.ApiManagement/service' KeyVault = 'Microsoft.KeyVault/vaults' ManagementGroup = '/providers/Microsoft.Management/managementGroups' Sql = 'Microsoft.Sql/servers' StorageAccount = 'Microsoft.Storage/storageAccounts' WebApp = 'Microsoft.Web/sites' } $uri = $uriByServiceType[$ServiceType] -replace ([regex]::Escape('{subscriptionId}')), $SubscriptionId $body = '"name": "{0}", "type": "{1}"' -f $Name, $typeByServiceType[$ServiceType] $response = (Invoke-WebRequest -Uri $uri -Method Post -Body "{$body}" -ContentType "application/json" -Headers @{Authorization = $AuthorizationToken }).content $response | ConvertFrom-Json | Select-Object @{N = 'Name'; E = { $Name } }, @{N = 'Type'; E = { $ServiceType } }, @{N = 'Available'; E = { $_ | Select-Object -ExpandProperty *available } }, Reason, Message }
To use it, you first have to get a bearer token, for either the current logged on user or for a service principal using one of the two functions Get-AccesTokenFromServicePrincipal or Get-AccesTokenFromCurrentUser:
function Get-AccesTokenFromServicePrincipal { param( [string] $TenantID, [string] $ClientID, [string] $ClientSecret ) $TokenEndpoint = 'https://login.windows.net/{0}/oauth2/token' -f $TenantID $ARMResource = 'https://management.core.windows.net/' $Body = @{ 'resource' = $ARMResource 'client_id' = $ClientID 'grant_type' = 'client_credentials' 'client_secret' = $ClientSecret } $params = @{ ContentType = 'application/x-www-form-urlencoded' Headers = @{'accept' = 'application/json' } Body = $Body Method = 'Post' URI = $TokenEndpoint } $token = Invoke-RestMethod @params ('Bearer ' + ($token.access_token).ToString()) } function Get-AccesTokenFromCurrentUser { $azContext = Get-AzContext $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList $azProfile $token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId) ('Bearer ' + $token.AccessToken) }
To get the current (already logged in) user’s bearer token, use:
$AuthorizationToken = Get-AccesTokenFromCurrentUser
Or to get a Service Principal (App Registration) bearer token, use:
$AuthorizationToken = Get-AccesTokenFromServicePrincipal ` -TenantID '<Directory Tenant ID>' ` -ClientID '<Application Client ID>' ` -ClientSecret '<Application Client Secret>'
And then, to test for the name availability for some of the services you can use:
Test-AzNameAvailability -Name martin -ServiceType ApiManagement -AuthorizationToken $AuthorizationToken -SubscriptionId $subscriptionId Test-AzNameAvailability -Name kv -ServiceType KeyVault -AuthorizationToken $AuthorizationToken -SubscriptionId $subscriptionId Test-AzNameAvailability -Name root -ServiceType ManagementGroup -AuthorizationToken $AuthorizationToken -SubscriptionId $subscriptionId Test-AzNameAvailability -Name sqlsrv1 -ServiceType Sql -AuthorizationToken $AuthorizationToken -SubscriptionId $subscriptionId Test-AzNameAvailability -Name storage -ServiceType StorageAccount -AuthorizationToken $AuthorizationToken -SubscriptionId $subscriptionId Test-AzNameAvailability -Name www -ServiceType WebApp -AuthorizationToken $AuthorizationToken -SubscriptionId $subscriptionId
This outputs:
Name : martin Type : ApiManagement Available : False reason : AlreadyExists message : martin is already in use. Please select a different name. Name : kv Type : KeyVault Available : False reason : Invalid message : Vault name must be between 3-24 alphanumeric characters. The name must begin with a letter, end with a letter or digit, and not contain consecutive hyphens. Name : root Type : ManagementGroup Available : False reason : AlreadyExists message : The group with the specified name already exists Name : martin Type : Sql Available : False reason : AlreadyExists message : Specified server name is already used. Name : storage Type : StorageAccount Available : False reason : AlreadyExists message : The storage account named storage is already taken. Name : www Type : WebApp Available : False reason : AlreadyExists message : Hostname 'www' already exists. Please select a different name.
So in a full script, you could use something like:
$params = @{ Name = 'myCoolWebSite' ServiceType = 'WebApp' AuthorizationToken = Get-AccesTokenFromCurrentUser SubscriptionId = $subscriptionId } if((Test-AzNameAvailability @params).Available) { # Continue with the deployment }
Closing notes
The checkNameAvailability API is available in several Azure providers, but because of time constraints I implemented the test only for a few of them (ApiManagement, KeyVault, ManagementGroup, Sql, StorageAccount and WebApp), so you are more than welcome to improve it.
The complete code with the functions and examples is published under my github Azure repository as: https://github.com/martin77s/Azure/blob/master/PS/Test-AzNameAvailability.ps1
You must log in to post a comment.