Infrastructure as Code: Building a Production-Ready AWS Environment with Terraform

Infrastructure as Code: Building a Production-Ready AWS Environment with Terraform

A comprehensive guide to setting up a production-ready AWS infrastructure using Terraform, including ECS clusters, RDS instances, Application Load Balancers, and automated CI/CD pipelines with CodeCommit.

Terraform

AWS

ECS

RDS

Load Balancer

CodeCommit

CI/CD

Infrastructure as Code


Introduction to Infrastructure as Code with Terraform

In this guide, we'll explore how to use Terraform to create a production-ready AWS infrastructure. We'll set up an ECS cluster for container orchestration, RDS for database management, Application Load Balancer for traffic distribution, and a CI/CD pipeline using AWS CodeCommit.

Project Structure and Prerequisites

# Project structure
.
├── environments/
│   ├── prod/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   └── staging/
├── modules/
│   ├── ecs/
│   ├── rds/
│   ├── alb/
│   └── pipeline/
└── versions.tf

# versions.tf
terraform {
  required_version = ">= 1.0.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}        

Setting up the ECS Cluster

First, let's create an ECS cluster with Fargate launch type for running containerized applications. We'll set up the necessary networking components and security groups.

# modules/ecs/main.tf
resource "aws_ecs_cluster" "main" {
  name = var.cluster_name

  setting {
    name  = "containerInsights"
    value = "enabled"
  }

  tags = var.tags
}

resource "aws_ecs_task_definition" "app" {
  family                   = var.app_name
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.cpu
  memory                   = var.memory
  execution_role_arn       = aws_iam_role.ecs_execution_role.arn
  task_role_arn            = aws_iam_role.ecs_task_role.arn

  container_definitions = jsonencode([
    {
      name      = var.app_name
      image     = var.container_image
      essential = true
      portMappings = [
        {
          containerPort = var.container_port
          protocol      = "tcp"
        }
      ]
      environment = var.environment_variables
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          awslogs-group         = aws_cloudwatch_log_group.app.name
          awslogs-region        = var.aws_region
          awslogs-stream-prefix = "ecs"
        }
      }
    }
  ])
}

resource "aws_ecs_service" "app" {
  name            = var.app_name
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = var.service_desired_count
  launch_type     = "FARGATE"

  network_configuration {
    security_groups  = [aws_security_group.ecs_tasks.id]
    subnets         = var.private_subnet_ids
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = var.target_group_arn
    container_name   = var.app_name
    container_port   = var.container_port
  }
}        

Configuring RDS Instance

Next, we'll set up an RDS instance in a private subnet with proper security groups and backup configurations.

# modules/rds/main.tf
resource "aws_db_subnet_group" "main" {
  name       = "${var.identifier}-subnet-group"
  subnet_ids = var.subnet_ids

  tags = var.tags
}

resource "aws_security_group" "rds" {
  name        = "${var.identifier}-rds-sg"
  description = "Security group for RDS instance"
  vpc_id      = var.vpc_id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [var.ecs_security_group_id]
  }
}

resource "aws_db_instance" "main" {
  identifier           = var.identifier
  allocated_storage    = var.allocated_storage
  storage_type         = "gp3"
  engine               = "postgres"
  engine_version       = var.engine_version
  instance_class       = var.instance_class
  db_name              = var.database_name
  username             = var.database_username
  password             = var.database_password
  parameter_group_name = "default.postgres14"

  vpc_security_group_ids = [aws_security_group.rds.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name

  backup_retention_period = 7
  backup_window          = "03:00-04:00"
  maintenance_window     = "Mon:04:00-Mon:05:00"

  multi_az               = var.environment == "production"
  skip_final_snapshot    = false
  deletion_protection    = true

  tags = var.tags
}        

Application Load Balancer Setup

The Application Load Balancer will distribute traffic across our ECS tasks and handle SSL termination.

# modules/alb/main.tf
resource "aws_lb" "main" {
  name               = var.alb_name
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = var.public_subnet_ids

  enable_deletion_protection = true

  tags = var.tags
}

resource "aws_lb_target_group" "app" {
  name        = var.app_name
  port        = var.container_port
  protocol    = "HTTP"
  vpc_id      = var.vpc_id
  target_type = "ip"

  health_check {
    healthy_threshold   = 3
    interval            = 30
    protocol            = "HTTP"
    matcher             = "200"
    timeout             = 5
    path                = var.health_check_path
    unhealthy_threshold = 3
  }
}

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.main.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  certificate_arn   = var.certificate_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}        

