Ansible provides extensive support for managing Microsoft Azure resources through the azure.azcollection collection. This guide covers common Azure operations and provides practical examples for automating your cloud infrastructure.
Install the Azure collection and its dependencies:
# Install Azure collection
ansible-galaxy collection install azure.azcollection
# Install Python dependencies
pip install azure-cli azure-mgmt-compute azure-mgmt-network azure-mgmt-storage azure-mgmt-sql
Ansible can authenticate with Azure using several methods:
az login
export AZURE_CLIENT_ID="your-app-id"
export AZURE_SECRET="your-password"
export AZURE_TENANT_ID="your-tenant-id"
export AZURE_SUBSCRIPTION_ID="your-subscription-id"
vars:
azure_client_id: "your-app-id"
azure_tenant_id: "your-tenant-id"
azure_subscription_id: "your-subscription-id"
azure_secret: "{{ vault_azure_secret }}"
vars:
azure_auth_type: msi
# Create service principal with contributor role
az ad sp create-for-rbac --name "ansible-spn" --role contributor \
--scopes /subscriptions/{subscription-id} \
--sdk-auth
# Output JSON for Ansible configuration
{
"clientId": "xxx",
"clientSecret": "xxx",
"subscriptionId": "xxx",
"tenantId": "xxx",
...
}
- name: Create Azure resource group
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
tasks:
- name: Create resource group
azure.azcollection.azure_rm_resourcegroup:
name: "{{ resource_group }}"
location: "{{ location }}"
tags:
Environment: Production
ManagedBy: Ansible
Project: MyApplication
- name: Delete Azure resource group
hosts: localhost
gather_facts: false
tasks:
- name: Delete resource group and all resources
azure.azcollection.azure_rm_resourcegroup:
name: my-rg
state: absent
force_delete_nonempty: true
- name: Create Azure VM
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
vm_name: web-server-01
vm_size: Standard_DS2_v2
admin_username: azureuser
admin_password: "{{ vault_admin_password }}"
tasks:
- name: Create public IP
azure.azcollection.azure_rm_publicipaddress:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}-pip"
location: "{{ location }}"
allocation_method: Dynamic
sku: Basic
- name: Create virtual network
azure.azcollection.azure_rm_virtualnetwork:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}-vnet"
location: "{{ location }}"
address_prefixes:
- 10.0.0.0/16
- name: Create subnet
azure.azcollection.azure_rm_subnet:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}-subnet"
virtual_network_name: "{{ vm_name }}-vnet"
address_prefix: 10.0.1.0/24
- name: Create network security group
azure.azcollection.azure_rm_securitygroup:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}-nsg"
location: "{{ location }}"
rules:
- name: AllowHTTP
protocol: Tcp
destination_port_range: 80
access: Allow
direction: Inbound
priority: 100
- name: AllowHTTPS
protocol: Tcp
destination_port_range: 443
access: Allow
direction: Inbound
priority: 101
- name: AllowSSH
protocol: Tcp
destination_port_range: 22
access: Allow
direction: Inbound
priority: 102
source_address_prefix: 10.0.0.0/8
- name: Create network interface
azure.azcollection.azure_rm_networkinterface:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}-nic"
location: "{{ location }}"
virtual_network_name: "{{ vm_name }}-vnet"
subnet_name: "{{ vm_name }}-subnet"
public_ip_name: "{{ vm_name }}-pip"
security_group_name: "{{ vm_name }}-nsg"
- name: Create VM
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
location: "{{ location }}"
vm_size: "{{ vm_size }}"
admin_username: "{{ admin_username }}"
admin_password: "{{ admin_password }}"
network_interfaces: "{{ vm_name }}-nic"
storage_account_name: "{{ vm_name }}sa"
storage_container_name: vhds
storage_blob_name: osdisk.vhd
image:
offer: UbuntuServer
publisher: Canonical
sku: 2204-LTS
version: latest
tags:
Environment: Production
Application: WebServer
ManagedBy: Ansible
- name: Create Azure VM with SSH key
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: westus
vm_name: dev-server
admin_username: azureuser
ssh_key_path: ~/.ssh/id_rsa.pub
tasks:
- name: Read SSH public key
ansible.builtin.slurp:
src: "{{ ssh_key_path }}"
register: ssh_key_file
- name: Create VM with SSH key
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
location: "{{ location }}"
vm_size: Standard_B2s
admin_username: "{{ admin_username }}"
ssh_password_enabled: false
ssh_public_keys:
- path: /home/{{ admin_username }}/.ssh/authorized_keys
key_data: "{{ ssh_key_file.content | b64decode }}"
network_interfaces: "{{ vm_name }}-nic"
image:
offer: UbuntuServer
publisher: Canonical
sku: 2204-LTS
version: latest
- name: Create Azure VM Scale Set
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
tasks:
- name: Create scale set
azure.azcollection.azure_rm_virtualmachinescaleset:
resource_group: "{{ resource_group }}"
name: app-scaleset
location: "{{ location }}"
vm_size: Standard_DS2_v2
capacity: 3
tier: Standard
upgrade_policy: Manual
admin_username: azureuser
admin_password: "{{ vault_admin_password }}"
ssh_password_enabled: true
image:
offer: UbuntuServer
publisher: Canonical
sku: 2204-LTS
version: latest
virtual_network_name: app-vnet
subnet_name: app-subnet
load_balancer: app-lb
data_disks:
- lun: 0
disk_size_gb: 128
managed_disk_type: Standard_LRS
tags:
Environment: Production
Application: AppServer
- name: Manage Azure VM state
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
vm_name: web-server-01
tasks:
- name: Stop VM
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
started: false
allocated: false
- name: Start VM
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
started: true
allocated: true
- name: Restart VM
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
restart: true
- name: Delete Azure VM
hosts: localhost
gather_facts: false
tasks:
- name: Delete VM and resources
azure.azcollection.azure_rm_virtualmachine:
resource_group: my-rg
name: web-server-01
state: absent
remove_on_absent:
- all
- name: Create Azure Storage account
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
storage_account: mystorageaccount123
tasks:
- name: Create storage account
azure.azcollection.azure_rm_storageaccount:
resource_group: "{{ resource_group }}"
name: "{{ storage_account }}"
location: "{{ location }}"
account_type: Standard_LRS
access_tier: Hot
minimum_tls_version: TLS1_2
allow_blob_public_access: false
network_acls:
bypass: AzureServices
default_action: Deny
tags:
Environment: Production
ManagedBy: Ansible
- name: Create Azure blob container
hosts: localhost
gather_facts: false
tasks:
- name: Create container
azure.azcollection.azure_rm_storageblob:
resource_group: my-rg
storage_account_name: mystorageaccount123
container_name: backups
blob_type: block
src: /path/to/backup.tar.gz
dest: backups/backup-{{ ansible_date_time.date }}.tar.gz
- name: Upload files to Azure Storage
hosts: localhost
gather_facts: false
tasks:
- name: Upload configuration file
azure.azcollection.azure_rm_storageblob:
resource_group: my-rg
storage_account_name: mystorageaccount123
container_name: configs
blob_type: block
src: /path/to/config.json
dest: app/config.json
content_type: application/json
- name: Upload with metadata
azure.azcollection.azure_rm_storageblob:
resource_group: my-rg
storage_account_name: mystorageaccount123
container_name: documents
blob_type: block
src: /path/to/document.pdf
dest: docs/document.pdf
metadata:
department: IT
classification: internal
- name: Download files from Azure Storage
hosts: all
gather_facts: false
tasks:
- name: Download configuration
azure.azcollection.azure_rm_storageblob:
resource_group: my-rg
storage_account_name: mystorageaccount123
container_name: configs
blob_name: app/config.json
dest: /etc/myapp/config.json
- name: Create Azure SQL Server
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
sql_server: my-sql-server-12345
admin_username: sqladmin
admin_password: "{{ vault_sql_password }}"
tasks:
- name: Create SQL server
azure.azcollection.azure_rm_sqlserver:
resource_group: "{{ resource_group }}"
name: "{{ sql_server }}"
location: "{{ location }}"
administrator_login: "{{ admin_username }}"
administrator_login_password: "{{ admin_password }}"
version: "12.0"
tags:
Environment: Production
Database: SQL
- name: Create Azure SQL Database
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
sql_server: my-sql-server-12345
tasks:
- name: Create database
azure.azcollection.azure_rm_sqldatabase:
resource_group: "{{ resource_group }}"
server: "{{ sql_server }}"
name: myapp_database
location: eastus
sku:
name: S1
tier: Standard
collation: SQL_Latin1_General_CP1_CI_AS
max_size_bytes: 268435456000
sample_name: AdventureWorksLT
- name: Configure SQL firewall rules
hosts: localhost
gather_facts: false
tasks:
- name: Allow Azure services
azure.azcollection.azure_rm_sqlfirewallrule:
resource_group: my-rg
server: my-sql-server-12345
name: AllowAzureServices
start_ip_address: 0.0.0.0
end_ip_address: 0.0.0.0
- name: Allow specific IP range
azure.azcollection.azure_rm_sqlfirewallrule:
resource_group: my-rg
server: my-sql-server-12345
name: AllowAppServers
start_ip_address: 10.0.0.0
end_ip_address: 10.255.255.255
- name: Create Azure VNet
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
tasks:
- name: Create virtual network
azure.azcollection.azure_rm_virtualnetwork:
resource_group: "{{ resource_group }}"
name: production-vnet
location: "{{ location }}"
address_prefixes:
- 10.0.0.0/16
dns_servers:
- 10.0.0.4
- 10.0.0.5
- name: Create public subnets
azure.azcollection.azure_rm_subnet:
resource_group: "{{ resource_group }}"
name: "{{ item.name }}"
virtual_network_name: production-vnet
address_prefix: "{{ item.cidr }}"
loop:
- { name: "public-subnet-1", cidr: "10.0.1.0/24" }
- { name: "public-subnet-2", cidr: "10.0.2.0/24" }
- name: Create private subnets
azure.azcollection.azure_rm_subnet:
resource_group: "{{ resource_group }}"
name: "{{ item.name }}"
virtual_network_name: production-vnet
address_prefix: "{{ item.cidr }}"
loop:
- { name: "private-subnet-1", cidr: "10.0.10.0/24" }
- { name: "private-subnet-2", cidr: "10.0.11.0/24" }
- name: Create NSG
hosts: localhost
gather_facts: false
tasks:
- name: Create web NSG
azure.azcollection.azure_rm_securitygroup:
resource_group: my-rg
name: web-nsg
location: eastus
rules:
- name: AllowHTTP
protocol: Tcp
destination_port_range: 80
access: Allow
direction: Inbound
priority: 100
- name: AllowHTTPS
protocol: Tcp
destination_port_range: 443
access: Allow
direction: Inbound
priority: 101
- name: AllowSSHInternal
protocol: Tcp
destination_port_range: 22
access: Allow
direction: Inbound
priority: 102
source_address_prefix: 10.0.0.0/8
purge_rules: false
- name: Create Azure Load Balancer
hosts: localhost
gather_facts: false
tasks:
- name: Create public IP for LB
azure.azcollection.azure_rm_publicipaddress:
resource_group: my-rg
name: lb-pip
location: eastus
allocation_method: Static
sku: Standard
- name: Create load balancer
azure.azcollection.azure_rm_loadbalancer:
resource_group: my-rg
name: web-lb
location: eastus
frontend_ip_configurations:
- name: frontend
public_ip_address: lb-pip
backend_address_pools:
- name: backend-pool
load_balancing_rules:
- name: http-rule
frontend_ip_configuration: frontend
backend_address_pool: backend-pool
protocol: Tcp
frontend_port: 80
backend_port: 80
probe_name: http-probe
- name: https-rule
frontend_ip_configuration: frontend
backend_address_pool: backend-pool
protocol: Tcp
frontend_port: 443
backend_port: 443
probe_name: https-probe
probes:
- name: http-probe
protocol: Http
port: 80
request_path: /health
interval: 5
fail_count: 2
- name: https-probe
protocol: Http
port: 443
request_path: /health
interval: 5
fail_count: 2
- name: Create AKS cluster
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
tasks:
- name: Create AKS cluster
azure.azcollection.azure_rm_aks:
resource_group: "{{ resource_group }}"
name: production-aks
location: "{{ location }}"
kubernetes_version: "1.28"
dns_prefix: production-aks
agent_pool_profiles:
- name: default
count: 3
vm_size: Standard_DS2_v2
type: VirtualMachineScaleSets
availability_zones:
- 1
- 2
- 3
enable_auto_scaling: true
min_count: 3
max_count: 10
os_disk_size_gb: 128
service_principal:
client_id: "{{ azure_client_id }}"
client_secret: "{{ azure_client_secret }}"
network_profile:
load_balancer_sku: standard
network_plugin: azure
network_policy: azure
addon_profiles:
http_application_routing:
enabled: false
oms_agent:
enabled: true
config:
log_analytics_workspace_resource_id: "/subscriptions/{{ subscription_id }}/resourceGroups/{{ resource_group }}/providers/Microsoft.OperationalInsights/workspaces/{{ workspace_name }}"
tags:
Environment: Production
ManagedBy: Ansible
- name: Scale AKS cluster
hosts: localhost
gather_facts: false
tasks:
- name: Update agent pool count
azure.azcollection.azure_rm_aks:
resource_group: my-rg
name: production-aks
location: eastus
agent_pool_profiles:
- name: default
count: 5
vm_size: Standard_DS2_v2
type: VirtualMachineScaleSets
enable_auto_scaling: true
min_count: 3
max_count: 10
- name: Create Azure Function App
hosts: localhost
gather_facts: false
vars:
resource_group: my-rg
location: eastus
tasks:
- name: Create app service plan
azure.azcollection.azure_rm_appserviceplan:
resource_group: "{{ resource_group }}"
name: function-plan
location: "{{ location }}"
sku:
tier: Consumption
size: Y1
os_type: Linux
reserved: true
- name: Create storage account for functions
azure.azcollection.azure_rm_storageaccount:
resource_group: "{{ resource_group }}"
name: funcstorage12345
location: "{{ location }}"
account_type: Standard_LRS
- name: Create function app
azure.azcollection.azure_rm_webapp:
resource_group: "{{ resource_group }}"
name: my-function-app
location: "{{ location }}"
plan:
name: function-plan
resource_group: "{{ resource_group }}"
frameworks:
- name: python
version: "3.11"
app_settings:
- name: FUNCTIONS_WORKER_RUNTIME
value: python
- name: AzureWebJobsStorage
value: "DefaultEndpointsProtocol=https;AccountName=funcstorage12345;..."
This example provisions a complete web application infrastructure on Azure:
- name: Deploy complete Azure infrastructure
hosts: localhost
gather_facts: false
vars:
resource_group: mywebapp-rg
location: eastus
environment: production
app_name: mywebapp
tasks:
# Resource Group
- name: Create resource group
azure.azcollection.azure_rm_resourcegroup:
name: "{{ resource_group }}"
location: "{{ location }}"
tags:
Environment: "{{ environment }}"
Application: "{{ app_name }}"
# VNet
- name: Create virtual network
azure.azcollection.azure_rm_virtualnetwork:
resource_group: "{{ resource_group }}"
name: "{{ app_name }}-vnet"
location: "{{ location }}"
address_prefixes:
- 10.0.0.0/16
# Subnets
- name: Create subnets
azure.azcollection.azure_rm_subnet:
resource_group: "{{ resource_group }}"
name: "{{ item.name }}"
virtual_network_name: "{{ app_name }}-vnet"
address_prefix: "{{ item.cidr }}"
loop:
- { name: "web-subnet", cidr: "10.0.1.0/24" }
- { name: "app-subnet", cidr: "10.0.2.0/24" }
- { name: "db-subnet", cidr: "10.0.3.0/24" }
# NSG
- name: Create web NSG
azure.azcollection.azure_rm_securitygroup:
resource_group: "{{ resource_group }}"
name: "{{ app_name }}-web-nsg"
location: "{{ location }}"
rules:
- name: AllowHTTP
protocol: Tcp
destination_port_range: 80
access: Allow
direction: Inbound
priority: 100
- name: AllowHTTPS
protocol: Tcp
destination_port_range: 443
access: Allow
direction: Inbound
priority: 101
- name: AllowSSH
protocol: Tcp
destination_port_range: 22
access: Allow
direction: Inbound
priority: 102
source_address_prefix: 10.0.0.0/8
# Public IP
- name: Create public IP
azure.azcollection.azure_rm_publicipaddress:
resource_group: "{{ resource_group }}"
name: "{{ app_name }}-pip"
location: "{{ location }}"
allocation_method: Dynamic
sku: Basic
# Network Interface
- name: Create NIC
azure.azcollection.azure_rm_networkinterface:
resource_group: "{{ resource_group }}"
name: "{{ app_name }}-nic"
location: "{{ location }}"
virtual_network_name: "{{ app_name }}-vnet"
subnet_name: web-subnet
public_ip_name: "{{ app_name }}-pip"
security_group_name: "{{ app_name }}-web-nsg"
# VMs
- name: Create web servers
azure.azcollection.azure_rm_virtualmachine:
resource_group: "{{ resource_group }}"
name: "{{ app_name }}-web-{{ item }}"
location: "{{ location }}"
vm_size: Standard_DS2_v2
admin_username: azureuser
admin_password: "{{ vault_admin_password }}"
network_interfaces: "{{ app_name }}-nic"
storage_account_name: "{{ app_name }}sa{{ item }}"
storage_container_name: vhds
storage_blob_name: osdisk.vhd
image:
offer: UbuntuServer
publisher: Canonical
sku: 2204-LTS
version: latest
tags:
Environment: "{{ environment }}"
Application: "{{ app_name }}"
Role: WebServer
loop: "{{ range(1, 3) | list }}"
# Storage Account
- name: Create storage account
azure.azcollection.azure_rm_storageaccount:
resource_group: "{{ resource_group }}"
name: "{{ app_name }}storage"
location: "{{ location }}"
account_type: Standard_LRS
tags:
Environment: "{{ environment }}"
Application: "{{ app_name }}"
tags:
Environment: "{{ environment }}"
Application: "{{ app_name }}"
ManagedBy: Ansible
CostCenter: "12345"
Owner: "team-name"
All Azure modules support idempotent operations:
- name: Ensure VM is running
azure.azcollection.azure_rm_virtualmachine:
resource_group: my-rg
name: web-server
started: true
- name: Ensure old VM is removed
azure.azcollection.azure_rm_virtualmachine:
resource_group: my-rg
name: old-server
state: absent
For high availability:
zones:
- "1"
- "2"
- "3"
- name: Enable boot diagnostics
azure.azcollection.azure_rm_virtualmachine:
resource_group: my-rg
name: web-server
boot_diagnostics:
enabled: true
storage_account_name: diagstorage123
Any questions?
Feel free to contact us. Find all contact information on our contact page.