Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • View All Red Hat Products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Secure Development & Architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • Product Documentation
    • API Catalog
    • Legacy Documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Zero trust automation on AWS with Ansible and Terraform

No keys, no ports, no problem

September 22, 2025
Alina Buzachis
Related topics:
Application modernizationAutomation and managementDevOps
Related products:
Red Hat Ansible Automation Platform

Share:

    In the blog post What's new in cloud automation: Red Hat Ansible Certified Content Collection for amazon.aws 10.0.0, we introduced version 10.0.0 of the Red Hat Ansible Certified Content Collection for amazon.aws. We highlighted key enhancements and full support for amazon.aws.aws_ssm, a security-focused, agent-based connection plug-in that represents a significant leap forward in how we automate Amazon EC2 infrastructure.

    In this follow-up, we'll move from concepts to execution by showcasing a real-world use case where we combine the following:

    • HashiCorp Terraform for provisioning private EC2 instances
    • AWS Systems Manager (SSM) as the connection mechanism
    • Ansible automation using the new amazon.aws.aws_ssm connection plug-in and the amazon.aws.aws_ec2 dynamic inventory plug-in

    We'll explore a scenario that demonstrates the power of this plug-in in the context of Day 0, Day 1, and Day 2 operations. We'll walk through provisioning infrastructure with Terraform, then managing configuration and lifecycle tasks using Red Hat Ansible Automation Platform, all without opening a single inbound port. This flow is illustrated in Figure 1.

    This scenario aligns with modern cloud architecture trends, including zero trust networking, no-SSH environments, and compliance-driven infrastructure management.

    Automation flow: Day 0, Day 1 and Day 2 Operations
    Figure 1: Automation flow: Day 0, Day 1, and Day 2 operations.

    Embracing zero trust: Why amazon.aws.aws_ssm?

    Traditionally, managing EC2 instances has required juggling key pairs, managing SSH access, and configuring security groups—all of which introduce security complexity and operational overhead. The amazon.aws.aws_ssm connection plug-in, newly supported in the amazon.aws 10.0.0 certified collection, changes that paradigm entirely.

    By using the AWS Systems Manager (SSM) agent pre-installed on supported EC2 AMIs, this plug-in allows Ansible Automation Platform to connect over the AWS-managed control plane, eliminating the need for SSH access or public IP addresses. That means:

    • No open inbound ports, including port 22
    • No SSH keys to manage or rotate
    • No bastion hosts
    • Cloud-native logging of session activity via CloudTrail and SSM
    • Better alignment with compliance frameworks like CIS, HIPAA, or ISO 27001

    This drastically reduces your attack surface and simplifies your operational model.

    Real-world scenario: Private web servers, fully automated

    Let's consider a common scenario: Suppose your security team mandates that all EC2 instances must reside in private subnets, without public IP addresses or SSH access. You need to provision a fleet of web servers and automate their configuration—all without compromising this zero trust security posture.

    In this scenario, we'll:

    1. Provision EC2 resources with Terraform.
    2. Enable remote access via SSM.
    3. Automate software installation and configuration using Ansible over SSM.
    4. Manage both Day 1 and Day 2 tasks.

    Day 0 Operations: Security-focused provisioning with Terraform

    Our journey begins with infrastructure provisioning. Using Terraform, we define a virtual private cloud (VPC), deploy private subnets, restrict all inbound traffic via security groups, and launch EC2 instances with IAM roles that enable SSM access.

    Here's a simplified Terraform configuration:

    # Configure the AWS Provider
    provider "aws" {
      region = var.aws_region
    }
    # --- VPC and Networking Setup ---
    # Create a new VPC
    resource "aws_vpc" "zero_trust_vpc" {
      cidr_block = "10.0.0.0/16"
      tags = {
        Name    = "${var.project_name}-vpc"
        Project = var.project_name
      }
    }
    # Create a public subnet for the NAT Gateway
    resource "aws_subnet" "public_subnet" {
      vpc_id            = aws_vpc.zero_trust_vpc.id
      cidr_block        = "10.0.1.0/24"
      availability_zone = "${var.aws_region}a" # Use a specific AZ for simplicity
      tags = {
        Name    = "${var.project_name}-public-subnet"
        Project = var.project_name
      }
    }
    # Create a private subnet for the EC2 instance
    resource "aws_subnet" "private_subnet" {
      vpc_id            = aws_vpc.zero_trust_vpc.id
      cidr_block        = "10.0.2.0/24"
      availability_zone = "${var.aws_region}a" # Use a specific AZ for simplicity
      tags = {
        Name    = "${var.project_name}-private-subnet"
        Project = var.project_name
      }
    }
    # Create an Internet Gateway for the public subnet
    resource "aws_internet_gateway" "igw" {
      vpc_id = aws_vpc.zero_trust_vpc.id
      tags = {
        Name    = "${var.project_name}-igw"
        Project = var.project_name
      }
    }
    # Create an Elastic IP for the NAT Gateway
    resource "aws_eip" "nat_eip" {
      vpc        = true
      depends_on = [aws_internet_gateway.igw] # Ensure IGW exists before EIP
      tags = {
        Name    = "${var.project_name}-nat-eip"
        Project = var.project_name
      }
    }
    # Create a NAT Gateway in the public subnet
    resource "aws_nat_gateway" "nat_gateway" {
      allocation_id = aws_eip.nat_eip.id
      subnet_id     = aws_subnet.public_subnet.id
      tags = {
        Name    = "${var.project_name}-nat-gateway"
        Project = var.project_name
      }
    }
    # Create a public route table
    resource "aws_route_table" "public_rt" {
      vpc_id = aws_vpc.zero_trust_vpc.id
      tags = {
        Name    = "${var.project_name}-public-rt"
        Project = var.project_name
      }
    }
    # Add route to Internet Gateway for public route table
    resource "aws_route" "public_internet_route" {
      route_table_id         = aws_route_table.public_rt.id
      destination_cidr_block = "0.0.0.0/0"
      gateway_id             = aws_internet_gateway.igw.id
    }
    # Associate public route table with public subnet
    resource "aws_route_table_association" "public_subnet_association" {
      subnet_id      = aws_subnet.public_subnet.id
      route_table_id = aws_route_table.public_rt.id
    }
    # Create a private route table
    resource "aws_route_table" "private_rt" {
      vpc_id = aws_vpc.zero_trust_vpc.id
      tags = {
        Name    = "${var.project_name}-private-rt"
        Project = var.project_name
      }
    }
    # Add route to NAT Gateway for private route table
    resource "aws_route" "private_nat_route" {
      route_table_id         = aws_route_table.private_rt.id
      destination_cidr_block = "0.0.0.0/0"
      nat_gateway_id         = aws_nat_gateway.nat_gateway.id
    }
    # Associate private route table with private subnet
    resource "aws_route_table_association" "private_subnet_association" {
      subnet_id      = aws_subnet.private_subnet.id
      route_table_id = aws_route_table.private_rt.id
    }
    # --- Security Group for EC2 Instance ---
    # Create a security group for the EC2 instance
    resource "aws_security_group" "ec2_sg" {
      name        = "${var.project_name}-ec2-sg"
      description = "Allow outbound traffic for private EC2"
      vpc_id      = aws_vpc.zero_trust_vpc.id
      # Allow all outbound traffic (needed for updates, SSM endpoints, etc.)
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
      # No inbound rules needed for SSH/HTTP as we are using SSM
      # If you need specific inbound rules are needed within the VPC, add them here.
      tags = {
        Name    = "${var.project_name}-ec2-sg"
        Project = var.project_name
      }
    }
    # --- S3 Bucket ---
    resource "aws_s3_bucket" "zero_trust_bucket" {
      bucket = "${var.project_name}-ssm-bucket" # S3 bucket names must be globally unique
      tags = {
        Name    = "${var.project_name}-ssm-bucket"
        Project = var.project_name
      }
    }
    # --- IAM Role for EC2 (SSM Permissions) ---
    # Create an IAM role for the EC2 instance
    resource "aws_iam_role" "ssm_ec2_role" {
      name = "${var.project_name}-ssm-ec2-role"
      assume_role_policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Action = "sts:AssumeRole"
            Effect = "Allow"
            Principal = {
              Service = "ec2.amazonaws.com"
            }
          },
        ]
      })
      tags = {
        Name    = "${var.project_name}-ssm-ec2-role"
        Project = var.project_name
      }
    }
    # Attach the AmazonSSMManagedInstanceCore policy to the role
    resource "aws_iam_role_policy_attachment" "ssm_policy_attachment" {
      role       = aws_iam_role.ssm_ec2_role.name
      policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
    }
    # Create an instance profile for the EC2 instance
    resource "aws_iam_instance_profile" "ssm_instance_profile" {
      name = "${var.project_name}-ssm-instance-profile"
      role = aws_iam_role.ssm_ec2_role.name
    }
    # --- EC2 Instance (Private) ---
    # Find the latest Amazon Linux 2 AMI
    data "aws_ami" "amazon_linux_2" {
      most_recent = true
      owners      = ["amazon"]
      filter {
        name   = "name"
        values = ["amzn2-ami-hvm-*-x86_64-gp2"]
      }
      filter {
        name   = "virtualization-type"
        values = ["hvm"]
      }
      filter {
        name   = "architecture"
        values = ["x86_64"]
      }
    }
    # Create the private EC2 instance
    resource "aws_instance" "private_webserver" {
      ami                         = data.aws_ami.amazon_linux_2.id
      instance_type               = var.instance_type
      subnet_id                   = aws_subnet.private_subnet.id
      vpc_security_group_ids      = [aws_security_group.ec2_sg.id]
      associate_public_ip_address = false # Crucial for zero-trust (no public IP)
      iam_instance_profile        = aws_iam_instance_profile.ssm_instance_profile.name
      tags = {
        Name    = "${var.project_name}-webserver"
        Project = var.project_name
      }
    }

    To bring this zero trust automation workflow to life, you can start by provisioning your infrastructure using the scripts provided in this GitHub repository. You can get started by cloning the repository and applying the Terraform configuration:

    git clone https://github.com/alinabuzachis/aws-ansible-zero-trust-automation.git
    cd terraform
    terraform init
    terraform apply

    This configuration ensures security-focused, SSM-enabled infrastructure is provisioned on Day 1, with no exposure to the public internet.

    Day 1 and Day 2 operations: Lifecycle automation with Ansible Automation Platform

    With the infrastructure in place and SSM connectivity configured, we now shift into Day 1 and Day 2 operations—the critical tasks that define how systems are configured, updated, and maintained over their lifecycle. These operations are where the real value of automation emerges: you're not just provisioning servers, but keeping them consistent, security-focused, and production-ready at all times.

    What sets this workflow apart is that all Ansible automation is executed via the amazon.aws.aws_ssm connection plug-in, without ever relying on SSH, key pairs, or opening inbound ports. Connections are tunneled through AWS Systems Manager (SSM), and all actions are traceable through CloudTrail and SSM session logs.

    Rather than relying on static inventory files, we'll use the amazon.aws.aws_ec2 dynamic inventory plug-in to automatically discover and group EC2 instances based on AWS tags or other metadata. This allows your Ansible Automation Platform environment to stay aligned with your cloud infrastructure—no manual updates required.

    Dynamic inventory with amazon.aws.aws_ec2: Let Ansible Automation Platform discover your infrastructure

    While you can manually track EC2 instance IDs in a static inventory, dynamic environments benefit greatly from Ansible's amazon.aws.aws_ec2 inventory plug-in. With this plug-in, Ansible Automation Platform can automatically discover and group your EC2 instances based on tags, regions, or instance attributes. This eliminates the need to update your inventory manually.

    Let's configure a dynamic inventory (aws_ec2.yml) that targets all EC2 instances tagged with Project=zero-trust-demo:

    plugin: amazon.aws.aws_ec2
    regions:
      - "eu-central-1"
    keyed_groups:
      - prefix: tags
        key: tag_
    filters:
      instance-state-name: running
      tag:Project: zero-trust-demo
    compose:
      ansible_host: instance_id
      ansible_connection: "'aws_ssm'"
      ansible_aws_ssm_bucket_name: '"zero-trust-demo-ssm-bucket"'
      ansible_aws_ssm_region: '"eu-central-1"'

    This configuration tells Ansible Automation Platform to:

    • Discover EC2 instances in the eu-central-1 region.
    • Target only instances tagged with Project=zero-trust-demo.
    • Use the EC2 instance ID as the hostname (required for SSM).
    • Automatically assign the amazon.aws.aws_ssm connection method and region per host.

    This makes your inventory flexible and fully automatic—perfect for modern infrastructure that changes frequently or spans multiple environments. This dynamic inventory adapts as your infrastructure evolves. Tag a new instance with Environment=dev, and Ansible Automation Platform will pick it up automatically. No need to manually update your inventory.

    Day 1 Ansible Playbook: Configure Apache via SSM

    Now that the infrastructure is discoverable, we can automate common configuration tasks using Ansible Automation Platform—reliably, flexibly, and at scale.

    Here's a simple example playbook (configure_web.yml) that installs and configures Apache on all matching instances:

    ---
    - name: Configure Apache on EC2 via SSM
      hosts: tag__Name_zero_trust_demo_webserver
      gather_facts: false
      tasks:
        - name: Install Apache HTTP Server
          ansible.builtin.package:
            name: httpd
            state: present
        - name: Enable and start Apache service
          ansible.builtin.service:
            name: httpd
            state: started
            enabled: true
        - name: Create a sample homepage
          ansible.builtin.copy:
            dest: /var/www/html/index.html
            content: "<h1>Welcome to Ansible over SSM!</h1>"

    This playbook runs against all instances dynamically grouped under tag__Name_zero_trust_demo_webserver, thanks to our inventory plug-in.

    You can execute this playbook in Ansible Automation Platform, which ensures that all necessary software is installed and configured on your infrastructure without requiring command-line intervention.

    Looking ahead: Security, scalability, and compliance

    This is a complete, security-focused configuration workflow, with no SSH keys, no exposed ports, and no infrastructure outside your control. You gain the full power of Ansible Automation Platform for post-deployment automation while aligning with cloud-native and zero trust principles.

    Typical Day 2 operations now become seamless:

    • Updating packages across private subnets
    • Rotating secrets without exposing endpoints
    • Applying compliance patches across fleets
    • Enforcing configuration drift remediation
    • Automating routine administrative tasks via cron jobs or scheduled playbooks

    Additionally, because all actions are routed through SSM, they are logged, monitored, and centrally auditable, which gives you better governance and compliance tracking without sacrificing agility.

    This approach scales effortlessly. Add new EC2 instances? Tag them and let your dynamic inventory detect them. Rotate credentials? Let IAM and SSM handle the heavy lifting. Need audit trails? SSM integrates with AWS CloudTrail for full session logging. Everything happens within your existing AWS IAM and logging architecture.

    Most importantly, this model aligns with evolving security expectations. Whether you're working in finance, healthcare, or any highly regulated industry, moving away from open SSH access toward network-isolated, centrally audited control is imperative.

    Final thoughts

    With the release of version 10.0.0 of the Red Hat Ansible Certified Content Collection for amazon.aws, we see a firm shift toward security-focused, cloud-native automation. Now fully supported, the amazon.aws.aws_ssm connection plug-in empowers users to automate in ways that are scalable, compliant, and operationally resilient.

    By combining HashiCorp Terraform, Red Hat Ansible Automation Platform, and SSM, you can confidently deliver Day 0, Day 1, and Day 2 operations for AWS environments that meet the highest standards of automation maturity and infrastructure security.

    Where to go next

    • Explore the latest articles about Ansible automation on Red Hat Developer and the Red Hat Blog.
    • Check out this YouTube playlist for everything about Ansible Collections.
    • If you're new to Ansible automation, download the Red Hat Certified Engineer (RHCE) Ansible Automation Study Guide (O'Reilly).
    • To learn more about Ansible Automation Platform, check out our getting started guide.

    Related Posts

    • How to create execution environments using ansible-builder

    • Set up a continuous integration pipeline with Ansible Automation Platform & GitLab

    • How to deploy Azure Red Hat OpenShift using Terraform

    • Monitor an Ansible Automation Platform database using Prometheus and Grafana

    • Patch updates on RHEL servers with Ansible Automation Platform 2.4

    • Generate Ansible Playbooks using natural language prompts

    Recent Posts

    • Customize RHEL CoreOS at scale: On-cluster image mode in OpenShift

    • How to set up KServe autoscaling for vLLM with KEDA

    • How I used Cursor AI to migrate a Bash test suite to Python

    • Install Python 3.13 on Red Hat Enterprise Linux from EPEL

    • Zero trust automation on AWS with Ansible and Terraform

    What’s up next?

    ansible-study-guide-ebook-tile card

    Download the Red Hat Certified Engineer (RHCE) Ansible Automation Study Guide and learn to build and operate scalable IT automation across cloud, hybrid, and edge environments.

    Get the e-book
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue