Agent skill

infra-automation-generator

Stars 15
Forks 7

Install this agent skill to your Project

npx add-skill https://github.com/diegopacheco/ai-playground/tree/main/pocs/agent-skill-infra-automation-generator/skills/infra-automation-generator

SKILL.md

Infra Automation Generator

You are an infrastructure automation generator. You scan the user's project, detect the language/framework and dependencies, then generate production-ready infrastructure files based on what the user selects.

Phase 1 — Project Scanning and Auto-Detection

Scan the current working directory for build files and framework indicators. Check these in order:

Build File Detection:

  • pom.xml or build.gradle or build.gradle.kts → Java
  • go.mod → Go
  • Cargo.toml → Rust
  • package.json → Node.js
  • requirements.txt or pyproject.toml or setup.py → Python

Framework Detection (scan source files after identifying language):

For Java: scan for @SpringBootApplication (Spring Boot), @ApplicationScoped with quarkus imports (Quarkus), @MicronautApplication (Micronaut)

For Go: scan imports for github.com/gin-gonic/gin (Gin), github.com/labstack/echo (Echo), github.com/go-chi/chi (Chi), net/http (stdlib)

For Rust: scan Cargo.toml dependencies for actix-web (Actix), axum (Axum), rocket (Rocket)

For Node.js: scan package.json dependencies for express (Express), fastify (Fastify), @nestjs/core (NestJS)

For Python: scan imports/dependencies for django (Django), flask (Flask), fastapi (FastAPI)

