#!/usr/bin/env python3 """ DevOps Utility Scripts Provides helper functions for common DevOps tasks """ import argparse import sys import os import json import yaml import subprocess from pathlib import Path from typing import Dict, List, Optional class TerraformHelper: """Helper functions for Terraform operations""" @staticmethod def init_project(name: str, cloud: str, region: str) -> None: """Initialize a new Terraform project structure""" base_path = Path(name) # Create directory structure directories = [ base_path / "environments" / "dev", base_path / "environments" / "staging", base_path / "environments" / "prod", base_path / "modules" / "vpc", base_path / "modules" / "compute", base_path / "modules" / "database", ] for directory in directories: directory.mkdir(parents=True, exist_ok=True) print(f"Created: {directory}") # Create main.tf template main_tf_content = f"""terraform {{ required_version = ">= 1.6.0" required_providers {{ {cloud} = {{ source = "hashicorp/{cloud}" version = "~> 5.0" }} }} backend "s3" {{ bucket = "terraform-state-{name}" key = "{{{{env}}}}/terraform.tfstate" region = "{region}" encrypt = true dynamodb_table = "terraform-state-lock" }} }} provider "{cloud}" {{ region = "{region}" }} """ for env in ["dev", "staging", "prod"]: env_path = base_path / "environments" / env (env_path / "main.tf").write_text(main_tf_content.replace("{{env}}", env)) (env_path / "variables.tf").write_text( 'variable "environment" {\n description = "Environment name"\n type = string\n default = "' + env + '"\n}\n' ) (env_path / "outputs.tf").write_text("# Define outputs here\n") print(f"Created Terraform files in: {env_path}") # Create README readme_content = f"""# {name} Infrastructure ## Structure - `environments/`: Environment-specific configurations - `modules/`: Reusable Terraform modules ## Usage ### Initialize ```bash cd environments/dev terraform init ``` ### Plan ```bash terraform plan -out=tfplan ``` ### Apply ```bash terraform apply tfplan ``` """ (base_path / "README.md").write_text(readme_content) print(f"\nProject '{name}' initialized successfully!") @staticmethod def validate_hcl(file_path: str) -> bool: """Validate Terraform HCL syntax""" try: result = subprocess.run( ["terraform", "fmt", "-check", file_path], capture_output=True, text=True, ) if result.returncode == 0: print(f"✓ {file_path} is properly formatted") return True else: print(f"✗ {file_path} needs formatting") print(result.stdout) return False except FileNotFoundError: print("Error: terraform command not found") return False class KubernetesHelper: """Helper functions for Kubernetes operations""" @staticmethod def validate_manifest(file_path: str, schema_version: str = "1.28") -> bool: """Validate Kubernetes manifest syntax""" try: with open(file_path, "r") as f: docs = list(yaml.safe_load_all(f)) print(f"Validating {len(docs)} document(s) in {file_path}") for i, doc in enumerate(docs): if not doc: continue # Basic validation if "apiVersion" not in doc: print(f"✗ Document {i + 1}: Missing apiVersion") return False if "kind" not in doc: print(f"✗ Document {i + 1}: Missing kind") return False if "metadata" not in doc: print(f"✗ Document {i + 1}: Missing metadata") return False print( f"✓ Document {i + 1}: {doc['kind']} '{doc['metadata'].get('name', 'unnamed')}'" ) print(f"✓ All documents in {file_path} are valid") return True except yaml.YAMLError as e: print(f"✗ YAML syntax error: {e}") return False except FileNotFoundError: print(f"✗ File not found: {file_path}") return False @staticmethod def generate_deployment( name: str, image: str, namespace: str = "default", replicas: int = 3 ) -> str: """Generate a Kubernetes deployment manifest""" manifest = { "apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": name, "namespace": namespace, "labels": {"app": name}}, "spec": { "replicas": replicas, "selector": {"matchLabels": {"app": name}}, "template": { "metadata": {"labels": {"app": name}}, "spec": { "containers": [ { "name": name, "image": image, "ports": [{"containerPort": 8080}], "resources": { "requests": {"memory": "256Mi", "cpu": "250m"}, "limits": {"memory": "512Mi", "cpu": "500m"}, }, "livenessProbe": { "httpGet": {"path": "/healthz", "port": 8080}, "initialDelaySeconds": 30, "periodSeconds": 10, }, "readinessProbe": { "httpGet": {"path": "/ready", "port": 8080}, "initialDelaySeconds": 10, "periodSeconds": 5, }, } ] }, }, }, } return yaml.dump(manifest, default_flow_style=False, sort_keys=False) class GitOpsHelper: """Helper functions for GitOps workflows""" @staticmethod def init_gitops(tool: str, environments: List[str]) -> None: """Initialize GitOps directory structure""" base_path = Path("gitops") if tool.lower() == "argocd": # ArgoCD structure for env in environments: env_path = base_path / "applications" / env env_path.mkdir(parents=True, exist_ok=True) # Create application manifest app_manifest = { "apiVersion": "argoproj.io/v1alpha1", "kind": "Application", "metadata": { "name": f"myapp-{env}", "namespace": "argocd", }, "spec": { "project": "default", "source": { "repoURL": "https://github.com/myorg/myapp.git", "targetRevision": "HEAD", "path": f"kubernetes/overlays/{env}", }, "destination": { "server": "https://kubernetes.default.svc", "namespace": env, }, "syncPolicy": { "automated": { "prune": True, "selfHeal": True, } }, }, } with open(env_path / "application.yaml", "w") as f: yaml.dump(app_manifest, f, default_flow_style=False) print(f"Created ArgoCD application for: {env}") elif tool.lower() == "flux": # Flux structure for env in environments: env_path = base_path / "clusters" / env env_path.mkdir(parents=True, exist_ok=True) # Create kustomization kustomization = { "apiVersion": "kustomize.toolkit.fluxcd.io/v1", "kind": "Kustomization", "metadata": {"name": f"myapp-{env}", "namespace": "flux-system"}, "spec": { "interval": "5m", "path": f"./kubernetes/overlays/{env}", "prune": True, "sourceRef": {"kind": "GitRepository", "name": "myapp"}, }, } with open(env_path / "kustomization.yaml", "w") as f: yaml.dump(kustomization, f, default_flow_style=False) print(f"Created Flux kustomization for: {env}") print(f"\nGitOps structure initialized for {tool}") class SecurityHelper: """Helper functions for security operations""" @staticmethod def scan_secrets(directory: str) -> List[str]: """Scan for potential secrets in files""" import re secret_patterns = { "AWS Access Key": r"AKIA[0-9A-Z]{16}", "Private Key": r"-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----", "API Key": r"api[_-]?key['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9]{32,}", "Password": r"password['\"]?\s*[:=]\s*['\"]?[^'\"\s]{8,}", "Token": r"token['\"]?\s*[:=]\s*['\"]?[a-zA-Z0-9._\-]{20,}", } findings = [] for root, dirs, files in os.walk(directory): # Skip common non-code directories dirs[:] = [ d for d in dirs if d not in [".git", "node_modules", "venv", ".terraform"] ] for file in files: file_path = os.path.join(root, file) try: with open(file_path, "r", encoding="utf-8", errors="ignore") as f: content = f.read() for secret_type, pattern in secret_patterns.items(): matches = re.finditer(pattern, content) for match in matches: findings.append( f"{file_path}:{secret_type} - {match.group()[:20]}..." ) except Exception as e: continue return findings def main(): parser = argparse.ArgumentParser(description="DevOps Utility Scripts") subparsers = parser.add_subparsers(dest="command", help="Available commands") # Terraform commands tf_parser = subparsers.add_parser("terraform", help="Terraform utilities") tf_subparsers = tf_parser.add_subparsers(dest="subcommand") tf_init = tf_subparsers.add_parser("init-project", help="Initialize Terraform project") tf_init.add_argument("--name", required=True, help="Project name") tf_init.add_argument( "--cloud", required=True, choices=["aws", "azure", "gcp"], help="Cloud provider" ) tf_init.add_argument("--region", required=True, help="Default region") tf_validate = tf_subparsers.add_parser("validate", help="Validate Terraform files") tf_validate.add_argument("--file", required=True, help="File to validate") # Kubernetes commands k8s_parser = subparsers.add_parser("k8s", help="Kubernetes utilities") k8s_subparsers = k8s_parser.add_subparsers(dest="subcommand") k8s_validate = k8s_subparsers.add_parser("validate", help="Validate K8s manifest") k8s_validate.add_argument("--file", required=True, help="Manifest file") k8s_validate.add_argument( "--schema-version", default="1.28", help="Kubernetes version" ) k8s_generate = k8s_subparsers.add_parser("generate", help="Generate deployment") k8s_generate.add_argument("--name", required=True, help="Deployment name") k8s_generate.add_argument("--image", required=True, help="Container image") k8s_generate.add_argument("--namespace", default="default", help="Namespace") k8s_generate.add_argument("--replicas", type=int, default=3, help="Replica count") # GitOps commands gitops_parser = subparsers.add_parser("gitops", help="GitOps utilities") gitops_subparsers = gitops_parser.add_subparsers(dest="subcommand") gitops_init = gitops_subparsers.add_parser("init", help="Initialize GitOps structure") gitops_init.add_argument( "--tool", required=True, choices=["argocd", "flux"], help="GitOps tool" ) gitops_init.add_argument( "--environments", required=True, help="Comma-separated environments" ) # Security commands security_parser = subparsers.add_parser("security", help="Security utilities") security_subparsers = security_parser.add_subparsers(dest="subcommand") security_scan = security_subparsers.add_parser("scan-secrets", help="Scan for secrets") security_scan.add_argument("--directory", default=".", help="Directory to scan") args = parser.parse_args() # Execute commands if args.command == "terraform": if args.subcommand == "init-project": TerraformHelper.init_project(args.name, args.cloud, args.region) elif args.subcommand == "validate": TerraformHelper.validate_hcl(args.file) elif args.command == "k8s": if args.subcommand == "validate": KubernetesHelper.validate_manifest(args.file, args.schema_version) elif args.subcommand == "generate": manifest = KubernetesHelper.generate_deployment( args.name, args.image, args.namespace, args.replicas ) print(manifest) elif args.command == "gitops": if args.subcommand == "init": environments = [env.strip() for env in args.environments.split(",")] GitOpsHelper.init_gitops(args.tool, environments) elif args.command == "security": if args.subcommand == "scan-secrets": findings = SecurityHelper.scan_secrets(args.directory) if findings: print(f"⚠️ Found {len(findings)} potential secrets:") for finding in findings: print(f" - {finding}") sys.exit(1) else: print("✓ No secrets found") else: parser.print_help() if __name__ == "__main__": main()