Skip to main content

Azure CLI vs PowerShell — Which One Should You Use?

· 6 min read
Goel Academy
DevOps & Cloud Learning Hub

You open a new terminal to manage Azure resources and immediately face a choice: az vm create or New-AzVM? Both do the same thing. Both are officially supported. Both will be around for years. The answer depends on your background, your OS, and what you are building. Let us settle this debate with facts.

The Two Official Tools

Microsoft maintains two first-party command-line tools for Azure:

FeatureAzure CLI (az)Azure PowerShell (Az)
LanguagePython-basedPowerShell module (.NET)
Syntax Styleaz <group> <command>Verb-AzNoun
Output DefaultJSONPowerShell objects
PlatformsWindows, macOS, Linux, DockerWindows, macOS, Linux
ShellBash, Zsh, Fish, CMDPowerShell 7+
Cloud ShellYesYes
Learning CurveLower for Linux/Mac usersLower for Windows admins

Installation

Azure CLI

# macOS
brew install azure-cli

# Ubuntu / Debian
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# Windows (winget)
winget install Microsoft.AzureCLI

# Docker
docker run -it mcr.microsoft.com/azure-cli

# Verify installation
az version

Azure PowerShell

# Install the Az module (PowerShell 7+)
Install-Module -Name Az -Repository PSGallery -Force

# Or update if already installed
Update-Module -Name Az

# Verify installation
Get-Module -Name Az -ListAvailable

Authentication Methods

Both tools support the same authentication methods, just with different syntax:

# Azure CLI — interactive browser login
az login

# Azure CLI — service principal
az login \
--service-principal \
--username <app-id> \
--password <client-secret> \
--tenant <tenant-id>

# Azure CLI — managed identity (on Azure VMs)
az login --identity
# PowerShell — interactive browser login
Connect-AzAccount

# PowerShell — service principal
$cred = New-Object PSCredential("<app-id>", (ConvertTo-SecureString "<secret>" -AsPlainText -Force))
Connect-AzAccount -ServicePrincipal -Credential $cred -Tenant "<tenant-id>"

# PowerShell — managed identity
Connect-AzAccount -Identity

Side-by-Side: Same Task, Both Tools

Here is where the rubber meets the road. Let us perform common operations in both tools:

Create a Resource Group

# Azure CLI
az group create --name rg-demo --location eastus
# PowerShell
New-AzResourceGroup -Name rg-demo -Location eastus

Create a Virtual Machine

