Azure,  Powershell

Invoke Azure Resource Provider actions with Powershell

Recently I started to convert all my script and modules to run on Powershell Core and I soon realized I have a problem. When it comes to Azure resources, I work with a combination of both ARM and RDFE and all is good in Powershell Desktop on Windows: just load (or let Powershell load for me) both the Azure module and the combination of Az.<RPName> components I need. I have now changed to Powershell Core as my default on Windows (on macOS/Linux I don’t really have a choice 😉) but I encountered compatibility and runtime errors with the Azure and Azure.Storage modules, even if I import Windows Compatibility first. Typically Powershell Core complains about duplicate assemblies already loaded but I also got weird runtime errors trying to run basic cmdlets. Since I want to move to Powershell Core anyway I decided to not try to figure out how to solve the problem but rather move to ARM completely, writing my own cmdlets and functions not otherwise available.

Luckily the Resource Providers I am interested in (Cloud Services for example) expose APIs and actions for Classic (RDFE) resources, so to get started I just need to find the right one 🤓. Enter Azure Resource Manager Resource Provider Operations: the list is too long to show here (and it would be soon outdated anyway) but it’s easy enough to get it from Powershell directly:

PS /> Get-AzProviderOperation | ? Operation -Like Microsoft* | measure

Count             : 5402
Average           : 
Sum               : 
Maximum           : 
Minimum           : 
StandardDeviation : 
Property          : 


PS /> Get-AzProviderOperation | ? Operation -Like Microsoft* | Format-Table ProviderNamespace, Operation, OperationName -AutoSize

ProviderNamespace                 Operation                                       OperationName
-----------------                 ---------                                       -------------
Domain Services Resource Provider Microsoft.AAD/unregister/action                 Unregister Domain Service
Domain Services Resource Provider Microsoft.AAD/register/action                   Register Domain Service
Domain Services Resource Provider Microsoft.AAD/domainServices/read               Read Domain Service
Domain Services Resource Provider Microsoft.AAD/domainServices/write              Write Domain Service
Domain Services Resource Provider Microsoft.AAD/domainServices/delete             Delete Domain Service
Domain Services Resource Provider Microsoft.AAD/locations/operationresults/read   
Domain Services Resource Provider Microsoft.AAD/Operations/read                   
Domain Services Resource Provider Microsoft.AAD/domainServices/oucontainer/read   Read Ou Container
Domain Services Resource Provider Microsoft.AAD/domainServices/oucontainer/write  Write Ou Container
Domain Services Resource Provider Microsoft.AAD/domainServices/oucontainer/delete Delete Ou Container

[...]

As I’m writing this, there are no ARM cmdlets to deal with Cloud Services but they are exposed as ARM resources anyway through the Compute resource provider, just looks for Microsoft.ClassicCompute/domainNames:

PS /> Get-AzResource -ResourceType Microsoft.ClassicCompute/domainNames

Name              : eus2testcloudservice
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames
Location          : eastus2
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2resourcegroup/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice

As any other Resource Provider, Microsoft.ClassicCompute allows to query its properties and take certain actions on its resources. Get-AzResourceProviderAction returns the list of available operations for a given Resource Provider:

PS /> Get-AzResourceProviderAction -OperationSearchString Microsoft.ClassicCompute/* | Format-List Operation, ResourceName, Description           

Operation    : Microsoft.ClassicCompute/register/action
ResourceName : 
Description  : Register to Classic Compute

Operation    : Microsoft.ClassicCompute/checkDomainNameAvailability/action
ResourceName : 
Description  : Checks the availability of a given domain name.

Operation    : Microsoft.ClassicCompute/moveSubscriptionResources/action
ResourceName : 
Description  : Move all classic resources to a different subscription.

Operation    : Microsoft.ClassicCompute/validateSubscriptionMoveAvailability/action
ResourceName : 
Description  : Validate the subscription's availability for classic move operation.

Operation    : Microsoft.ClassicCompute/domainNames/read
ResourceName : Domain Name
Description  : Return the domain names for resources.

Operation    : Microsoft.ClassicCompute/domainNames/write
ResourceName : Domain Name
Description  : Add or modify the domain names for resources.

Operation    : Microsoft.ClassicCompute/domainNames/delete
ResourceName : Domain Name
Description  : Remove the domain names for resources.

Operation    : Microsoft.ClassicCompute/domainNames/swap/action
ResourceName : Domain Name
Description  : Swaps the staging slot to the production slot.

[...]

If you want to restrict your search to a specific Type, just modify the query like this:

PS /> Get-AzResourceProviderAction -OperationSearchString Microsoft.ClassicCompute/domainNames/* | Format-List Operation, ResourceName, Description      

Operation    : Microsoft.ClassicCompute/domainNames/read
ResourceName : Domain Name
Description  : Return the domain names for resources.

Operation    : Microsoft.ClassicCompute/domainNames/write
ResourceName : Domain Name
Description  : Add or modify the domain names for resources.

Operation    : Microsoft.ClassicCompute/domainNames/delete
ResourceName : Domain Name
Description  : Remove the domain names for resources.

Operation    : Microsoft.ClassicCompute/domainNames/swap/action
ResourceName : Domain Name
Description  : Swaps the staging slot to the production slot.

Operation    : Microsoft.ClassicCompute/domainNames/serviceCertificates/read
ResourceName : Service Certificate
Description  : Returns the service certificates used.

Operation    : Microsoft.ClassicCompute/domainNames/serviceCertificates/write
ResourceName : Service Certificate
Description  : Add or modify the service certificates used.

Operation    : Microsoft.ClassicCompute/domainNames/serviceCertificates/delete
ResourceName : Service Certificate
Description  : Delete the service certificates used.

Operation    : Microsoft.ClassicCompute/domainNames/extensions/read
ResourceName : Domain Name Extension
Description  : Returns the domain name extensions.

Now for the fun part, let’s assume I want to read details about a Cloud Service Deployment Slots, the operation name is Microsoft.ClassicCompute/domainNames/slots:

PS /> Get-AzResource -ResourceType 'Microsoft.ClassicCompute/domainNames/slots' -ResourceName eus2testcloudservice -ResourceGroupName eus2testcloudservice -ApiVersion '2018-06-01'       

Name              : Staging
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots
Location          : 
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Staging

Name              : Production
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots
Location          : 
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Production

This tells us the Cloud Service has two deployments slots and we get their ResourceId. Now let’s assume I want to get more details about the Production slot: I could simply append the slot name to the ResourceType parameter (e.g. Microsoft.ClassicCompute/domainNames/slots/production) or I could use the ResourceId, like this:

PS /> Get-AzResource -ResourceId /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/production -ApiVersion '2018-06-01'

Name              : production
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots
Location          : 
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/production

At first sight the object returned is fairly simple but often in Powershell the output shown at the console is a formatted and cut-down version of the actual data returned by the command; in this case, if we inspect the returned object passing it to Get-Member we can see what else is available:

PS /> Get-AzResource -ResourceId /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/production -ApiVersion '2018-06-01' | Get-Member

   TypeName: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource
Name                  MemberType Definition
----                  ---------- ----------
Equals                Method     bool Equals(System.Object obj)
GetHashCode           Method     int GetHashCode()
GetType               Method     type GetType()
ToString              Method     string ToString()
ChangedTime           Property   System.Nullable[datetime] ChangedTime {get;set;}
CreatedTime           Property   System.Nullable[datetime] CreatedTime {get;set;}
ETag                  Property   string ETag {get;set;}
ExtensionResourceName Property   string ExtensionResourceName {get;set;}
ExtensionResourceType Property   string ExtensionResourceType {get;set;}
Id                    Property   string Id {get;set;}
Identity              Property   Microsoft.Azure.Management.ResourceManager.Models.Identity Identity {get;set;}
Kind                  Property   string Kind {get;set;}
Location              Property   string Location {get;set;}
ManagedBy             Property   string ManagedBy {get;set;}
Name                  Property   string Name {get;set;}
ParentResource        Property   string ParentResource {get;set;}
Plan                  Property   Microsoft.Azure.Management.ResourceManager.Models.Plan Plan {get;set;}
Properties            Property   psobject Properties {get;set;}
ResourceGroupName     Property   string ResourceGroupName {get;set;}
ResourceId            Property   string ResourceId {get;set;}
ResourceName          Property   string ResourceName {get;set;}
ResourceType          Property   string ResourceType {get;set;}
Sku                   Property   Microsoft.Azure.Management.ResourceManager.Models.Sku Sku {get;set;}
SubscriptionId        Property   string SubscriptionId {get;set;}
Tags                  Property   System.Collections.Generic.IDictionary[string,string] Tags {get;set;}
Type                  Property   string Type {get;set;}

Particularly interesting is the Properties property: at closer inspection you’ll see this contains the Cloud Service configuration (cscfg). It is possible to update the service configuration programmatically by updating this Properties value, something like:

$cscfg = Get-AzResource -ResourceType "Microsoft.ClassicCompute/domainNames/deploymentSlots/production" -ResourceName eus2testcloudservice -ResourceGroupName eus2testcloudservice -ApiVersion '2018-06-01'

[xml]$xml = $cscfg.Properties.configuration
$updatedSetting = $xml.ServiceConfiguration.Role.ConfigurationSettings.Setting | Where-Object {$_.name -eq 'SettingToUpdate'}
$updatedSetting.value = 'NewValue'
$updatedCscfg = @{"deploymentLabel" = "$($cscfg.Properties.DeploymentLabel)"; "configuration" = $xml.OuterXml}

Set-AzResource -ResourceType "Microsoft.ClassicCompute/domainNames/deploymentSlots/production" -ResourceName eus2testcloudservice -ResourceGroupName eus2testcloudservice -ApiVersion '2018-06-01' -Force -Properties $updatedCscfg

A Cloud Service runs on one or more instances (virtual machines) so it can be useful to know their state. When running this query I got an interesting error that’s worth highlighting:

PS /> Get-AzResource -ResourceId /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/production/roles/Orchestrator.Web.Cloud/roleInstances -ApiVersion '2018-06-01'

Get-AzResource : NoRegisteredProviderFound : No registered resource provider found for location 'northcentralus' and API version '2018-06-01' for type 'domainNames/slots/roles'. The supported api-versions are '2014-01-01, 2014-06-01, 2015-06-01, 2015-10-01, 2015-12-01, 2016-04-01, 2016-11-01'. The supported locations are 'eastasia, southeastasia, eastus, eastus2, westus, westus2, northcentralus, southcentralus, westcentralus, centralus, northeurope, westeurope, japaneast, japanwest, brazilsouth, australiaeast, australiasoutheast, southindia, centralindia, westindia, canadacentral, canadaeast, eastus2stage, northcentralusstage, uksouth, ukwest, koreacentral, koreasouth, francecentral, southafricanorth'.
At line:1 char:1
+ Get-AzResource  -ResourceId /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : CloseError: (:) [Get-AzResource], ErrorResponseMessageException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet

This error means the slot resource type is not configured to use the API version '2018-06-11': the error message suggests which API versions are available for this resource type (there is an alternative way to get the available APIs, just keep reading 😉). If I retry with one of the supported API versions I get this:

PS /> Get-AzResource -ResourceId /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/production/roles/WebRole/roleInstances -ApiVersion '2016-11-01'

Name              : WebRole_IN_0
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances
Location          :
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Production/roles/WebRole/roleInstances/WebRole_IN_0

Name              : WebRole_IN_1
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances
Location          :
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Production/roles/WebRole/roleInstances/WebRole_IN_1

Name              : WebRole_IN_2
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances
Location          :
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Production/roles/WebRole/roleInstances/WebRole_IN_2

Name              : WebRole_IN_3
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances
Location          :
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Production/roles/WebRole/roleInstances/WebRole_IN_3

Name              : WebRole_IN_4
ResourceGroupName : eus2testcloudservice
ResourceType      : Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances
Location          :
ResourceId        : /subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/Production/roles/WebRole/roleInstances/WebRole_IN_4

Time to take some kind of action on one of these VMs, let’s say I want to reboot instance 0. Let’s check again the list of available Provider actions:

PS /> Get-AzResourceProviderAction -OperationSearchString Microsoft.ClassicCompute/domainNames/slots/* | Format-Table Operation, Description

Operation                                                                                                 Description
---------                                                                                                 -----------
Microsoft.ClassicCompute/domainNames/slots/read                                                           Shows the deployment slots.
Microsoft.ClassicCompute/domainNames/slots/write                                                          Creates or update the deployment.
Microsoft.ClassicCompute/domainNames/slots/delete                                                         Deletes a given deployment slot.
Microsoft.ClassicCompute/domainNames/slots/start/action                                                   Starts a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/stop/action                                                    Suspends the deployment slot.
Microsoft.ClassicCompute/domainNames/slots/validateMigration/action                                       Validates migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/prepareMigration/action                                        Prepares migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/commitMigration/action                                         Commits migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/abortMigration/action                                          Aborts migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/state/start/write                                              Changes the deployment slot state to stopped.
Microsoft.ClassicCompute/domainNames/slots/state/stop/write                                               Changes the deployment slot state to started.
Microsoft.ClassicCompute/domainNames/slots/upgradeDomain/write                                            Walk upgrade the domain.
Microsoft.ClassicCompute/domainNames/slots/operationStatuses/read                                         Reads the operation status for the domain names slots.
Microsoft.ClassicCompute/domainNames/slots/roles/read                                                     Get the role for the deployment slot.
Microsoft.ClassicCompute/domainNames/slots/roles/write                                                    Add role for the deployment slot.
Microsoft.ClassicCompute/domainNames/slots/roles/extensionReferences/operationStatuses/read               Reads the operation status for the domain names slots roles extension refe…
Microsoft.ClassicCompute/domainNames/slots/roles/extensionReferences/read                                 Returns the extension reference for the deployment slot role.
Microsoft.ClassicCompute/domainNames/slots/roles/extensionReferences/write                                Add or modify the extension reference for the deployment slot role.
Microsoft.ClassicCompute/domainNames/slots/roles/extensionReferences/delete                               Remove the extension reference for the deployment slot role.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/operationStatuses/read                     Gets the operation status for the role instance on domain names slot role.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/downloadremotedesktopconnectionfile/action Downloads remote desktop connection file for the role instance on the doma…
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/read                                       Get the role instance.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/restart/action                             Restarts role instances.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/reimage/action                             Reimages the role instance.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/rebuild/action                             Rebuilds the role instance.
Microsoft.ClassicCompute/domainNames/slots/roles/providers/Microsoft.Insights/diagnosticSettings/read     Get the diagnostics settings.
Microsoft.ClassicCompute/domainNames/slots/roles/providers/Microsoft.Insights/diagnosticSettings/write    Add or modify diagnostics settings.
Microsoft.ClassicCompute/domainNames/slots/roles/providers/Microsoft.Insights/metricDefinitions/read      Gets the metrics definitions.
Microsoft.ClassicCompute/domainNames/slots/roles/skus/read                                                Get role sku for the deployment slot.
Microsoft.ClassicCompute/domainNames/slots/roles/metricdefinitions/read                                   Get the role metric definition for the domain name.
Microsoft.ClassicCompute/domainNames/slots/roles/metrics/read                                             Get role metric for the domain name.
Microsoft.ClassicCompute/domainNames/slots/roles/operationstatuses/read                                   Get the operation status for the domain names slot role.

Or we can list only the action operations

PS /> Get-AzResourceProviderAction -OperationSearchString Microsoft.ClassicCompute/domainNames/slots/*/action | Format-Table Operation, Description

