AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible for Cloud Automation: AWS, Azure, and GCP Complete Guide

By Luca Berton · Published 2024-01-01 · Category: installation

Complete guide to Ansible cloud automation for AWS, Azure, and GCP. Provision EC2 instances, Azure VMs, and GCE instances.

Ansible manages all three major clouds through dedicated collections — amazon.aws, azure.azcollection, and google.cloud. Same YAML playbooks, same automation patterns, different providers. Here's how to provision and manage infrastructure across AWS, Azure, and GCP.

Setup

Install Collections

# AWS
ansible-galaxy collection install amazon.aws
pip install boto3 botocore

# Azure ansible-galaxy collection install azure.azcollection pip install -r ~/.ansible/collections/ansible_collections/azure/azcollection/requirements.txt

# GCP ansible-galaxy collection install google.cloud pip install google-auth google-api-python-client

Authentication

AWS

# Option 1: Environment variables
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="secret..."
export AWS_REGION="us-east-1"

# Option 2: AWS CLI profile aws configure --profile ansible export AWS_PROFILE=ansible

# Option 3: IAM role (on EC2 instances) — no credentials needed

# Or in playbook
- amazon.aws.ec2_instance:
    access_key: "{{ vault_aws_access_key }}"
    secret_key: "{{ vault_aws_secret_key }}"
    region: us-east-1

Azure

# Option 1: Environment variables
export AZURE_SUBSCRIPTION_ID="..."
export AZURE_CLIENT_ID="..."
export AZURE_SECRET="..."
export AZURE_TENANT="..."

# Option 2: Azure CLI (interactive) az login

GCP

# Option 1: Service account JSON
export GCP_SERVICE_ACCOUNT_FILE=/path/to/service-account.json

# Option 2: Application Default Credentials gcloud auth application-default login

# Or in playbook
- google.cloud.gcp_compute_instance:
    auth_kind: serviceaccount
    service_account_file: /path/to/sa.json
    project: my-project

See also: AAP 2.6 Cloud Automation: AWS, Azure, and GCP with Ansible

AWS Automation

Provision EC2 Instances

---
- name: AWS Infrastructure
  hosts: localhost
  connection: local
  vars:
    region: us-east-1
    instance_type: t3.medium
    ami: ami-0c55b159cbfafe1f0
  tasks:
    - name: Create VPC
      amazon.aws.ec2_vpc_net:
        name: production-vpc
        cidr_block: 10.0.0.0/16
        region: "{{ region }}"
        tags:
          Environment: production
      register: vpc

- name: Create subnet amazon.aws.ec2_vpc_subnet: vpc_id: "{{ vpc.vpc.id }}" cidr: 10.0.1.0/24 az: "{{ region }}a" region: "{{ region }}" tags: Name: public-subnet register: subnet

- name: Create internet gateway amazon.aws.ec2_vpc_igw: vpc_id: "{{ vpc.vpc.id }}" region: "{{ region }}" register: igw

- name: Create security group amazon.aws.ec2_security_group: name: web-sg description: Web server security group vpc_id: "{{ vpc.vpc.id }}" region: "{{ region }}" rules: - proto: tcp ports: [80, 443] cidr_ip: 0.0.0.0/0 - proto: tcp ports: [22] cidr_ip: 10.0.0.0/8 register: sg

- name: Launch EC2 instances amazon.aws.ec2_instance: name: "web-{{ item }}" instance_type: "{{ instance_type }}" image_id: "{{ ami }}" subnet_id: "{{ subnet.subnet.id }}" security_group: "{{ sg.group_id }}" key_name: deploy-key region: "{{ region }}" network: assign_public_ip: true tags: Role: webserver Environment: production wait: true loop: "{{ range(1, 4) | list }}" register: ec2_instances

S3 Bucket Management

- name: Create S3 bucket
  amazon.aws.s3_bucket:
    name: my-app-assets-{{ aws_account_id }}
    region: "{{ region }}"
    versioning: true
    encryption: AES256
    public_access:
      block_public_acls: true
      block_public_policy: true
      ignore_public_acls: true
      restrict_public_buckets: true
    tags:
      Purpose: application-assets

- name: Upload files to S3 amazon.aws.s3_object: bucket: my-app-assets-{{ aws_account_id }} object: "config/{{ item | basename }}" src: "{{ item }}" mode: put loop: "{{ lookup('fileglob', 'configs/*', wantlist=True) }}"

Application Load Balancer

- name: Create ALB
  amazon.aws.elb_application_lb:
    name: web-alb
    region: "{{ region }}"
    subnets:
      - "{{ subnet_a.subnet.id }}"
      - "{{ subnet_b.subnet.id }}"
    security_groups:
      - "{{ sg.group_id }}"
    listeners:
      - Protocol: HTTPS
        Port: 443
        Certificates:
          - CertificateArn: "{{ acm_cert_arn }}"
        DefaultActions:
          - Type: forward
            TargetGroupName: web-targets

- name: Create target group amazon.aws.elb_target_group: name: web-targets protocol: HTTP port: 80 vpc_id: "{{ vpc.vpc.id }}" region: "{{ region }}" health_check_path: /health targets: - Id: "{{ item.instance_id }}" Port: 80 loop: "{{ ec2_instances.results }}"

Azure Automation

Provision Azure VMs

---
- name: Azure Infrastructure
  hosts: localhost
  connection: local
  vars:
    resource_group: production-rg
    location: eastus
  tasks:
    - name: Create resource group
      azure.azcollection.azure_rm_resourcegroup:
        name: "{{ resource_group }}"
        location: "{{ location }}"
        tags:
          Environment: production

- name: Create virtual network azure.azcollection.azure_rm_virtualnetwork: resource_group: "{{ resource_group }}" name: production-vnet address_prefixes: "10.0.0.0/16"

- name: Create subnet azure.azcollection.azure_rm_subnet: resource_group: "{{ resource_group }}" virtual_network: production-vnet name: web-subnet address_prefix: "10.0.1.0/24"

- name: Create network security group azure.azcollection.azure_rm_securitygroup: resource_group: "{{ resource_group }}" name: web-nsg rules: - name: AllowHTTP protocol: Tcp destination_port_range: [80, 443] access: Allow priority: 100 direction: Inbound - name: AllowSSH protocol: Tcp destination_port_range: 22 access: Allow priority: 200 direction: Inbound source_address_prefix: 10.0.0.0/8

- name: Create public IP azure.azcollection.azure_rm_publicipaddress: resource_group: "{{ resource_group }}" name: "web-{{ item }}-pip" allocation_method: Static sku: Standard loop: "{{ range(1, 4) | list }}" register: public_ips

- name: Create VM azure.azcollection.azure_rm_virtualmachine: resource_group: "{{ resource_group }}" name: "web-{{ item }}" vm_size: Standard_B2s admin_username: deploy ssh_password_enabled: false ssh_public_keys: - path: /home/deploy/.ssh/authorized_keys key_data: "{{ lookup('file', '~/.ssh/deploy_key.pub') }}" image: offer: 0001-com-ubuntu-server-jammy publisher: Canonical sku: 22_04-lts version: latest os_disk_size_gb: 30 virtual_network: production-vnet subnet: web-subnet tags: Role: webserver loop: "{{ range(1, 4) | list }}"

Azure Kubernetes Service (AKS)

- name: Create AKS cluster
  azure.azcollection.azure_rm_aks:
    resource_group: "{{ resource_group }}"
    name: production-aks
    location: "{{ location }}"
    kubernetes_version: "1.30"
    dns_prefix: prod-aks
    agent_pool_profiles:
      - name: default
        count: 3
        vm_size: Standard_D2s_v3
        os_disk_size_gb: 100
        mode: System
    network_profile:
      network_plugin: azure
      service_cidr: 172.16.0.0/16
      dns_service_ip: 172.16.0.10
    tags:
      Environment: production

