Listen to this Post

Introduction:
Modern DevSecOps demands automation that embeds security from the first line of code to the final container deployment. While many organizations rely on complex CI/CD tools like Jenkins or GitHub Actions, a streamlined approach using Ansible can provision infrastructure, enforce security gates, and deploy applications with idempotent reliability. This article dissects a production-ready pipeline that transforms three bare EC2 instances into a fully automated, security-scanned container deployment—all driven by two Ansible playbooks and a strict “fail fast” philosophy.
Learning Objectives:
- Implement an end-to-end CI/CD pipeline using only Ansible for infrastructure provisioning, configuration management, and deployment.
- Integrate static code analysis (SonarQube) and container vulnerability scanning (Trivy) as mandatory security gates.
- Automate SSH fingerprint management to eliminate manual intervention when scaling to hundreds of target nodes.
- Build idempotent deployment strategies that ensure consistent application state across multiple environments.
You Should Know:
- Automating SSH Host Key Verification for Zero-Touch Provisioning
When Ansible connects to a new EC2 instance, the default SSH behavior prompts the user to accept the host’s fingerprint. Manually typing “yes” for each target breaks automation at scale. The solution involves pre-populating the `known_hosts` file using `ssh-keyscan` before executing any playbook.
Step‑by‑step guide:
- After launching EC2 instances, retrieve their public IPs or DNS names.
- Use `ssh-keyscan` to fetch and store host keys without manual confirmation.
For each target IP, add its key to the Ansible controller's known_hosts ssh-keyscan -H 192.168.1.10 >> ~/.ssh/known_hosts ssh-keyscan -H 192.168.1.11 >> ~/.ssh/known_hosts
3. Alternatively, loop through an inventory file:
while read ip; do ssh-keyscan -H "$ip" >> ~/.ssh/known_hosts; done < inventory.txt
4. Verify that Ansible can now connect without prompts:
ansible all -i inventory.ini -m ping
This step is critical for CI/CD pipelines where new ephemeral instances are created per build.
2. Multi-Target Tool Installation with Idempotent Playbooks
A single Ansible playbook (install-tools.yml) ensures all target nodes have identical toolchains: OpenJDK 17, Maven, Docker, SonarQube container, SonarScanner, and Trivy. Idempotency guarantees that repeated runs do not break existing installations.
Step‑by‑step guide:
- Define tasks that check for existing installations before executing changes.
- Example task for Docker installation and permission hardening:
</li> <li>name: Install Docker apt: name: docker.io state: present notify: start docker</p></li> <li><p>name: Set Docker socket permissions file: path: /var/run/docker.sock mode: '0666'
- Use handlers to start services only when changes occur.
- For SonarQube, deploy as a container to keep dependencies isolated:
</li> <li>name: Run SonarQube container docker_container: name: sonarqube image: sonarqube:latest ports:</li> <li>"9000:9000" restart_policy: always
- Verify installations with ad-hoc commands:
ansible targets -m command -a "java -version" ansible targets -m command -a "docker --version"
3. Git Repository Synchronization for Continuous Code Updates
To ensure target nodes always reflect the latest source code, the pipeline uses `git clone` on the first run and `git pull` on subsequent executions. This eliminates the need for manual code transfers or external artifact repositories.
Step‑by‑step guide:
- Implement a task that checks for the existence of the repository directory.
</li> <li>name: Clone repo if not present git: repo: https://github.com/username/repo.git dest: /opt/app when: not repo_exists.stat.exists</p></li> <li><p>name: Pull latest changes git: repo: https://github.com/username/repo.git dest: /opt/app update: yes
- Use a `stat` module to test directory existence before deciding which action to take.
- This approach ensures that every playbook run operates on the current commit, enabling continuous delivery without manual triggers.
4. Compilation, Unit Testing, and Fail-Fast Gates
Before any packaging or deployment, the pipeline compiles the code and executes unit tests. If tests fail, the entire pipeline halts—preventing broken code from advancing to security scans or production.
Step‑by‑step guide:
- Execute Maven commands with error handling.
</li> <li>name: Compile source code command: mvn compile args: chdir: /opt/app register: compile_result failed_when: compile_result.rc != 0</p></li> <li><p>name: Run unit tests command: mvn test args: chdir: /opt/app register: test_result failed_when: test_result.rc != 0
- The `failed_when` directive ensures Ansible stops immediately on failure.
- This “fail fast” principle saves time and resources by catching issues early.
5. SonarQube Static Analysis with Secrets Management
Static code analysis is enforced via SonarQube, with credentials securely stored in Ansible Vault. The project must pass the Quality Gate before proceeding; otherwise, the pipeline aborts.
Step‑by‑step guide:
- Encrypt sensitive data using Ansible Vault.
ansible-vault encrypt_string 'your_sonar_token' --name 'sonar_token'
- Include the encrypted variables in your playbook.
- Run SonarScanner via the container:
</li> <li>name: Run SonarQube analysis command: > docker run --rm -v /opt/app:/usr/src sonarsource/sonar-scanner-cli -Dsonar.projectKey=my_project -Dsonar.host.url={{ sonar_url }} -Dsonar.login={{ sonar_token }} - After analysis, query the Quality Gate status. If failed, halt the pipeline.
6. Building the JAR and Docker Image
With passing tests and quality gates, the pipeline compiles a JAR, builds a Docker image, and tags it for deployment. All steps remain idempotent.
Step‑by‑step guide:
- Use Maven to package the application.
</li> <li>name: Build JAR command: mvn package -DskipTests args: chdir: /opt/app
- Build the Docker image using the Dockerfile in the repository.
</li> <li>name: Build Docker image docker_image: name: myapp:latest build: path: /opt/app source: build
- Tag the image with a version (e.g., build number) for traceability.
7. Trivy Container Vulnerability Scanning
Before pushing to Docker Hub, the image undergoes a comprehensive vulnerability scan using Trivy. An HTML report is generated per target, providing an audit trail.
Step‑by‑step guide:
- Install Trivy if not already present.
wget https://github.com/aquasecurity/trivy/releases/download/v0.18.3/trivy_0.18.3_Linux-64bit.deb sudo dpkg -i trivy_0.18.3_Linux-64bit.deb
- Run the scan and generate an HTML report.
</li> <li>name: Scan image with Trivy command: trivy image --format template --template "@contrib/html.tpl" -o trivy_report.html myapp:latest
- Fail the playbook if critical vulnerabilities are found:
</li> <li>name: Fail if critical vulnerabilities detected fail: msg: "Critical vulnerabilities found. Aborting deployment." when: "'CRITICAL' in trivy_report.stdout"
8. Pushing to Docker Hub and Idempotent Deployment
The final stage authenticates to Docker Hub, pushes the scanned image, and deploys it idempotently—stopping the old container and launching the new one only if the image has changed.
Step‑by‑step guide:
- Authenticate to Docker Hub using Vault-stored credentials.
</li> <li>name: Login to Docker Hub docker_login: registry_url: https://index.docker.io/v1/ username: "{{ docker_hub_user }}" password: "{{ docker_hub_token }}" - Push the image:
</li> <li>name: Push image to Docker Hub docker_image: name: myapp:latest repository: username/myapp:latest push: yes source: local
- Deploy idempotently:
</li> <li>name: Stop and remove old container docker_container: name: myapp state: absent</p></li> <li><p>name: Run new container docker_container: name: myapp image: username/myapp:latest state: started ports:</p></li> <li>"8080:8080"
What Undercode Say:
- Automation without CI/CD bloat: Ansible can replace heavyweight CI/CD tools for many use cases, proving that simplicity and idempotency often trump complex orchestration.
- Security gates are non-negotiable: Embedding SonarQube static analysis and Trivy container scans directly into the provisioning pipeline ensures that security is not an afterthought.
- SSH key management is foundational: Pre-populating `known_hosts` with `ssh-keyscan` is a small but critical step that enables true scale—without it, automation fails at the first manual prompt.
- Idempotency is the key to reliability: Every playbook task must be written to safely run multiple times, ensuring that infrastructure remains in the desired state regardless of execution frequency.
Prediction:
As organizations continue to adopt infrastructure-as-code principles, we will see a rise in Ansible-native CI/CD pipelines that treat security scans as first-class citizens. The trend will move away from monolithic CI/CD platforms toward composable, lightweight automation stacks that integrate vulnerability scanning, compliance checks, and deployment in a single workflow. Expect tooling to evolve with tighter integration between configuration management, container security, and cloud-native secret management—making DevSecOps pipelines more accessible to teams without dedicated platform engineering resources.
▶️ Related Video (86% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Zoumana Ouattara – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


