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.