The Az PowerShell module is the official toolkit for Azure automation from PowerShell. It replaced AzureRM in 2019 — if you still see AzureRM.* cmdlets in older scripts, migrate (Microsoft ended support for AzureRM in 2024).
Installation
# Cross-platform install (pwsh 7+)
Install-Module Az -Scope CurrentUser -Repository PSGallery -Force
# Az is a meta-module that depends on dozens of Az.* sub-modules (Az.Compute, Az.Storage, ...).
# For lighter scripts, install only what you need:
Install-Module Az.Accounts, Az.Compute, Az.Storage -Scope CurrentUser
Update-Module Az
Get-Module Az -ListAvailable
Authenticating
Interactive (developer machine)
Connect-AzAccount
# After the browser flow, see what you got:
Get-AzContext
Get-AzSubscription
Set-AzContext -Subscription "Production"
The context is the (account, subscription, tenant) triple all subsequent cmdlets operate against.
Service Principal (CI / scripts)
# Create a service principal once (Azure CLI or via Microsoft Entra)
# Save credentials securely (Key Vault, environment variable, secret store).
$credential = New-Object PSCredential -ArgumentList $env:AZ_CLIENT_ID, (ConvertTo-SecureString $env:AZ_CLIENT_SECRET -AsPlainText -Force)
Connect-AzAccount -ServicePrincipal -Credential $credential -TenantId $env:AZ_TENANT_ID
Set-AzContext -Subscription $env:AZ_SUBSCRIPTION_ID
Managed Identity (Azure VM / Azure Functions / runbook)
Connect-AzAccount -Identity
# That's it. Az queries the IMDS endpoint for a token.
Best practice: never use a service principal where Managed Identity works. No secrets to rotate, no leaks possible.
Federated Credentials (GitHub Actions / Azure DevOps)
OIDC-based auth from GitHub Actions to Azure has no long-lived secret:
# From inside a GitHub Action with azure/login@v2:
Connect-AzAccount -ServicePrincipal -ApplicationId $env:AZURE_CLIENT_ID `
-Tenant $env:AZURE_TENANT_ID -FederatedToken $env:ACTIONS_ID_TOKEN_REQUEST_TOKEN
The azure/login action handles this for you; just call PowerShell after it succeeds.
Working with Resources
Resource groups
New-AzResourceGroup -Name rg-web-prod -Location westeurope
Get-AzResourceGroup
Get-AzResource -ResourceGroupName rg-web-prod
Remove-AzResourceGroup -Name rg-web-prod -Force
Virtual machines
# List
Get-AzVM | Format-Table Name, ResourceGroupName, Location
# Get one and inspect
$vm = Get-AzVM -Name web01 -ResourceGroupName rg-web-prod
$vm | Get-Member
$vm.HardwareProfile.VmSize # → Standard_D4s_v5
# Stop (deallocate to stop billing)
Stop-AzVM -Name web01 -ResourceGroupName rg-web-prod -Force
# Resize
Update-AzVM -VM ($vm | Set-AzVMSize -VMSize Standard_D2s_v5) -ResourceGroupName rg-web-prod
# Create from minimal params (auto-creates VNet, public IP, etc.)
New-AzVM -Name app01 -ResourceGroupName rg-app-prod -Location westeurope `
-Image UbuntuLTS -Size Standard_D2s_v5 -OpenPorts 22, 443
Storage
$ctx = (Get-AzStorageAccount -Name mystorage -ResourceGroupName rg-data).Context
# List blobs
Get-AzStorageContainer -Context $ctx
Get-AzStorageBlob -Container backups -Context $ctx
# Upload / download
Set-AzStorageBlobContent -File ./data.csv -Container backups -Blob data.csv -Context $ctx
Get-AzStorageBlobContent -Container backups -Blob data.csv -Destination ./downloaded.csv -Context $ctx
Networking
# NSG management
$nsg = Get-AzNetworkSecurityGroup -Name nsg-web -ResourceGroupName rg-web-prod
$nsg | Add-AzNetworkSecurityRuleConfig -Name AllowHTTPS `
-Access Allow -Direction Inbound -Priority 100 `
-SourceAddressPrefix Internet -SourcePortRange * `
-DestinationAddressPrefix * -DestinationPortRange 443 -Protocol Tcp |
Set-AzNetworkSecurityGroup
Role assignments
New-AzRoleAssignment -ObjectId $userId -RoleDefinitionName Reader -Scope (Get-AzResourceGroup rg-prod).ResourceId
Get-AzRoleAssignment -ResourceGroupName rg-prod
Get-AzRoleDefinition | Where-Object Name -like "*Storage*"
Common Patterns
Tag everything
$tags = @{
Environment = "Production"
CostCenter = "cc-1234"
Team = "payments"
Owner = "platform@acme.io"
}
# Apply to a resource group
Set-AzResourceGroup -Name rg-payments-prod -Tag $tags
# Apply to a single resource
$rg = Get-AzResourceGroup rg-payments-prod
Get-AzResource -ResourceGroupName $rg.ResourceGroupName | ForEach-Object {
Set-AzResource -ResourceId $_.Id -Tag $tags -Force
}
Bulk operations with parallel pipeline
$vms = Get-AzVM
$vms | ForEach-Object -Parallel {
Import-Module Az.Compute
Stop-AzVM -Name $_.Name -ResourceGroupName $_.ResourceGroupName -Force
} -ThrottleLimit 10
Important caveat: -Parallel spawns separate runspaces; you must re-import modules and re-establish Az context inside the block, or share state with $using:.
Querying with Resource Graph
Az PowerShell can query Azure Resource Graph with KQL — fast across the entire estate:
Install-Module Az.ResourceGraph -Scope CurrentUser
Search-AzGraph -Query "
Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| where properties.hardwareProfile.vmSize startswith 'Standard_D'
| project name, resourceGroup, vmSize=properties.hardwareProfile.vmSize, location
"
Far faster than enumerating with Get-AzVM across subscriptions.
Az CLI vs Az PowerShell
Microsoft maintains two equivalent tooling stacks:
Az CLI (az) | Az PowerShell (Az) |
|---|---|
| Bash-friendly; works well with jq | Object pipeline; integrates with PowerShell ecosystem |
| JSON output by default | Native objects; ConvertTo-Json when needed |
| One executable, faster startup | PowerShell module; slightly slower load |
| Identical underlying API coverage | Identical underlying API coverage |
Use what your team prefers. Many shops use both — Az CLI in Bash CI pipelines, Az PowerShell in Windows / runbook scenarios.
Best Practices
- Pin module versions in production scripts (
#Requires -Modules @{ ModuleName="Az"; ModuleVersion="11.0" }). - Use Managed Identity wherever possible.
- Set the context explicitly at the top — never assume the right subscription is active.
- Wrap destructive operations in
-WhatIfsupport so callers can dry-run. - Use Resource Graph for cross-subscription queries — orders of magnitude faster.
- Keep up with monthly Az releases — new resources land in
previewversions first.
Az covers Azure resources. The Microsoft 365 side — users, groups, mailboxes, Teams, SharePoint — is the territory of Microsoft Graph, the next lesson.