Setting up CodeCommit CI/CD Pipeline

Finally, we'll create a CI/CD pipeline using AWS CodeCommit, CodeBuild, and CodePipeline for automated deployments.

# modules/pipeline/main.tf
resource "aws_codecommit_repository" "app" {
  repository_name = var.repository_name
  description     = "Application repository"
}

resource "aws_codebuild_project" "app" {
  name          = var.project_name
  description   = "Build project for ${var.project_name}"
  build_timeout = "10"
  service_role  = aws_iam_role.codebuild.arn

  artifacts {
    type = "CODEPIPELINE"
  }

  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = true

    environment_variable {
      name  = "AWS_DEFAULT_REGION"
      value = var.aws_region
    }
  }

  source {
    type      = "CODEPIPELINE"
    buildspec = "buildspec.yml"
  }
}

resource "aws_codepipeline" "app" {
  name     = var.pipeline_name
  role_arn = aws_iam_role.codepipeline.arn

  artifact_store {
    location = aws_s3_bucket.artifacts.bucket
    type     = "S3"
  }

  stage {
    name = "Source"

    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "CodeCommit"
      version          = "1"
      output_artifacts = ["source_output"]

      configuration = {
        RepositoryName = aws_codecommit_repository.app.repository_name
        BranchName     = var.branch_name
      }
    }
  }

  stage {
    name = "Build"

    action {
      name            = "Build"
      category        = "Build"
      owner           = "AWS"
      provider        = "CodeBuild"
      input_artifacts = ["source_output"]
      version         = "1"

      configuration = {
        ProjectName = aws_codebuild_project.app.name
      }
    }
  }

  stage {
    name = "Deploy"

    action {
      name            = "Deploy"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      input_artifacts = ["build_output"]
      version         = "1"

      configuration = {
        ClusterName = var.ecs_cluster_name
        ServiceName = var.ecs_service_name
      }
    }
  }
}        

Putting It All Together

Now let's see how to combine all these modules to create a complete infrastructure.

# environments/prod/main.tf
module "ecs" {
  source = "../../modules/ecs"

  cluster_name    = "production-cluster"
  app_name        = "my-application"
  container_image = "${var.ecr_repository_url}:latest"
  
  vpc_id             = module.vpc.vpc_id
  private_subnet_ids = module.vpc.private_subnet_ids
  target_group_arn   = module.alb.target_group_arn

  tags = var.tags
}

module "rds" {
  source = "../../modules/rds"

  identifier         = "production-db"
  allocated_storage  = 100
  instance_class     = "db.t3.large"
  database_name      = "appdb"
  database_username  = var.db_username
  database_password  = var.db_password

  vpc_id               = module.vpc.vpc_id
  subnet_ids           = module.vpc.database_subnet_ids
  ecs_security_group_id = module.ecs.ecs_security_group_id

  environment = "production"
  tags        = var.tags
}

module "alb" {
  source = "../../modules/alb"

  alb_name          = "production-alb"
  vpc_id            = module.vpc.vpc_id
  public_subnet_ids = module.vpc.public_subnet_ids
  certificate_arn   = var.certificate_arn

  tags = var.tags
}

module "pipeline" {
  source = "../../modules/pipeline"

  repository_name   = "my-application"
  project_name      = "my-application-build"
  pipeline_name     = "my-application-pipeline"
  branch_name       = "main"
  
  ecs_cluster_name  = module.ecs.cluster_name
  ecs_service_name  = module.ecs.service_name

  tags = var.tags
}        

Security Best Practices

  • Use KMS encryption for sensitive data
  • Implement proper IAM roles with least privilege
  • Enable VPC flow logs for network monitoring
  • Use security groups to restrict access
  • Enable encryption at rest for RDS
  • Implement proper backup and disaster recovery

Monitoring and Maintenance

  • Set up CloudWatch alarms for key metrics
  • Enable ECS container insights
  • Configure RDS enhanced monitoring
  • Implement proper logging and tracing
  • Set up automated backups
  • Plan for regular maintenance windows

This infrastructure setup provides a solid foundation for running containerized applications in AWS. The combination of ECS, RDS, and Application Load Balancer, along with the automated CI/CD pipeline, creates a robust and scalable environment suitable for production workloads. Remember to adjust the configurations based on your specific requirements and always follow AWS best practices for security and performance.

要查看或添加评论,请登录

Abdul Basit的更多文章

社区洞察

其他会员也浏览了