100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Infrastructure as Code — Terraform & Ansible
50 minintermediate

Ansible Roles — App Deploy, OS Hardening and CloudWatch Agent

What You'll Build

You will extend the cricket analytics Ansible configuration with three roles: 'cricket_app_deploy' (pulls the application from S3, installs Python dependencies, configures the systemd service unit), 'cricket_os_hardening' (applies CIS-aligned OS hardening: SSH configuration, kernel parameters, file permissions, auditd), and 'cricket_cloudwatch_agent' (installs and configures the CloudWatch agent to collect CPU, memory, disk and application metrics). Each role is independently testable with Molecule and is applied by the site.yml playbook to the correct host groups.

Analogy🏏Cricket
🏏 Think of it like cricket: You are designing the BCCI's standard cricket ground specification module — a parameterisable blueprint that can be instantiated for any venue in the country without redrawing it from scratch. The blueprint fixes the non-negotiables every certified ground shares: a boundary perimeter (the VPC and its CIDR), spectator zones with public access (public subnets with an Internet Gateway route), and restricted player-and-officials zones behind accreditation checks (private subnets routing through NAT). What varies per venue arrives as parameters: how many stands to build (var.azs and subnet counts), whether this is a full international stadium or a modest district ground (var.single_nat_gateway as the cost lever — one shared service corridor instead of one per stand), and the venue's name and signage (tags). Wankhede and a Ranchi district ground are instantiated from the same drawing with different inputs — and when the board later improves the blueprint (adds flow logs, tightens NACLs), every venue inherits the improvement on its next renovation (module version bump) instead of each ground hand-patching its own architecture. That is the entire economics of module authorship: design once, rigorously, then stamp out consistent grounds forever.
yaml
# cricket_os_hardening/tasks/main.yml — CIS-aligned OS hardening
---
- name: Configure SSH  disable root login and password auth
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '{{ item.regexp }}'
    line: '{{ item.line }}'
    state: present
    validate: 'sshd -t -f %s'
  loop:
    - { regexp: '^PermitRootLogin',         line: 'PermitRootLogin no' }
    - { regexp: '^PasswordAuthentication',  line: 'PasswordAuthentication no' }
    - { regexp: '^X11Forwarding',           line: 'X11Forwarding no' }
    - { regexp: '^MaxAuthTries',            line: 'MaxAuthTries 4' }
    - { regexp: '^ClientAliveInterval',     line: 'ClientAliveInterval 300' }
    - { regexp: '^ClientAliveCountMax',     line: 'ClientAliveCountMax 2' }
  notify: Restart sshd
  tags: [hardening, ssh]

- name: Apply security kernel parameters
  ansible.posix.sysctl:
    name: '{{ item.key }}'
    value: '{{ item.value | string }}'
    state: present
    reload: true
    sysctl_set: true
  loop: '{{ security_sysctl_settings | dict2items }}'
  loop_control:
    label: '{{ item.key }}'
  vars:
    security_sysctl_settings:
      kernel.randomize_va_space: 2
      net.ipv4.conf.all.send_redirects: 0
      net.ipv4.conf.default.send_redirects: 0
      net.ipv4.conf.all.accept_source_route: 0
      net.ipv4.conf.default.accept_source_route: 0
      net.ipv4.conf.all.log_martians: 1
      net.ipv4.icmp_echo_ignore_broadcasts: 1
      net.ipv4.tcp_syncookies: 1
      vm.swappiness: 10
  tags: [hardening, sysctl]

- name: Install and enable auditd
  ansible.builtin.package:
    name: audit
    state: present
  tags: [hardening, auditd]

- name: Enable auditd service
  ansible.builtin.service:
    name: auditd
    enabled: true
    state: started
  tags: [hardening, auditd]

# cricket_cloudwatch_agent/tasks/main.yml
---
- name: Download CloudWatch Agent
  ansible.builtin.get_url:
    url: 'https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm'
    dest: /tmp/amazon-cloudwatch-agent.rpm
    mode: '0644'
  tags: [cloudwatch]

- name: Install CloudWatch Agent
  ansible.builtin.dnf:
    name: /tmp/amazon-cloudwatch-agent.rpm
    state: present
    disable_gpg_check: true
  tags: [cloudwatch]

- name: Deploy CloudWatch Agent configuration
  ansible.builtin.template:
    src: cloudwatch-agent-config.json.j2
    dest: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
    mode: '0644'
  notify: Restart CloudWatch Agent
  tags: [cloudwatch]

- name: Start CloudWatch Agent
  ansible.builtin.command:
    cmd: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl
         -a fetch-config -m ec2 -s
         -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
  changed_when: true
  tags: [cloudwatch]
Analogy🏏Cricket
🏏 Think of it like cricket: Notice what the composition in main.tf actually does with the modules: nothing is built twice. The VPC module you authored in M2 is instantiated here unchanged — the rehearsed powerplay routine executed again in a bigger match, not re-invented for it — and every new component plugs into its published outputs: the ALB takes module.vpc.public_subnet_ids the way the fielding plan takes the ground dimensions as given, the ASG takes the private subnet IDs, the RDS subnet group takes the database tier's. That chain of references IS the architecture — Terraform reads it and derives the entire build order (the graph knows subnets precede the ALB, and the ALB's target group precedes the ASG that registers into it) with no explicit sequencing from you, the way a competent operations team derives the setup schedule from the venue drawings rather than being told step by step. The production patterns are non-negotiable for the same reasons their cricket counterparts are: Multi-AZ RDS is the synchronised duplicate record room in a second building (a lost AZ loses no data), create_before_destroy on the launch template is the replacement keeper drilled before the incumbent leaves (no capacity gap during updates), and prevent_destroy on the database is heritage protection on the trophy room — the one demolition that must never be a side effect of a routine renovation.
  • OS hardening tasks use 'ansible.builtin.lineinfile' to modify specific lines in SSH configuration files — the 'validate' parameter runs 'sshd -t' to verify the SSH config is valid before applying, preventing lockout from a broken SSH config.
  • The CloudWatch agent installation uses 'ansible.builtin.get_url' to download the RPM directly from Amazon's S3 bucket — this is the idiomatic way to install packages not available in the OS package repository.
  • All three hardening categories (SSH config, kernel parameters, auditd) are tagged independently, enabling 'ansible-playbook --tags ssh' or '--tags sysctl' for targeted re-application of specific hardening controls.
Lesson 31 of 33
0% complete