# Azure CLI
az vm create \
--resource-group rg-demo \
--name vm-demo \
--image Ubuntu2204 \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys
# PowerShell
New-AzVM `
-ResourceGroupName rg-demo `
-Name vm-demo `
-Image Ubuntu2204 `
-Size Standard_B2s `
-Credential (Get-Credential)

List All VMs

# Azure CLI
az vm list \
--resource-group rg-demo \
--output table
# PowerShell
Get-AzVM -ResourceGroupName rg-demo | Format-Table Name, Location, VmSize

Delete a Resource Group

# Azure CLI
az group delete --name rg-demo --yes --no-wait
# PowerShell
Remove-AzResourceGroup -Name rg-demo -Force -AsJob

Azure Cloud Shell

Do not want to install anything? Azure Cloud Shell gives you a browser-based terminal with both tools pre-installed, pre-authenticated, and backed by a persistent Azure Files share.

Access it at shell.azure.com or click the terminal icon in the Azure portal.

Cloud Shell includes:

  • Azure CLI and PowerShell pre-installed
  • 5 GB persistent storage
  • Common tools: git, vim, terraform, kubectl, docker
  • Automatic authentication with your portal session
# Check which tools are available in Cloud Shell
az version && pwsh -Command "Get-Module Az -ListAvailable | Select-Object Version"

Output Formats and Querying

Azure CLI Output Formats

Azure CLI supports four output formats:

# JSON (default) — best for scripting
az vm list --output json

# Table — best for human readability
az vm list --output table

# TSV — best for piping to other commands
az vm list --query "[].name" --output tsv

# YAML — best for configuration files
az vm list --output yaml

JMESPath Queries (Azure CLI)

JMESPath is the query language built into Azure CLI. It is incredibly powerful for filtering and transforming output:

# Get only VM names
az vm list --query "[].name" --output tsv

# Get VMs with specific properties
az vm list \
--query "[].{Name:name, Size:hardwareProfile.vmSize, OS:storageProfile.osDisk.osType}" \
--output table

# Filter VMs by size
az vm list \
--query "[?hardwareProfile.vmSize=='Standard_B2s'].name" \
--output tsv

# Get the first VM's resource group
az vm list --query "[0].resourceGroup" --output tsv

PowerShell Object Pipeline

PowerShell works with objects, not text. This is its superpower:

# Filter and format with pipeline
Get-AzVM | Where-Object { $_.HardwareProfile.VmSize -eq "Standard_B2s" } |
Select-Object Name, Location |
Sort-Object Name

# Export to CSV
Get-AzVM | Select-Object Name, Location, @{N='Size';E={$_.HardwareProfile.VmSize}} |
Export-Csv -Path vms.csv -NoTypeInformation

# Count VMs by location
Get-AzVM | Group-Object Location | Select-Object Name, Count

Scripting Patterns

Azure CLI Bash Script

#!/bin/bash
# Deploy a web app with database

RESOURCE_GROUP="rg-webapp-prod"
LOCATION="eastus"
APP_NAME="mywebapp-$(date +%s)"

# Create infrastructure
az group create --name $RESOURCE_GROUP --location $LOCATION

az appservice plan create \
--name "${APP_NAME}-plan" \
--resource-group $RESOURCE_GROUP \
--sku B1 --is-linux

az webapp create \
--resource-group $RESOURCE_GROUP \
--plan "${APP_NAME}-plan" \
--name $APP_NAME \
--runtime "NODE|18-lts"

# Get the URL
URL=$(az webapp show \
--resource-group $RESOURCE_GROUP \
--name $APP_NAME \
--query defaultHostName \
--output tsv)

echo "App deployed at: https://$URL"

Azure PowerShell Script

# Deploy a web app with database

$ResourceGroup = "rg-webapp-prod"
$Location = "eastus"
$AppName = "mywebapp-$(Get-Date -Format 'yyyyMMddHHmmss')"

# Create infrastructure
New-AzResourceGroup -Name $ResourceGroup -Location $Location

New-AzAppServicePlan `
-Name "$AppName-plan" `
-ResourceGroupName $ResourceGroup `
-Location $Location `
-Tier Basic `
-Linux

New-AzWebApp `
-ResourceGroupName $ResourceGroup `
-AppServicePlan "$AppName-plan" `
-Name $AppName

# Get the URL
$App = Get-AzWebApp -ResourceGroupName $ResourceGroup -Name $AppName
Write-Host "App deployed at: https://$($App.DefaultHostName)"

When to Use Which

Here is the practical guidance:

ScenarioRecommended ToolWhy
Linux/macOS daily workAzure CLINative bash integration, simpler syntax
Windows administrationPowerShellObject pipeline, Active Directory integration
CI/CD pipelines (GitHub Actions)Azure CLIEasier to embed in YAML, bash-friendly
Complex automation scriptsPowerShellError handling, object manipulation, modules
Quick one-off commandsAzure CLILess typing, tab completion
Existing PowerShell infrastructurePowerShellConsistent tooling across your scripts
Learning Azure for the first timeAzure CLILower barrier to entry

The real answer: Learn both. Use Azure CLI for interactive work and quick scripts. Use PowerShell when you need structured object manipulation or when your team is already in the PowerShell ecosystem.

Wrapping Up

There is no wrong choice between Azure CLI and PowerShell — both are actively maintained and feature-complete. If you are on a Mac or Linux machine, Azure CLI will feel more natural. If you are automating complex Windows-heavy infrastructure, PowerShell's object pipeline is hard to beat. The good news? Cloud Shell gives you both, so you can switch between them depending on the task.


Next up: Azure App Service — deploy your web application to production in 5 minutes flat with deployment slots, auto-scaling, and custom domains.