Table of Contents
Cheatsheet Azure CLI
Summary: AzureCLI hints, tips, oneliners and best practices. I prefer working with the Azure CLI when setting up test environments as it's fast, it's easy to see which settings you're deploying and the use of variables makes different snippets work very good together. When looking for such snippets, look for the deploy keyword.
Date: 8 December 2024
Useful Links
Login to Azure
az login az account set --subscription xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx az account show
Get Objects
# Check if a resourcegroup exists and set a variable RgExists=$(az group exists -n $(rgName)) echo "$(rgName) exists: $RgExists" # Get firewall Id firewallId=$(az network firewall show --name $(azureFirewallName) --resource-group $(rgName) --query id --output tsv) # Get Log Analytics Workspace Id workspaceId=$(az monitor log-analytics workspace show --workspace-name $(lawName) --resource-group $(rgName) --subscription $(subId) --query id -o tsv) # Get Virtual Network Gateway (VPN) Id vngId=$(az network vnet-gateway show --name $(virtualNetworkGatewayName) --resource-group $(rgName) --query id --output tsv) # Get Keyvault Id keyvaultId=$(az keyvault show --name $(keyVaultName) --resource-group $(rgName) --query id --output tsv)
Tags
resourceGroup=$(az group show -n poc-$(team)-$(pocnaam) --query id --output tsv) echo 'The resource group has the following tags' az tag list --resource-id $resourceGroup echo 'add a tag' az tag update --resource-id $resourceGroup --operation Merge --tags DateOne="$(firstdate)" DateTwo="$(seconddate)"
Azure Resource Graph Explorer Query
# check if a resource has a specific tag set tag=$(az graph query -q "resourcecontainers | where type == 'microsoft.resources/subscriptions/resourcegroups' and name == 'poc-$(team)-$(pocnaam)' | where tags contains 'startdate'" | jq '.data[].name') echo "Tag date: $tag" # do something if the tag is empty if [ -z "$tag" ]; then echo "do something, the tag is not set" else echo "do something else, the tag is set" fi # do something if the tag is not empty if [ -n "$bedrijf" ]; then echo "do something, the tag is set" else echo "do something else, the tag is not set" fi
BICEP deployment
# deploy resourcegroup in a subscription az deployment sub create \ --location $(location) \ --name "hub-$(deploymentSuffix)" \ --template-file ./modules/resourcegroup.bicep \ --parameters ./hub/hub-rg-parameterfile.jsonc \ --parameters location=$(location) # deploy resources in resourcegroup az deployment group create \ --resource-group $(rgName) \ --name "hub-vnet-$(deploymentSuffix)" \ --template-file ./hub/hub-vnet.bicep \ --parameters deploymentSuffix=$(deploymentSuffix) \ --parameters subId=$(subId) \ --parameters vnetName=$(vnetName)
Assign Roles
Assign a role to as group on a storage account (Storage Blob Data Owner). Note that this was used in a azure devops pipeline with parameters as variables.
az role assignment create \ --role "Storage Blob Data Owner" \ --assignee-object-id "cd96f93g-3973-4d94-e2c9-adacd18c2312" \ --assignee-principal-type "Group" \ --scope "/subscriptions/$(subscriptionId)/resourceGroups/$(ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$(StorageAccountName)"
Keyvaults
Show all secrets in a keyvault and make it easy to read
# All secrets az keyvault secret list --vault-name kv-euw-dev-001 --output table # One secret az keyvault secret show --vault-name kv-euw-dev-001 --name app-id-clientid -o table az keyvault secret show --vault-name kv-euw-dev-001 --name app-id-secret -o table # Just list all of the names az keyvault secret list --vault-name kv-euw-vtx-dev-001 --query [].name --output table
Bastion
Get rdp file from bastion# variables tenantId="c2a75ggc-b442-5678-9234-1e906d30ede2" subscriptionId="c2a75ggc-b442-5678-9234-48a7fab3b688" rg="rg-euw-bastion" bastionName="bas-euw-global-001" vmId="/subscriptions/c2a75ggc-b442-5678-9234-aebcd3d45789/resourceGroups/rg-euw-app01/providers/Microsoft.Compute/virtualMachines/vm-app1-001" # Authenticate to Azure PIM groups Start-Process "https://portal.azure.com/#view/Microsoft_Azure_PIMCommon/ActivationMenuBlade/~/aadgroup" # Configure the azure cli client az logout az config set core.login_experience_v2=off az login --tenant $tenantId az account set --subscription $subscriptionId az network bastion rdp --name $bastionName --resource-group $rg --target-resource-id $vmId --configure
Deploy
In this section you'll find snippets to deploy resources in Azure. The code is written, unless otherwise stated, in PowerShell and can be deployed using the cloud shell.
Use the –no-wait parameter to deploy resources in the background. This is useful when you're deploying multiple resources and don't want to wait for each deployment to finish. You can check the status of the deployment in the deployment section of the resource group in the Azure portal.
After some deployments additional commands are shown, for example to retrieve the id of the resource. This is done when the id is needed in a subsequent deployment.
Use the following command to get the status of the last deployment in a resource group:
az deployment group show --resource-group $rg --name $((az deployment group list --resource-group $rg --query "[0].name").trim('"')) --output table
Use the following command to delete a resource group and all resources in it:
az group delete --name $rg --yes --no-wait
Variables
This is a collection of variables used in the snippets below.
# Define a identifier which will be used in all resource names, for example: "tst", "poc", "shift" $project = "tst" # Define the resource group and location $rg = "rg-$project" $loc = "westeurope" # Define the default username and password $admin = "azadmin" $password = "P@ssword1234" # Define the virtual network and subnets $vnet = "vnet-$project" $vnetAddressSpace = "10.0.0.0/8" $subnet = "snet-$project" $subnetAddressSpace00 = "10.0.0.0/24" $subnetAddressSpace01 = "10.0.1.0/24" $subnetAddressSpace02 = "10.0.2.0/24" $subnetAddressSpace03 = "10.0.3.0/24" $subnetAddressSpace04 = "10.0.4.0/24" $subnetAddressSpace05 = "10.0.5.0/24" $subnetAddressSpace06 = "10.0.6.0/24" $subnetAddressSpace07 = "10.0.7.0/24" $subnetAddressSpace08 = "10.0.8.0/24" $subnetAddressSpace09 = "10.0.9.0/24" $subnetAddressSpace10 = "10.0.10.0/24" $vmname = "vm-$($project)01" # Bastion $bastion = "bas-$project" # Storage account must be globally unique, lowercase, numbers and between 3 and 24 characters $sa = "st$project" # Managed identity $id = "id-$project" # Keyvault $kv = "kv-$project" $kvSecret = "secret-$project" # Azure SQL Database server and database $sqlServer = "sql-$project" $dbName = "sqldb-$project"
Resourcegroup
az group create ` --name $rg ` --location $loc
Virtual Network
# Create a virtual network az network vnet create ` --name $vnet ` --resource-group $rg ` --location $loc ` --address-prefixes $vnetAddressSpace # Create a subnet az network vnet subnet create ` --name $subnet ` --resource-group $rg ` --vnet-name $vnet ` --address-prefix $subnetAddressSpace00 $subnetId = (az network vnet subnet show ` --resource-group $rg ` --vnet-name $vnet ` --name $subnet ` --query "id").Trim('"') Write-Host $subnetId
Virtual Machine
# Create a Windows Server 2022 virtual machine, without a public IP address, with a system-assigned managed identity, in the vnet and subnet created above, with a 64GB data disk, secure boot and trusted launch enabled. All resources will be deleted when the VM is deleted. # Create data disk az disk create ` --resource-group $rg ` --name "disk-$vmname" ` --location $loc ` --size-gb 64 ` --public-network-access Disabled ` --sku Standard_LRS # Create VM # Note: when creating a domain controller set the --data-disk-caching to None az vm create ` --resource-group $rg ` --name $vmname ` --location $loc ` --image Win2022Datacenter ` --size Standard_DS1_v2 ` --assign-identity [system] ` --nsg "" ` --admin-username $admin ` --admin-password $password ` --public-ip-address "" ` --accelerated-networking false ` --nic-delete-option delete ` --vnet-name $vnet ` --subnet $subnet ` --storage-sku Standard_LRS ` --os-disk-name "osdisk-$vmname" ` --os-disk-delete-option delete ` --attach-data-disks "disk-$vmname" ` --data-disk-caching ReadWrite ` --data-disk-delete-option delete ` --enable-hibernation false ` --enable-secure-boot true ` --security-type TrustedLaunch ` --accept-term ` --no-wait # Set auto shutdown at 16:00 UTC (17:00 CET) az vm auto-shutdown -g $rg -n $vmname --time 16:00
Bastion
# Create bastion subnet az network vnet subnet create ` --name AzureBastionSubnet ` --resource-group $rg ` --vnet-name $vnet ` --address-prefix $subnetAddressSpace01 ` --no-wait # Create public ip address az network public-ip create ` --resource-group $rg ` --name "pip-$bastion" ` --sku Standard ` --location $loc # Create bastion az network bastion create ` --name $bastion ` --public-ip-address "pip-$bastion" ` --resource-group $rg ` --vnet-name $vnet ` --location $loc ` --sku Basic ` --no-wait
Storage Account
# Storage account az storage account create ` --resource-group $rg ` --name $sa ` --location $loc ` --kind StorageV2 ` --sku Standard_LRS ` --enable-large-file-share ` --min-tls-version TLS1_2 ` --https-only true ` --allow-blob-public-access false # Get storage account Id $saId = (az storage account show ` --resource-group $rg ` --name $sa ` --query "id").trim('"') Write-Host $saId # Storage container az storage container create ` --name "cont-$project" ` --account-name $sa # File share az storage share-rm create ` --resource-group $rg ` --storage-account $sa ` --name "share$sa" ` --quota 1024 ` --enabled-protocols SMB # Create a directory for user profiles az storage directory create ` --account-name $sa ` --share-name "share$sa" ` --name "userprofiles"
Enable Data Protection and Lock
As this is mostly used for test environments I've also included the commands to disable the data protection.
# Enabling container soft delete az storage account blob-service-properties update ` --enable-container-delete-retention true ` --container-delete-retention-days 5 ` --account-name $sa ` --resource-group $rg # az storage account blob-service-properties update --enable-container-delete-retention false --account-name $sa --resource-group $rg # Enabling blob soft delete az extension add -n storage-preview az storage fs service-properties update ` --account-name $sa ` --delete-retention true ` --delete-retention-period 5 # az storage fs service-properties update --account-name $sa --delete-retention false # Set a resource lock on the storage account az lock create ` --name "LockStorageAccount" ` --lock-type CanNotDelete ` --resource-group $rg ` --resource $sa ` --resource-type "Microsoft.Storage/storageAccounts" ` --notes "This lock is to prevent accidental deletion of the storage account" # az lock delete --name LockStorageAccount --resource-group $rg --resource $sa --resource-type "Microsoft.Storage/storageAccounts"
Private Endpoint
# Get group Id and private dns zone $privateLinkGroupId = (Get-AzPrivateLinkResource -PrivateLinkResourceId $saId).GroupId Write-Host $privateLinkGroupId # Multiple options are returned, using blob for now $privateLinkGroupId = "blob" $privateLinkZoneName = ((Get-AzPrivateLinkResource -PrivateLinkResourceId $saId).RequiredZoneNames).trim('{}') Write-Host $privateLinkZoneName # Multiple options are returned, using blob for now $privateLinkZoneName = "privatelink.blob.core.windows.net" # Create private endpoint az network private-endpoint create ` --resource-group $rg ` --name "pep-$sa" ` --location $loc ` --subnet $subnetId ` --private-connection-resource-id $saId ` --group-id $privateLinkGroupId ` --connection-name "con-$sa" # Get private endpoint Id $pepId = (az network private-endpoint show ` --resource-group $rg ` --name "pep-$sa" ` --query "id").Trim('"') Write-Host $pepId # Get nic from pep $nicPepId = (az network private-endpoint show ` --ids $pepId ` --query "networkInterfaces[0].id").Trim('"') Write-Host $nicPepId # Get IP address from nic $nicIp = (az network nic show ` --ids $nicPepId ` --query "ipConfigurations[0].privateIPAddress").Trim('"') Write-Host $nicIp
Private DNS Zone
az network private-dns zone create ` --resource-group $rg ` --name $privateLinkZoneName # Create a link between the virtual network and the private DNS zone az network private-dns link vnet create ` --resource-group $rg ` --zone-name $privateLinkZoneName ` --name $vnet-DnsLink ` --virtual-network $vnet ` --registration-enabled false
Private DNS Record
# create recordset az network private-dns record-set a create ` --resource-group $rg ` --zone-name $privateLinkZoneName ` --name $sa # Create a record az network private-dns record-set a add-record ` --resource-group $rg ` --zone-name $privateLinkZoneName ` --record-set-name $sa ` --ipv4-address $nicIp
Managed Identity
# Managed identity az identity create ` --name $id ` --resource-group $rg ` --location $loc # Get the managed identity client id (Client Id (Application Id) is used to authenticate workloads) $idId = az identity show ` --name $id ` --resource-group $rg ` --query clientId ` --output tsv Write-Host $idId # Get the managed identity object id (Object (Principal) Id is used to authorize workloads, aka, assign permissions in Azure) $idPrincipalId = az identity show ` --name $id ` --resource-group $rg ` --query principalId ` --output tsv Write-Host $idPrincipalId
Keyvault
az keyvault create ` --name $kv ` --resource-group $rg ` --location $loc ` --enable-purge-protection ` --enable-rbac-authorization ` --retention-days 7 # Get the keyvault id $kvId = az keyvault show ` --resource-group $rg ` --name $kv ` --query id ` --output tsv Write-Host $kvId
Note that enabling purge protection is required for workload identity to work with Azure Key Vault. Because this is a test environment the retention days are set to 7.
Assign Permissions
# Assign permissions to the current user $userId = az account show ` --query user.name ` --output tsv # Set permissions for the user az role assignment create ` --assignee $userId ` --role "Key Vault Secrets Officer" ` --scope $kvId # Set permissions for the workload identity az role assignment create ` --assignee-object-id $idPrincipalId ` --role "Key Vault Secrets User" ` --scope $kvId ` --assignee-principal-type ServicePrincipal
Create Secret
# Create a secret in the key vault az keyvault secret set ` --vault-name $kv ` --name $kvSecret ` --value $password
Azure SQL Server
# create db server az sql server create ` --name $sqlServer ` --resource-group $rg ` --location $loc ` --admin-user $admin ` --admin-password $password # Get Sql Server Id $sqlServerId = (az sql server show ` --name $sqlServer ` --resource-group $rg ` --query "id").trim('"') Write-Host $sqlServerId
Private Endpoint
# Get group Id and private dns zone $privateLinkGroupId = (Get-AzPrivateLinkResource -PrivateLinkResourceId $sqlServerId).GroupId Write-Host $privateLinkGroupId # Note, sometimes the command below can return multiple domains $privateLinkZoneName = ((Get-AzPrivateLinkResource -PrivateLinkResourceId $sqlServerId).RequiredZoneNames).trim('{}') Write-Host $privateLinkZoneName # Create private endpoint az network private-endpoint create ` --resource-group $rg ` --name "pep-$sqlServer" ` --location $loc ` --subnet $subnetId ` --private-connection-resource-id $sqlServerId ` --group-id $privateLinkGroupId ` --connection-name "con-$sqlServer" # Get private endpoint Id $pepId = (az network private-endpoint show ` --resource-group $rg ` --name "pep-$sqlServer" ` --query "id").Trim('"') Write-Host $pepId # Get nic from pep $nicPepId = (az network private-endpoint show ` --ids $pepId ` --query "networkInterfaces[0].id").Trim('"') Write-Host $nicPepId # Get IP address from nic $nicIp = (az network nic show ` --ids $nicPepId ` --query "ipConfigurations[0].privateIPAddress").Trim('"') Write-Host $nicIp
Private DNS Zone
# Private DNS zone az network private-dns zone create ` --resource-group $rg ` --name $privateLinkZoneName # Create a link between the virtual network and the private DNS zone az network private-dns link vnet create ` --resource-group $rg ` --zone-name $privateLinkZoneName ` --name $vnet-DnsLink ` --virtual-network $vnet ` --registration-enabled false
Private DNS Record
# private dns record # create recordset az network private-dns record-set a create ` --resource-group $rg ` --zone-name $privateLinkZoneName ` --name $sqlServer # Create a record az network private-dns record-set a add-record ` --resource-group $rg ` --zone-name $privateLinkZoneName ` --record-set-name $sqlServer ` --ipv4-address $nicIp
Azure SQL Hyperscale Database
When creating a Hyperscale database, carefully consider the setting for backup-storage-redundancy. Storage redundancy can only be specified during the database creation process for Hyperscale databases. You can choose locally redundant, zone-redundant, or geo-redundant storage. The selected storage redundancy option will be used for the lifetime of the database for both data storage redundancy and backup storage redundancy. Allowed values for the backup-storage-redundancy parameter are: Local, Zone, Geo. Unless explicitly specified, databases will be configured to use geo-redundant backup storage.
# create db az sql db create ` --resource-group $rg ` --server $sqlServer ` --name $dbname ` --edition Hyperscale ` --compute-model Serverless ` --family Gen5 ` --capacity 2 ` --backup-storage-redundancy Local ` --ha-replicas 0 ` --yes