Generating AWS Architecture Diagrams with Kiro CLI and Agent Skills

While trying to follow the official AWS tutorial β€œBuild AWS architecture diagrams using Amazon Q CLI and MCP” to generate architecture diagrams for my projects, I hit a wall: the MCP server awslabs.aws-diagram-mcp-server has been discontinued. All versions were yanked from PyPI. The functionality migrated to the Agent Skills system in the deploy-on-aws plugin from awslabs/agent-plugins. In this post, I share the updated path to configure everything and generate professional diagrams in .drawio format. ...

May 12, 2026 Β· 4 min Β· 826 words Β· Matheus Costa

Kiro: Transforming DevOps with AI Agents

You are a DevOps specialist juggling continuous integration and deployment (CI/CD) pipelines, infrastructure provisioning, and developer productivity services in an Amazon Web Services (AWS) environment. You’ve probably wondered: β€œWith all these generative artificial intelligence offerings, is there a service that can actually make my work more efficient?” The answer is yes. In this post, I introduce Kiro, an agentic AI development service, and show how it can save you hours on your daily automation tasks. Learn how the Kiro command line interface (CLI) transforms DevOps automation with AI-powered agents that install, configure, and deploy infrastructure using natural language commands in minutes. ...

December 16, 2025 Β· 10 min Β· 1993 words Β· Matheus Costa

DevSecOps vs. Chaos: Testing Amazon Q in the Real World (and Nothing Perfect)

DevSecOps vs. Chaos: Testing Amazon Q in the Real World (and Nothing Perfect) Event: DevSecOps Technical Event Date: September 29, 2025 Location: Online Duration: 45 minutes Audience: DevOps Engineers, Security Engineers, Developers πŸ“‹ About the Talk πŸ’‘ What happens when we put Amazon Q to solve real problems in messy environments? In this presentation, I demonstrate in practice how Amazon Q behaves when confronted with reality: broken pipelines, misconfigured settings, and security risks. With humor, live demos, and real-world lessons, we explore the limits and possibilities of AI in real-world DevSecOps scenarios. ...

September 29, 2025 Β· 3 min Β· 429 words Β· Matheus Costa

Redis Helper: Complete Tool for Redis Management

Redis Helper: Complete Tool for Redis Management Over the past few days, a specific demand for Redis configurations arose in my work environment. With it came the need for efficient monitoring and validation that Redis instances were functional, as well as easy access to manage instances running on other hosts. πŸš€ The Real Problem As many DevOps professionals know, managing multiple Redis instances can be challenging: Scattered monitoring across different tools Lack of real-time visibility into performance Difficulty with automated backup/restore Absence of centralized security auditing Complexity in managing clusters πŸ’‘ The Solution: Redis Helper I used my real need as a foundation and, with the help of Amazon Q, developed a complete tool that addresses all these pain points in an integrated way. ...

July 30, 2025 Β· 4 min Β· 652 words Β· Matheus Costa

Infrastructure as Code with Terraform on AWS: Best Practices and Automation

