🚀Automate Docker Image Build, Trivy Scan & Push to Docker Hub Using OpenTofu


In this article, I’ll walk you through a complete real-time DevOps automation workflow:

✔ Build a Docker image from a Python application
✔ Scan the image using Trivy (HIGH+CRITICAL severity)
✔ Save scan report to /home/ubuntu/trivy-report.txt
✔ If scan passes → tag & push image to Docker Hub
✔ If scan fails → block push
✔ Entire workflow automated using OpenTofu (null_resource + local-exec)

This is a real-time DevOps automation task, similar to what you would implement in CI pipelines — but fully executed using OpenTofu on a server.




🗂 Project Structure

pythonapp/
└── tofu
    ├── Dockerfile
    ├── app
    │   ├── app.py
    │   └── requirements.txt
    ├── main.tf
    ├── outputs.tf
    ├── scripts
    │   └── build_scan_push.sh
    ├── terraform.tfstate
    ├── terraform.tfstate.backup
    └── variables.tf
Enter fullscreen mode

Exit fullscreen mode

1




Pre-req:

Install the Docker

# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <
Enter fullscreen mode

Exit fullscreen mode

Install Trivy

sudo apt-get install wget gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
Enter fullscreen mode

Exit fullscreen mode




🐍 Step 1 — Minimal Python Application

app/app.py

from flask import Flask

app = Flask(__name__)

@app.route("https://dev.to/")
def home():
    return "Hello from Secure Python App!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
Enter fullscreen mode

Exit fullscreen mode

app/requirements.txt

Flask==2.2.5
Enter fullscreen mode

Exit fullscreen mode




🐳 Step 2 — Dockerfile for Python Application

Dockerfile

FROM python:3.10-slim

WORKDIR /app

COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ .

EXPOSE 5000

CMD ["python", "app.py"]

Enter fullscreen mode

Exit fullscreen mode




🔍 Step 3 — Trivy + Build + Scan + Push Script

scripts/build_scan_push.sh

#!/bin/bash
IMAGE_NAME=$1
IMAGE_TAG=$2
DOCKER_USER=$3
DOCKER_PASS=$4

REPORT_PATH="/home/ubuntu/trivy-report.txt"

echo "=== Building Docker image ==="
docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" .

echo "=== Running Trivy scan ==="
trivy image --exit-code 1 --severity HIGH,CRITICAL "${IMAGE_NAME}:${IMAGE_TAG}" > "$REPORT_PATH" 2>&1
SCAN_STATUS=$?

echo "=== Scan report stored at $REPORT_PATH ==="

if [ $SCAN_STATUS -ne 0 ]; then
    echo "❌ Trivy scan failed — image NOT pushed!"
    exit 1
fi

echo "=== Logging in to Docker Hub ==="
echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin

echo "=== Tagging image ==="
docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "${DOCKER_USER}/${IMAGE_NAME}:${IMAGE_TAG}"

echo "=== Pushing image ==="
docker push "${DOCKER_USER}/${IMAGE_NAME}:${IMAGE_TAG}"

echo "✔ Scan passed — image pushed successfully!"
Enter fullscreen mode

Exit fullscreen mode

Make script executable:

chmod +x scripts/build_scan_push.sh
Enter fullscreen mode

Exit fullscreen mode




🌱 Step 4 — OpenTofu Variables

variables.tf

variable "image_name" {
  type = string
}

variable "image_tag" {
  type = string
}

variable "docker_username" {
  type = string
}

variable "docker_password" {
  type = string
  sensitive = true
}
Enter fullscreen mode

Exit fullscreen mode




🔧 Step 5 — Main OpenTofu Configuration

main.tf

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

resource "null_resource" "build_scan_push" {
  provisioner "local-exec" {
    command = <
Enter fullscreen mode

Exit fullscreen mode




📤 Step 6 — Outputs

outputs.tf

output "docker_image" {
  value = "${var.image_name}:${var.image_tag}"
}
Enter fullscreen mode

Exit fullscreen mode



🧪 Step 7 — Run OpenTofu

cd pythonapp/tofu/
tofu init
tofu fmt
tofu validate
tofu plan
tofu apply
Enter fullscreen mode

Exit fullscreen mode

2




Uploaded to Docker Hub:

latchudevops/python-secure-app:v1
Enter fullscreen mode

Exit fullscreen mode

3




🛡 Why This Workflow Is Production-Ready

| Step                | Purpose                             |
| ------------------- | ----------------------------------- |
| Docker Build        | Standard packaging of application   |
| Trivy Scan          | Security scanning (HIGH + CRITICAL) |
| Fail-Fast Push      | Prevent vulnerable images           |
| OpenTofu Automation | IaC-driven repeatable workflow      |
| Sensitive Variables | Credentials kept securely           |
Enter fullscreen mode

Exit fullscreen mode


🌟 Thanks for reading! If this post added value, a like ❤️, follow, or share would encourage me to keep creating more content.


— Latchu | Senior DevOps & Cloud Engineer

☁️ AWS | GCP | ☸️ Kubernetes | 🔐 Security | ⚡ Automation
📌 Sharing hands-on guides, best practices & real-world cloud solutions



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *