From 90cb8bc4f2b050996d72a2501dc716d1a108c64a Mon Sep 17 00:00:00 2001 From: Alis Akers Date: Tue, 25 Jun 2024 12:54:57 -0700 Subject: [PATCH] gh workflow updates --- .github/workflows/ecr_deployment.yaml | 64 +++++----- terraform/implementation/ecs/ecs.sh | 22 ++-- .../setup/{variables.tf => _variable.tf} | 4 + terraform/implementation/setup/iam.tf | 119 ++++++++++++++++++ terraform/implementation/setup/main.tf | 2 +- terraform/implementation/setup/setup.sh | 13 +- terraform/modules/ecr/_data.tf | 15 --- terraform/modules/ecr/docker.tf | 20 ++- 8 files changed, 199 insertions(+), 60 deletions(-) rename terraform/implementation/setup/{variables.tf => _variable.tf} (75%) create mode 100644 terraform/implementation/setup/iam.tf diff --git a/.github/workflows/ecr_deployment.yaml b/.github/workflows/ecr_deployment.yaml index 8907e754..fbb9d0d9 100644 --- a/.github/workflows/ecr_deployment.yaml +++ b/.github/workflows/ecr_deployment.yaml @@ -1,36 +1,36 @@ -name: Deploy to ECR +name: Deploy to ECS on: push: branches: - #- main - 1611-create-elastic-container-service +permissions: + id-token: write + contents: read jobs: - deploy: + terraform: + name: Run Terraform runs-on: ubuntu-latest - + defaults: + run: + shell: bash + working-directory: ./terraform/implementation/ecs steps: - - name: Checkout code - uses: actions/checkout@v2 + - name: Check Out Changes + uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v4 with: - region: us-east-1 - - # - name: Authenticate Docker Registry for ECR - # run: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 339712971032.dkr.ecr.us-east-1.amazonaws.com + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-session-name: githubECSDeploymentWorkflow + aws-region: us-east-1 - - uses: hashicorp/setup-terraform@v2.0.3 - with: - terraform_version: 1.3.3 - - name: Init Terraform - working-directory: ./terraform/implementation/ecs + - name: Load variables env: ENVIRONMENT: dev BUCKET: infra-tfstate-alis-default-aizwjxuh @@ -40,16 +40,14 @@ jobs: PROJECT: infra shell: bash run: | - touch $ENVIRONMENT.tfvars - echo "owner = \"$OWNER\"" - echo "project = \"$PROJECT\"" - echo "region = \"$REGION\"" - ./ecs.sh -e dev --ci - - # - name: Apply Terraform - # working-directory: ./terraform/implementation/ecs - # env: - # ENVIRONMENT: value - # shell: bash - # run: | - # terraform apply -var-file="$ENVIRONMENT.tfvars" + echo "ENVIRONMENT=$ENVIRONMENT" >> .env + echo "BUCKET=$BUCKET" >> .env + echo "DYNAMODB_TABLE=$DYNAMODB_TABLE" >> .env + echo "REGION=$REGION" >> .env + echo "owner = \"$OWNER\"" >> $ENVIRONMENT.tfvars + echo "project = \"$PROJECT\"" >> $ENVIRONMENT.tfvars + echo "region = \"$REGION\"" >> $ENVIRONMENT.tfvars + + - name: Terraform + run: | + # ./ecs.sh -e dev --ci \ No newline at end of file diff --git a/terraform/implementation/ecs/ecs.sh b/terraform/implementation/ecs/ecs.sh index be6efd65..354b7aee 100755 --- a/terraform/implementation/ecs/ecs.sh +++ b/terraform/implementation/ecs/ecs.sh @@ -116,12 +116,13 @@ terraform init \ -backend-config "region=$REGION" \ || (echo "terraform init failed, exiting..." && exit 1) -if [ "$CI" = false ]; then - # Check if workspace exists - if terraform workspace list | grep -q "$ENVIRONMENT"; then - echo "Selecting $ENVIRONMENT terraform workspace" - terraform workspace select "$ENVIRONMENT" - else + +# Check if workspace exists +if terraform workspace list | grep -q "$ENVIRONMENT"; then + echo "Selecting $ENVIRONMENT terraform workspace" + terraform workspace select "$ENVIRONMENT" +else + if [ "$CI" = false ]; then read -p "Workspace '$ENVIRONMENT' does not exist. Do you want to create it? (y/n): " choice if [[ $choice =~ ^[Yy]$ ]]; then echo "Creating '$ENVIRONMENT' terraform workspace" @@ -130,7 +131,14 @@ if [ "$CI" = false ]; then echo "Workspace creation cancelled." exit 1 fi + else + echo "Creating '$ENVIRONMENT' terraform workspace" + terraform workspace new "$ENVIRONMENT" fi fi -terraform apply -var-file="$ENVIRONMENT.tfvars" +if [ "$CI" = false ]; then + terraform apply -var-file="$ENVIRONMENT.tfvars" +else + terraform apply -auto-approve -var-file="$ENVIRONMENT.tfvars" +fi \ No newline at end of file diff --git a/terraform/implementation/setup/variables.tf b/terraform/implementation/setup/_variable.tf similarity index 75% rename from terraform/implementation/setup/variables.tf rename to terraform/implementation/setup/_variable.tf index 8e9c1f9e..b2f5a284 100644 --- a/terraform/implementation/setup/variables.tf +++ b/terraform/implementation/setup/_variable.tf @@ -9,3 +9,7 @@ variable "region" { type = string default = "us-east-1" } +variable "github_repo" { + type = string + default = "" +} \ No newline at end of file diff --git a/terraform/implementation/setup/iam.tf b/terraform/implementation/setup/iam.tf new file mode 100644 index 00000000..bddcab99 --- /dev/null +++ b/terraform/implementation/setup/iam.tf @@ -0,0 +1,119 @@ +data "aws_caller_identity" "current" {} + +# # create a role that can be assumed to pull and push docker images from +data "aws_iam_policy_document" "github_assume_role" { + statement { + principals { + type = "Federated" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"] + } + actions = [ + "sts:AssumeRoleWithWebIdentity" + ] + condition { + test = "StringEquals" + variable = "token.actions.githubusercontent.com:aud" + values = ["sts.amazonaws.com",] + } + condition { + test = "StringLike" + variable = "token.actions.githubusercontent.com:sub" + values = [ + "repo:${var.github_repo}:*", + ] + } + } +} + +data "aws_iam_policy_document" "github" { + statement { + actions = [ + "appmesh:*", + "dynamodb:*", + "ec2:*", + "ecr:*", + "ecs:*", + "elasticloadbalancing:*", + "iam:*", + "logs:*", + "s3:*", + "servicediscovery:*", + "ecs:UpdateService", + # "appmesh:DescribeMesh", + # "appmesh:ListTagsForResource", + # "appmesh:DescribeVirtualNode", + # "appmesh:DescribeVirtualService", + # "dynamodb:GetItem", + # "dynamodb:PutItem", + # "dynamodb:DeleteItem", + # "ec2:DescribeAddresses", + # "ec2:DescribeVpcs", + # "ec2:DescribeVpcAttribute", + # "ec2:DescribeSubnets", + # "ec2:DescribeRouteTables", + # "ec2:DescribeInternetGateways", + # "ec2:DescribeSecurityGroups", + # "ec2:DescribeNetworkAcls", + # "ec2:DescribeAddress", + # "ec2:DescribeSecurityGroupRules", + # "ec2:DescribeFlowLogs", + # "ec2:DescribeNatGateways", + # "ecr:GetAuthorizationToken", + # "ecr:DescribeRepositories", + # "ecr:ListTagsForResource", + # "ecs:DescribeClusters", + # "ecs:DescribeTaskDefinition", + # "ecs:DescribeServices", + # "elasticloadbalancing:DescribeTargetGroups", + # "elasticloadbalancing:DescribeLoadBalancers", + # "elasticloadbalancing:DescribeTargetGroupAttributes", + # "elasticloadbalancing:DescribeLoadBalancerAttributes", + # "elasticloadbalancing:DescribeTags", + # "elasticloadbalancing:DescribeListeners", + # "elasticloadbalancing:DescribeRules", + # "iam:GetRole", + # "iam:GetPolicy", + # "iam:ListRolePolicies", + # "iam:GetPolicyVersion", + # "iam:ListAttachedRolePolicies", + # "logs:DescribeLogGroups", + # "logs:ListTagsLogGroup", + # "s3:listBucket", + # "s3:PutObject", + # "s3:PutObjectAcl", + # "s3:GetObject", + # "s3:GetObjectAcl", + # "s3:GetObjectAttributes", + # "servicediscovery:GetNamespace", + # "servicediscovery:ListTagsForResource", + # "servicediscovery:GetService", + ] + resources = [ + "*" + # aws_dynamodb_table.tfstate_lock.arn, + # "${aws_dynamodb_table.tfstate_lock.arn}/*", + # aws_s3_bucket.tfstate.arn, + # "${aws_s3_bucket.tfstate.arn}/*", + # "arn:aws:ec2:::", + # "arn:aws:ecr:${var.region}:${data.aws_caller_identity.current.account_id}:repository/*", + # "arn:aws:ecs:${var.region}:${data.aws_caller_identity.current.account_id}:cluster/*", + # "arn:aws:logs:${var.region}:${data.aws_caller_identity.current.account_id}:log-group::*", + # "arn:aws:appmesh:${var.region}:${data.aws_caller_identity.current.account_id}:mesh/*" + ] + } +} + +resource "aws_iam_policy" "github" { + name = "${var.project}-github-policy-${var.owner}-${terraform.workspace}" + policy = data.aws_iam_policy_document.github.json +} + +resource "aws_iam_role" "github" { + name = "${var.project}-github-role-${var.owner}-${terraform.workspace}" + assume_role_policy = data.aws_iam_policy_document.github_assume_role.json +} + +resource "aws_iam_role_policy_attachment" "github" { + role = aws_iam_role.github.name + policy_arn = aws_iam_policy.github.arn +} diff --git a/terraform/implementation/setup/main.tf b/terraform/implementation/setup/main.tf index 2db95a16..ffb5b52f 100644 --- a/terraform/implementation/setup/main.tf +++ b/terraform/implementation/setup/main.tf @@ -77,4 +77,4 @@ resource "local_file" "ecs_env" { REGION=${var.region} EOT filename = "../ecs/.env" -} \ No newline at end of file +} diff --git a/terraform/implementation/setup/setup.sh b/terraform/implementation/setup/setup.sh index 552a85b1..b717e0ab 100755 --- a/terraform/implementation/setup/setup.sh +++ b/terraform/implementation/setup/setup.sh @@ -52,13 +52,22 @@ fi if ! grep -q "project" "$ENVIRONMENT.tfvars"; then read -p "What is this project called? ( default=dibbs ): " project_choice project_choice=${project_choice:-dibbs} - echo "project = \"$project_choice\"" >> "$ENVIRONMENT.tfvars" + echo "project = \"$project_choice\"" >> "$ENVIRONMENT.tfvars" fi if ! grep -q "region" "$ENVIRONMENT.tfvars"; then read -p "What aws region are you setting up in? ( default=us-east-1 ): " region_choice region_choice=${region_choice:-us-east-1} - echo "region = \"$region_choice\"" >> "$ENVIRONMENT.tfvars" + echo "region = \"$region_choice\"" >> "$ENVIRONMENT.tfvars" +fi + +if ! grep -q "github_repo" "$ENVIRONMENT.tfvars"; then + read -p "Are you using GitHub for your source control? (y/n): " github_choice + if [[ "$github_choice" =~ ^[Yy]$ ]]; then + read -p "What is the organization/repo value for assume role? ( default=\"\" ): " repo_choice + repo_choice=${repo_choice:-""} + echo "github_repo = \"$repo_choice\"" >> "$ENVIRONMENT.tfvars" + fi fi echo "Running Terraform with the following variables:" diff --git a/terraform/modules/ecr/_data.tf b/terraform/modules/ecr/_data.tf index d0d7307f..f579672f 100644 --- a/terraform/modules/ecr/_data.tf +++ b/terraform/modules/ecr/_data.tf @@ -1,18 +1,3 @@ -data "aws_iam_policy_document" "ecr_policy" { - - for_each = var.service_data - statement { - actions = [ - "ecr:GetAuthorizationToken", - "ecr:BatchCheckLayerAvailability", - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - ] - - resources = ["arn:aws:ecs:${var.region}:${var.aws_caller_identity}:cluster/${var.ecs_cluster_name}/${each.key}"] - } -} - data "docker_registry_image" "ghcr_data" { for_each = var.service_data name = "ghcr.io/cdcgov/phdi/${each.key}:${var.phdi_version}" diff --git a/terraform/modules/ecr/docker.tf b/terraform/modules/ecr/docker.tf index 5ab09bfd..2b69f7a9 100644 --- a/terraform/modules/ecr/docker.tf +++ b/terraform/modules/ecr/docker.tf @@ -4,7 +4,7 @@ resource "docker_image" "ghcr_image" { for_each = var.service_data name = data.docker_registry_image.ghcr_data[each.key].name keep_locally = true - pull_triggers = [data.docker_registry_image.ghcr_data[each.key].sha256_digest] + pull_triggers = [data.docker_registry_image.ghcr_data[each.key].sha256_digest, plantimestamp()] force_remove = true } @@ -12,15 +12,31 @@ resource "docker_tag" "tag_for_aws" { for_each = var.service_data source_image = docker_image.ghcr_image[each.key].name target_image = "${aws_ecr_repository.repo[each.key].repository_url}:${var.phdi_version}" + lifecycle { + replace_triggered_by = [ + null_resource.docker_tag + ] + } } resource "docker_registry_image" "my_docker_image" { for_each = var.service_data name = "${aws_ecr_repository.repo[each.key].repository_url}:${var.phdi_version}" - depends_on = [docker_tag.tag_for_aws, aws_ecr_repository.repo] + depends_on = [ + docker_image.ghcr_image, + docker_tag.tag_for_aws, + aws_ecr_repository.repo + ] keep_remotely = true triggers = { sha256_digest = data.docker_registry_image.ghcr_data[each.key].sha256_digest } } + +resource "null_resource" "docker_tag" { + for_each = docker_image.ghcr_image + triggers = { + docker_image = each.value.id + } +}