Port Detection:

  • Java Spring Boot: check application.properties or application.yml for server.port, default 8080
  • Go: scan for ListenAndServe or framework listen calls, default 8080
  • Rust Actix: scan for .bind(), default 8080
  • Rust Axum: scan for listener bind address, default 3000
  • Node.js Express/Fastify: scan for .listen(, default 3000
  • Python Django: default 8000
  • Python Flask/FastAPI: default 8000

Dependency Detection:

  • PostgreSQL: look for postgresql, postgres, pg in connection strings, configs, or dependencies
  • MySQL: look for mysql, mariadb in connection strings, configs, or dependencies
  • MongoDB: look for mongodb, mongo in connection strings, configs, or dependencies
  • Redis: look for redis, ioredis, lettuce in configs or dependencies
  • RabbitMQ: look for amqp, rabbitmq in configs or dependencies
  • Kafka: look for kafka in configs or dependencies

If no build file is detected: Ask the user to specify their language and framework manually.

Report findings to the user:

Detected: {Language} / {Framework} ({build_file})
  - Port: {port}
  - Database: {db_type} (if found)
  - Cache: {cache_type} (if found)
  - Message Broker: {broker_type} (if found)

Phase 2 — User Choice Menu

Present this menu using the AskUserQuestion tool:

What do you want to generate? (enter numbers separated by commas, e.g. 1,2,4)

  1. Containerfile + podman-compose + start/stop/test scripts
  2. Kubernetes manifests (Deployment, Service, ConfigMap, HPA)
  3. Helm chart
  4. OpenTofu modules (AWS)
  5. Ansible playbooks

Example: 1,2,4

Phase 3 — Follow-Up Questions

Only ask these if triggered by the user's selections:

If both 2 (K8s) AND 3 (Helm) are selected: Ask: "Helm chart is a superset of K8s manifests. Generate both anyway? (y/n)" If no, drop K8s manifests and keep only Helm.

If both 2 (K8s) AND 5 (Ansible) are selected: Ask: "K8s and Ansible are alternatives for deployment. Which one do you prefer? (k8s is default)" Keep only the one the user picks.

If 4 (OpenTofu) is selected: Ask: "Which AWS infrastructure patterns do you need? (enter numbers, e.g. 1,2,3)" Pre-check patterns based on detected dependencies (e.g., if PostgreSQL detected, pre-suggest RDS):

  1. VPC (networking, subnets, NAT gateway)
  2. ECS (container service, ALB, task definition)
  3. RDS (relational database)
  4. S3 (object storage)
  5. ElastiCache (Redis)

Phase 4 — Generation

Generate in dependency order: Containerfile first, then K8s/Helm/Ansible, then OpenTofu.

Set these variables for use across all generators:

  • PROJECT_NAME: derived from the directory name, lowercase, hyphens for spaces
  • IMAGE_NAME: PROJECT_NAME:latest
  • DETECTED_PORT: from Phase 1
  • DETECTED_LANG: from Phase 1
  • DETECTED_FRAMEWORK: from Phase 1

If Containerfile was NOT selected but other generators were, use PROJECT_NAME:latest as the image name and note this in the output.

Generator 1: Containerfile + podman-compose + scripts

Generate these files at the project root. Never write comments in any generated file.

Containerfile — multi-stage build optimized for the detected language:

For Java (Spring Boot/Quarkus/Micronaut):

FROM eclipse-temurin:21-jdk AS build
WORKDIR /app
COPY . .
RUN ./mvnw package -DskipTests -q
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE {port}
ENTRYPOINT ["java", "-jar", "app.jar"]

If build.gradle is present, use ./gradlew build -x test instead and copy from build/libs/.

For Go:

FROM golang:1.23-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o app .
FROM alpine:3.20
WORKDIR /app
COPY --from=build /app/app .
EXPOSE {port}
ENTRYPOINT ["./app"]

For Rust:

FROM rust:1.82-slim AS build
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main(){}" > src/main.rs && cargo build --release && rm -rf src
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
WORKDIR /app
COPY --from=build /app/target/release/{project_name} .
EXPOSE {port}
ENTRYPOINT ["./{project_name}"]

For Node.js:

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
FROM node:22-alpine
WORKDIR /app
COPY --from=build /app .
EXPOSE {port}
ENTRYPOINT ["node", "{main_file}"]

Detect main file from package.json "main" or "scripts.start".

For Python:

FROM python:3.12-slim AS build
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
FROM python:3.12-slim
WORKDIR /app
COPY --from=build /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=build /app .
EXPOSE {port}
ENTRYPOINT ["python", "-m", "{module}"]

For FastAPI use uvicorn {module}:app --host 0.0.0.0 --port {port}. For Django use gunicorn.

podman-compose.yaml:

yaml
version: "3.8"
services:
  app:
    build:
      context: .
      dockerfile: Containerfile
    ports:
      - "{port}:{port}"
    depends_on: [list detected dependencies]
    environment: [relevant env vars]

Add service blocks for each detected dependency (postgres, redis, etc.) with standard images and default configs.

start.sh:

bash
#!/bin/bash
podman-compose up --build -d
echo "Waiting for app to start..."
until podman-compose exec app curl -sf http://localhost:{port}/health > /dev/null 2>&1 || podman-compose ps | grep -q "Up"; do
  sleep 1
done
echo "App is running on port {port}"

If there is no health endpoint, use podman-compose ps check only.

stop.sh:

bash
#!/bin/bash
podman-compose down
echo "App stopped"

test.sh:

bash
#!/bin/bash
echo "Testing app on port {port}..."
RESULT=$(curl -sf http://localhost:{port}/ 2>&1)
if [ $? -eq 0 ]; then
  echo "PASS: App is responding"
  echo "$RESULT" | head -5
else
  echo "FAIL: App is not responding on port {port}"
  exit 1
fi

Make all .sh files executable with chmod +x.

Generator 2: Kubernetes Manifests

Generate files under infra/k8s/.

namespace.yaml:

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: {project_name}

deployment.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {project_name}
  namespace: {project_name}
  labels:
    app: {project_name}
spec:
  replicas: 2
  selector:
    matchLabels:
      app: {project_name}
  template:
    metadata:
      labels:
        app: {project_name}
    spec:
      containers:
        - name: {project_name}
          image: {image_name}
          ports:
            - containerPort: {port}
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          livenessProbe:
            httpGet:
              path: /health
              port: {port}
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: {port}
            initialDelaySeconds: 5
            periodSeconds: 5

Adjust probe paths based on framework conventions (Spring Boot uses /actuator/health, etc.).

service.yaml:

yaml
apiVersion: v1
kind: Service
metadata:
  name: {project_name}
  namespace: {project_name}
spec:
  type: ClusterIP
  selector:
    app: {project_name}
  ports:
    - port: {port}
      targetPort: {port}
      protocol: TCP

configmap.yaml:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {project_name}-config
  namespace: {project_name}
data:
  APP_PORT: "{port}"

Add entries for any detected configuration values (database URLs, cache hosts, etc.) using placeholder values.

hpa.yaml:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {project_name}
  namespace: {project_name}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {project_name}
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Generator 3: Helm Chart

Generate files under infra/helm/.

Chart.yaml:

yaml
apiVersion: v2
name: {project_name}
description: Helm chart for {project_name}
type: application
version: 0.1.0
appVersion: "1.0.0"

values.yaml:

yaml
replicaCount: 2
image:
  repository: {project_name}
  tag: latest
  pullPolicy: IfNotPresent
service:
  type: ClusterIP
  port: {port}
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 512Mi
autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70
health:
  path: /health
  port: {port}

Add sections for detected dependencies (database host/port, redis, etc.).

templates/_helpers.tpl:

{{- define "{project_name}.fullname" -}}
{{- .Release.Name }}-{{ .Chart.Name }}
{{- end }}
{{- define "{project_name}.labels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{- end }}
{{- define "{project_name}.selectorLabels" -}}
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

templates/deployment.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "{project_name}.fullname" . }}
  labels:
    {{- include "{project_name}.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "{project_name}.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "{project_name}.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.port }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          livenessProbe:
            httpGet:
              path: {{ .Values.health.path }}
              port: {{ .Values.health.port }}
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: {{ .Values.health.path }}
              port: {{ .Values.health.port }}
            initialDelaySeconds: 5
            periodSeconds: 5

templates/service.yaml:

yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "{project_name}.fullname" . }}
  labels:
    {{- include "{project_name}.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  selector:
    {{- include "{project_name}.selectorLabels" . | nindent 4 }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.port }}
      protocol: TCP

templates/configmap.yaml:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "{project_name}.fullname" . }}-config
  labels:
    {{- include "{project_name}.labels" . | nindent 4 }}
data:
  APP_PORT: "{{ .Values.service.port }}"

templates/hpa.yaml:

yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ include "{project_name}.fullname" . }}
  labels:
    {{- include "{project_name}.labels" . | nindent 4 }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "{project_name}.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}

Generator 4: OpenTofu Modules (AWS)

Generate files under infra/tofu/. Only generate modules the user selected in the follow-up question.

providers.tf:

hcl
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
provider "aws" {
  region = var.aws_region
}

backend.tf:

hcl
terraform {
  backend "s3" {
    bucket = "{project_name}-tfstate"
    key    = "state/terraform.tfstate"
    region = "us-east-1"
  }
}

variables.tf — always include:

hcl
variable "aws_region" {
  type    = string
  default = "us-east-1"
}
variable "project_name" {
  type    = string
  default = "{project_name}"
}
variable "environment" {
  type    = string
  default = "dev"
}

Add variables specific to each selected pattern.

VPC module (if selected): Generate vpc.tf with:

  • VPC with CIDR 10.0.0.0/16
  • 2 public subnets (10.0.1.0/24, 10.0.2.0/24)
  • 2 private subnets (10.0.10.0/24, 10.0.20.0/24)
  • Internet Gateway
  • NAT Gateway in first public subnet
  • Route tables for public (IGW) and private (NAT) subnets
  • Default security group

ECS module (if selected): Generate ecs.tf with:

  • ECS Cluster (Fargate)
  • Task Definition referencing the container image, port, CPU/memory
  • ECS Service with desired count 2
  • ALB + Target Group + Listener on port 80 forwarding to container port
  • IAM execution role and task role
  • Security groups for ALB and ECS tasks

RDS module (if selected): Generate rds.tf with:

  • RDS instance (PostgreSQL 16 by default, or MySQL if detected)
  • db.t3.micro instance class
  • 20GB gp3 storage
  • DB subnet group using private subnets
  • Security group allowing access from ECS/app security group
  • Parameter group

S3 module (if selected): Generate s3.tf with:

  • S3 bucket with project name prefix
  • Versioning enabled
  • Server-side encryption (AES256)
  • Lifecycle rule: transition to IA after 90 days, Glacier after 365 days
  • Block public access

ElastiCache module (if selected): Generate elasticache.tf with:

  • ElastiCache Redis cluster (single node, cache.t3.micro)
  • Subnet group using private subnets
  • Security group allowing access from ECS/app security group
  • Parameter group for Redis 7

outputs.tf — include outputs for each selected pattern:

hcl
output "vpc_id" { value = aws_vpc.main.id }
output "ecs_cluster_name" { value = aws_ecs_cluster.main.name }
output "alb_dns_name" { value = aws_lb.main.dns_name }
output "rds_endpoint" { value = aws_db_instance.main.endpoint }
output "s3_bucket_name" { value = aws_s3_bucket.main.id }
output "redis_endpoint" { value = aws_elasticache_cluster.main.cache_nodes[0].address }

Only include outputs for patterns that were selected.

Generator 5: Ansible Playbooks

Generate files under infra/ansible/.

playbook.yaml:

yaml
- name: Deploy {project_name}
  hosts: app_servers
  become: true
  roles:
    - app

inventory/hosts.yaml:

yaml
all:
  children:
    app_servers:
      hosts:
        server1:
          ansible_host: 0.0.0.0
          ansible_user: ubuntu
          ansible_ssh_private_key_file: ~/.ssh/id_rsa

roles/app/tasks/main.yaml:

yaml
- name: Install podman
  ansible.builtin.package:
    name: podman
    state: present
- name: Copy Containerfile
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../../Containerfile"
    dest: "/opt/{{ project_name }}/Containerfile"
- name: Build container image
  ansible.builtin.command:
    cmd: podman build -t {{ project_name }}:latest -f Containerfile .
    chdir: "/opt/{{ project_name }}"
- name: Run container
  ansible.builtin.command:
    cmd: podman run -d --name {{ project_name }} -p {{ app_port }}:{{ app_port }} {{ project_name }}:latest

roles/app/templates/app.env.j2:

APP_PORT={{ app_port }}

Add environment variables for detected dependencies.

group_vars/all.yaml:

yaml
project_name: {project_name}
app_port: {port}

Add variables for detected dependencies (db_host, redis_host, etc.).

Phase 5 — Summary

After generation, print a summary:

Generated infrastructure files:

  Containerfile + podman-compose + scripts (if selected)
    - Containerfile
    - podman-compose.yaml
    - start.sh / stop.sh / test.sh

  Kubernetes manifests (if selected)
    - infra/k8s/namespace.yaml
    - infra/k8s/deployment.yaml
    - infra/k8s/service.yaml
    - infra/k8s/configmap.yaml
    - infra/k8s/hpa.yaml

  Helm chart (if selected)
    - infra/helm/Chart.yaml
    - infra/helm/values.yaml
    - infra/helm/templates/*.yaml

  OpenTofu modules (if selected)
    - infra/tofu/providers.tf
    - infra/tofu/backend.tf
    - infra/tofu/variables.tf
    - infra/tofu/outputs.tf
    - infra/tofu/{selected_patterns}.tf

  Ansible playbooks (if selected)
    - infra/ansible/playbook.yaml
    - infra/ansible/inventory/hosts.yaml
    - infra/ansible/roles/app/tasks/main.yaml
    - infra/ansible/group_vars/all.yaml

Next steps:
  - Review generated files and adjust values for your environment
  - For Containerfile: run ./start.sh to build and start
  - For K8s: kubectl apply -f infra/k8s/
  - For Helm: helm install {project_name} infra/helm/
  - For OpenTofu: cd infra/tofu && tofu init && tofu plan
  - For Ansible: cd infra/ansible && ansible-playbook -i inventory/hosts.yaml playbook.yaml

Rules

  • Never write comments in generated files
  • Never use docker, always use podman and podman-compose
  • Keep generated files compact, no unnecessary blank lines
  • Sleep values in scripts never exceed 1 second
  • Use loops with condition checks for readiness waits
  • All generated files must be valid and ready to use with minimal edits
  • Warn before overwriting existing files
  • Use the detected project name, port, and dependencies consistently across all generated files

Expand your agent's capabilities with these related and highly-rated skills.

diegopacheco/ai-playground

json-formatter

Validate, format, and minify JSON files when users request JSON validation, formatting, or ask to validate their JSONs

15 7
Explore
diegopacheco/ai-playground

bruno-generator

Scans the entire codebase, detects all HTTP/API endpoints across Java/Spring Boot, Node/Express, Go/Gin, Rust/Actix+Axum, Python/Django, and generates a complete Bruno API client project with .bru files, sample requests, and environments.

15 7
Explore
diegopacheco/ai-playground

leak-detect

Scan code for leaked PII, secrets/credentials, and security vulnerabilities that would get you hacked in production.

15 7
Explore
diegopacheco/ai-playground

skill-evaluator

This skill should be used when the user asks to "evaluate a skill", "review skill quality", "score my skill", "check skill best practices", "rate my skills", "evaluate all skills", "compare skills", or wants to assess skill quality across criteria like clarity, token efficiency, anti-cheating, quality gates, determinism, scope discipline, error recovery, observability, and idempotency.

15 7
Explore
diegopacheco/ai-playground

metrics-report

Scan an entire codebase, discover and run all test types, compute hybrid coverage, evaluate quality, and generate a full metrics report website with trends and charts.

15 7
Explore
diegopacheco/ai-playground

threat-analyst

Maps all external-facing endpoints, inputs, and auth boundaries. Scans the whole codebase and produces threat-analysis.md with attack vectors, protection scores, diagrams, and remediation actions.

15 7
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results