Operation                                                                                                 Description
---------                                                                                                 -----------
Microsoft.ClassicCompute/domainNames/slots/start/action                                                   Starts a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/stop/action                                                    Suspends the deployment slot.
Microsoft.ClassicCompute/domainNames/slots/validateMigration/action                                       Validates migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/prepareMigration/action                                        Prepares migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/commitMigration/action                                         Commits migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/abortMigration/action                                          Aborts migration of a deployment slot.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/downloadremotedesktopconnectionfile/action Downloads remote desktop connection file for the role instance on the doma…
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/restart/action                             Restarts role instances.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/reimage/action                             Reimages the role instance.
Microsoft.ClassicCompute/domainNames/slots/roles/roleInstances/rebuild/action                             Rebuilds the role instance.

The roleInstances/restart action is what we need and we can invoke it with Invoke-AzResourceAction. If we use ResourceType we need to be careful to update it to include the role name hosting the VM we want to restart and instance name (the VM name) to restart.

Invoke-AzResourceAction -ResourceType 'Microsoft.ClassicCompute/domainNames/slots/production/roles/WebRole/roleInstances/WebRole_IN_0' -Action 'restart' -ApiVersion '2016-11-01' -ResourceGroupName 'eus2testcloudservice' -ResourceName 'eus2testcloudservice' -Force

