Deploying Templates with Azure CLI and PowerShell
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Deploying Templates with Azure CLI and PowerShell
Introduction to Infrastructure as Code in Azure
In the early days of cloud computing, administrators often configured resources manually through a graphical interface, clicking through screens to provision virtual machines, storage accounts, and networking components. While this approach works for one-off tasks, it creates significant challenges in professional environments. Manual configurations are difficult to track, impossible to replicate exactly, and prone to human error. As organizations move toward modern cloud operations, the ability to define infrastructure as code (IaC) has become a fundamental skill for engineers.
Infrastructure as Code allows you to describe your environment using declarative files—such as Azure Resource Manager (ARM) templates or Bicep files—that state exactly what the final state of your infrastructure should look like. Once these files are written, you need a way to send them to Azure so the platform can build the resources for you. This is where automation tools like the Azure Command-Line Interface (CLI) and Azure PowerShell come into play. By using these tools, you transform the act of deploying infrastructure from a manual, click-heavy process into a repeatable, version-controlled, and automated task.
This lesson explores how to use Azure CLI and Azure PowerShell to deploy these templates. We will move beyond the basic commands and examine how to structure your deployment scripts, handle parameter files, manage resource groups, and implement best practices that keep your infrastructure organized and secure. Whether you are working with legacy JSON-based ARM templates or the more modern Bicep language, the principles of deployment via command-line tools remain the cornerstone of professional Azure administration.
Understanding the Deployment Workflow
Before diving into the specific commands for Azure CLI and PowerShell, it is essential to understand the underlying workflow of a template deployment. Regardless of the tool you choose, the Azure Resource Manager (ARM) service acts as the central orchestrator. When you initiate a deployment, you are essentially sending a request to the ARM API, which validates your template, checks for required permissions, and then proceeds to provision or modify the resources defined in your file.
The deployment process typically follows a standard sequence of events:
- Authentication: You must sign in to your Azure account and establish a secure context for your session.
- Resource Group Selection: Every deployment must target a specific resource group (or a subscription, for tenant-level deployments).
- Parameter Management: You define dynamic values, such as environment names or VM sizes, using parameter files or command-line arguments to keep your templates reusable.
- Validation: The ARM service checks the template syntax and ensures that the requested resources are available in the target region.
- Execution: The service begins creating or updating resources in the order specified by the dependency graph within your template.
- Reporting: Once finished, the tool provides a status report, confirming whether the resources were successfully created or if specific errors occurred.
Callout: ARM Templates vs. Bicep ARM templates are written in JSON, which is a machine-readable format that can be verbose and difficult for humans to edit. Bicep is a domain-specific language (DSL) that compiles down to ARM templates; it provides a much cleaner, more readable syntax, better support for modularity, and improved validation. While both are used for the same goal, Bicep is the recommended standard for new projects because it drastically reduces the lines of code required to define complex infrastructure.
Preparing Your Environment
To deploy templates effectively, your local machine must be properly configured. You need the Azure CLI and/or Azure PowerShell modules installed, and you must be authenticated with an account that has at least the "Contributor" role on the target resource group.
Installing the Tools
If you haven't already, install the Azure CLI from the official Microsoft website. For PowerShell users, ensure you have the Az module installed, which is the current, supported way to manage Azure resources. You can install the Az module by running Install-Module -Name Az -AllowClobber in an elevated PowerShell window.
Authentication
Before running any deployment commands, you must authenticate.
- Azure CLI: Run
az login. This will open a browser window for you to sign in. If you have multiple subscriptions, useaz account set --subscription <subscription-id>to ensure you are targeting the correct environment. - Azure PowerShell: Run
Connect-AzAccount. Similarly, useSet-AzContext -SubscriptionId <subscription-id>to select the correct subscription if necessary.
Note: Always verify your active subscription before running deployment commands. Running a deployment against a production subscription when you intended to target a development sandbox is a common and potentially costly mistake.
Deploying with Azure CLI
The Azure CLI is a cross-platform command-line tool that is highly popular among developers and DevOps engineers. It is particularly effective for automation scripts running in Linux environments or CI/CD pipelines.
The az deployment group create Command
The primary command for deploying templates in the Azure CLI is az deployment group create. This command requires the name of the resource group and the path to your template file.
Basic Example:
az deployment group create \
--resource-group MyResourceGroup \
--template-file ./main.bicep \
--parameters environment=production
In this example, we specify the target resource group, the location of our Bicep file, and a parameter to override the default value defined in the template. If you have a separate parameters JSON file, you can use the --parameters flag to point to that file instead:
az deployment group create \
--resource-group MyResourceGroup \
--template-file ./main.bicep \
--parameters @parameters.json
Handling Deployment Scopes
While most deployments target a resource group, you might occasionally need to deploy at the subscription or management group level (for example, creating a new resource group or assigning policy definitions). You can switch the command accordingly:
az deployment sub createfor subscription-level deployments.az deployment mg createfor management group-level deployments.
Deploying with Azure PowerShell
Azure PowerShell is deeply integrated with the Windows ecosystem and is the preferred choice for many system administrators who are comfortable with the object-oriented nature of PowerShell.
The New-AzResourceGroupDeployment Command
The PowerShell equivalent to the CLI command is New-AzResourceGroupDeployment. It follows a similar structure but uses PowerShell syntax and objects.
Basic Example:
New-AzResourceGroupDeployment -ResourceGroupName "MyResourceGroup" `
-TemplateFile "./main.bicep" `
-Environment "production"
If you prefer using a parameter file, the command is slightly different:
New-AzResourceGroupDeployment -ResourceGroupName "MyResourceGroup" `
-TemplateFile "./main.bicep" `
-TemplateParameterFile "./parameters.json"
Managing Output and Results
PowerShell provides rich object output. When you run a deployment, it returns a PSAzureDeployment object containing detailed information about the deployment status, the correlation ID, and any errors encountered. This is highly useful for scripting; you can capture the result in a variable to perform further actions based on success or failure:
$result = New-AzResourceGroupDeployment -ResourceGroupName "MyResourceGroup" -TemplateFile "./main.bicep"
if ($result.ProvisioningState -eq "Succeeded") {
Write-Host "Deployment completed successfully!"
} else {
Write-Error "Deployment failed."
}
Advanced Deployment Strategies
As your infrastructure grows, you will need to move beyond simple one-line commands. Professional deployment strategies incorporate validation, incremental updates, and parameter management.
Validating Templates
Before deploying, it is a best practice to validate your template to catch syntax errors or conflicts before they hit the Azure API. Both tools support a "what-if" or "validate" mode.
- Azure CLI:
az deployment group validate --resource-group MyRG --template-file main.bicep - Azure PowerShell:
Test-AzResourceGroupDeployment -ResourceGroupName MyRG -TemplateFile main.bicep
These commands simulate the deployment process and return any errors without actually changing your infrastructure. This is invaluable when working on complex templates where a small typo could break a production environment.
The What-If Operation
The "What-If" operation is perhaps the most important safety feature in modern Azure deployments. It allows you to see exactly what will happen before it happens. It compares your current state with the desired state defined in your template and lists the resources that will be created, modified, or deleted.
- Azure CLI:
az deployment group what-if --resource-group MyRG --template-file main.bicep - Azure PowerShell:
New-AzResourceGroupDeployment -ResourceGroupName MyRG -TemplateFile main.bicep -WhatIf
Always use the What-If command, especially when updating existing resources. It helps you avoid accidental deletions or destructive modifications to critical infrastructure.
Callout: The Power of What-If Many engineers fear that running a deployment script will accidentally delete existing resources. The "What-If" operation provides a clear audit trail of changes. It categorizes changes into "Create," "Delete," "Modify," or "NoChange," giving you the confidence to execute the deployment without fear of unintended consequences.
Parameter Management Best Practices
Hardcoding values inside your templates is a common pitfall that limits reusability. Instead, you should use parameters to define environment-specific settings.
Separation of Concerns
Maintain a clear separation between your template files (which define what resources exist) and your parameter files (which define how they are configured for a specific environment). For example, you might have:
main.bicep: The core infrastructure definition.dev.parameters.json: Configuration for your development environment.prod.parameters.json: Configuration for your production environment.
Using Key Vault for Secrets
Never include sensitive information, such as database passwords or API keys, in your parameter files. Instead, store those secrets in an Azure Key Vault and reference them in your deployment. Both ARM and Bicep templates support referencing Key Vault secrets by their resource ID, allowing the deployment process to securely retrieve the value at runtime.
Common Pitfalls and Troubleshooting
Even with the best tools, deployments can fail. Understanding the common failure modes will save you significant time during debugging.
Dependency Issues
Azure Resource Manager tries to deploy resources in parallel to save time. However, some resources require others to exist first (e.g., a virtual network must exist before a virtual machine can be attached to it). If your template does not properly define these dependencies, the deployment will fail with an error stating that a resource could not be found. In Bicep, you can often rely on implicit dependencies, but sometimes you must explicitly define them using the dependsOn property.
Quota Limitations
A very common error occurs when you attempt to deploy a resource that exceeds your subscription quota. For example, if you try to deploy a VM SKU that is not available in your region or if you hit the limit on the number of public IP addresses, the deployment will fail. Always check your subscription limits in the Azure Portal before attempting large-scale deployments.
Resource Group Lock
If you or another administrator have applied a "ReadOnly" or "CanNotDelete" lock to a resource group, your deployment will fail if it attempts to modify or delete resources within that group. Always check for locks if you receive "Authorization Failed" or "Access Denied" errors, even if your account has sufficient permissions.
Handling Errors
When a deployment fails, the error message can sometimes be cryptic. Start by checking the "Deployment" tab in the Azure Portal for your resource group. Click on the failed deployment to see the specific operation that failed and the detailed JSON error message provided by the Azure API. This is almost always more descriptive than the error output you receive in the terminal.
Comparison Table: CLI vs. PowerShell
| Feature | Azure CLI | Azure PowerShell |
|---|---|---|
| Primary Use Case | Cross-platform, Linux, CI/CD | Windows-centric, Complex automation |
| Syntax Style | Unix-style, declarative | Object-oriented, verb-noun |
| Installation | Single binary | PowerShell Module (Az) |
| Output Format | JSON (default) | PowerShell Objects |
| Best For | Fast scripts, Bash integration | Deep integration with Windows/AD |
Step-by-Step: A Typical Deployment Workflow
To solidify these concepts, let’s walk through the standard steps required to deploy a simple storage account using Bicep and the Azure CLI.
Step 1: Define the Resource
Create a file named storage.bicep:
param location string = resourceGroup().location
param storageName string
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: storageName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
Step 2: Create a Parameter File
Create a file named storage.parameters.json:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageName": {
"value": "mystorageaccount123"
}
}
}
Step 3: Run the What-If Operation
Before deploying, verify the plan:
az deployment group what-if --resource-group MyRG --template-file storage.bicep --parameters @storage.parameters.json
Step 4: Execute the Deployment
If the plan looks correct, trigger the deployment:
az deployment group create --resource-group MyRG --template-file storage.bicep --parameters @storage.parameters.json
Step 5: Verify the Resource
Finally, confirm the resource exists:
az storage account show --name mystorageaccount123 --resource-group MyRG
Best Practices for Production Environments
When moving from individual learning to team-based production environments, follow these industry-standard practices:
- Version Control: Always store your template and parameter files in a Git repository. Never deploy directly from a local folder without committing your changes.
- Modularization: Break large templates into smaller, reusable modules. For example, have a network module, a database module, and a compute module. This makes your code easier to maintain and test.
- Naming Conventions: Implement a strict naming convention for all Azure resources. Use prefixes for environments (e.g.,
prod-web-01) and regions. - Tagging: Use tags on all resources to track cost, owner, and environment. You can pass these tags through your templates as parameters.
- CI/CD Integration: Use Azure DevOps or GitHub Actions to automate your deployments. This ensures that every change is validated and deployed through a consistent pipeline rather than from an individual's laptop.
- Principle of Least Privilege: Use Service Principals for automated deployments. Do not use your personal user account to run production deployment pipelines. Grant the Service Principal only the permissions it needs to perform the deployment.
Summary: Key Takeaways
Deploying templates with Azure CLI and PowerShell is a critical skill for managing modern cloud infrastructure. By moving away from manual configuration, you gain the ability to create consistent, reliable, and version-controlled environments.
- Infrastructure as Code (IaC) is Mandatory: Manual configuration does not scale and leads to "configuration drift." Use templates (Bicep or ARM) to define your desired state.
- CLI vs. PowerShell: Both tools are powerful and capable. Choose the one that best fits your environment and team expertise, but understand that both interact with the same underlying ARM API.
- Always Validate and "What-If": Never assume a deployment will work as intended. Use the
validateandwhat-ifcommands to identify potential issues and review changes before they are applied. - Separate Parameters from Logic: Use parameter files for environment-specific configurations. This keeps your template code clean and reusable across development, testing, and production environments.
- Security First: Never store secrets in plaintext. Utilize Azure Key Vault to manage sensitive data and reference those secrets within your templates.
- Automate Everything: Transition from manual command-line execution to automated pipelines (CI/CD) as soon as possible. Automation reduces human error and provides an audit trail for every change made to your infrastructure.
- Embrace Bicep: If you are still writing raw JSON ARM templates, begin transitioning to Bicep. It is the modern standard, significantly easier to read, and simplifies the management of complex resource dependencies.
By following these practices, you move from being a reactive administrator to a proactive infrastructure engineer. You gain the ability to provision complex environments in minutes, recover from disasters by redeploying known configurations, and ensure that your cloud footprint remains secure and well-documented. Start by practicing with small, isolated resource groups, and gradually build toward modular, automated deployments that serve your entire organization's needs.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons