From 9fcb0614c74afe90a5920224147b3581e751b8f4 Mon Sep 17 00:00:00 2001 From: mikewrighton Date: Fri, 29 Apr 2022 09:54:33 -0400 Subject: [PATCH] Service 2: backend-fargate-svc (#15) Adding backend-fargate-svc. --- .../dev-resources/proton.auto.tfvars.json | 57 +++ .../dev-resources/proton.variables.tf | 47 +++ .../backend-fargate-svc/v1/.compatible-envs | 1 + .../v1/instance_infrastructure/config.tf | 27 ++ .../v1/instance_infrastructure/data.tf | 16 + .../v1/instance_infrastructure/main.tf | 170 ++++++++ .../v1/instance_infrastructure/manifest.yaml | 5 + .../v1/pipeline_infrastructure/config.tf | 27 ++ .../v1/pipeline_infrastructure/data.tf | 294 +++++++++++++ .../v1/pipeline_infrastructure/locals.tf | 4 + .../v1/pipeline_infrastructure/main.tf | 397 ++++++++++++++++++ .../v1/pipeline_infrastructure/manifest.yaml | 5 + .../v1/pipeline_infrastructure/outputs.tf | 3 + .../backend-fargate-svc/v1/schema/schema.yaml | 67 +++ 14 files changed, 1120 insertions(+) create mode 100644 service-templates/backend-fargate-svc/dev-resources/proton.auto.tfvars.json create mode 100644 service-templates/backend-fargate-svc/dev-resources/proton.variables.tf create mode 100644 service-templates/backend-fargate-svc/v1/.compatible-envs create mode 100644 service-templates/backend-fargate-svc/v1/instance_infrastructure/config.tf create mode 100644 service-templates/backend-fargate-svc/v1/instance_infrastructure/data.tf create mode 100644 service-templates/backend-fargate-svc/v1/instance_infrastructure/main.tf create mode 100644 service-templates/backend-fargate-svc/v1/instance_infrastructure/manifest.yaml create mode 100644 service-templates/backend-fargate-svc/v1/pipeline_infrastructure/config.tf create mode 100644 service-templates/backend-fargate-svc/v1/pipeline_infrastructure/data.tf create mode 100644 service-templates/backend-fargate-svc/v1/pipeline_infrastructure/locals.tf create mode 100644 service-templates/backend-fargate-svc/v1/pipeline_infrastructure/main.tf create mode 100644 service-templates/backend-fargate-svc/v1/pipeline_infrastructure/manifest.yaml create mode 100644 service-templates/backend-fargate-svc/v1/pipeline_infrastructure/outputs.tf create mode 100644 service-templates/backend-fargate-svc/v1/schema/schema.yaml diff --git a/service-templates/backend-fargate-svc/dev-resources/proton.auto.tfvars.json b/service-templates/backend-fargate-svc/dev-resources/proton.auto.tfvars.json new file mode 100644 index 0000000..3ce7c32 --- /dev/null +++ b/service-templates/backend-fargate-svc/dev-resources/proton.auto.tfvars.json @@ -0,0 +1,57 @@ +{ + "service": { + "name": "backend-fargate-svc", + "inputs": { + }, + "repository_connection_arn": "REPLACE_ME", + "repository_id": "REPLACE_ME", + "branch_name": "REPLACE_ME" + }, + "service_instances": [ + { + "name": "instance1", + "inputs": { + }, + "outputs": { + "LambdaRuntime": "nodejs12.x" + }, + "environment": { + "account_id": "", + "name": "", + "outputs": {} + } + } + ], + "pipeline": { + "inputs": { + "service_dir": "ecs-backend", + "environment_account_ids": "", + "unit_test_command": "echo 'add your unit test command here'", + "dockerfile": "Dockerfile" + } + }, + "environment": { + "outputs": { + "ServiceTaskDefExecutionRoleArn" : "REPLACE_ME", + "CloudMapNamespaceId": "REPLACE_ME", + "VpcId": "REPLACE_ME", + "SnsTopicArn": "REPLACE_ME", + "SnsTopicName": "REPLACE_ME", + "ClusterName": "REPLACE_ME", + "PrivateSubnetOneId": "REPLACE_ME", + "PrivateSubnetTwoId": "REPLACE_ME", + "PublicSubnetOneId": "REPLACE_ME", + "PublicSubnetTwoId": "REPLACE_ME" + } + }, + "service_instance": { + "name": "instance1", + "inputs": { + "port": "80", + "image": "public.ecr.aws/z9d2n7e1/nginx:1.21.0", + "task_size": "x-small", + "desired_count": "1", + "subnet_type": "public" + } + } +} diff --git a/service-templates/backend-fargate-svc/dev-resources/proton.variables.tf b/service-templates/backend-fargate-svc/dev-resources/proton.variables.tf new file mode 100644 index 0000000..da7eb91 --- /dev/null +++ b/service-templates/backend-fargate-svc/dev-resources/proton.variables.tf @@ -0,0 +1,47 @@ + +variable "service_instances" { + type = list( + object({ + name = string + inputs = map(string) + outputs = map(string) + environment = object({ + account_id = string + name = string + outputs = map(string) + }) + }) + ) +} + +variable "service_instance" { + type = object({ + name = string + inputs = map(string) + }) + default = null +} + +variable "service" { + type = object({ + name = string + repository_id = string + repository_connection_arn = string + branch_name = string + }) + default = null +} + +variable "environment" { + type = object({ + outputs = map(string) + }) + default = null +} + +variable "pipeline" { + type = object({ + inputs = map(string) + }) + default = null +} \ No newline at end of file diff --git a/service-templates/backend-fargate-svc/v1/.compatible-envs b/service-templates/backend-fargate-svc/v1/.compatible-envs new file mode 100644 index 0000000..d0ce3ee --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/.compatible-envs @@ -0,0 +1 @@ +fargate-env:1 diff --git a/service-templates/backend-fargate-svc/v1/instance_infrastructure/config.tf b/service-templates/backend-fargate-svc/v1/instance_infrastructure/config.tf new file mode 100644 index 0000000..9650397 --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/instance_infrastructure/config.tf @@ -0,0 +1,27 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } + + backend "s3" {} +} + +# Configure the AWS Provider +provider "aws" { + region = var.aws_region + alias = "default" + + default_tags { + tags = { + "proton:service" = var.service.name + } + } +} + +variable "aws_region" { + type = string + default = "us-east-1" +} diff --git a/service-templates/backend-fargate-svc/v1/instance_infrastructure/data.tf b/service-templates/backend-fargate-svc/v1/instance_infrastructure/data.tf new file mode 100644 index 0000000..6bc17ba --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/instance_infrastructure/data.tf @@ -0,0 +1,16 @@ +data "aws_region" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_partition" "current" {} + +data "aws_iam_policy_document" "sns_publish_policy_document" { + statement { + actions = [ + "sns:Publish" + ] + resources = [ + var.environment.outputs.SnsTopicArn + ] + } +} \ No newline at end of file diff --git a/service-templates/backend-fargate-svc/v1/instance_infrastructure/main.tf b/service-templates/backend-fargate-svc/v1/instance_infrastructure/main.tf new file mode 100644 index 0000000..abb9f69 --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/instance_infrastructure/main.tf @@ -0,0 +1,170 @@ +resource "aws_iam_role" "service_task_def_role" { + assume_role_policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Principal" : { + "Service" : "ecs-tasks.amazonaws.com" + }, + "Action" : "sts:AssumeRole" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "ssn_publish_policy" { + role = aws_iam_role.service_task_def_role.name + policy_arn = aws_iam_policy.sns_publish_policy.arn +} + +resource "aws_iam_policy" "sns_publish_policy" { + policy = data.aws_iam_policy_document.sns_publish_policy_document.json +} + +variable "task_sizes" { + default = { + x-small = { cpu = 256, memory = 512 } + small = { cpu = 512, memory = 1024 } + medium = { cpu = 1024, memory = 2048 } + large = { cpu = 2048, memory = 4096 } + x-large = { cpu = 4096, memory = 8192 } + } +} + +resource "aws_ecs_task_definition" "service_task_def" { + container_definitions = jsonencode([ + { + essential = true, + image = var.service_instance.inputs.image, + logConfiguration = { + logDriver = "awslogs", + options = { + awslogs-group : aws_cloudwatch_log_group.service_log_group.name, + awslogs-region : var.aws_region, + awslogs-stream-prefix : "${var.service.name}/${var.service_instance.name}" + } + }, + name = var.service_instance.name, + portMappings = [ + { + containerPort = tonumber(var.service_instance.inputs.port) + protocol = "tcp" + } + ], + environment = [ + { name = "SNS_TOPIC_ARN", value = "{'ping':'${var.environment.outputs.SnsTopicArn}'}" }, + { name = "SNS_REGION", value = var.aws_region } + ], + } + ]) + cpu = lookup(var.task_sizes[var.service_instance.inputs.task_size], "cpu") + execution_role_arn = var.environment.outputs.ServiceTaskDefExecutionRoleArn + family = "${var.service.name}_${var.service_instance.name}" + memory = lookup(var.task_sizes[var.service_instance.inputs.task_size], "memory") + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + task_role_arn = aws_iam_role.service_task_def_role.arn +} + +resource "aws_cloudwatch_log_group" "service_log_group" { +} + +resource "aws_ecs_service" "service" { + cluster = var.environment.outputs.ClusterName + name = "${var.service.name}_${var.service_instance.name}" + deployment_maximum_percent = 200 + deployment_minimum_healthy_percent = 50 + desired_count = var.service_instance.inputs.desired_count + enable_ecs_managed_tags = false + launch_type = "FARGATE" + network_configuration { + assign_public_ip = var.service_instance.inputs.subnet_type == "private" ? false : true + security_groups = [aws_security_group.service_security_group.id] + subnets = var.service_instance.inputs.subnet_type == "private" ? [ + var.environment.outputs.PrivateSubnetOneId, var.environment.outputs.PrivateSubnetTwoId + ] : [ + var.environment.outputs.PublicSubnetOneId, var.environment.outputs.PublicSubnetTwoId + ] + } + + service_registries { + registry_arn = aws_service_discovery_service.cloudmap_service.arn + } + task_definition = aws_ecs_task_definition.service_task_def.arn +} + +resource "aws_service_discovery_service" "cloudmap_service" { + dns_config { + dns_records { + ttl = 60 + type = "A" + } + namespace_id = var.environment.outputs.CloudMapNamespaceId + routing_policy = "MULTIVALUE" + } + health_check_custom_config { + failure_threshold = 1 + } + name = "${var.service.name}.${var.service_instance.name}" + namespace_id = var.environment.outputs.CloudMapNamespaceId +} + +resource "aws_security_group" "service_security_group" { + description = "Automatically created Security Group for the Service" + + ingress { + cidr_blocks = ["0.0.0.0/0"] + description = "Allow all inbound traffic by default" + protocol = "-1" + to_port = 0 + from_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + description = "Allow all outbound traffic by default" + protocol = "-1" + to_port = 0 + from_port = 0 + } + vpc_id = var.environment.outputs.VpcId +} + +resource "aws_appautoscaling_target" "service_task_count_target" { + depends_on = [aws_ecs_service.service] + max_capacity = 10 + min_capacity = 1 + resource_id = "service/${var.environment.outputs.ClusterName}/${aws_ecs_service.service.name}" + role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService" + scalable_dimension = "ecs:service:DesiredCount" + service_namespace = "ecs" +} + +resource "aws_appautoscaling_policy" "service_task_count_target_cpu_scaling" { + name = "${aws_ecs_service.service.name}_BackendFargateServiceTaskCountTargetCpuScaling" + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.service_task_count_target.resource_id + scalable_dimension = aws_appautoscaling_target.service_task_count_target.scalable_dimension + service_namespace = aws_appautoscaling_target.service_task_count_target.service_namespace + target_tracking_scaling_policy_configuration { + target_value = 50 + predefined_metric_specification { + predefined_metric_type = "ECSServiceAverageCPUUtilization" + } + } +} + +resource "aws_appautoscaling_policy" "service_task_count_target_memory_scaling" { + name = "${aws_ecs_service.service.name}_BackendFargateServiceTaskCountTargetMemoryScaling" + policy_type = "TargetTrackingScaling" + resource_id = aws_appautoscaling_target.service_task_count_target.resource_id + scalable_dimension = aws_appautoscaling_target.service_task_count_target.scalable_dimension + service_namespace = aws_appautoscaling_target.service_task_count_target.service_namespace + target_tracking_scaling_policy_configuration { + target_value = 50 + predefined_metric_specification { + predefined_metric_type = "ECSServiceAverageMemoryUtilization" + } + } +} diff --git a/service-templates/backend-fargate-svc/v1/instance_infrastructure/manifest.yaml b/service-templates/backend-fargate-svc/v1/instance_infrastructure/manifest.yaml new file mode 100644 index 0000000..8d90345 --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/instance_infrastructure/manifest.yaml @@ -0,0 +1,5 @@ +infrastructure: + templates: + - file: "*" + rendering_engine: hcl + template_language: terraform \ No newline at end of file diff --git a/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/config.tf b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/config.tf new file mode 100644 index 0000000..903d3db --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/config.tf @@ -0,0 +1,27 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } + + backend "s3" {} +} + +# Configure the AWS Provider +provider "aws" { + region = var.aws_region + alias = "default" + + default_tags { + tags = { + "proton:pipeline" = var.service.name + } + } +} + +variable "aws_region" { + type = string + default = "us-east-1" +} diff --git a/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/data.tf b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/data.tf new file mode 100644 index 0000000..35c3722 --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/data.tf @@ -0,0 +1,294 @@ +data "aws_region" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_partition" "current" {} + +data "aws_iam_policy_document" "publish_role_policy_document" { + statement { + effect = "Allow" + resources = [ + "arn:aws:logs:${local.region}:${local.account_id}:log-group:/aws/codebuild/${aws_codebuild_project.build_project.name}", + "arn:aws:logs:${local.region}:${local.account_id}:log-group:/aws/codebuild/${aws_codebuild_project.build_project.name}*" + ] + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + } + statement { + effect = "Allow" + resources = [ + "arn:aws:codebuild:${local.region}:${local.account_id}:report-group:/${aws_codebuild_project.build_project.name}*", + ] + actions = [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ] + } + statement { + effect = "Allow" + resources = ["*"] + actions = ["ecr:GetAuthorizationToken"] + } + statement { + effect = "Allow" + resources = [ + aws_ecr_repository.ecr_repo.arn + ] + actions = [ + "ecr:BatchCheckLayerAvailability", + "ecr:CompleteLayerUpload", + "ecr:GetAuthorizationToken", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ] + } + statement { + effect = "Allow" + resources = ["*"] + actions = ["proton:GetService"] + } + statement { + effect = "Allow" + resources = [ + aws_s3_bucket.pipeline_artifacts_bucket.arn, + "${aws_s3_bucket.pipeline_artifacts_bucket.arn}*" + ] + actions = [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject*", + "s3:Abort*" + ] + } + statement { + effect = "Allow" + resources = [aws_kms_key.pipeline_artifacts_bucket_key.arn] + actions = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ] + } +} + +data "aws_iam_policy_document" "deployment_role_policy" { + statement { + effect = "Allow" + resources = [ + "arn:aws:logs:${local.region}:${local.account_id}:log-group:/aws/codebuild/deploy-*", + "arn:aws:logs:${local.region}:${local.account_id}:log-group:/aws/codebuild/deploy-:*", + ] + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + } + statement { + effect = "Allow" + resources = [ + "arn:aws:codebuild:${local.region}:${local.account_id}:report-group:/deploy--*", + ] + actions = [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ] + } + statement { + effect = "Allow" + resources = ["*"] + actions = [ + "proton:GetServiceInstance", + "proton:UpdateServiceInstance" + ] + } + statement { + effect = "Allow" + resources = [ + aws_s3_bucket.pipeline_artifacts_bucket.arn, + "${aws_s3_bucket.pipeline_artifacts_bucket.arn}/*" + ] + actions = [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ] + } + statement { + effect = "Allow" + resources = [aws_kms_key.pipeline_artifacts_bucket_key.arn] + actions = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ] + } +} + +data "aws_iam_policy_document" "pipeline_artifacts_bucket_key_policy" { + statement { + effect = "Allow" + resources = ["*"] + principals { + identifiers = ["arn:aws:iam::${local.account_id}:root"] + type = "AWS" + } + actions = [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:GenerateDataKey", + "kms:TagResource", + "kms:UntagResource" + ] + } + + statement { + effect = "Allow" + resources = ["*"] + principals { + identifiers = [aws_iam_role.pipeline_role.arn] + type = "AWS" + } + actions = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ] + } + + statement { + effect = "Allow" + resources = ["*"] + principals { + identifiers = [aws_iam_role.publish_role.arn] + type = "AWS" + } + actions = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ] + } + + statement { + effect = "Allow" + resources = ["*"] + principals { + identifiers = [aws_iam_role.deployment_role.arn] + type = "AWS" + } + actions = [ + "kms:DescribeKey", + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ] + } +} + +data "aws_iam_policy_document" "pipeline_role_policy" { + statement { + effect = "Allow" + resources = [ + aws_s3_bucket.pipeline_artifacts_bucket.arn, + "${aws_s3_bucket.pipeline_artifacts_bucket.arn}*" + ] + actions = [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject*", + "s3:Abort*" + ] + } + + statement { + effect = "Allow" + resources = [aws_kms_key.pipeline_artifacts_bucket_key.arn] + actions = [ + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*" + ] + } + + statement { + effect = "Allow" + resources = ["*"] + actions = [ + "codestar-connections:*" + ] + } + + statement { + effect = "Allow" + resources = [aws_iam_role.pipeline_build_codepipeline_action_role.arn] + actions = [ + "sts:AssumeRole" + ] + } + + statement { + effect = "Allow" + resources = [aws_iam_role.pipeline_deploy_codepipeline_action_role.arn] + actions = [ + "sts:AssumeRole" + ] + } +} + +data "aws_iam_policy_document" "pipeline_build_codepipeline_action_role_policy" { + statement { + effect = "Allow" + resources = [aws_codebuild_project.build_project.arn] + actions = [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ] + } +} + +data "aws_iam_policy_document" "pipeline_deploy_codepipeline_action_role_policy" { + statement { + effect = "Allow" + resources = ["arn:aws:codebuild:${local.region}:${local.account_id}:project/deploy-*", ] + actions = [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ] + } +} \ No newline at end of file diff --git a/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/locals.tf b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/locals.tf new file mode 100644 index 0000000..2714438 --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/locals.tf @@ -0,0 +1,4 @@ +locals { + account_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.id +} \ No newline at end of file diff --git a/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/main.tf b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/main.tf new file mode 100644 index 0000000..67fbb42 --- /dev/null +++ b/service-templates/backend-fargate-svc/v1/pipeline_infrastructure/main.tf @@ -0,0 +1,397 @@ +resource "aws_ecr_repository" "ecr_repo" { + name = "${var.service.name}-repo" +} + +resource "aws_ecr_repository_policy" "ecr_repo_policy" { + count = var.pipeline.inputs.environment_account_ids != "" ? 1 : 0 + repository = aws_ecr_repository.ecr_repo.name + policy = data.aws_iam_policy_document.ecr_repo_policy_document.json +} + +data "aws_iam_policy_document" "ecr_repo_policy_document" { + statement { + effect = "Allow" + principals { + type = "AWS" + identifiers = [for id in split(",", var.pipeline.inputs.environment_account_ids) : "arn:aws:iam::${id}:root"] + } + actions = [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ] + } +} + +resource "aws_codebuild_project" "build_project" { + name = "${var.service.name}-build-project" + service_role = aws_iam_role.publish_role.arn + + artifacts { + type = "CODEPIPELINE" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "aws/codebuild/amazonlinux2-x86_64-standard:3.0" + type = "LINUX_CONTAINER" + image_pull_credentials_type = "CODEBUILD" + privileged_mode = true + + environment_variable { + name = "repo_name" + value = aws_ecr_repository.ecr_repo.name + } + + environment_variable { + name = "service_name" + value = var.service.name + } + } + + source { + buildspec = < yq_linux_amd64.sha", + "wget https://github.com/mikefarah/yq/releases/download/3.4.0/yq_linux_amd64", + "sha256sum -c yq_linux_amd64.sha", + "mv yq_linux_amd64 /usr/bin/yq", + "chmod +x /usr/bin/yq" + ] + }, + "pre_build": { + "commands": [ + "cd $CODEBUILD_SRC_DIR/${var.pipeline.inputs.service_dir}", + "$(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)", + "${var.pipeline.inputs.unit_test_command}" + ] + }, + "build": { + "commands": [ + "IMAGE_REPO_NAME=$repo_name", + "IMAGE_TAG=$CODEBUILD_BUILD_NUMBER", + "IMAGE_ID=${local.account_id}.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG", + "docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG -f ${var.pipeline.inputs.dockerfile} .", + "docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $IMAGE_ID;", + "docker push $IMAGE_ID" + ] + }, + "post_build": { + "commands": [ + "aws proton --region $AWS_DEFAULT_REGION get-service --name $service_name | jq -r .service.spec > service.yaml", + "yq w service.yaml 'instances[*].spec.image' \"$IMAGE_ID\" > rendered_service.yaml" + ] + } + }, + "artifacts": { + "files": [ + "${var.pipeline.inputs.service_dir}/rendered_service.yaml" + ] + } + } +EOF + + type = "CODEPIPELINE" + } + + encryption_key = aws_kms_key.pipeline_artifacts_bucket_key.arn +} + +resource "aws_codebuild_project" "deploy_project" { + for_each = { for instance in var.service_instances : instance.name => instance } + + name = "deploy-${var.service.name}-${index(var.service_instances, each.value)}" + service_role = aws_iam_role.deployment_role.arn + + artifacts { + type = "CODEPIPELINE" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "aws/codebuild/amazonlinux2-x86_64-standard:3.0" + type = "LINUX_CONTAINER" + privileged_mode = false + image_pull_credentials_type = "CODEBUILD" + + environment_variable { + name = "service_instance_name" + value = each.value.name + } + + environment_variable { + name = "service_name" + value = var.service.name + } + } + + source { + type = "CODEPIPELINE" + buildspec = <