Ansible
Chapter 43: Ansible on AWS
Section titled “Chapter 43: Ansible on AWS”Configuration Management with Ansible
Section titled “Configuration Management with Ansible”43.1 Overview
Section titled “43.1 Overview”Ansible is an open-source automation platform that simplifies configuration management, application deployment, and task automation on AWS infrastructure.
Ansible Overview+------------------------------------------------------------------+| || +------------------------+ || | Ansible | || +------------------------+ || | || +---------------------+---------------------+ || | | | | || v v v v || +----------+ +----------+ +----------+ +----------+ || | Agentless| | Playbooks| | Modules | | Inventory| || | | | | | | | | || | - SSH | | - YAML | | - AWS | | - Static | || | - WinRM | | - Tasks | | - Cloud | | - Dynamic| || | - Python | | - Roles | | - System | | - Groups | || +----------+ +----------+ +----------+ +----------+ || |+------------------------------------------------------------------+Key Features
Section titled “Key Features”| Feature | Description |
|---|---|
| Agentless | No software installation required on targets |
| Playbooks | YAML-based automation scripts |
| Modules | Pre-built automation units |
| Inventory | Dynamic or static host management |
43.2 Ansible Architecture
Section titled “43.2 Ansible Architecture”Ansible Components
Section titled “Ansible Components” Ansible Architecture+------------------------------------------------------------------+| || Control Node || +----------------------------------------------------------+ || | | || | +----------+ +----------+ +----------+ +----------+ | || | | Ansible | | Playbooks| | Inventory| | Modules | | || | | Core | | | | | | | | || | +----------+ +----------+ +----------+ +----------+ | || | | || +--------------------------+-------------------------------+ || | || +----------------+----------------+ || | | | || v v v || +----------+ +----------+ +----------+ || | Managed | | Managed | | Managed | || | Node 1 | | Node 2 | | Node N | || | (EC2) | | (EC2) | | (EC2) | || +----------+ +----------+ +----------+ || | | | || v v v || SSH/WinRM SSH/WinRM SSH/WinRM || |+------------------------------------------------------------------+AWS Integration Points
Section titled “AWS Integration Points” Ansible AWS Integration+------------------------------------------------------------------+| || +------------------+ +------------------+ +------------------+ || | EC2 Instances | | AWS Services | | Infrastructure | || | | | | | | || | - Configuration | | - S3 Buckets | | - VPC Creation | || | - Deployment | | - RDS Databases | | - Security Groups| || | - Orchestration | | - Lambda | | - IAM Roles | || +------------------+ +------------------+ +------------------+ || || +------------------+ +------------------+ +------------------+ || | Dynamic Inventory| | Cloud Modules | | Automation | || | | | | | | || | - AWS EC2 | | - ec2_instance | | - Scaling | || | - AWS RDS | | - s3_bucket | | - Updates | || | - AWS Route53 | | - rds_instance | | - Compliance | || +------------------+ +------------------+ +------------------+ || |+------------------------------------------------------------------+43.3 Installation and Configuration
Section titled “43.3 Installation and Configuration”Installing Ansible
Section titled “Installing Ansible”# Ubuntu/Debiansudo apt updatesudo apt install ansible
# RHEL/CentOSsudo yum install epel-releasesudo yum install ansible
# Using pip (recommended)pip install ansible
# Install AWS collectionansible-galaxy collection install amazon.aws
# Install additional dependenciespip install boto3 botocoreAnsible Configuration
Section titled “Ansible Configuration”# ansible.cfg[defaults]inventory = ./inventory/aws_ec2.ymlroles_path = ./rolescollections_path = ./collectionsremote_user = ec2-userprivate_key_file = ~/.ssh/aws-key.pemhost_key_checking = Falsestdout_callback = yamlretry_files_enabled = Falselog_path = ./logs/ansible.log
[privilege_escalation]become = Truebecome_method = sudobecome_user = rootbecome_ask_pass = False
[ssh_connection]ssh_args = -o ForwardAgent=yes -o StrictHostKeyChecking=nopipelining = TrueAWS Credentials Configuration
Section titled “AWS Credentials Configuration”# Environment variablesexport AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLEexport AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYexport AWS_REGION=us-east-1
# AWS CLI configurationaws configure
# IAM role (recommended for EC2)# Attach role with appropriate policies to EC2 instance43.4 Dynamic Inventory
Section titled “43.4 Dynamic Inventory”AWS EC2 Dynamic Inventory
Section titled “AWS EC2 Dynamic Inventory”plugin: aws_ec2regions: - us-east-1 - us-west-2
# Group by tagskeyed_groups: - key: tags.Environment prefix: env - key: tags.Application prefix: app - key: tags.Role prefix: role
# Group by instance typegroups: web_servers: "'web' in tags.Role" db_servers: "'database' in tags.Role" production: "tags.Environment == 'production'"
# Filtersfilters: instance-state-name: running
# Compose variablescompose: ansible_host: public_ip_address private_ip: private_ip_address instance_id: instance_id
# Hostnamehostnames: - tag:Name - private-ip-address
# Exclude instancesexclude_filters: - tag:ExcludeFromAnsible: "true"Inventory Output Example
Section titled “Inventory Output Example”# List inventoryansible-inventory -i inventory/aws_ec2.yml --list
# Output:{ "_meta": { "hostvars": { "i-1234567890abcdef0": { "ansible_host": "54.123.45.67", "private_ip": "10.0.1.10", "instance_id": "i-1234567890abcdef0", "tags": { "Environment": "production", "Role": "web", "Name": "web-server-01" } } } }, "env_production": ["i-1234567890abcdef0"], "role_web": ["i-1234567890abcdef0"], "web_servers": ["i-1234567890abcdef0"]}Static Inventory
Section titled “Static Inventory”[web_servers]web-01 ansible_host=54.123.45.67web-02 ansible_host=54.123.45.68
[db_servers]db-01 ansible_host=10.0.2.10db-02 ansible_host=10.0.2.11
[production:children]web_serversdb_servers
[web_servers:vars]ansible_user=ec2-useransible_ssh_private_key_file=~/.ssh/prod-key.pem
[db_servers:vars]ansible_user=ubuntuansible_ssh_private_key_file=~/.ssh/prod-key.pem43.5 Playbooks
Section titled “43.5 Playbooks”Basic Playbook Structure
Section titled “Basic Playbook Structure”---- name: Configure web servers hosts: web_servers become: yes
vars: nginx_version: "1.18.0" app_port: 8080
tasks: - name: Update apt cache apt: update_cache: yes cache_valid_time: 3600 when: ansible_os_family == "Debian"
- name: Install Nginx package: name: nginx state: present
- name: Configure Nginx template: src: templates/nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' notify: Restart Nginx
- name: Start Nginx service: name: nginx state: started enabled: yes
handlers: - name: Restart Nginx service: name: nginx state: restartedMulti-Play Playbook
Section titled “Multi-Play Playbook”---- name: Configure VPC hosts: localhost connection: local gather_facts: no
tasks: - name: Create VPC amazon.aws.ec2_vpc: name: "{{ vpc_name }}" cidr: "{{ vpc_cidr }}" region: "{{ aws_region }}" state: present register: vpc_result
- name: Provision EC2 instances hosts: localhost connection: local gather_facts: no
tasks: - name: Launch EC2 instances amazon.aws.ec2_instance: name: "{{ item.name }}" instance_type: "{{ item.type }}" image_id: "{{ ami_id }}" region: "{{ aws_region }}" vpc_subnet_id: "{{ subnet_id }}" security_group: "{{ security_group }}" key_name: "{{ key_name }}" count: "{{ item.count }}" state: present loop: - { name: 'web-server', type: 't3.medium', count: 2 } - { name: 'app-server', type: 't3.large', count: 2 } register: ec2_result
- name: Configure instances hosts: tag_Role_web become: yes
roles: - common - nginx - application43.6 AWS Modules
Section titled “43.6 AWS Modules”EC2 Instance Management
Section titled “EC2 Instance Management”# Launch EC2 instance- name: Launch EC2 instance amazon.aws.ec2_instance: name: "web-server-{{ inventory_hostname }}" instance_type: t3.medium image_id: ami-0abcdef1234567890 region: us-east-1 vpc_subnet_id: subnet-12345678 security_groups: - sg-12345678 key_name: my-keypair tags: Environment: production Role: web volumes: - device_name: /dev/sda1 ebs: volume_size: 20 volume_type: gp3 encrypted: yes user_data: | #!/bin/bash echo "User data script" > /tmp/userdata.log state: present register: ec2_instance
# Terminate instance- name: Terminate instance amazon.aws.ec2_instance: instance_ids: ["i-1234567890abcdef0"] state: absent
# Start/Stop instance- name: Stop instance amazon.aws.ec2_instance: instance_ids: ["i-1234567890abcdef0"] state: stopped
- name: Start instance amazon.aws.ec2_instance: instance_ids: ["i-1234567890abcdef0"] state: startedS3 Bucket Management
Section titled “S3 Bucket Management”# Create S3 bucket- name: Create S3 bucket amazon.aws.s3_bucket: name: "{{ bucket_name }}" region: us-east-1 state: present versioning: yes encryption: aws:kms kms_master_key_id: "{{ kms_key_id }}" public_access: block_public_acls: true block_public_policy: true ignore_public_acls: true restrict_public_buckets: true tags: Environment: production Project: web-app
# Upload file to S3- name: Upload file to S3 amazon.aws.s3_object: bucket: "{{ bucket_name }}" object: "/config/app.conf" src: "./files/app.conf" mode: upload encryption: aws:kms
# Download file from S3- name: Download file from S3 amazon.aws.s3_object: bucket: "{{ bucket_name }}" object: "/config/app.conf" dest: "/tmp/app.conf" mode: getRDS Management
Section titled “RDS Management”# Create RDS instance- name: Create RDS instance community.aws.rds_instance: db_instance_identifier: "{{ db_identifier }}" db_instance_class: db.t3.medium engine: postgres engine_version: "14.7" allocated_storage: 100 storage_type: gp3 master_username: "{{ db_user }}" master_user_password: "{{ db_password }}" vpc_security_group_ids: - "{{ security_group_id }}" db_subnet_group_name: "{{ subnet_group }}" multi_az: yes backup_retention_period: 7 backup_window: "03:00-04:00" maintenance_window: "sun:04:00-sun:05:00" storage_encrypted: yes kms_key_id: "{{ kms_key_id }}" state: present register: rds_instance
# Create RDS snapshot- name: Create RDS snapshot community.aws.rds_snapshot: db_instance_identifier: "{{ db_identifier }}" db_snapshot_identifier: "{{ snapshot_name }}" state: present
# Delete RDS instance- name: Delete RDS instance community.aws.rds_instance: db_instance_identifier: "{{ db_identifier }}" state: absent skip_final_snapshot: no final_snapshot_identifier: "{{ final_snapshot_name }}"VPC Management
Section titled “VPC Management”# Create VPC- name: Create VPC amazon.aws.ec2_vpc_net: name: "{{ vpc_name }}" cidr_block: "{{ vpc_cidr }}" region: "{{ aws_region }}" dns_hostnames: yes dns_support: yes state: present tags: Environment: production register: vpc
# Create subnet- name: Create subnet amazon.aws.ec2_vpc_subnet: vpc_id: "{{ vpc.vpc.id }}" cidr: "{{ subnet_cidr }}" az: "{{ availability_zone }}" region: "{{ aws_region }}" state: present tags: Name: "{{ subnet_name }}" register: subnet
# Create security group- name: Create security group amazon.aws.ec2_security_group: name: "{{ sg_name }}" description: "{{ sg_description }}" vpc_id: "{{ vpc.vpc.id }}" region: "{{ aws_region }}" rules: - proto: tcp from_port: 22 to_port: 22 cidr_ip: 0.0.0.0/0 - proto: tcp from_port: 443 to_port: 443 cidr_ip: 0.0.0.0/0 state: present register: security_group
# Create internet gateway- name: Create internet gateway amazon.aws.ec2_vpc_igw: vpc_id: "{{ vpc.vpc.id }}" region: "{{ aws_region }}" state: present tags: Name: "{{ igw_name }}" register: igwIAM Management
Section titled “IAM Management”# Create IAM role- name: Create IAM role amazon.aws.iam_role: name: "{{ role_name }}" assume_role_policy_document: | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } state: present register: iam_role
# Attach policy to role- name: Attach policy to role amazon.aws.iam_role_policy_attachment: role_name: "{{ role_name }}" policy_arn: arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess state: present
# Create IAM user- name: Create IAM user amazon.aws.iam_user: name: "{{ user_name }}" state: present tags: Environment: production
# Create access key- name: Create access key amazon.aws.iam_access_key: user_name: "{{ user_name }}" state: present register: access_key43.7 Roles
Section titled “43.7 Roles”Role Structure
Section titled “Role Structure”roles/+------------------------------------------------------------------+| || web-server/ || +-- tasks/ || | +-- main.yml # Main task list || | +-- install.yml # Install tasks || | +-- configure.yml # Configuration tasks || | || +-- handlers/ || | +-- main.yml # Handlers || | || +-- templates/ || | +-- nginx.conf.j2 # Nginx config template || | +-- app.conf.j2 # App config template || | || +-- files/ || | +-- index.html # Static files || | || +-- vars/ || | +-- main.yml # Role variables || | +-- debian.yml # OS-specific variables || | || +-- defaults/ || | +-- main.yml # Default variables || | || +-- meta/ || | +-- main.yml # Role dependencies || | || +-- tests/ || | +-- test.yml # Test playbook || | +-- inventory # Test inventory || | || +-- README.md # Role documentation || |+------------------------------------------------------------------+Role Tasks
Section titled “Role Tasks”---- name: Include install tasks include_tasks: install.yml tags: install
- name: Include configure tasks include_tasks: configure.yml tags: configure
# roles/web-server/tasks/install.yml---- name: Update apt cache apt: update_cache: yes cache_valid_time: 3600 when: ansible_os_family == "Debian"
- name: Install Nginx package: name: nginx state: present
- name: Install dependencies package: name: - curl - wget - jq state: present
# roles/web-server/tasks/configure.yml---- name: Create directories file: path: "{{ item }}" state: directory owner: www-data group: www-data mode: '0755' loop: - /var/www/html - /var/log/nginx
- name: Configure Nginx template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf owner: root group: root mode: '0644' notify: Restart Nginx
- name: Start Nginx service: name: nginx state: started enabled: yesRole Handlers
Section titled “Role Handlers”---- name: Restart Nginx service: name: nginx state: restarted
- name: Reload Nginx service: name: nginx state: reloaded
- name: Validate Nginx config command: nginx -t changed_when: falseRole Defaults
Section titled “Role Defaults”---nginx_worker_processes: autonginx_worker_connections: 1024nginx_keepalive_timeout: 65nginx_listen_port: 80nginx_server_name: "_"nginx_root: /var/www/htmlnginx_index_files: - index.html - index.htmRole Meta
Section titled “Role Meta”---galaxy_info: author: DevOps Team description: Web server configuration role company: Company Name license: MIT min_ansible_version: 2.12 platforms: - name: Ubuntu versions: - jammy - focal - name: EL versions: - 8 - 9 galaxy_tags: - web - nginx - server
dependencies: - role: common vars: common_packages: - curl - wget43.8 Templates
Section titled “43.8 Templates”Jinja2 Templates
Section titled “Jinja2 Templates”{# roles/web-server/templates/nginx.conf.j2 #}# Nginx configuration managed by Ansible# Do not edit manually
worker_processes {{ nginx_worker_processes }};error_log /var/log/nginx/error.log;pid /run/nginx.pid;
events { worker_connections {{ nginx_worker_connections }};}
http { include /etc/nginx/mime.types; default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on; tcp_nopush on; keepalive_timeout {{ nginx_keepalive_timeout }}; gzip on;
{% for upstream in nginx_upstreams %} upstream {{ upstream.name }} { {% for server in upstream.servers %} server {{ server.host }}:{{ server.port }}; {% endfor %} } {% endfor %}
server { listen {{ nginx_listen_port }}; server_name {{ nginx_server_name }}; root {{ nginx_root }};
index {% for file in nginx_index_files %}{{ file }} {% endfor %};
location / { try_files $uri $uri/ =404; }
{% if nginx_ssl_enabled %} location ~ /\.ht { deny all; } {% endif %}
{% for location in nginx_locations %} location {{ location.path }} { {{ location.config }} } {% endfor %} }}Application Configuration Template
Section titled “Application Configuration Template”{# roles/app/templates/app.conf.j2 #}[database]host = {{ db_host }}port = {{ db_port }}name = {{ db_name }}user = {{ db_user }}password = {{ db_password | b64decode }}
[application]environment = {{ app_environment }}debug = {{ app_debug | default('false') }}secret_key = {{ app_secret_key }}log_level = {{ app_log_level | default('INFO') }}
{% if app_features is defined %}[features]{% for feature in app_features %}{{ feature.name }} = {{ feature.enabled }}{% endfor %}{% endif %}
[servers]{% for server in groups['app_servers'] %}{{ hostvars[server]['inventory_hostname'] }} = {{ hostvars[server]['ansible_host'] }}{% endfor %}43.9 Variables
Section titled “43.9 Variables”Variable Precedence
Section titled “Variable Precedence” Variable Precedence (Low to High)+------------------------------------------------------------------+| || 1. command line values (ansible-playbook -e "var=value") || 2. role defaults (roles/x/defaults/main.yml) || 3. inventory file or script group vars || 4. inventory group_vars/all || 5. playbook group_vars/all || 6. inventory group_vars/* || 7. playbook group_vars/* || 8. inventory file or script host vars || 9. inventory host_vars/* || 10. playbook host_vars/* || 11. host facts / cached set_facts || 12. play vars || 13. play vars_prompt || 14. play vars_files || 15. role vars (roles/x/vars/main.yml) || 16. block vars || 17. task vars (only for tasks) || 18. include_vars || 19. set_facts / registered vars || 20. role (and include_role) params || 21. include params || 22. extra vars (ansible-playbook -e) || |+------------------------------------------------------------------+Group Variables
Section titled “Group Variables”---aws_region: us-east-1environment: productiontimezone: UTC
# group_vars/web_servers.yml---nginx_listen_port: 80nginx_worker_processes: autonginx_ssl_enabled: true
# group_vars/db_servers.yml---db_engine: postgresdb_version: "14.7"db_port: 5432Host Variables
Section titled “Host Variables”---ansible_host: 54.123.45.67private_ip: 10.0.1.10nginx_listen_port: 8080
# host_vars/db-01.yml---ansible_host: 10.0.2.10db_name: production_dbdb_user: app_userVault for Secrets
Section titled “Vault for Secrets”# Create encrypted fileansible-vault create secrets.yml
# Edit encrypted fileansible-vault edit secrets.yml
# Encrypt existing fileansible-vault encrypt secrets.yml
# Decrypt fileansible-vault decrypt secrets.yml
# View encrypted fileansible-vault view secrets.yml# secrets.yml (encrypted)---db_password: !vault | $ANSIBLE_VAULT;1.1;AES256 6638643965396638646638646638643965396638646638646638643965396638 ...app_secret_key: !vault | $ANSIBLE_VAULT;1.1;AES256 6638643965396638646638646638643965396638646638646638643965396638 ...# Run playbook with vaultansible-playbook site.yml --ask-vault-passansible-playbook site.yml --vault-password-file vault_pass.txt43.10 Advanced Patterns
Section titled “43.10 Advanced Patterns”Rolling Updates
Section titled “Rolling Updates”---- name: Rolling update hosts: web_servers serial: 1 become: yes
pre_tasks: - name: Remove from load balancer community.aws.elb_instance: name: "{{ elb_name }}" instance_id: "{{ ec2_instance_id }}" state: absent delegate_to: localhost
tasks: - name: Update application git: repo: "{{ app_repo }}" dest: /opt/app version: "{{ app_version }}"
- name: Restart application systemd: name: app state: restarted
post_tasks: - name: Wait for application wait_for: port: "{{ app_port }}" delay: 10 timeout: 60
- name: Add back to load balancer community.aws.elb_instance: name: "{{ elb_name }}" instance_id: "{{ ec2_instance_id }}" state: present delegate_to: localhostBlue-Green Deployment
Section titled “Blue-Green Deployment”---- name: Blue-Green Deployment hosts: localhost connection: local gather_facts: no
vars: blue_asg: web-asg-blue green_asg: web-asg-green elb_name: web-elb
tasks: - name: Determine active environment command: aws elb describe-load-balancers --load-balancer-name {{ elb_name }} register: elb_info changed_when: false
- name: Set target environment set_fact: target_env: "{{ 'green' if 'blue' in active_asg else 'blue' }}" vars: active_asg: "{{ elb_info.stdout | from_json | json_query('LoadBalancerDescriptions[0].Instances[0].InstanceId') }}"
- name: Scale up target environment amazon.aws.ec2_asg: name: "{{ target_env == 'green' | ternary(green_asg, blue_asg) }}" desired_capacity: 3 state: present
- name: Wait for instances to be healthy command: aws elb describe-instance-health --load-balancer-name {{ elb_name }} register: health_check until: health_check.stdout | from_json | json_query('InstanceStates[?State==`InService`]') | length == 3 retries: 30 delay: 30
- name: Switch traffic to target environment command: aws elb deregister-instances-from-load-balancer --load-balancer-name {{ elb_name }} --instances {{ active_instances }} vars: active_instances: "{{ elb_info.stdout | from_json | json_query('LoadBalancerDescriptions[0].Instances[*].InstanceId') | join(' ') }}"
- name: Scale down old environment amazon.aws.ec2_asg: name: "{{ target_env == 'green' | ternary(blue_asg, green_asg) }}" desired_capacity: 0 state: presentAsynchronous Tasks
Section titled “Asynchronous Tasks”---- name: Long-running tasks hosts: all become: yes
tasks: - name: Start long-running update command: /opt/app/update.sh async: 3600 poll: 0 register: update_task
- name: Continue with other tasks debug: msg: "Update started, continuing with other tasks"
- name: Check update status async_status: jid: "{{ update_task.ansible_job_id }}" register: job_result until: job_result.finished retries: 60 delay: 6043.11 CI/CD Integration
Section titled “43.11 CI/CD Integration”GitLab CI Integration
Section titled “GitLab CI Integration”stages: - validate - test - deploy
variables: ANSIBLE_HOST_KEY_CHECKING: "false"
validate: stage: validate image: ansible/ansible:latest script: - ansible-lint playbooks/ - ansible-playbook --syntax-check playbooks/site.yml rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"
test: stage: test image: ansible/ansible:latest script: - ansible-playbook playbooks/test.yml -i inventory/test.yml --check rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"
deploy_staging: stage: deploy image: ansible/ansible:latest script: - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - ansible-playbook playbooks/deploy.yml -i inventory/staging.yml environment: name: staging rules: - if: $CI_COMMIT_BRANCH == "develop"
deploy_production: stage: deploy image: ansible/ansible:latest script: - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - ansible-playbook playbooks/deploy.yml -i inventory/production.yml --vault-password-file $VAULT_PASSWORD environment: name: production when: manual rules: - if: $CI_COMMIT_BRANCH == "main"GitHub Actions Integration
Section titled “GitHub Actions Integration”name: Ansible Deployment
on: push: branches: [main] pull_request: branches: [main]
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11'
- name: Install Ansible run: | pip install ansible ansible-lint ansible-galaxy collection install amazon.aws
- name: Lint playbooks run: ansible-lint playbooks/
deploy: needs: lint runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3
- name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11'
- name: Install Ansible run: | pip install ansible boto3 botocore ansible-galaxy collection install amazon.aws
- name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1
- name: Run Ansible playbook run: | ansible-playbook playbooks/deploy.yml \ -i inventory/aws_ec2.yml \ --vault-password-file <(echo "${{ secrets.VAULT_PASSWORD }}")43.12 Best Practices
Section titled “43.12 Best Practices”Security Best Practices
Section titled “Security Best Practices” Ansible Security Best Practices+------------------------------------------------------------------+| || 1. Credential Management || +--------------------------------------------------------+ || | - Use Ansible Vault for secrets | || | - Use IAM roles for AWS operations | || | - Rotate credentials regularly | || | - Never commit secrets to version control | || +--------------------------------------------------------+ || || 2. Access Control || +--------------------------------------------------------+ || | - Use least privilege for IAM roles | || | - Restrict SSH access to control nodes | || | - Use bastion hosts for private instances | || | - Enable MFA for AWS accounts | || +--------------------------------------------------------+ || || 3. Network Security || +--------------------------------------------------------+ || | - Use VPN or private links for management | || | - Restrict security group rules | || | - Use encrypted connections (SSH, WinRM) | || +--------------------------------------------------------+ || |+------------------------------------------------------------------+Performance Optimization
Section titled “Performance Optimization”# Enable pipelining for faster execution# ansible.cfg[ssh_connection]pipelining = True
# Use async for long-running tasks- name: Long task command: /opt/long-task.sh async: 3600 poll: 0
# Use serial for controlled execution- name: Controlled update hosts: all serial: 5 # Process 5 hosts at a time
# Use strategy plugins- name: Mitogen strategy hosts: all strategy: mitogen_linear # Requires mitogen pluginCode Organization
Section titled “Code Organization”ansible-project/+------------------------------------------------------------------+| || playbooks/ # Playbooks || +-- site.yml # Main playbook || +-- deploy.yml # Deployment playbook || +-- update.yml # Update playbook || || roles/ # Roles || +-- common/ || +-- web-server/ || +-- database/ || || inventory/ # Inventory || +-- aws_ec2.yml # Dynamic inventory || +-- staging/ # Staging environment || +-- production/ # Production environment || || group_vars/ # Group variables || +-- all.yml || +-- web_servers.yml || +-- db_servers.yml || || host_vars/ # Host variables || +-- web-01.yml || +-- db-01.yml || || files/ # Static files || templates/ # Jinja2 templates || vars/ # Extra variables || secrets/ # Vault-encrypted files || ansible.cfg # Ansible configuration || requirements.yml # Galaxy requirements || |+------------------------------------------------------------------+43.13 Troubleshooting
Section titled “43.13 Troubleshooting”Common Issues
Section titled “Common Issues” Ansible Troubleshooting+------------------------------------------------------------------+| || Issue: SSH Connection Failed || +--------------------------------------------------------+ || | Solutions: | || | - Check SSH key permissions (600) | || | - Verify security group allows SSH | || | - Check ansible_user matches AMI | || | - Enable host_key_checking = False | || +--------------------------------------------------------+ || || Issue: Permission Denied || +--------------------------------------------------------+ || | Solutions: | || | - Add become: yes to playbook | || | - Check sudo permissions | || | - Verify user has sudo access | || +--------------------------------------------------------+ || || Issue: AWS Module Failures || +--------------------------------------------------------+ || | Solutions: | || | - Check AWS credentials | || | - Verify IAM permissions | || | - Install boto3/botocore | || | - Check region configuration | || +--------------------------------------------------------+ || |+------------------------------------------------------------------+Debug Mode
Section titled “Debug Mode”# Verbose outputansible-playbook site.yml -vansible-playbook site.yml -vvv # More verboseansible-playbook site.yml -vvvv # Debug level
# Check mode (dry run)ansible-playbook site.yml --check
# Diff mode (show changes)ansible-playbook site.yml --diff
# Step mode (confirm each task)ansible-playbook site.yml --step
# Start at specific taskansible-playbook site.yml --start-at-task="Install Nginx"
# List hostsansible-playbook site.yml --list-hosts
# List tagsansible-playbook site.yml --list-tags43.14 Key Takeaways
Section titled “43.14 Key Takeaways”| Topic | Key Points |
|---|---|
| Agentless | No software installation required on targets |
| Dynamic Inventory | Use aws_ec2 plugin for automatic inventory |
| Modules | Use AWS modules for infrastructure management |
| Roles | Organize code into reusable roles |
| Vault | Encrypt sensitive data with Ansible Vault |
| CI/CD | Integrate with pipelines for automated deployment |
43.15 References
Section titled “43.15 References”Next Chapter: Chapter 44 - AWS Config & Resource Compliance