Skip to main content

Provider Configuration

Ansible connects to managed nodes and cloud APIs through a combination of inventory configuration, connection plugins, and collection-specific settings. This page covers the canonical way to configure each.

SSH Connection​

SSH is the default connection method for Linux and macOS hosts. Configure it in ansible.cfg and inventory. Never pass connection parameters as CLI flags in CI.

# ansible.cfg
[defaults]
remote_user = ansible

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=accept-new
pipelining = true
transfer_method = smart

Enabling pipelining reduces the number of SSH connections per task and significantly improves performance. It requires that requiretty is disabled in /etc/sudoers on managed hosts.

Bastion / Jump Host​

For hosts reachable only through a bastion, set the ansible_ssh_common_args variable at the group level:

# inventories/production/group_vars/all.yml
ansible_ssh_common_args: >-
-o ProxyJump=bastion.example.com
-o StrictHostKeyChecking=accept-new

WinRM (Windows Hosts)​

For Windows hosts, use the winrm connection plugin provided by ansible.windows:

# host_vars/win-host-01.yml
ansible_connection: winrm
ansible_winrm_transport: kerberos
ansible_winrm_server_cert_validation: validate
ansible_port: 5986

Never use ansible_winrm_server_cert_validation: ignore outside of a lab environment.

Local Connection​

Use connection: local only for tasks that run on the control node itself, such as invoking a cloud API or generating a local file:

- name: Generate the Terraform backend configuration
ansible.builtin.template:
src: backend.tf.j2
dest: "{{ playbook_dir }}/backend.tf"
connection: local
delegate_to: localhost

Cloud Provider Configuration​

AWS​

Install and pin the AWS collection:

# collections/requirements.yml
collections:
- name: amazon.aws
version: ">=7.0.0,<8.0.0"

Authenticate using IAM roles attached to the control node (EC2 instance profile or ECS task role). Never hardcode access keys in variables or environment files.

# Correct: relies on instance profile / environment credentials
- name: List S3 buckets
amazon.aws.s3_bucket_info:
region: us-east-1
register: s3_buckets

If running outside AWS (e.g. a local developer machine or GitHub Actions), use short-lived credentials obtained via aws sts assume-role and injected as environment variables by the CI pipeline. Store the role ARN in a non-secret CI variable; never store the resulting credentials beyond the lifetime of the job.

Azure​

# collections/requirements.yml
collections:
- name: azure.azcollection
version: ">=2.0.0,<3.0.0"

Authenticate with a service principal whose credentials are retrieved from a secrets manager at runtime:

- name: Read the Azure service principal credentials
community.hashi_vault.vault_kv2_get:
path: secret/azure/sp
register: azure_sp
no_log: true

- name: Create the resource group
azure.azcollection.azure_rm_resourcegroup:
name: myapp-production-rg
location: eastus
subscription_id: "{{ azure_sp.data.data.subscription_id }}"
client_id: "{{ azure_sp.data.data.client_id }}"
secret: "{{ azure_sp.data.data.client_secret }}"
tenant: "{{ azure_sp.data.data.tenant_id }}"
no_log: true

GCP​

# collections/requirements.yml
collections:
- name: google.cloud
version: ">=1.3.0,<2.0.0"

Prefer Workload Identity Federation over service account JSON keys. When a key file is unavoidable, retrieve it from a secrets manager and write it to a temporary file with ansible.builtin.tempfile, then delete it in an always: block:

- name: Write the GCP service account key to a temp file
ansible.builtin.tempfile:
suffix: .json
register: gcp_key_file
no_log: true

- name: Populate the GCP key file
ansible.builtin.copy:
content: "{{ gcp_sa_key }}"
dest: "{{ gcp_key_file.path }}"
mode: "0600"
no_log: true

- name: Perform GCP tasks
google.cloud.gcp_compute_instance_info:
project: myproject
zone: us-central1-a
auth_kind: serviceaccount
service_account_file: "{{ gcp_key_file.path }}"
register: gcp_instances

- name: Remove the GCP key file
ansible.builtin.file:
path: "{{ gcp_key_file.path }}"
state: absent
when: gcp_key_file.path is defined

Inventory Plugins​

Prefer dynamic inventories over static hosts.yml files for cloud environments. Pin the plugin version alongside the collection:

# inventories/production/hosts.aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
compose:
ansible_host: public_ip_address

Run ansible-inventory --list to validate the dynamic inventory before a playbook run.