Of course you can always use the full ResourceId if you prefer:

Invoke-AzResourceAction -ResourceId '/subscriptions/648df103-9854-4ba0-a627-47f8ce40e1c3/resourceGroups/eus2testcloudservice/providers/Microsoft.ClassicCompute/domainNames/eus2testcloudservice/slots/production/roles/WebRole/roleInstances/WebRole_IN_0' -Action 'restart' -ApiVersion '2016-11-01' -Force

Finally, here’s a quick bit of code to list the available ApiVersion for a Resource Provider, just pass the proper Provider name as $ProviderNamespace:

$ResourceTypes = Get-AzResourceProvider -ProviderNamespace $ProviderNamespace | Select-Object -ExpandProperty 'ResourceTypes' | Select-Object -ExpandProperty 'ApiVersions' -Unique

foreach ($type in $ResourceTypes) {
    Get-AzResourceProvider -ProviderNamespace $ProviderNamespace | 
    Select-Object -ExpandProperty 'ResourceTypes' | 
    Where-Object 'ResourceTypeName' -EQ $type | 
    Select-Object @{l = 'ResourceTypeName'; e = { $_.ResourceTypeName } }, ApiVersions
}

Cloud Services are just one example, with CosmosDb being another very good one. Actually, CosmosDb does not have very good Powershell support (I could not find an official CosmosDb module by Microsoft) but again, the Resource Provider exposes a number of Resources and Actions. I may just use the Azure CLI but I may have more fun writing my own cmdlets and maybe share them on Github 🤓


Imagination is the beginning of creation. You imagine what you desire, you will what you imagine and at last you create what you will. – George Bernard Shaw  

One Comment

  • Bojan

    Such a great article, thank you for this!

    I do have issues with executing operations on classic cloud services unfortunately.

    I am trying to execute stop on a production slot on one of mine cloud services, but I get request URI invalid error. Get-AzResource on that path returns instance tho, so I do not know what is wrong.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.