See also: Ansible Dynamic Inventory: Complete Guide to AWS, Azure, GCP, and Custom Plugins

GCP Automation

Provision GCE Instances

---
- name: GCP Infrastructure
  hosts: localhost
  connection: local
  vars:
    project: my-gcp-project
    zone: us-central1-a
    region: us-central1
  tasks:
    - name: Create VPC network
      google.cloud.gcp_compute_network:
        name: production-network
        project: "{{ project }}"
        auto_create_subnetworks: false
        auth_kind: serviceaccount
        service_account_file: "{{ gcp_sa_file }}"
      register: network

- name: Create subnet google.cloud.gcp_compute_subnetwork: name: web-subnet ip_cidr_range: 10.0.1.0/24 network: "{{ network }}" region: "{{ region }}" project: "{{ project }}" auth_kind: serviceaccount service_account_file: "{{ gcp_sa_file }}" register: subnet

- name: Create firewall rules google.cloud.gcp_compute_firewall: name: allow-web network: "{{ network }}" project: "{{ project }}" allowed: - ip_protocol: tcp ports: ['80', '443'] source_ranges: ['0.0.0.0/0'] target_tags: ['web'] auth_kind: serviceaccount service_account_file: "{{ gcp_sa_file }}"

- name: Create GCE instances google.cloud.gcp_compute_instance: name: "web-{{ item }}" machine_type: e2-medium zone: "{{ zone }}" project: "{{ project }}" disks: - auto_delete: true boot: true initialize_params: source_image: projects/ubuntu-os-cloud/global/images/family/ubuntu-2204-lts disk_size_gb: 30 network_interfaces: - network: "{{ network }}" subnetwork: "{{ subnet }}" access_configs: - name: External NAT type: ONE_TO_ONE_NAT tags: items: ['web'] metadata: ssh-keys: "deploy:{{ lookup('file', '~/.ssh/deploy_key.pub') }}" auth_kind: serviceaccount service_account_file: "{{ gcp_sa_file }}" loop: "{{ range(1, 4) | list }}"

GKE Cluster

- name: Create GKE cluster
  google.cloud.gcp_container_cluster:
    name: production-gke
    location: "{{ region }}"
    project: "{{ project }}"
    initial_node_count: 3
    node_config:
      machine_type: e2-standard-4
      disk_size_gb: 100
      oauth_scopes:
        - https://www.googleapis.com/auth/cloud-platform
    network: "{{ network.name }}"
    subnetwork: "{{ subnet.name }}"
    auth_kind: serviceaccount
    service_account_file: "{{ gcp_sa_file }}"

Multi-Cloud Patterns

Dynamic Inventory

# aws_inventory.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
  - eu-west-1
filters:
  tag:Managed: ansible
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: placement.region
    prefix: region
compose:
  ansible_host: public_ip_address
# azure_inventory.yml
plugin: azure.azcollection.azure_rm
include_vm_resource_groups:
  - production-rg
keyed_groups:
  - key: tags.Role
    prefix: role
compose:
  ansible_host: public_ipv4_addresses[0]
# Use multiple inventories
ansible-playbook -i aws_inventory.yml -i azure_inventory.yml site.yml

Multi-Cloud Deployment Playbook

---
- name: Deploy to AWS
  hosts: localhost
  connection: local
  tasks:
    - name: Provision AWS infrastructure
      ansible.builtin.include_role:
        name: aws_infra
      vars:
        instance_count: 3
        instance_type: t3.medium

- name: Deploy to Azure hosts: localhost connection: local tasks: - name: Provision Azure infrastructure ansible.builtin.include_role: name: azure_infra vars: instance_count: 3 vm_size: Standard_B2s

- name: Configure all servers hosts: role_webserver become: true roles: - common - nginx - app_deploy

