diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01aa82727..4680b0d8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: name: OpenAPI path: OpenAPI - docker: + package: runs-on: ubuntu-latest name: Build containers env: @@ -52,6 +52,8 @@ jobs: CDP_PERSON_PORT: 8833 CDP_FORMS_PORT: 8844 CDP_DATA_SHARING_PORT: 8855 + outputs: + version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 with: @@ -78,21 +80,48 @@ jobs: run: make down env: IMAGE_VERSION: ${{ steps.version.outputs.version }} + - name: Save Docker Images + run: | + IMAGES=$(docker images | awk '$1 ~ /cabinetoffice\/cdp-/ && $2 ~ /^'$IMAGE_VERSION'$/ { print $1":"$2}' | tr '\n' ' ') + echo "Images to be saved: $IMAGES" + docker save -o cdp-images.tar $IMAGES + env: + IMAGE_VERSION: ${{ steps.version.outputs.version }} + - name: Upload Docker Images as Artifacts + uses: actions/upload-artifact@v4 + with: + name: docker-images + path: cdp-images.tar - docs: + publish: runs-on: ubuntu-latest - name: Build documentation + name: Publish containers + needs: [test, package] + steps: - uses: actions/checkout@v4 + + - name: Download Docker Images + uses: actions/download-artifact@v4 with: - fetch-depth: 0 - - name: Build - run: cd docs && make build - - name: Publish - if: github.ref == 'refs/heads/main' && github.repository_owner == 'cabinetoffice' - run: cd docs && make publish - - name: Upload Documentation - uses: actions/upload-artifact@v4 + name: docker-images + + - name: Load Docker Images + run: docker load -i cdp-images.tar + + - name: Set up AWS CLI + uses: aws-actions/configure-aws-credentials@v4 with: - name: Documentation - path: docs/build/* + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-2 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Generate Image Version + id: image_version + run: | + IMAGE_VERSION=${{ needs.package.outputs.version }} + echo "IMAGE_VERSION=$IMAGE_VERSION" >> $GITHUB_ENV diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..a154d9bf0 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,26 @@ +name: Docs + +on: + push: + branches: [main] + pull_request: + +jobs: + + docs: + runs-on: ubuntu-latest + name: Build documentation + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Build + run: cd docs && make build + - name: Publish + if: github.ref == 'refs/heads/main' && github.repository_owner == 'cabinetoffice' + run: cd docs && make publish + - name: Upload Documentation + uses: actions/upload-artifact@v4 + with: + name: Documentation + path: docs/build/* diff --git a/.mise.toml b/.mise.toml index 2c85749a9..57c59e7c6 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,4 +1,4 @@ [tools] dotnet = "8.0.300" -terraform = "1.8.2" -terragrunt = "0.58.2" +terraform = "1.9.2" +terragrunt = "0.62.0" diff --git a/Makefile b/Makefile index 09905b61e..46147935f 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,6 @@ AWS_ACCOUNT_ID=$$(aws sts get-caller-identity | jq -r '.Account') REPO_URL := $(AWS_ACCOUNT_ID).dkr.ecr.eu-west-2.amazonaws.com IMAGES := cdp-organisation-information-migrations cdp-data-sharing cdp-entity-verification cdp-forms cdp-organisation-app cdp-organisation cdp-person cdp-tenant cdp-authority -TAGGED_IMAGES := $(addprefix cabinetoffice/,$(addsuffix :latest,$(IMAGES))) -TAGGED_REPO_IMAGES := $(addprefix $(REPO_URL)/,$(TAGGED_IMAGES)) DOCKER_COMPOSE_CMD=IMAGE_VERSION=$(IMAGE_VERSION) docker compose # Extracts targets and their comments @@ -163,12 +161,16 @@ aws-push-authority-private-key: ## Push Authority's private key to the target AW aws secretsmanager create-secret --name cdp-sirsi-authority-keys --secret-string "$$(jq -n --arg priv "$$(cat ./terragrunt/secrets/authority-private-key.pem)" '{PRIVATE: $$priv}')"; \ fi -aws-push-to-ecr: build-docker ## Build, tag and push Docker images to ECR - $(foreach image,$(TAGGED_IMAGES),docker tag $(image) $(REPO_URL)/$(notdir $(basename $(image)));) +aws-push-to-ecr: IMAGE_VERSION ?= latest +aws-push-to-ecr: ## Tag latest built Docker images and push to ECR + $(foreach image,$(IMAGES),docker tag cabinetoffice/$(image):$(IMAGE_VERSION) $(REPO_URL)/$(image):$(IMAGE_VERSION);) aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin $(REPO_URL) - $(foreach image,$(IMAGES),docker push $(REPO_URL)/$(image);) + $(foreach image,$(IMAGES),docker push $(REPO_URL)/$(image):$(IMAGE_VERSION);) .PHONY: aws-push-to-ecr +aws-build-and-push-ecr: build-docker aws-push-to-ecr ## Build, tag and push Docker images to ECR +.PHONY: aws-build-and-push-ecr + version-commit: COMMIT_REF ?= HEAD version-commit: ## Determines the last commit hash @git rev-parse --short "$(COMMIT_REF)" diff --git a/terragrunt/components/common/networking/terragrunt.hcl b/terragrunt/components/common/networking/terragrunt.hcl index 4641d1d18..be80c736a 100644 --- a/terragrunt/components/common/networking/terragrunt.hcl +++ b/terragrunt/components/common/networking/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//networking" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//networking" : null } include { diff --git a/terragrunt/components/core/iam/terragrunt.hcl b/terragrunt/components/core/iam/terragrunt.hcl index d557a697e..f6711f95e 100644 --- a/terragrunt/components/core/iam/terragrunt.hcl +++ b/terragrunt/components/core/iam/terragrunt.hcl @@ -20,6 +20,7 @@ locals { } inputs = { + account_ids = local.global_vars.locals.account_ids tags = local.tags terraform_operators = local.global_vars.locals.terraform_operators tfstate_bucket_name = local.global_vars.locals.tg.state_bucket diff --git a/terragrunt/components/core/networking/terragrunt.hcl b/terragrunt/components/core/networking/terragrunt.hcl index e67f5cee3..ff76130e5 100644 --- a/terragrunt/components/core/networking/terragrunt.hcl +++ b/terragrunt/components/core/networking/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//core-networking" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//core-networking" : null } include { diff --git a/terragrunt/components/core/security-groups/terragrunt.hcl b/terragrunt/components/core/security-groups/terragrunt.hcl index 30114376b..4deb53cb5 100644 --- a/terragrunt/components/core/security-groups/terragrunt.hcl +++ b/terragrunt/components/core/security-groups/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//core-security-groups" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//core-security-groups" : null } include { diff --git a/terragrunt/components/orchestrator/ecr/terragrunt.hcl b/terragrunt/components/orchestrator/ecr/terragrunt.hcl new file mode 100644 index 000000000..c41e9023c --- /dev/null +++ b/terragrunt/components/orchestrator/ecr/terragrunt.hcl @@ -0,0 +1,28 @@ +terraform { + source = local.global_vars.locals.environment == "orchestrator" ? "../../../modules//orchestrator/ecr" : null +} + +include { + path = find_in_parent_folders() +} + +locals { + + global_vars = read_terragrunt_config(find_in_parent_folders("terragrunt.hcl")) + orchestrator_vars = read_terragrunt_config(find_in_parent_folders("orchestrator.hcl")) + + tags = merge( + local.global_vars.inputs.tags, + local.orchestrator_vars.inputs.tags, + { + component = "orchestrator-ecr" + } + ) + +} + +inputs = { + account_ids = local.global_vars.locals.account_ids + service_configs = local.global_vars.locals.service_configs + tags = local.tags +} diff --git a/terragrunt/components/orchestrator/iam/terragrunt.hcl b/terragrunt/components/orchestrator/iam/terragrunt.hcl new file mode 100644 index 000000000..51bede0f4 --- /dev/null +++ b/terragrunt/components/orchestrator/iam/terragrunt.hcl @@ -0,0 +1,27 @@ +terraform { + source = local.global_vars.locals.environment == "orchestrator" ? "../../../modules//orchestrator/iam" : null +} + +include { + path = find_in_parent_folders() +} + +locals { + + global_vars = read_terragrunt_config(find_in_parent_folders("terragrunt.hcl")) + orchestrator_vars = read_terragrunt_config(find_in_parent_folders("orchestrator.hcl")) + + tags = merge( + local.global_vars.inputs.tags, + local.orchestrator_vars.inputs.tags, + { + component = "orchestrator-iam" + } + ) +} + +inputs = { + account_ids = local.global_vars.locals.account_ids + tags = local.tags + terraform_operators = local.global_vars.locals.terraform_operators +} diff --git a/terragrunt/components/orchestrator/orchestrator.hcl b/terragrunt/components/orchestrator/orchestrator.hcl new file mode 100644 index 000000000..d814bde32 --- /dev/null +++ b/terragrunt/components/orchestrator/orchestrator.hcl @@ -0,0 +1,9 @@ +locals { + tags = { + component_root = "orchestrator" + } +} + +inputs = { + tags = local.tags +} diff --git a/terragrunt/components/service/api-gateway/terragrunt.hcl b/terragrunt/components/service/api-gateway/terragrunt.hcl index 09cf106ff..d56f10253 100644 --- a/terragrunt/components/service/api-gateway/terragrunt.hcl +++ b/terragrunt/components/service/api-gateway/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//api-gateway" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//api-gateway" : null } include { diff --git a/terragrunt/components/service/database/terragrunt.hcl b/terragrunt/components/service/database/terragrunt.hcl index 58e7b4efe..c85aecace 100644 --- a/terragrunt/components/service/database/terragrunt.hcl +++ b/terragrunt/components/service/database/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//database" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//database" : null } include { diff --git a/terragrunt/components/service/ecs/terragrunt.hcl b/terragrunt/components/service/ecs/terragrunt.hcl index f52d3a17a..a759bb977 100644 --- a/terragrunt/components/service/ecs/terragrunt.hcl +++ b/terragrunt/components/service/ecs/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//ecs" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//ecs" : null } include { @@ -76,6 +76,8 @@ dependency service_database { } inputs = { + + account_ids = local.global_vars.locals.account_ids service_configs = local.global_vars.locals.service_configs tags = local.tags diff --git a/terragrunt/components/service/telemetry/terragrunt.hcl b/terragrunt/components/service/telemetry/terragrunt.hcl index a875e8e85..8957807aa 100644 --- a/terragrunt/components/service/telemetry/terragrunt.hcl +++ b/terragrunt/components/service/telemetry/terragrunt.hcl @@ -1,5 +1,5 @@ terraform { - source = "../../../modules//telemetry" + source = local.global_vars.locals.environment != "orchestrator" ? "../../../modules//telemetry" : null } include { @@ -58,6 +58,7 @@ dependency service_ecs { } inputs = { + account_ids = local.global_vars.locals.account_ids grafana_config = local.global_vars.locals.tools_configs.grafana service_configs = local.global_vars.locals.service_configs tags = local.tags diff --git a/terragrunt/components/terragrunt.hcl b/terragrunt/components/terragrunt.hcl index 8341d3b46..db77a1fc7 100644 --- a/terragrunt/components/terragrunt.hcl +++ b/terragrunt/components/terragrunt.hcl @@ -1,15 +1,38 @@ locals { + account_ids = { + for name, env in local.environments : name => env.account_id + } + cidr_b_development = 3 cidr_b_integration = 4 + cidr_b_orchestrator = 5 cidr_b_production = 1 cidr_b_staging = 2 environment = get_env("TG_ENVIRONMENT", "development") environments = { + orchestrator = { + cidr_block = "10.${local.cidr_b_orchestrator}.0.0/16" + account_id = 891377225335 + name = "orchestrator" + postgres_instance_type = "db.t4g.micro" + private_subnets = [ + "10.${local.cidr_b_orchestrator}.101.0/24", + "10.${local.cidr_b_orchestrator}.102.0/24", + "10.${local.cidr_b_orchestrator}.103.0/24" + ] + public_subnets = [ + "10.${local.cidr_b_orchestrator}.1.0/24", + "10.${local.cidr_b_orchestrator}.2.0/24", + "10.${local.cidr_b_orchestrator}.3.0/24" + ] + top_level_domain = "findatender.codatt.net" + } development = { cidr_block = "10.${local.cidr_b_development}.0.0/16" + account_id = 471112892058 name = "dev" postgres_instance_type = "db.t4g.micro" private_subnets = [ @@ -26,6 +49,7 @@ locals { } staging = { cidr_block = "10.${local.cidr_b_staging}.0.0/16" + account_id = 905418042182 name = "staging" postgres_instance_type = "db.t4g.micro" private_subnets = [ @@ -42,6 +66,7 @@ locals { } integration = { cidr_block = "10.${local.cidr_b_integration}.0.0/16" + account_id = 767397666448 name = "integration" postgres_instance_type = "db.t4g.micro" private_subnets = [ @@ -58,6 +83,7 @@ locals { } production = { cidr_block = "10.${local.cidr_b_production}.0.0/16" + account_id = 471112843276 name = "production" postgres_instance_type = "db.t4g.micro" private_subnets = [ diff --git a/terragrunt/modules/core-iam/ci-datasource.tf b/terragrunt/modules/core-iam/ci-datasource.tf index 411a9d3d9..d7fed6129 100644 --- a/terragrunt/modules/core-iam/ci-datasource.tf +++ b/terragrunt/modules/core-iam/ci-datasource.tf @@ -1,3 +1,6 @@ +# Note! +# Resources in this file are shared with orchestrator/iam module + data "aws_iam_policy_document" "terraform_assume" { statement { actions = ["sts:AssumeRole"] @@ -13,6 +16,14 @@ data "aws_iam_policy_document" "terraform_assume" { } } +data "aws_iam_policy_document" "terraform_assume_orchestrator_role" { + statement { + effect = "Allow" + actions = ["sts:AssumeRole"] + resources = ["arn:aws:iam::${local.orchestrator_account_id}:role/cdp-sirsi-orchestrator-read-service-version"] + } +} + data "aws_iam_policy_document" "terraform" { statement { @@ -202,8 +213,9 @@ data "aws_iam_policy_document" "terraform_global" { statement { actions = [ - "ecr:CreateRepository", + "ecr:BatchGetImage", "ecr:GetAuthorizationToken", + "ecr:GetDownloadUrlForLayer", "ecs:Create*", "ecs:DeregisterTaskDefinition", "ecs:DescribeTaskDefinition", @@ -268,7 +280,8 @@ data "aws_iam_policy_document" "terraform_product" { resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/cdp-sirsi-*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/cdp-sirsi-*", - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/*" + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/*", + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/cdp-sirsi-*" ] sid = "ManageProductIAMs" } @@ -290,9 +303,7 @@ data "aws_iam_policy_document" "terraform_product" { actions = ["ec2:*"] effect = "Allow" resources = [ - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*/cdp-sirsi-*", - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:vpc/cdp-sirsi-*", - "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:elastic-ip/cdp-sirsi-*" + "arn:aws:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*/cdp-sirsi-*" ] sid = "ManageProductEC2" } diff --git a/terragrunt/modules/core-iam/ci.tf b/terragrunt/modules/core-iam/ci.tf index 4af0333b9..8d6ddc38f 100644 --- a/terragrunt/modules/core-iam/ci.tf +++ b/terragrunt/modules/core-iam/ci.tf @@ -1,3 +1,6 @@ +# Note! +# Resources in this file are shared with orchestrator/iam module + resource "aws_iam_role" "terraform" { assume_role_policy = data.aws_iam_policy_document.terraform_assume.json name = "${local.name_prefix}-${var.environment}-terraform" @@ -25,6 +28,12 @@ resource "aws_iam_policy" "terraform_product" { tags = var.tags } +resource "aws_iam_policy" "terraform_assume_orchestrator_role" { + name = "${local.name_prefix}-terraform-assume-orchestrator-role" + description = "Policy to allow assuming the orchestrator role" + policy = data.aws_iam_policy_document.terraform_assume_orchestrator_role.json +} + resource "aws_iam_role_policy_attachment" "terraform" { policy_arn = aws_iam_policy.terraform.arn role = aws_iam_role.terraform.name @@ -39,3 +48,8 @@ resource "aws_iam_role_policy_attachment" "terraform_production" { policy_arn = aws_iam_policy.terraform_product.arn role = aws_iam_role.terraform.name } + +resource "aws_iam_role_policy_attachment" "terraform_assume_orchestrator_role" { + role = aws_iam_role.terraform.name + policy_arn = aws_iam_policy.terraform_assume_orchestrator_role.arn +} diff --git a/terragrunt/modules/core-iam/locals.tf b/terragrunt/modules/core-iam/locals.tf index 879480b91..374804d49 100644 --- a/terragrunt/modules/core-iam/locals.tf +++ b/terragrunt/modules/core-iam/locals.tf @@ -1,3 +1,7 @@ +# Note! +# Resources in this file are shared with orchestrator/iam module + locals { - name_prefix = var.product.resource_name + name_prefix = var.product.resource_name + orchestrator_account_id = var.account_ids["orchestrator"] } diff --git a/terragrunt/modules/core-iam/outputs.tf b/terragrunt/modules/core-iam/outputs.tf index d4a00087c..00d414e62 100644 --- a/terragrunt/modules/core-iam/outputs.tf +++ b/terragrunt/modules/core-iam/outputs.tf @@ -14,6 +14,14 @@ output "cloudwatch_events_name" { value = aws_iam_role.cloudwatch_events.name } +output "db_connection_step_function_arn" { + value = aws_iam_role.db_connection_step_function.arn +} + +output "db_connection_step_function_name" { + value = aws_iam_role.db_connection_step_function.name +} + output "ecs_task_arn" { value = aws_iam_role.ecs_task.arn } @@ -30,14 +38,6 @@ output "ecs_task_name" { value = aws_iam_role.ecs_task.name } -output "db_connection_step_function_arn" { - value = aws_iam_role.db_connection_step_function.arn -} - -output "db_connection_step_function_name" { - value = aws_iam_role.db_connection_step_function.name -} - output "service_deployer_step_function_arn" { value = aws_iam_role.service_deployer_step_function.arn } diff --git a/terragrunt/modules/core-iam/variables.tf b/terragrunt/modules/core-iam/variables.tf index fb28182c2..7b4f57047 100644 --- a/terragrunt/modules/core-iam/variables.tf +++ b/terragrunt/modules/core-iam/variables.tf @@ -1,3 +1,8 @@ +variable "account_ids" { + description = "Map of all accounts and their IDs" + type = map(string) +} + variable "environment" { description = "The environment we are provisioning" type = string diff --git a/terragrunt/modules/core-networking/outputs.tf b/terragrunt/modules/core-networking/outputs.tf index ef090d72a..3b9fc1205 100644 --- a/terragrunt/modules/core-networking/outputs.tf +++ b/terragrunt/modules/core-networking/outputs.tf @@ -43,6 +43,11 @@ output "public_subnets_cidr_blocks" { value = aws_subnet.public.*.cidr_block } +output "vpc_cider" { + description = "ID of the VPC" + value = aws_vpc.this.cidr_block +} + output "vpc_cidr_block" { description = "VPC CIDR block" value = aws_vpc.this.cidr_block @@ -52,8 +57,3 @@ output "vpc_id" { description = "ID of the VPC" value = aws_vpc.this.id } - -output "vpc_cider" { - description = "ID of the VPC" - value = aws_vpc.this.cidr_block -} diff --git a/terragrunt/modules/database/variables.tf b/terragrunt/modules/database/variables.tf index a1d0a0a9a..a0f507cf6 100644 --- a/terragrunt/modules/database/variables.tf +++ b/terragrunt/modules/database/variables.tf @@ -42,13 +42,13 @@ variable "role_cloudwatch_events_name" { type = string } -variable "role_db_connection_step_function_name" { - description = "Name of the IAM role used by the Step Function in charge of updating DB connection secret" +variable "role_db_connection_step_function_arn" { + description = "ARN of the IAM role used by the Step Function in charge of updating DB connection secret" type = string } -variable "role_db_connection_step_function_arn" { - description = "ARN of the IAM role used by the Step Function in charge of updating DB connection secret" +variable "role_db_connection_step_function_name" { + description = "Name of the IAM role used by the Step Function in charge of updating DB connection secret" type = string } diff --git a/terragrunt/modules/ecs/cloudwatch.tf b/terragrunt/modules/ecs/cloudwatch.tf index 5ebdc4e21..a51e33267 100644 --- a/terragrunt/modules/ecs/cloudwatch.tf +++ b/terragrunt/modules/ecs/cloudwatch.tf @@ -35,32 +35,3 @@ resource "aws_cloudwatch_log_group" "tasks" { tags = var.tags } - -resource "aws_cloudwatch_event_rule" "ecr_push" { - for_each = aws_ecr_repository.this - - name = "${local.name_prefix}-ecr-push-to-${each.value.name}" - description = "CloudWatch Event rule to detect ECR push events to ${each.value.name}" - - event_pattern = jsonencode( - { - "source" : ["aws.ecr"], - "detail-type" : ["ECR Image Action"], - "detail" : { - "action-type" : ["PUSH"], - "image-tag" : ["latest"], - "repository-name" : [each.value.name] - "result" : ["SUCCESS"], - } - } - ) - - tags = var.tags -} - -resource "aws_cloudwatch_event_target" "trigger_service_deployment" { - for_each = aws_cloudwatch_event_rule.ecr_push - rule = each.value.name - arn = each.key == "organisation-information-migrations" ? aws_sfn_state_machine.ecs_run_migration.arn : aws_sfn_state_machine.ecs_force_deploy[each.key].arn - role_arn = var.role_cloudwatch_events_arn -} diff --git a/terragrunt/modules/ecs/datasource.tf b/terragrunt/modules/ecs/datasource.tf index 42cf70abd..96ec796ad 100644 --- a/terragrunt/modules/ecs/datasource.tf +++ b/terragrunt/modules/ecs/datasource.tf @@ -83,3 +83,33 @@ data "aws_iam_policy_document" "step_function_manage_services" { sid = "MangeIAM" } } + +data "aws_iam_policy_document" "ecr_pull_from_orchestrator" { + statement { + actions = [ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:GetAuthorizationToken" + ] + resources = ["*"] + } +} + +# Configure the provider to assume the role in the orchestrator account and fetch the latest service version +provider "aws" { + alias = "orchestrator" + region = "eu-west-2" +} + +provider "aws" { + alias = "orchestrator_assume_role" + region = "eu-west-2" + assume_role { + role_arn = "arn:aws:iam::${local.orchestrator_account_id}:role/cdp-sirsi-orchestrator-read-service-version" + } +} + +data "aws_ssm_parameter" "orchestrator_service_version" { + provider = aws.orchestrator_assume_role + name = "/cdp-sirsi-service-version" +} diff --git a/terragrunt/modules/ecs/iam.tf b/terragrunt/modules/ecs/iam.tf index d84f27cb8..762aaa604 100644 --- a/terragrunt/modules/ecs/iam.tf +++ b/terragrunt/modules/ecs/iam.tf @@ -9,6 +9,17 @@ resource "aws_iam_role_policy_attachment" "ecs_task_access_secrets" { role = var.role_ecs_task_exec_name } +resource "aws_iam_policy" "ecr_pull_from_orchestrator" { + name = "${local.name_prefix}-ecr-pull-from-orchestrator" + policy = data.aws_iam_policy_document.ecr_pull_from_orchestrator.json + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "ecr_pull_from_orchestrator" { + policy_arn = aws_iam_policy.ecr_pull_from_orchestrator.arn + role = var.role_ecs_task_exec_name +} + resource "aws_iam_policy" "cloudwatch_event_invoke_deployer_step_function" { name = "${local.name_prefix}-invoke-deployer-step-function" description = "Policy for CloudWatch Events to invoke Step Functions" diff --git a/terragrunt/modules/ecs/locals.tf b/terragrunt/modules/ecs/locals.tf index f79fde192..f5a277421 100644 --- a/terragrunt/modules/ecs/locals.tf +++ b/terragrunt/modules/ecs/locals.tf @@ -1,12 +1,8 @@ locals { - service_environment = var.environment == "production" ? "Production" : "Development" - - ecr_urls = [ - for repos in aws_ecr_repository.this.* : { for repo, attr in repos : repo => attr.repository_url } - ][ - 0 - ] + ecr_urls = { + for task in local.tasks : task => "${local.orchestrator_account_id}.dkr.ecr.eu-west-2.amazonaws.com/cdp-${task}" + } name_prefix = var.product.resource_name @@ -18,11 +14,17 @@ locals { } } + orchestrator_account_id = var.account_ids["orchestrator"] + + orchestrator_service_version = data.aws_ssm_parameter.orchestrator_service_version.value + services = [ for name, config in var.service_configs : config.name if config.name != "organisation-information-migrations" ] + service_environment = var.environment == "production" ? "Production" : "Development" + tasks = [ for name, config in var.service_configs : config.name diff --git a/terragrunt/modules/ecs/service-authority.tf b/terragrunt/modules/ecs/service-authority.tf index 3cc2724b8..692a11af4 100644 --- a/terragrunt/modules/ecs/service-authority.tf +++ b/terragrunt/modules/ecs/service-authority.tf @@ -10,7 +10,7 @@ module "ecs_service_authority" { conn_string_location = var.db_connection_secret_arn environment = local.service_environment host_port = var.service_configs.authority.port - image = "${local.ecr_urls[var.service_configs.authority.name]}:latest" + image = "${local.ecr_urls[var.service_configs.authority.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.authority.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-data-sharing.tf b/terragrunt/modules/ecs/service-data-sharing.tf index 96f9f9895..9a3398155 100644 --- a/terragrunt/modules/ecs/service-data-sharing.tf +++ b/terragrunt/modules/ecs/service-data-sharing.tf @@ -8,7 +8,7 @@ module "ecs_service_data_sharing" { cpu = var.service_configs.data_sharing.cpu environment = local.service_environment host_port = var.service_configs.data_sharing.port - image = "${local.ecr_urls[var.service_configs.data_sharing.name]}:latest" + image = "${local.ecr_urls[var.service_configs.data_sharing.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.data_sharing.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-entity-verification.tf b/terragrunt/modules/ecs/service-entity-verification.tf index ee72aabb1..66c2ea682 100644 --- a/terragrunt/modules/ecs/service-entity-verification.tf +++ b/terragrunt/modules/ecs/service-entity-verification.tf @@ -8,7 +8,7 @@ module "ecs_service_entity_verification" { cpu = var.service_configs.entity_verification.cpu environment = local.service_environment host_port = var.service_configs.entity_verification.port - image = "${local.ecr_urls[var.service_configs.entity_verification.name]}:latest" + image = "${local.ecr_urls[var.service_configs.entity_verification.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.entity_verification.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-forms.tf b/terragrunt/modules/ecs/service-forms.tf index 1a6dcdad1..3ca19c714 100644 --- a/terragrunt/modules/ecs/service-forms.tf +++ b/terragrunt/modules/ecs/service-forms.tf @@ -8,7 +8,7 @@ module "ecs_service_forms" { cpu = var.service_configs.forms.cpu environment = local.service_environment host_port = var.service_configs.forms.port - image = "${local.ecr_urls[var.service_configs.forms.name]}:latest" + image = "${local.ecr_urls[var.service_configs.forms.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.forms.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-organisation-app.tf b/terragrunt/modules/ecs/service-organisation-app.tf index 33fef699f..285ab8d48 100644 --- a/terragrunt/modules/ecs/service-organisation-app.tf +++ b/terragrunt/modules/ecs/service-organisation-app.tf @@ -8,7 +8,7 @@ module "ecs_service_organisation_app" { cpu = var.service_configs.organisation_app.cpu environment = local.service_environment host_port = var.service_configs.organisation_app.port - image = "${local.ecr_urls[var.service_configs.organisation_app.name]}:latest" + image = "${local.ecr_urls[var.service_configs.organisation_app.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.organisation_app.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-organisation.tf b/terragrunt/modules/ecs/service-organisation.tf index 0bc5eb6dd..f93a7faf2 100644 --- a/terragrunt/modules/ecs/service-organisation.tf +++ b/terragrunt/modules/ecs/service-organisation.tf @@ -9,7 +9,7 @@ module "ecs_service_organisation" { conn_string_location = var.db_connection_secret_arn environment = local.service_environment host_port = var.service_configs.organisation.port - image = "${local.ecr_urls[var.service_configs.organisation.name]}:latest" + image = "${local.ecr_urls[var.service_configs.organisation.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.organisation.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-person.tf b/terragrunt/modules/ecs/service-person.tf index 6ce33ac8a..9112f4a76 100644 --- a/terragrunt/modules/ecs/service-person.tf +++ b/terragrunt/modules/ecs/service-person.tf @@ -9,7 +9,7 @@ module "ecs_service_person" { conn_string_location = var.db_connection_secret_arn environment = local.service_environment host_port = var.service_configs.person.port - image = "${local.ecr_urls[var.service_configs.person.name]}:latest" + image = "${local.ecr_urls[var.service_configs.person.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.person.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/service-tenant.tf b/terragrunt/modules/ecs/service-tenant.tf index 40b480756..8ad85e1e8 100644 --- a/terragrunt/modules/ecs/service-tenant.tf +++ b/terragrunt/modules/ecs/service-tenant.tf @@ -9,7 +9,7 @@ module "ecs_service_tenant" { conn_string_location = var.db_connection_secret_arn environment = local.service_environment host_port = var.service_configs.tenant.port - image = "${local.ecr_urls[var.service_configs.tenant.name]}:latest" + image = "${local.ecr_urls[var.service_configs.tenant.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.tenant.name].name lg_prefix = "app" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/task-organisation-information-migrations.tf b/terragrunt/modules/ecs/task-organisation-information-migrations.tf index b87719041..65b29c1cd 100644 --- a/terragrunt/modules/ecs/task-organisation-information-migrations.tf +++ b/terragrunt/modules/ecs/task-organisation-information-migrations.tf @@ -7,7 +7,7 @@ module "ecs_service_organisation_information_migrations" { cpu = var.service_configs.organisation_information_migrations.cpu conn_string_location = var.db_connection_secret_arn environment = local.service_environment - image = "${local.ecr_urls[var.service_configs.organisation_information_migrations.name]}:latest" + image = "${local.ecr_urls[var.service_configs.organisation_information_migrations.name]}:${local.orchestrator_service_version}" lg_name = aws_cloudwatch_log_group.tasks[var.service_configs.organisation_information_migrations.name].name lg_prefix = "db" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/ecs/variables.tf b/terragrunt/modules/ecs/variables.tf index 2fbbcdb18..d5457d95e 100644 --- a/terragrunt/modules/ecs/variables.tf +++ b/terragrunt/modules/ecs/variables.tf @@ -1,3 +1,8 @@ +variable "account_ids" { + description = "Map of all accounts and their IDs" + type = map(string) +} + variable "alb_sg_id" { description = "Application load-balancer security group ID" type = string @@ -118,13 +123,13 @@ variable "tags" { type = map(string) } -variable "vpc_id" { - description = "The ID of the VPC" +variable "vpc_cider" { + description = "VPC's IPv4 CIDR" type = string } -variable "vpc_cider" { - description = "VPC's IPv4 CIDR" +variable "vpc_id" { + description = "The ID of the VPC" type = string } diff --git a/terragrunt/modules/orchestrator/ecr/cloudwatch.tf b/terragrunt/modules/orchestrator/ecr/cloudwatch.tf new file mode 100644 index 000000000..2fd161b21 --- /dev/null +++ b/terragrunt/modules/orchestrator/ecr/cloudwatch.tf @@ -0,0 +1,28 @@ +resource "aws_cloudwatch_event_rule" "ecr_push" { + for_each = aws_ecr_repository.this + + name = "${local.name_prefix}-ecr-push-to-${each.value.name}" + description = "CloudWatch Event rule to detect ECR push events to ${each.value.name}" + + event_pattern = jsonencode( + { + "source" : ["aws.ecr"], + "detail-type" : ["ECR Image Action"], + "detail" : { + "action-type" : ["PUSH"], + "image-tag" : ["latest"], + "repository-name" : [each.value.name] + "result" : ["SUCCESS"], + } + } + ) + + tags = var.tags +} + +# resource "aws_cloudwatch_event_target" "trigger_service_deployment" { +# for_each = aws_cloudwatch_event_rule.ecr_push +# rule = each.value.name +# arn = each.key == "organisation-information-migrations" ? aws_sfn_state_machine.ecs_run_migration.arn : aws_sfn_state_machine.ecs_force_deploy[each.key].arn +# role_arn = var.role_cloudwatch_events_arn +# } diff --git a/terragrunt/modules/orchestrator/ecr/datasource.tf b/terragrunt/modules/orchestrator/ecr/datasource.tf new file mode 100644 index 000000000..253663ee4 --- /dev/null +++ b/terragrunt/modules/orchestrator/ecr/datasource.tf @@ -0,0 +1,87 @@ +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + +data "aws_iam_policy_document" "ecr_repo_policy_document" { + statement { + sid = "ECRRead" + + principals { + type = "AWS" + identifiers = local.read_principals + } + actions = [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetAuthorizationToken", + "ecr:GetDownloadUrlForLayer", + "ecr:GetRepositoryPolicy", + "ecr:ListImages", + ] + } + + statement { + sid = "ECRPull" + effect = "Allow" + principals { + type = "AWS" + identifiers = local.read_principals + } + + actions = [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:DescribeImages", + "ecr:DescribeRepositories", + "ecr:GetAuthorizationToken", + "ecr:GetDownloadUrlForLayer", + "ecr:GetRepositoryPolicy", + "ecr:ListImages", + ] + } + + statement { + sid = "ECRWrite" + + effect = "Allow" + + actions = [ + "ecr:GetAuthorizationToken", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:DescribeRepositories", + "ecr:ListImages", + "ecr:DescribeImages", + ] + + principals { + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + type = "AWS" + } + } +} + +data "aws_iam_policy_document" "orchestrator_read_service_version_assume_role" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "AWS" + identifiers = [for name, id in var.account_ids : "arn:aws:iam::${id}:role/cdp-sirsi-${name}-terraform"] + } + } +} + + +data "aws_iam_policy_document" "orchestrator_read_service_version" { + statement { + actions = ["ssm:GetParameter"] + resources = ["arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/cdp-sirsi-service-version"] + } +} \ No newline at end of file diff --git a/terragrunt/modules/orchestrator/ecr/iam.tf b/terragrunt/modules/orchestrator/ecr/iam.tf new file mode 100644 index 000000000..24ef6d27e --- /dev/null +++ b/terragrunt/modules/orchestrator/ecr/iam.tf @@ -0,0 +1,23 @@ +resource "aws_ecr_repository_policy" "policy" { + for_each = toset(local.repositories) + + policy = data.aws_iam_policy_document.ecr_repo_policy_document.json + repository = aws_ecr_repository.this[each.key].name +} + +resource "aws_iam_role" "orchestrator_read_service_version" { + assume_role_policy = data.aws_iam_policy_document.orchestrator_read_service_version_assume_role.json + name = "${local.name_prefix}-orchestrator-read-service-version" + tags = var.tags +} + +resource "aws_iam_policy" "orchestrator_read_service_version" { + description = "Policy to allow reading SSM parameters in the orchestrator account" + name = "${local.name_prefix}-orchestrator-read-service-version" + policy = data.aws_iam_policy_document.orchestrator_read_service_version.json +} + +resource "aws_iam_role_policy_attachment" "orchestrator_read_service_version" { + policy_arn = aws_iam_policy.orchestrator_read_service_version.arn + role = aws_iam_role.orchestrator_read_service_version.name +} diff --git a/terragrunt/modules/orchestrator/ecr/locals.tf b/terragrunt/modules/orchestrator/ecr/locals.tf new file mode 100644 index 000000000..598841512 --- /dev/null +++ b/terragrunt/modules/orchestrator/ecr/locals.tf @@ -0,0 +1,14 @@ +locals { + + name_prefix = var.product.resource_name + + read_principals = [for name, id in var.account_ids : "arn:aws:iam::${id}:root"] + + repositories = concat( + [ + for name, config in var.service_configs : + config.name + ], + ["grafana", "codebuild"]) + +} diff --git a/terragrunt/modules/ecs/ecr.tf b/terragrunt/modules/orchestrator/ecr/main.tf similarity index 55% rename from terragrunt/modules/ecs/ecr.tf rename to terragrunt/modules/orchestrator/ecr/main.tf index 0080b476d..ec37eabaa 100644 --- a/terragrunt/modules/ecs/ecr.tf +++ b/terragrunt/modules/orchestrator/ecr/main.tf @@ -1,8 +1,8 @@ resource "aws_ecr_repository" "this" { - for_each = toset(local.tasks) + for_each = toset(local.repositories) name = "cdp-${each.value}" - image_tag_mutability = "MUTABLE" + image_tag_mutability = contains(["grafana", "codebuild"], each.value) ? "MUTABLE" : "IMMUTABLE" image_scanning_configuration { scan_on_push = true diff --git a/terragrunt/modules/orchestrator/ecr/variables.tf b/terragrunt/modules/orchestrator/ecr/variables.tf new file mode 100644 index 000000000..eb676dbf8 --- /dev/null +++ b/terragrunt/modules/orchestrator/ecr/variables.tf @@ -0,0 +1,34 @@ +variable "account_ids" { + description = "Map of all accounts and their IDs" + type = map(string) +} + +variable "environment" { + description = "The environment we are provisioning" + type = string +} + +variable "product" { + description = "product's common attributes" + type = object({ + name = string + resource_name = string + public_hosted_zone = string + }) +} + +variable "service_configs" { + description = "Map of services to their ports" + type = map(object({ + cpu = number + memory = number + name = string + port = number + port_host = number + })) +} + +variable "tags" { + description = "Tags to apply to all resources in this module" + type = map(string) +} diff --git a/terragrunt/modules/orchestrator/iam/ci-datasource.tf b/terragrunt/modules/orchestrator/iam/ci-datasource.tf new file mode 120000 index 000000000..c4f324399 --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/ci-datasource.tf @@ -0,0 +1 @@ +../../core-iam/ci-datasource.tf \ No newline at end of file diff --git a/terragrunt/modules/orchestrator/iam/ci.tf b/terragrunt/modules/orchestrator/iam/ci.tf new file mode 120000 index 000000000..ac040b6c8 --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/ci.tf @@ -0,0 +1 @@ +../../core-iam/ci.tf \ No newline at end of file diff --git a/terragrunt/modules/orchestrator/iam/datasource.tf b/terragrunt/modules/orchestrator/iam/datasource.tf new file mode 100644 index 000000000..fbf8c87e2 --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/datasource.tf @@ -0,0 +1,45 @@ +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + +data "aws_iam_policy_document" "ecr_push_policy" { + statement { + actions = [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:CompleteLayerUpload", + "ecr:GetDownloadUrlForLayer", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart", + ] + resources = [ + "arn:aws:ecr:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:repository/cdp-*" + ] + effect = "Allow" + } + + statement { + actions = [ + "ecr:GetAuthorizationToken", + ] + resources = [ + "*" + ] + effect = "Allow" + } +} + +data "aws_iam_policy_document" "ssm_update_policy" { + statement { + actions = [ + "ssm:PutParameter", + "ssm:GetParameter", + "ssm:DeleteParameter" + ] + resources = ["*"] # @TODO: (ABN) Limit me + effect = "Allow" + } +} + + diff --git a/terragrunt/modules/orchestrator/iam/github.tf b/terragrunt/modules/orchestrator/iam/github.tf new file mode 100644 index 000000000..e153db123 --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/github.tf @@ -0,0 +1,20 @@ +resource "aws_iam_user" "github_user" { + name = "${local.name_prefix}-github-user" + tags = var.tags +} + +resource "aws_iam_user_policy" "ecr_push_policy" { + name = "ECRPushPolicy" + user = aws_iam_user.github_user.name + policy = data.aws_iam_policy_document.ecr_push_policy.json +} + +resource "aws_iam_user_policy" "ssm_update_policy" { + name = "SSMUpdatePolicy" + user = aws_iam_user.github_user.name + policy = data.aws_iam_policy_document.ssm_update_policy.json +} + +resource "aws_iam_access_key" "github_user_access_key" { + user = aws_iam_user.github_user.name +} diff --git a/terragrunt/modules/orchestrator/iam/locals.tf b/terragrunt/modules/orchestrator/iam/locals.tf new file mode 120000 index 000000000..fcbbbebd4 --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/locals.tf @@ -0,0 +1 @@ +../../core-iam/locals.tf \ No newline at end of file diff --git a/terragrunt/modules/orchestrator/iam/outputs.tf b/terragrunt/modules/orchestrator/iam/outputs.tf new file mode 100644 index 000000000..aca1cc88f --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/outputs.tf @@ -0,0 +1,12 @@ +output "github_user_access_key_id" { + value = aws_iam_access_key.github_user_access_key.id +} + +output "github_user_secret_access_key" { + value = aws_iam_access_key.github_user_access_key.secret + sensitive = true +} + +output "terraform_role_arn" { + value = aws_iam_role.terraform.arn +} diff --git a/terragrunt/modules/orchestrator/iam/variables.tf b/terragrunt/modules/orchestrator/iam/variables.tf new file mode 100644 index 000000000..44f0a81f2 --- /dev/null +++ b/terragrunt/modules/orchestrator/iam/variables.tf @@ -0,0 +1,28 @@ +variable "account_ids" { + description = "Map of all accounts and their IDs" + type = map(string) +} + +variable "environment" { + description = "The environment we are provisioning" + type = string +} + +variable "product" { + description = "product's common attributes" + type = object({ + name = string + resource_name = string + public_hosted_zone = string + }) +} + +variable "tags" { + description = "Tags to apply to all resources in this module" + type = map(string) +} + +variable "terraform_operators" { + description = "List of IAM user ARNs allowed to assume terraform roles" + type = list(string) +} diff --git a/terragrunt/modules/telemetry/ecr.tf b/terragrunt/modules/telemetry/ecr.tf deleted file mode 100644 index 6e461e842..000000000 --- a/terragrunt/modules/telemetry/ecr.tf +++ /dev/null @@ -1,10 +0,0 @@ -resource "aws_ecr_repository" "grafana" { - name = "cdp-${var.grafana_config.name}" - image_tag_mutability = "MUTABLE" - - image_scanning_configuration { - scan_on_push = true - } - - tags = var.tags -} diff --git a/terragrunt/modules/telemetry/locals.tf b/terragrunt/modules/telemetry/locals.tf index 990be0047..a51481890 100644 --- a/terragrunt/modules/telemetry/locals.tf +++ b/terragrunt/modules/telemetry/locals.tf @@ -1,6 +1,8 @@ locals { name_prefix = var.product.resource_name + orchestrator_account_id = var.account_ids["orchestrator"] + service_widgets = [ for idx, service in values(var.service_configs) : [ { diff --git a/terragrunt/modules/telemetry/service-grafana.tf b/terragrunt/modules/telemetry/service-grafana.tf index 632c5e710..a9a1dd397 100644 --- a/terragrunt/modules/telemetry/service-grafana.tf +++ b/terragrunt/modules/telemetry/service-grafana.tf @@ -10,7 +10,7 @@ module "ecs_service_grafana" { gf_admin_password = "${aws_secretsmanager_secret.grafana_credentials.arn}:ADMIN_PASSWORD::" gf_admin_user = "${aws_secretsmanager_secret.grafana_credentials.arn}:ADMIN_USERNAME::" host_port = var.grafana_config.port - image = "${aws_ecr_repository.grafana.repository_url}:latest" + image = "${local.orchestrator_account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/cdp-grafana:latest" lg_name = aws_cloudwatch_log_group.grafana.name lg_prefix = "telemetry" lg_region = data.aws_region.current.name diff --git a/terragrunt/modules/telemetry/variables.tf b/terragrunt/modules/telemetry/variables.tf index 6d434ca74..7ce5bd9f9 100644 --- a/terragrunt/modules/telemetry/variables.tf +++ b/terragrunt/modules/telemetry/variables.tf @@ -1,3 +1,8 @@ +variable "account_ids" { + description = "Map of all accounts and their IDs" + type = map(string) +} + variable "ecs_alb_sg_id" { description = "Application load-balancer security group ID" type = string diff --git a/terragrunt/providers.tf b/terragrunt/providers.tf index c79044814..7cb48aa7b 100644 --- a/terragrunt/providers.tf +++ b/terragrunt/providers.tf @@ -1,5 +1,5 @@ terraform { - required_version = "= 1.8.2" + required_version = "= 1.9.2" required_providers { aws = { version = "~> 5.48.0" diff --git a/terragrunt/tools/codebuild/Dockerfile b/terragrunt/tools/codebuild/Dockerfile new file mode 100644 index 000000000..b54cfdedc --- /dev/null +++ b/terragrunt/tools/codebuild/Dockerfile @@ -0,0 +1,34 @@ +FROM amazonlinux:latest + +# Build arguments for Terraform and Terragrunt versions +ARG TERRAFORM_VERSION +ARG TERRAGRUNT_VERSION + +# Install yum-config-manager to manage your repositories +RUN yum install -y yum-utils + +# Use yum-config-manager to add the official HashiCorp Linux repository +RUN yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo + +# Install Terraform +RUN yum install -y terraform-${TERRAFORM_VERSION} + +# Install necessary tools +RUN yum install -y wget unzip + +# Download and install Terragrunt +RUN wget https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64 +RUN mv terragrunt_linux_amd64 /usr/local/bin/terragrunt +RUN chmod +x /usr/local/bin/terragrunt + +# Verify Terraform and Terragrunt are installed +RUN terraform --version +RUN terragrunt --version + +# Install AWS CLI +RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +RUN unzip awscliv2.zip +RUN ./aws/install + +# Verify AWS CLI is installed +RUN aws --version diff --git a/terragrunt/tools/codebuild/README.md b/terragrunt/tools/codebuild/README.md new file mode 100644 index 000000000..303cfbab1 --- /dev/null +++ b/terragrunt/tools/codebuild/README.md @@ -0,0 +1,38 @@ +# Coldbuild + +This configuration is to build custom CodeBuild image to be used by CodeBuild jobs in different accounts. + +> In the following examples ave is alias for `aws-vault exec` command. +Feel free to use any convenient AWS profiler instead. + +## build + +```shell +TERRAFORM_VERSION=$(grep -Po '(?<=terraform = ")[^"]*' ../../../.mise.toml) +TERRAGRUNT_VERSION=$(grep -Po '(?<=terragrunt = ")[^"]*' ../../../.mise.toml) + +if [ -z "$TERRAFORM_VERSION" ] || [ -z "$TERRAGRUNT_VERSION" ]; then + echo "Error: Could not find Terraform or Terragrunt versions in .mise.toml" + exit 1 +fi + +docker build --build-arg TERRAFORM_VERSION=$TERRAFORM_VERSION\ + --build-arg TERRAGRUNT_VERSION=$TERRAGRUNT_VERSION \ + -t cabinetoffice/cdp-codebuild . + +echo -e "Built completed, and the new image contains:\n $(docker run --rm cabinetoffice/cdp-codebuild terraform version)\n $(docker run --rm cabinetoffice/cdp-codebuild terragrunt --version)\n$(docker run --rm cabinetoffice/cdp-codebuild aws --version)" + +``` + +## Deploy + +### Push to ECR + +There are individual ECR repositories in each account. Using the following commands, we can push the built image to different accounts. + +```shell +ACCOUNT_ID=$(ave aws sts get-caller-identity | jq -r '.Account') +ave aws ecr get-login-password --region eu-west-2 | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com +docker tag cabinetoffice/cdp-codebuild:latest ${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/cdp-codebuild:latest +docker push ${ACCOUNT_ID}.dkr.ecr.eu-west-2.amazonaws.com/cdp-codebuild:latest +``` diff --git a/terragrunt/tools/scripts/delete_tf_cache.sh b/terragrunt/tools/scripts/delete_tf_cache.sh new file mode 100755 index 000000000..b3c71d845 --- /dev/null +++ b/terragrunt/tools/scripts/delete_tf_cache.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +root_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )" + +echo "🔎 Clearing Terraform and Terragrunt cache files..." +find "${root_dir}" -type d -name '.terragrunt-cache' -prune -exec echo "␡ Removing {}" \; -exec rm -rf '{}' + +find "${root_dir}" -type d -name '.terraform' -prune -exec echo "␡ Removing {}" \; -exec rm -rf '{}' + +find "${root_dir}" -type d -name '.terraform.d' -prune -exec echo "␡ Removing {}" \; -exec rm -rf '{}' + +find "${root_dir}" -type f -name '.terraform.lock.hcl' -delete +find "${root_dir}" -type f -name 'temp_providers.tf' -delete +echo "😎 Done." diff --git a/terragrunt/tools/order.py b/terragrunt/tools/scripts/order.py similarity index 100% rename from terragrunt/tools/order.py rename to terragrunt/tools/scripts/order.py