From de73a0d1ce69bb68fb6b2c17c902a9c5280e9638 Mon Sep 17 00:00:00 2001 From: Ronaldo Macapobre Date: Wed, 18 Dec 2024 01:33:50 +0000 Subject: [PATCH] - Added prod env for deploying infra to prod - Add default env value=dev when GHA is triggered via PR merge where environment becomes blank - Removed wildcard for IAM --- .github/workflows/build-infra.yml | 3 +- .github/workflows/publish-infra.yml | 9 +- .../cloud/environments/prod/backend.tfvars | 4 + .../cloud/environments/prod/main.tf | 281 ++++++++++++++++++ .../cloud/environments/prod/prod.tfvars | 10 + .../cloud/environments/prod/providers.tf | 21 ++ .../cloud/environments/prod/variables.tf | 74 +++++ .../cloud/environments/test/.gitkeep | 0 infrastructure/cloud/modules/IAM/main.tf | 12 +- 9 files changed, 401 insertions(+), 13 deletions(-) create mode 100644 infrastructure/cloud/environments/prod/backend.tfvars create mode 100644 infrastructure/cloud/environments/prod/main.tf create mode 100644 infrastructure/cloud/environments/prod/prod.tfvars create mode 100644 infrastructure/cloud/environments/prod/providers.tf create mode 100644 infrastructure/cloud/environments/prod/variables.tf delete mode 100644 infrastructure/cloud/environments/test/.gitkeep diff --git a/.github/workflows/build-infra.yml b/.github/workflows/build-infra.yml index beec15a0..044f0345 100644 --- a/.github/workflows/build-infra.yml +++ b/.github/workflows/build-infra.yml @@ -22,7 +22,7 @@ on: - prod env: - WORKING_DIRECTORY: "./infrastructure/cloud/environments/${{ inputs.environment }}" + WORKING_DIRECTORY: "./infrastructure/cloud/environments/${{ inputs.environment || 'dev' }}" jobs: build: @@ -48,7 +48,6 @@ jobs: echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_ENV else echo "environment=dev" >> $GITHUB_ENV - echo "WORKING_DIRECTORY=${{ env.WORKING_DIRECTORY }}/dev" >> $GITHUB_ENV fi - name: Checkout repository diff --git a/.github/workflows/publish-infra.yml b/.github/workflows/publish-infra.yml index 1a52f296..52cbe3d7 100644 --- a/.github/workflows/publish-infra.yml +++ b/.github/workflows/publish-infra.yml @@ -22,11 +22,11 @@ on: - prod env: - WORKING_DIRECTORY: "./infrastructure/cloud/environments/${{ inputs.environment }}" + WORKING_DIRECTORY: "./infrastructure/cloud/environments/${{ inputs.environment || 'dev' }}" DUMMY_IMAGE_NAME: dummy-image GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper - APP_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-app-repo-${{ inputs.environment }} - LAMBDA_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-lambda-repo-${{ inputs.environment }} + APP_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-app-repo-${{ inputs.environment || 'dev' }} + LAMBDA_ECR_REPO_URL: ${{ vars.AWS_ACCOUNT }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.APP_NAME }}-lambda-repo-${{ inputs.environment || 'dev' }} jobs: deploy: @@ -53,7 +53,6 @@ jobs: echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_ENV else echo "environment=dev" >> $GITHUB_ENV - echo "WORKING_DIRECTORY=${{ env.WORKING_DIRECTORY }}/dev" >> $GITHUB_ENV fi - name: Checkout repository @@ -64,7 +63,7 @@ jobs: with: sarif_file: tfsec.sarif working_directory: ${{ env.WORKING_DIRECTORY }} - tfsec_args: "--tfvars-file=${{ env.WORKING_DIRECTORY }}/${{ inputs.environment }}.tfvars" + tfsec_args: "--tfvars-file=${{ env.WORKING_DIRECTORY }}/${{ env.environment }}.tfvars" - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v3 diff --git a/infrastructure/cloud/environments/prod/backend.tfvars b/infrastructure/cloud/environments/prod/backend.tfvars new file mode 100644 index 00000000..1ed0e227 --- /dev/null +++ b/infrastructure/cloud/environments/prod/backend.tfvars @@ -0,0 +1,4 @@ +bucket = "terraform-remote-state-b5e4f5-prod" +dynamodb_table = "terraform-remote-state-lock-b5e4f5" +key = "terraform.tfstate" +region = "ca-central-1" diff --git a/infrastructure/cloud/environments/prod/main.tf b/infrastructure/cloud/environments/prod/main.tf new file mode 100644 index 00000000..de1689df --- /dev/null +++ b/infrastructure/cloud/environments/prod/main.tf @@ -0,0 +1,281 @@ +# +# "initial" stack containing resources that main stack depends on (e.g. ECR, KMS, openshiftuser) +# +module "initial" { + source = "../../modules/initial" + openshift_iam_user = var.openshift_iam_user + iam_user_table_name = var.iam_user_table_name + test_s3_bucket_name = var.test_s3_bucket_name + region = var.region + kms_key_name = var.kms_key_name + app_name = var.app_name + environment = var.environment +} + +# +# The "main" stack +# +data "aws_caller_identity" "current" {} + +# VPC +data "aws_vpc" "vpc" { + id = var.vpc_id +} + +# Security Groups +data "aws_security_group" "web_sg" { + name = "Web_sg" +} + +data "aws_security_group" "app_sg" { + name = "App_sg" +} + +data "aws_security_group" "data_sg" { + name = "Data_sg" +} + +# +# Modules +# + +# Create Secrets placeholder for Secrets Manager +module "secrets_manager" { + source = "../../modules/SecretsManager" + environment = var.environment + app_name = var.app_name + region = var.region + kms_key_arn = module.initial.kms_key_arn + rotate_key_lambda_arn = module.lambda.lambda_functions["rotate-key"].arn +} + +# Create RDS Database +module "rds" { + source = "../../modules/RDS" + environment = var.environment + app_name = var.app_name + db_username = module.secrets_manager.db_username + db_password = module.secrets_manager.db_password + data_sg_id = data.aws_security_group.data_sg.id + vpc_id = data.aws_vpc.vpc.id + kms_key_arn = module.initial.kms_key_arn + rds_db_ca_cert = var.rds_db_ca_cert + all_subnet_ids = module.subnets.all_subnet_ids +} + +# Create IAM Roles/Policies +module "iam" { + source = "../../modules/IAM" + environment = var.environment + app_name = var.app_name + kms_key_arn = module.initial.kms_key_arn + app_ecr_repo_arn = module.initial.app_ecr.ecr_repo_arn + openshift_iam_user = var.openshift_iam_user + iam_user_table_name = var.iam_user_table_name + secrets_arn_list = module.secrets_manager.secrets_arn_list + account_id = data.aws_caller_identity.current.account_id + kms_key_id = module.initial.kms_key_arn + region = var.region +} + +# Parse Subnets +module "subnets" { + source = "../../modules/Subnets" + web_subnet_names = var.web_subnet_names + app_subnet_names = var.app_subnet_names + data_subnet_names = var.data_subnet_names + vpc_id = data.aws_vpc.vpc.id +} + +# Create Target Groups +module "tg_web" { + source = "../../modules/TargetGroup" + environment = var.environment + app_name = var.app_name + name = "web" + port = 8080 + health_check_path = "/" + vpc_id = data.aws_vpc.vpc.id + protocol = "HTTPS" +} + +module "tg_api" { + source = "../../modules/TargetGroup" + environment = var.environment + app_name = var.app_name + name = "api" + port = 5000 + health_check_path = "/api/test/headers" + vpc_id = data.aws_vpc.vpc.id + protocol = "HTTP" +} + +# Setup ALB Listeners +module "alb" { + source = "../../modules/ALB" + environment = var.environment + app_name = var.app_name + lb_name = var.lb_name + cert_domain_name = var.cert_domain_name + tg_web_arn = module.tg_web.tg_arn + tg_api_arn = module.tg_api.tg_arn +} + +# Create Lambda Functions +module "lambda" { + source = "../../modules/Lambda" + environment = var.environment + app_name = var.app_name + lambda_role_arn = module.iam.lambda_role_arn + apigw_execution_arn = module.apigw.apigw_execution_arn + lambda_ecr_repo_url = module.initial.lambda_ecr.ecr_repo_url + mtls_secret_name = module.secrets_manager.mtls_secret_name + lambda_memory_size = var.lambda_memory_size + functions = { + "authorizer" = { + http_method = "*" + resource_path = "" + env_variables = { + VERIFY_SECRET_NAME = module.secrets_manager.api_authorizer_secret.name + } + }, + "rotate-key" = { + http_method = "POST" + resource_path = "/*" + statement_id_prefix = "AllowSecretsManagerInvoke" + source_arn = module.secrets_manager.api_authorizer_secret.arn + principal = "secretsmanager.amazonaws.com" + env_variables = { + VERIFY_SECRET_NAME = module.secrets_manager.api_authorizer_secret.name + CLUSTER_NAME = module.ecs_cluster.ecs_cluster.name + } + } + } +} + +# Create Cloudwatch LogGroups +module "ecs_api_td_log_group" { + source = "../../modules/Cloudwatch/LogGroup" + environment = var.environment + app_name = var.app_name + kms_key_arn = module.initial.kms_key_arn + resource_name = "ecs" + name = "api-td" +} + +module "ecs_web_td_log_group" { + source = "../../modules/Cloudwatch/LogGroup" + environment = var.environment + app_name = var.app_name + kms_key_arn = module.initial.kms_key_arn + resource_name = "ecs" + name = "web-td" +} + +module "apigw_api_log_group" { + source = "../../modules/Cloudwatch/LogGroup" + environment = var.environment + app_name = var.app_name + kms_key_arn = module.initial.kms_key_arn + resource_name = "apigateway" + name = "api" +} + +# Create API Gateway +module "apigw" { + source = "../../modules/APIGateway" + environment = var.environment + app_name = var.app_name + region = var.region + account_id = data.aws_caller_identity.current.account_id + lambda_functions = module.lambda.lambda_functions + ecs_execution_role_arn = module.iam.ecs_execution_role_arn + log_group_arn = module.apigw_api_log_group.log_group.arn + apigw_logging_role_arn = module.iam.apigw_logging_role_arn +} + +# Create ECS Cluster +module "ecs_cluster" { + source = "../../modules/ECS/Cluster" + environment = var.environment + app_name = var.app_name + name = "app" +} + +# Create Web ECS Task Definition +module "ecs_web_td" { + source = "../../modules/ECS/TaskDefinition" + environment = var.environment + app_name = var.app_name + name = "web" + region = var.region + ecs_execution_role_arn = module.iam.ecs_execution_role_arn + ecr_repository_url = module.initial.app_ecr.ecr_repo_url + port = 8080 + secret_env_variables = module.secrets_manager.web_secrets + kms_key_arn = module.initial.kms_key_arn + log_group_name = module.ecs_web_td_log_group.log_group.name +} + +# Create API ECS Task Definition +module "ecs_api_td" { + source = "../../modules/ECS/TaskDefinition" + environment = var.environment + app_name = var.app_name + name = "api" + region = var.region + ecs_execution_role_arn = module.iam.ecs_execution_role_arn + ecr_repository_url = module.initial.app_ecr.ecr_repo_url + port = 5000 + env_variables = [ + { + name = "CORS_DOMAIN" + value = module.alb.default_lb_dns_name + }, + { + name = "AWS_API_GATEWAY_URL" + value = module.apigw.apigw_invoke_url + } + ] + secret_env_variables = module.secrets_manager.api_secrets + kms_key_arn = module.initial.kms_key_arn + log_group_name = module.ecs_api_td_log_group.log_group.name +} + +# Create Web ECS Service +module "ecs_web_service" { + source = "../../modules/ECS/Service" + environment = var.environment + app_name = var.app_name + name = "web" + ecs_cluster_id = module.ecs_cluster.ecs_cluster.id + ecs_td_arn = module.ecs_web_td.ecs_td_arn + tg_arn = module.tg_web.tg_arn + sg_id = data.aws_security_group.app_sg.id + subnet_ids = module.subnets.web_subnets_ids + port = module.ecs_web_td.port +} + +# Create Api ECS Service +module "ecs_api_service" { + source = "../../modules/ECS/Service" + environment = var.environment + app_name = var.app_name + name = "api" + ecs_cluster_id = module.ecs_cluster.ecs_cluster.id + ecs_td_arn = module.ecs_api_td.ecs_td_arn + tg_arn = module.tg_api.tg_arn + sg_id = data.aws_security_group.app_sg.id + subnet_ids = module.subnets.app_subnets_ids + port = module.ecs_api_td.port +} + +# WAF +module "waf" { + source = "../../modules/WAF" + environment = var.environment + app_name = var.app_name + region = var.region + allowed_ip_ranges = module.secrets_manager.allowed_ip_ranges + default_lb_arn = module.alb.default_lb_arn +} diff --git a/infrastructure/cloud/environments/prod/prod.tfvars b/infrastructure/cloud/environments/prod/prod.tfvars new file mode 100644 index 00000000..9ef6f3dc --- /dev/null +++ b/infrastructure/cloud/environments/prod/prod.tfvars @@ -0,0 +1,10 @@ +region = "ca-central-1" +test_s3_bucket_name = "jasper-test-s3-bucket-test" +web_subnet_names = ["Web_Prod_aza_net", "Web_Prod_azb_net"] +app_subnet_names = ["App_Prod_aza_net", "App_Prod_azb_net"] +data_subnet_names = ["Data_Prod_aza_net", "Data_Prod_azb_net"] +openshift_iam_user = "openshiftuserprod" +iam_user_table_name = "BCGOV_IAM_USER_TABLE" +lb_name = "default" +rds_db_ca_cert = "rds-ca-rsa2048-g1" +cert_domain_name = "*.example.ca" diff --git a/infrastructure/cloud/environments/prod/providers.tf b/infrastructure/cloud/environments/prod/providers.tf new file mode 100644 index 00000000..ab701174 --- /dev/null +++ b/infrastructure/cloud/environments/prod/providers.tf @@ -0,0 +1,21 @@ +terraform { + required_version = "~> 1.10.2" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.81.0" + } + + tls = { + source = "hashicorp/tls" + version = "4.0.6" + } + } + + backend "s3" { + } +} + +provider "aws" { + region = var.region +} diff --git a/infrastructure/cloud/environments/prod/variables.tf b/infrastructure/cloud/environments/prod/variables.tf new file mode 100644 index 00000000..5acbe981 --- /dev/null +++ b/infrastructure/cloud/environments/prod/variables.tf @@ -0,0 +1,74 @@ +variable "test_s3_bucket_name" { + description = "The name of the S3 bucket to create for testing" + type = string +} + +variable "region" { + description = "The AWS region" + type = string +} + +variable "kms_key_name" { + description = "Name of KMS key" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "environment" { + description = "The AWS environment to deploy to" + type = string +} + +variable "vpc_id" { + description = "The provisioned VPC ID" + type = string +} + +variable "web_subnet_names" { + description = "List of Subnets for Web" + type = list(string) +} + +variable "app_subnet_names" { + description = "List of Subnets for App" + type = list(string) +} + +variable "data_subnet_names" { + description = "List of Subnets for Data" + type = list(string) +} + +variable "openshift_iam_user" { + description = "Openshift IAM Username" + type = string +} + +variable "iam_user_table_name" { + description = "The BCGOV DynamoDb IAM user table" + type = string +} + +variable "lb_name" { + description = "The BCGOV provisioned Load Balancer name" + type = string +} + +variable "rds_db_ca_cert" { + description = "The Certifiate Authority identifier used in RDS" + type = string +} + +variable "cert_domain_name" { + description = "The BCGov provisioned certificate domain name" + type = string +} + +variable "lambda_memory_size" { + description = "The Lambda Function default Memory Size" + type = number +} diff --git a/infrastructure/cloud/environments/test/.gitkeep b/infrastructure/cloud/environments/test/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/infrastructure/cloud/modules/IAM/main.tf b/infrastructure/cloud/modules/IAM/main.tf index ff08e3fb..2057a0d9 100644 --- a/infrastructure/cloud/modules/IAM/main.tf +++ b/infrastructure/cloud/modules/IAM/main.tf @@ -108,7 +108,7 @@ resource "aws_iam_role_policy" "ecs_execution_policy" { ], Effect = "Allow", Resource = [ - "arn:aws:logs:*:*:log-group:/aws/ecs/*:*" + "arn:aws:logs:${var.region}:${var.account_id}:log-group:/aws/ecs/*:*" ] }, { @@ -132,7 +132,7 @@ resource "aws_iam_role_policy" "ecs_execution_policy" { "rds:Connect", "rds:DescribeDBSecurityGroups" ], - "Resource" : "arn:aws:rds:*:*:db:${var.app_name}-postgres-db-${var.environment}" + "Resource" : "arn:aws:rds:${var.region}:${var.account_id}:db:${var.app_name}-postgres-db-${var.environment}" } ] }) @@ -277,7 +277,7 @@ resource "aws_iam_policy" "lambda_role_policy" { "logs:CreateLogStream", "logs:PutLogEvents" ] - Resource = "arn:aws:logs:*:*:log-group:/aws/lambda/${var.app_name}-*-lambda-${var.environment}:*" + Resource = "arn:aws:logs:${var.region}:${var.account_id}:log-group:/aws/lambda/${var.app_name}-*-lambda-${var.environment}:*" }, { "Effect" : "Allow", @@ -287,8 +287,8 @@ resource "aws_iam_policy" "lambda_role_policy" { "ecs:ListServices" ], "Resource" : [ - "arn:aws:ecs:*:*:cluster/${var.app_name}-app-cluster-${var.environment}", - "arn:aws:ecs:*:*:service/${var.app_name}-app-cluster-${var.environment}/${var.app_name}-*-ecs-service-${var.environment}" + "arn:aws:ecs:${var.region}:${var.account_id}:cluster/${var.app_name}-app-cluster-${var.environment}", + "arn:aws:ecs:${var.region}:${var.account_id}:service/${var.app_name}-app-cluster-${var.environment}/${var.app_name}-*-ecs-service-${var.environment}" ] }, { @@ -299,7 +299,7 @@ resource "aws_iam_policy" "lambda_role_policy" { "ecr:BatchCheckLayerAvailability" ], "Resource" : [ - "arn:aws:ecr:*:*:repository/${var.app_name}-*-repo-${var.environment}" + "arn:aws:ecr:${var.region}:${var.account_id}:repository/${var.app_name}-*-repo-${var.environment}" ] } ]