Cloud-Agnostic Variables

# group_vars/all.yml
cloud_config:
  aws:
    instance_type: t3.medium
    ami: ami-0c55b159cbfafe1f0
    region: us-east-1
  azure:
    vm_size: Standard_B2s
    image: Canonical:0001-com-ubuntu-server-jammy:22_04-lts:latest
    location: eastus
  gcp:
    machine_type: e2-medium
    image: ubuntu-2204-lts
    zone: us-central1-a

# Use: cloud_config[cloud_provider].instance_type

See also: Automating Azure DevTest Labs Course by Luca Berton | Pluralsight

Common Operations

Snapshot and Backup

# AWS EBS snapshot
- amazon.aws.ec2_snapshot:
    instance_id: "{{ instance_id }}"
    description: "Backup {{ ansible_date_time.date }}"
    wait: true
    tags:
      Backup: daily

# Azure disk snapshot - azure.azcollection.azure_rm_snapshot: resource_group: "{{ resource_group }}" name: "backup-{{ ansible_date_time.date }}" creation_data: create_option: Copy source_id: "{{ disk_id }}"

# GCP disk snapshot - google.cloud.gcp_compute_snapshot: name: "backup-{{ ansible_date_time.date }}" source_disk: "{{ disk }}" zone: "{{ zone }}" project: "{{ project }}" auth_kind: serviceaccount service_account_file: "{{ gcp_sa_file }}"

DNS Management

# AWS Route53
- amazon.aws.route53:
    zone: example.com
    record: app.example.com
    type: A
    value: "{{ alb_dns_name }}"
    alias: true
    alias_hosted_zone_id: "{{ alb_zone_id }}"

# Azure DNS - azure.azcollection.azure_rm_dnsrecordset: resource_group: dns-rg zone_name: example.com relative_name: app record_type: A records: - entry: "{{ public_ip }}"

# GCP Cloud DNS - google.cloud.gcp_dns_resource_record_set: name: app.example.com. managed_zone: "{{ dns_zone }}" type: A ttl: 300 target: - "{{ external_ip }}" project: "{{ project }}" auth_kind: serviceaccount service_account_file: "{{ gcp_sa_file }}"

FAQ

Should I use Ansible or Terraform for cloud provisioning?

Use Terraform for infrastructure provisioning (VMs, networks, load balancers) — it has state tracking and plan/apply workflows. Use Ansible for configuring what runs on that infrastructure. For simple cloud setups, Ansible alone works fine. For complex multi-resource dependencies, Terraform excels.

How do I handle credentials securely?

Use Ansible Vault for storing cloud credentials, or better — use IAM roles (AWS), Managed Identity (Azure), or Service Accounts (GCP) that don't require storing secrets at all. In CI/CD, use the platform's secret management (GitHub Actions secrets, GitLab CI variables).

Can Ansible manage serverless resources?

Yes. AWS Lambda, Azure Functions, and GCP Cloud Functions all have Ansible modules. You can deploy function code, configure triggers, and manage API gateways through playbooks.

How does dynamic inventory work with auto-scaling?

Dynamic inventory plugins query the cloud API in real-time. When auto-scaling adds instances, they appear in the next inventory refresh. Tag-based grouping ensures new instances get the correct role assignments automatically.

What about cloud costs?

Ansible can help manage costs: schedule instance start/stop, right-size instances based on metrics, clean up unused resources, and enforce tagging policies. Combine with cloud-native cost tools for full visibility.

Conclusion

Ansible's cloud collections give you consistent YAML-based automation across AWS, Azure, and GCP. Use dynamic inventory for auto-discovery, cloud-agnostic variables for portability, and combine with Terraform for complex provisioning. The same team, same playbook patterns, same CI/CD pipeline — regardless of which cloud you're deploying to.

Related Articles

Ansible vs Terraform 2026Ansible for KubernetesAnsible Vault Deep DiveAnsible for AWS Cloud Automation

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home