Kaizen
Examples

Full stack app

A commercial reference stack in kaizen-ext with a container service, load balancer, Postgres, and an uploads bucket.

examples/full-stack-app is the commercial reference stack. It deploys a containerized web app to the kaizen-ext account in us-east-2: a Fargate service behind a load balancer, a Postgres database, and an S3 bucket for user uploads. The agent playbook uses it as the fallback when the juniper reference app is not available.

What it builds

ModulePurpose in this stack
ecrPrivate repository for the app image
albPublic HTTPS endpoint with the account's ACM certificate
ecs-clusterCluster named after the app
ecs-serviceThe app on Fargate, with DATABASE_URL from SSM
rds-postgresPostgres that accepts connections from the app's tasks only
s3-bucketUploads bucket with CORS for browser uploads

The stack does not create a VPC. It looks up the shared kaizen-ext-vpc by its Name tag, which the README calls Pattern B. A commented networking block shows the Pattern A alternative for the first app in a new account.

Files

examples/full-stack-app/
provider.tf   # aws ~> 5.0, S3 backend kaizenlabs-ext-terraform-state, key my-app/terraform.tfstate
locals.tf     # app name, sizes, bucket name, certificate ARN, tags
main.tf       # VPC lookup, module calls, SSM DATABASE_URL, task role S3 policy
outputs.tf    # alb_dns, ecr_repository_url, rds_host, s3_uploads_bucket

Key excerpts

The VPC lookup finds the shared network and its subnets by tag.

main.tf
data "aws_vpc" "main" {
  tags = { Name = "kaizen-ext-vpc" }
}

data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.main.id]
  }
  tags = { tier = "private" }
}

The database accepts connections only from the app's security group, and the connection string goes into SSM with TLS required.

main.tf
module "rds" {
  source = "git::ssh://git@github.com/the-kaizen-labs/terraform-modules.git//rds-postgres?ref=v1.7.0"

  environment        = local.environment
  app_name           = local.app_name
  vpc_id             = data.aws_vpc.main.id
  private_subnet_ids = data.aws_subnets.private.ids

  allowed_security_group_ids = [module.ecs_service.security_group_id]

  instance_class = local.rds_instance_class
  db_name        = local.rds_db_name
  db_username    = local.rds_username
  db_password    = data.aws_ssm_parameter.db_password.value

  tags = local.tags
}

resource "aws_ssm_parameter" "database_url" {
  name  = "/${local.app_name}/database-url"
  type  = "SecureString"
  value = "postgresql://${local.rds_username}:${data.aws_ssm_parameter.db_password.value}@${module.rds.db_host}:5432/${local.rds_db_name}?sslmode=require"

  tags = local.tags
}

The app reaches its bucket through a policy on the task role, scoped to that one bucket.

main.tf
resource "aws_iam_role_policy" "task_s3" {
  name = "${local.app_name}-task-s3"
  role = module.ecs_service.task_role_name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Action = ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket"]
      Resource = [
        module.s3_uploads.bucket_arn,
        "${module.s3_uploads.bucket_arn}/*",
      ]
    }]
  })
}

Before the first plan

Store the database password at /my-app/db-password in SSM, and replace ACCOUNT_ID and CERTIFICATE_ID in locals.tf with the real certificate ARN.

On this page