Introduction Infrastructure as Code (IaC) has revolutionized the way we manage cloud infrastructure. Terraform, combined with AWS, offers a powerful solution for creating, modifying, and versioning infrastructure in a declarative and reproducible way. Why Terraform + AWS? Terraform Advantages βœ… Multi-cloud - Support for multiple providers βœ… Declarative - Describes the desired state βœ… Planning - Preview changes before applying βœ… State Management - Centralized state control βœ… Modularity - Code reuse Benefits on AWS πŸš€ Scalability - Infrastructure that grows with demand πŸ”’ Security - Integrated security controls πŸ’° Cost-effective - Automatic resource optimization πŸ”„ Automation - Automated deployment and management Terraform Project Structure Directory Organization terraform-aws-infrastructure/ β”œβ”€β”€ environments/ β”‚ β”œβ”€β”€ dev/ β”‚ β”‚ β”œβ”€β”€ main.tf β”‚ β”‚ β”œβ”€β”€ variables.tf β”‚ β”‚ β”œβ”€β”€ outputs.tf β”‚ β”‚ └── terraform.tfvars β”‚ β”œβ”€β”€ staging/ β”‚ └── production/ β”œβ”€β”€ modules/ β”‚ β”œβ”€β”€ vpc/ β”‚ β”œβ”€β”€ ec2/ β”‚ β”œβ”€β”€ rds/ β”‚ β”œβ”€β”€ s3/ β”‚ └── security-groups/ β”œβ”€β”€ shared/ β”‚ β”œβ”€β”€ backend.tf β”‚ └── providers.tf └── scripts/ β”œβ”€β”€ deploy.sh └── destroy.sh Base Configuration Provider Configuration # providers.tf terraform { required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } random = { source = "hashicorp/random" version = "~> 3.1" } } backend "s3" { bucket = "terraform-state-bucket" key = "infrastructure/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "terraform-locks" } } provider "aws" { region = var.aws_region default_tags { tags = { Environment = var.environment Project = var.project_name ManagedBy = "Terraform" Owner = var.owner CostCenter = var.cost_center } } } Variables Configuration # variables.tf variable "aws_region" { description = "AWS region for resources" type = string default = "us-east-1" } variable "environment" { description = "Environment name" type = string validation { condition = contains(["dev", "staging", "production"], var.environment) error_message = "Environment must be dev, staging, or production." } } variable "project_name" { description = "Name of the project" type = string } variable "vpc_cidr" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16" } variable "availability_zones" { description = "List of availability zones" type = list(string) default = ["us-east-1a", "us-east-1b", "us-east-1c"] } Reusable Terraform Modules VPC Module # modules/vpc/main.tf resource "aws_vpc" "main" { cidr_block = var.cidr_block enable_dns_hostnames = true enable_dns_support = true tags = { Name = "${var.name}-vpc" } } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = { Name = "${var.name}-igw" } } resource "aws_subnet" "public" { count = length(var.public_subnets) vpc_id = aws_vpc.main.id cidr_block = var.public_subnets[count.index] availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true tags = { Name = "${var.name}-public-${count.index + 1}" Type = "Public" } } resource "aws_subnet" "private" { count = length(var.private_subnets) vpc_id = aws_vpc.main.id cidr_block = var.private_subnets[count.index] availability_zone = var.availability_zones[count.index] tags = { Name = "${var.name}-private-${count.index + 1}" Type = "Private" } } resource "aws_route_table" "public" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.main.id } tags = { Name = "${var.name}-public-rt" } } resource "aws_route_table_association" "public" { count = length(aws_subnet.public) subnet_id = aws_subnet.public[count.index].id route_table_id = aws_route_table.public.id } # NAT Gateway for private subnets resource "aws_eip" "nat" { count = var.enable_nat_gateway ? length(var.public_subnets) : 0 domain = "vpc" tags = { Name = "${var.name}-nat-eip-${count.index + 1}" } depends_on = [aws_internet_gateway.main] } resource "aws_nat_gateway" "main" { count = var.enable_nat_gateway ? length(var.public_subnets) : 0 allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id tags = { Name = "${var.name}-nat-${count.index + 1}" } } resource "aws_route_table" "private" { count = var.enable_nat_gateway ? length(var.private_subnets) : 0 vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.main[count.index].id } tags = { Name = "${var.name}-private-rt-${count.index + 1}" } } resource "aws_route_table_association" "private" { count = var.enable_nat_gateway ? length(aws_subnet.private) : 0 subnet_id = aws_subnet.private[count.index].id route_table_id = aws_route_table.private[count.index].id } Security Groups Module # modules/security-groups/main.tf resource "aws_security_group" "web" { name_prefix = "${var.name}-web-" vpc_id = var.vpc_id description = "Security group for web servers" ingress { description = "HTTP" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "HTTPS" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "${var.name}-web-sg" } lifecycle { create_before_destroy = true } } resource "aws_security_group" "database" { name_prefix = "${var.name}-db-" vpc_id = var.vpc_id description = "Security group for database servers" ingress { description = "MySQL/Aurora" from_port = 3306 to_port = 3306 protocol = "tcp" security_groups = [aws_security_group.web.id] } ingress { description = "PostgreSQL" from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [aws_security_group.web.id] } tags = { Name = "${var.name}-db-sg" } lifecycle { create_before_destroy = true } } EC2 Module with Auto Scaling # modules/ec2/main.tf data "aws_ami" "amazon_linux" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["amzn2-ami-hvm-*-x86_64-gp2"] } } resource "aws_launch_template" "web" { name_prefix = "${var.name}-web-" image_id = data.aws_ami.amazon_linux.id instance_type = var.instance_type vpc_security_group_ids = var.security_group_ids user_data = base64encode(templatefile("${path.module}/user_data.sh", { environment = var.environment })) iam_instance_profile { name = aws_iam_instance_profile.web.name } block_device_mappings { device_name = "/dev/xvda" ebs { volume_size = var.root_volume_size volume_type = "gp3" encrypted = true } } tag_specifications { resource_type = "instance" tags = { Name = "${var.name}-web-instance" } } lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "web" { name = "${var.name}-web-asg" vpc_zone_identifier = var.subnet_ids target_group_arns = var.target_group_arns health_check_type = "ELB" health_check_grace_period = 300 min_size = var.min_size max_size = var.max_size desired_capacity = var.desired_capacity launch_template { id = aws_launch_template.web.id version = "$Latest" } tag { key = "Name" value = "${var.name}-web-asg" propagate_at_launch = false } instance_refresh { strategy = "Rolling" preferences { min_healthy_percentage = 50 } } } # IAM Role for EC2 instances resource "aws_iam_role" "web" { name = "${var.name}-web-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } } ] }) } resource "aws_iam_instance_profile" "web" { name = "${var.name}-web-profile" role = aws_iam_role.web.name } resource "aws_iam_role_policy_attachment" "web_ssm" { role = aws_iam_role.web.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } Environment Implementation Development Environment # environments/dev/main.tf module "vpc" { source = "../../modules/vpc" name = "${var.project_name}-${var.environment}" cidr_block = var.vpc_cidr availability_zones = var.availability_zones public_subnets = ["10.0.1.0/24", "10.0.2.0/24"] private_subnets = ["10.0.10.0/24", "10.0.20.0/24"] enable_nat_gateway = false # Cost savings in dev } module "security_groups" { source = "../../modules/security-groups" name = "${var.project_name}-${var.environment}" vpc_id = module.vpc.vpc_id } module "web_servers" { source = "../../modules/ec2" name = "${var.project_name}-${var.environment}" environment = var.environment instance_type = "t3.micro" min_size = 1 max_size = 2 desired_capacity = 1 subnet_ids = module.vpc.public_subnet_ids security_group_ids = [module.security_groups.web_sg_id] } Production Environment # environments/production/main.tf module "vpc" { source = "../../modules/vpc" name = "${var.project_name}-${var.environment}" cidr_block = var.vpc_cidr availability_zones = var.availability_zones public_subnets = ["10.1.1.0/24", "10.1.2.0/24", "10.1.3.0/24"] private_subnets = ["10.1.10.0/24", "10.1.20.0/24", "10.1.30.0/24"] enable_nat_gateway = true } module "security_groups" { source = "../../modules/security-groups" name = "${var.project_name}-${var.environment}" vpc_id = module.vpc.vpc_id } module "web_servers" { source = "../../modules/ec2" name = "${var.project_name}-${var.environment}" environment = var.environment instance_type = "t3.medium" min_size = 2 max_size = 10 desired_capacity = 3 subnet_ids = module.vpc.private_subnet_ids security_group_ids = [module.security_groups.web_sg_id] } module "database" { source = "../../modules/rds" name = "${var.project_name}-${var.environment}" engine = "mysql" engine_version = "8.0" instance_class = "db.t3.medium" allocated_storage = 100 subnet_ids = module.vpc.private_subnet_ids security_group_ids = [module.security_groups.database_sg_id] backup_retention = 7 multi_az = true } Automation and CI/CD GitLab CI Pipeline # .gitlab-ci.yml stages: - validate - plan - apply - destroy variables: TF_ROOT: ${CI_PROJECT_DIR} TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_ENVIRONMENT_NAME} cache: key: "${TF_ROOT}" paths: - ${TF_ROOT}/.terraform before_script: - cd ${TF_ROOT}/environments/${CI_ENVIRONMENT_NAME} - terraform --version - terraform init -backend-config="address=${TF_ADDRESS}" -backend-config="lock_address=${TF_ADDRESS}/lock" -backend-config="unlock_address=${TF_ADDRESS}/lock" -backend-config="username=${CI_USERNAME}" -backend-config="password=${CI_JOB_TOKEN}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5" validate: stage: validate script: - terraform validate - terraform fmt -check only: - merge_requests - main plan: stage: plan script: - terraform plan -out="planfile" artifacts: name: plan paths: - ${TF_ROOT}/environments/${CI_ENVIRONMENT_NAME}/planfile only: - merge_requests - main apply: stage: apply script: - terraform apply -input=false "planfile" dependencies: - plan when: manual only: - main environment: name: ${CI_ENVIRONMENT_NAME} destroy: stage: destroy script: - terraform destroy -auto-approve when: manual only: - main environment: name: ${CI_ENVIRONMENT_NAME} action: stop Automation Scripts #!/bin/bash # scripts/deploy.sh set -e ENVIRONMENT=${1:-dev} ACTION=${2:-plan} echo "πŸš€ Deploying to $ENVIRONMENT environment" cd "environments/$ENVIRONMENT" # Initialize Terraform terraform init # Validate configuration terraform validate # Format code terraform fmt -recursive case $ACTION in "plan") echo "πŸ“‹ Planning infrastructure changes..." terraform plan -var-file="terraform.tfvars" ;; "apply") echo "πŸ”¨ Applying infrastructure changes..." terraform plan -var-file="terraform.tfvars" -out=tfplan terraform apply tfplan rm tfplan ;; "destroy") echo "πŸ’₯ Destroying infrastructure..." terraform plan -destroy -var-file="terraform.tfvars" -out=tfplan terraform apply tfplan rm tfplan ;; *) echo "❌ Invalid action. Use: plan, apply, or destroy" exit 1 ;; esac echo "βœ… Operation completed successfully!" Monitoring and Observability CloudWatch Integration # modules/monitoring/main.tf resource "aws_cloudwatch_dashboard" "main" { dashboard_name = "${var.name}-infrastructure" dashboard_body = jsonencode({ widgets = [ { type = "metric" x = 0 y = 0 width = 12 height = 6 properties = { metrics = [ ["AWS/EC2", "CPUUtilization", "AutoScalingGroupName", var.asg_name], ["AWS/ApplicationELB", "TargetResponseTime", "LoadBalancer", var.alb_name] ] period = 300 stat = "Average" region = var.aws_region title = "Infrastructure Metrics" } } ] }) } resource "aws_cloudwatch_metric_alarm" "high_cpu" { alarm_name = "${var.name}-high-cpu" comparison_operator = "GreaterThanThreshold" evaluation_periods = "2" metric_name = "CPUUtilization" namespace = "AWS/EC2" period = "300" statistic = "Average" threshold = "80" alarm_description = "This metric monitors ec2 cpu utilization" dimensions = { AutoScalingGroupName = var.asg_name } alarm_actions = [aws_sns_topic.alerts.arn] } resource "aws_sns_topic" "alerts" { name = "${var.name}-infrastructure-alerts" } Security and Compliance Terraform Security Scanning # .github/workflows/security-scan.yml name: Security Scan on: pull_request: branches: [main] jobs: security-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run Checkov uses: bridgecrewio/checkov-action@master with: directory: . framework: terraform output_format: sarif output_file_path: reports/results.sarif - name: Run TFSec uses: aquasecurity/[email protected] with: soft_fail: true - name: Run Terrascan uses: accurics/terrascan-action@main with: iac_type: terraform iac_version: v14 policy_type: aws State File Security # Backend configuration with encryption terraform { backend "s3" { bucket = "terraform-state-secure-bucket" key = "infrastructure/terraform.tfstate" region = "us-east-1" encrypt = true kms_key_id = "arn:aws:kms:us-east-1:account:key/key-id" dynamodb_table = "terraform-locks" # Versioning enabled on S3 bucket versioning = true # Server-side encryption server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" kms_master_key_id = "arn:aws:kms:us-east-1:account:key/key-id" } } } } } Best Practices 1. Code Organization βœ… Use reusable modules βœ… Separate environments into directories βœ… Keep files small and focused βœ… Use consistent naming conventions βœ… Document modules and variables 2. State Management βœ… Use remote backend (S3 + DynamoDB) βœ… Enable state versioning βœ… Configure locks to avoid conflicts βœ… Encrypt state files βœ… Regularly back up the state 3. Security βœ… Use the least privilege principle βœ… Encrypt data in transit and at rest βœ… Implement resource tagging βœ… Use secrets management βœ… Perform regular security scanning 4. Performance and Costs βœ… Use data sources for existing resources βœ… Implement lifecycle rules βœ… Monitor costs with tags βœ… Use spot instances when appropriate βœ… Optimize storage classes Common Troubleshooting 1. State Lock Issues # Force unlock (use with caution) terraform force-unlock LOCK_ID # Check current state terraform show # Import existing resources terraform import aws_instance.example i-1234567890abcdef0 2. Dependency Issues # Explicit dependencies resource "aws_instance" "web" { # ... configuration ... depends_on = [ aws_security_group.web, aws_subnet.public ] } 3. Provider Version Conflicts # Lock provider versions terraform { required_providers { aws = { source = "hashicorp/aws" version = "= 5.0.1" # Exact version } } } Conclusion Terraform offers a robust platform for implementing Infrastructure as Code on AWS. By following the best practices presented, you can: ...

July 16, 2025 Β· 9 min Β· 1891 words Β· Matheus Costa

CI/CD with GitHub Actions: Automating Deploy to Cloudflare Pages

Introduction to CI/CD Continuous Integration/Continuous Deployment (CI/CD) is an essential practice in modern development that automates the process of code integration, testing, and deployment. Why GitHub Actions? Advantages βœ… Native integration with GitHub βœ… Free for public repositories βœ… Marketplace with thousands of actions βœ… Support for multiple languages and platforms βœ… Execution in Docker containers Basic Concepts Workflow: Automated process Job: Set of steps executed on a runner Step: Individual task Action: Reusable code block Setting Up the Pipeline 1. Workflow Structure Create the file .github/workflows/deploy.yml: ...

July 16, 2025 Β· 3 min Β· 507 words Β· Matheus Costa

Amazon Q Developer in Practice: Revolutionizing Development with AI

Amazon Q Developer represents a revolution in how we develop and operate applications on AWS. This generative AI assistant not only accelerates development but also optimizes operations and solves complex problems in real time. πŸš€ What is Amazon Q Developer? Amazon Q Developer is a generative AI assistant specialized in software development and AWS operations. It combines: Intelligent and contextual code generation Analysis and optimization of AWS infrastructure Automatic resolution of operational issues Native integration with development tools Key Capabilities πŸ€– Code Generation: Generates code in multiple languages πŸ” Code Analysis: Analyzes and optimizes existing code πŸ› οΈ Infrastructure as Code: Creates and optimizes Terraform/CloudFormation templates πŸ”§ Troubleshooting: Automatically identifies and resolves issues πŸ“Š Cost Optimization: Suggests AWS cost improvements πŸ› οΈ Setup and Getting Started 1. Installation in VS Code # Install Amazon Q extension code --install-extension amazonwebservices.amazon-q-vscode 2. AWS Credentials Configuration # Configure AWS CLI aws configure # Or use AWS SSO aws configure sso 3. Activating Amazon Q Open VS Code Press Ctrl+Shift+P (or Cmd+Shift+P on Mac) Type β€œAmazon Q: Sign In” Follow the authentication process πŸ’» Practical Use Cases 1. AWS Code Generation Prompt: β€œCreate a Lambda function in Python that processes SQS messages and saves to DynamoDB” ...

December 10, 2024 Β· 7 min Β· 1334 words Β· Matheus Costa