diff --git a/ci/pipeline.yml b/ci/pipeline.yml index dc24f0692..a8c1bb4d0 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -126,7 +126,7 @@ jobs: trigger: true - task: terraform-plan-external-development file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &external-development-params TERRAFORM_ACTION: plan STACK_NAME: external-development @@ -163,7 +163,7 @@ jobs: passed: [plan-external-development] - task: terraform-apply-external-development file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *external-development-params TERRAFORM_ACTION: apply @@ -185,7 +185,7 @@ jobs: trigger: true - task: terraform-plan-external-staging file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &external-staging-params TERRAFORM_ACTION: plan STACK_NAME: external-staging @@ -222,7 +222,7 @@ jobs: passed: [plan-external-staging] - task: terraform-apply-external-staging file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *external-staging-params TERRAFORM_ACTION: apply @@ -244,7 +244,7 @@ jobs: trigger: true - task: terraform-plan-external-production file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &external-production-params TERRAFORM_ACTION: plan STACK_NAME: external-production @@ -272,7 +272,7 @@ jobs: passed: [plan-external-production] - task: terraform-apply-external-production file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *external-production-params TERRAFORM_ACTION: apply @@ -294,7 +294,7 @@ jobs: trigger: true - task: plan-dns file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &dns-params TERRAFORM_ACTION: plan STACK_NAME: dns @@ -319,7 +319,7 @@ jobs: passed: [plan-dns] - task: terraform-apply-dns file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *dns-params TERRAFORM_ACTION: apply @@ -335,7 +335,7 @@ jobs: - task: terraform-plan-tooling tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &tooling-params TERRAFORM_ACTION: plan STACK_NAME: tooling @@ -412,7 +412,7 @@ jobs: - task: terraform-apply-tooling tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *tooling-params TERRAFORM_ACTION: apply @@ -470,7 +470,7 @@ jobs: - task: terraform-plan-development tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &development-params TERRAFORM_ACTION: plan STACK_NAME: development @@ -537,7 +537,7 @@ jobs: TF_VAR_cg_egress_ip_set_arn: ((cg_egress_ip_set_arn)) TF_VAR_cidr_blocks: ((cidr_blocks)) TF_VAR_domains_lbgroup_count: 2 - TF_VAR_waf_regex_rules: '((development_waf_regex_rules))' + TF_VAR_waf_regex_rules: "((development_waf_regex_rules))" TF_VAR_aws_lb_listener_ssl_policy: "ELBSecurityPolicy-TLS13-1-2-FIPS-2023-04" - *notify-slack @@ -552,7 +552,7 @@ jobs: - task: terraform-apply-development tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *development-params TERRAFORM_ACTION: apply @@ -646,7 +646,7 @@ jobs: - task: terraform-plan-staging tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &staging-params TERRAFORM_ACTION: plan STACK_NAME: staging @@ -711,7 +711,7 @@ jobs: TF_VAR_cg_egress_ip_set_arn: ((cg_egress_ip_set_arn)) TF_VAR_cidr_blocks: ((cidr_blocks)) TF_VAR_domains_lbgroup_count: 3 - TF_VAR_waf_regex_rules: '((staging_waf_regex_rules))' + TF_VAR_waf_regex_rules: "((staging_waf_regex_rules))" TF_VAR_aws_lb_listener_ssl_policy: "ELBSecurityPolicy-TLS13-1-2-FIPS-2023-04" - *notify-slack @@ -725,7 +725,7 @@ jobs: - task: terraform-apply-staging tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *staging-params TERRAFORM_ACTION: apply @@ -818,7 +818,7 @@ jobs: - task: terraform-plan-production tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &production-params TERRAFORM_ACTION: plan STACK_NAME: production @@ -883,7 +883,7 @@ jobs: TF_VAR_cg_egress_ip_set_arn: ((cg_egress_ip_set_arn)) TF_VAR_cidr_blocks: ((cidr_blocks)) TF_VAR_domains_lbgroup_count: 4 - TF_VAR_waf_regex_rules: '((production_waf_regex_rules))' + TF_VAR_waf_regex_rules: "((production_waf_regex_rules))" - *notify-slack - name: apply-production @@ -896,7 +896,7 @@ jobs: - task: terraform-apply-production tags: [iaas] file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *production-params TERRAFORM_ACTION: apply @@ -986,7 +986,7 @@ jobs: trigger: true - task: terraform-plan-ecr file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &tf-ecr TERRAFORM_ACTION: plan TEMPLATE_SUBDIR: terraform/stacks/ecr @@ -1005,7 +1005,7 @@ jobs: passed: [plan-ecr] - task: terraform-apply-ecr file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *tf-ecr TERRAFORM_ACTION: apply @@ -1020,7 +1020,7 @@ jobs: trigger: true - task: terraform-plan-concourse-staging file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &tf-concourse-staging TERRAFORM_ACTION: plan TEMPLATE_SUBDIR: terraform/stacks/concourse @@ -1043,7 +1043,7 @@ jobs: passed: [plan-concourse-staging] - task: terraform-apply-concourse-staging file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *tf-concourse-staging TERRAFORM_ACTION: apply @@ -1058,7 +1058,7 @@ jobs: trigger: true - task: terraform-plan-concourse-production file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: &tf-concourse-production TERRAFORM_ACTION: plan TEMPLATE_SUBDIR: terraform/stacks/concourse @@ -1081,7 +1081,7 @@ jobs: passed: [plan-concourse-production] - task: terraform-apply-concourse-production file: pipeline-tasks/terraform-apply.yml - input_mapping: {terraform-templates: cg-provision-repo} + input_mapping: { terraform-templates: cg-provision-repo } params: <<: *tf-concourse-production TERRAFORM_ACTION: apply @@ -1831,4 +1831,4 @@ resource_types: aws_secret_access_key: ((ecr_aws_secret)) repository: concourse-http-jq-resource aws_region: us-gov-west-1 - tag: latest \ No newline at end of file + tag: latest diff --git a/terraform/modules/csb/README.md b/terraform/modules/csb/README.md index 55909c3f5..82ebc82e2 100644 --- a/terraform/modules/csb/README.md +++ b/terraform/modules/csb/README.md @@ -3,3 +3,11 @@ Resources related to the Cloud Service Broker. See also https://github.com/cloud-gov/csb. + +## Why two modules? + +The `iam` module contains the IAM policy that the broker uses to deploy resources on behalf of customers. This is distinct from the IAM policy in the `broker` module used by Cloud Foundry to pull the CSB image. Some brokerpaks must create resources in AWS Commercial, so both a Commercial and a GovCloud IAM user must be created. They are managed in a separate module for the following reasons: + +- To support deploying them to two separate partitions using two providers, without forcing the rest of the broker resources to specify `provider=`, since they will only ever be deployed to GovCloud. +- To keep the policies together in the codebase, since they are related. (We could have split up the CSB resources by GovCloud vs Commercial, but the brokerpak-related policies would no longer all be in one place.) +- To maintain a dedicated space for the policies, which are expected to grow as we add more brokerpaks. diff --git a/terraform/modules/csb/broker/main.tf b/terraform/modules/csb/broker/main.tf index 5895c626b..afc9cf19c 100644 --- a/terraform/modules/csb/broker/main.tf +++ b/terraform/modules/csb/broker/main.tf @@ -17,3 +17,24 @@ module "db" { rds_allow_major_version_upgrade = var.rds_allow_major_version_upgrade rds_apply_immediately = var.rds_apply_immediately } + +data "terraform_remote_state" "ecr" { + backend = "s3" + + config = { + bucket = var.remote_state_bucket + region = var.remote_state_region + key = "${var.ecr_stack_name}/terraform.tfstate" + } +} + +locals { + csb_ecr_repository_arn = data.terraform_remote_state.ecr.outputs.repository_arns["csb"] +} + +// A user with ECR pull permissions so Cloud Foundry can pull the CSB image. +module "ecr_user" { + source = "../../iam_user/ecr_pull_user" + username = "csb-ecr-${var.stack_description}" + repository_arn = local.csb_ecr_repository_arn +} diff --git a/terraform/modules/csb/broker/outputs.tf b/terraform/modules/csb/broker/outputs.tf new file mode 100644 index 000000000..abb99bbde --- /dev/null +++ b/terraform/modules/csb/broker/outputs.tf @@ -0,0 +1,46 @@ +output "ecr_user_username" { + value = module.ecr_user.username +} + +output "ecr_user_access_key_id_curr" { + value = module.ecr_user.access_key_id_curr +} + +output "ecr_user_secret_access_key_curr" { + value = module.ecr_user.secret_access_key_curr + sensitive = true +} + +output "ecr_user_access_key_id_prev" { + value = module.ecr_user.access_key_id_prev +} + +output "ecr_user_secret_access_key_prev" { + value = module.ecr_user.secret_access_key_prev + sensitive = true +} + +output "rds_host" { + value = module.db.rds_host +} + +output "rds_port" { + value = module.db.rds_port +} + +output "rds_url" { + value = module.db.rds_url +} + +output "rds_name" { + value = module.db.rds_name +} + +output "rds_username" { + value = module.db.rds_username +} + +output "rds_password" { + value = module.db.rds_password + sensitive = true +} diff --git a/terraform/modules/csb/broker/variables.tf b/terraform/modules/csb/broker/variables.tf index ab00c8975..71551a4e2 100644 --- a/terraform/modules/csb/broker/variables.tf +++ b/terraform/modules/csb/broker/variables.tf @@ -3,6 +3,19 @@ variable "stack_description" { description = "Like development, staging, or production." } +variable "remote_state_bucket" { + type = string +} + +variable "remote_state_region" { + type = string +} + +variable "ecr_stack_name" { + type = string + description = "The name of the stack that configures ECR." +} + # RDS variables variable "rds_instance_type" { diff --git a/terraform/modules/csb/broker/versions.tf b/terraform/modules/csb/broker/versions.tf index 8e99822cd..9eee0c214 100644 --- a/terraform/modules/csb/broker/versions.tf +++ b/terraform/modules/csb/broker/versions.tf @@ -5,5 +5,9 @@ terraform { source = "hashicorp/aws" version = "< 6.0.0" } + cloudfoundry = { + source = "cloudfoundry-community/cloudfoundry" + version = "< 1.0" + } } } diff --git a/terraform/modules/csb/iam/outputs.tf b/terraform/modules/csb/iam/outputs.tf index 4703e4bab..966607f00 100644 --- a/terraform/modules/csb/iam/outputs.tf +++ b/terraform/modules/csb/iam/outputs.tf @@ -7,7 +7,8 @@ output "access_key_id_prev" { } output "secret_access_key_prev" { - value = "" + value = "" + sensitive = true } output "access_key_id_curr" { diff --git a/terraform/modules/iam_user/ecr_pull_user/outputs.tf b/terraform/modules/iam_user/ecr_pull_user/outputs.tf new file mode 100644 index 000000000..8335ce384 --- /dev/null +++ b/terraform/modules/iam_user/ecr_pull_user/outputs.tf @@ -0,0 +1,21 @@ +output "username" { + value = var.username +} + +output "access_key_id_prev" { + value = "" +} + +output "secret_access_key_prev" { + value = "" + sensitive = true +} + +output "access_key_id_curr" { + value = aws_iam_access_key.iam_access_key_v3.id +} + +output "secret_access_key_curr" { + value = aws_iam_access_key.iam_access_key_v3.secret + sensitive = true +} diff --git a/terraform/modules/iam_user/ecr_pull_user/policy.json b/terraform/modules/iam_user/ecr_pull_user/policy.json new file mode 100644 index 000000000..1fd7a2b7b --- /dev/null +++ b/terraform/modules/iam_user/ecr_pull_user/policy.json @@ -0,0 +1,15 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:GetAuthorizationToken" + ], + "Resource": "${repository_arn}" + } + ] +} diff --git a/terraform/modules/iam_user/ecr_pull_user/user.tf b/terraform/modules/iam_user/ecr_pull_user/user.tf new file mode 100644 index 000000000..0557cb01c --- /dev/null +++ b/terraform/modules/iam_user/ecr_pull_user/user.tf @@ -0,0 +1,20 @@ +data "template_file" "policy" { + template = file("${path.module}/policy.json") + vars = { + "repository_arn" = var.repository_arn + } +} + +resource "aws_iam_user" "iam_user" { + name = var.username +} + +resource "aws_iam_access_key" "iam_access_key_v3" { + user = aws_iam_user.iam_user.name +} + +resource "aws_iam_user_policy" "iam_policy" { + name = "${aws_iam_user.iam_user.name}-policy" + user = aws_iam_user.iam_user.name + policy = data.template_file.policy.rendered +} diff --git a/terraform/modules/iam_user/ecr_pull_user/variables.tf b/terraform/modules/iam_user/ecr_pull_user/variables.tf new file mode 100644 index 000000000..cabfbfe32 --- /dev/null +++ b/terraform/modules/iam_user/ecr_pull_user/variables.tf @@ -0,0 +1,9 @@ +variable "username" { + type = string + description = "The username of the IAM user." +} + +variable "repository_arn" { + type = string + description = "The ARN of the repository in ECR from which the user will pull images." +} diff --git a/terraform/modules/iam_user/ecr_pull_user/versions.tf b/terraform/modules/iam_user/ecr_pull_user/versions.tf new file mode 100644 index 000000000..8e99822cd --- /dev/null +++ b/terraform/modules/iam_user/ecr_pull_user/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 0.15" + required_providers { + aws = { + source = "hashicorp/aws" + version = "< 6.0.0" + } + } +} diff --git a/terraform/stacks/ecr/outputs.tf b/terraform/stacks/ecr/outputs.tf new file mode 100644 index 000000000..8194f10bd --- /dev/null +++ b/terraform/stacks/ecr/outputs.tf @@ -0,0 +1,3 @@ +output "repository_arns" { + value = { for k, v in aws_ecr_repository.repository : k => v.arn } +} diff --git a/terraform/stacks/external/outputs.tf b/terraform/stacks/external/outputs.tf index 531f9c112..b9290a644 100644 --- a/terraform/stacks/external/outputs.tf +++ b/terraform/stacks/external/outputs.tf @@ -140,11 +140,14 @@ output "lets_encrypt_secret_access_key_curr" { sensitive = true } -output "csb_access_key_id_curr" { - value = module.csb_iam.access_key_id_curr -} - -output "csb_secret_access_key_curr" { - sensitive = true - value = module.csb_iam.secret_access_key_curr +output "csb" { + description = "Values required to deploy the Cloud Service Broker." + value = { + broker_user = { + access_key_id_curr = module.csb_iam.access_key_id_curr + secret_access_key_curr = module.csb_iam.secret_access_key_curr + access_key_id_prev = module.csb_iam.access_key_id_prev + secret_access_key_prev = module.csb_iam.secret_access_key_prev + } + } } diff --git a/terraform/stacks/main/outputs.tf b/terraform/stacks/main/outputs.tf index 4a57a35dd..44e9f2c9f 100644 --- a/terraform/stacks/main/outputs.tf +++ b/terraform/stacks/main/outputs.tf @@ -744,3 +744,31 @@ output "tcp_lb_listener_ports" { output "tcp_lb_security_groups" { value = concat(module.cf.tcp_lb_security_groups.*.id, [module.stack.bosh_security_group]) } + + +output "csb" { + description = "Values required to deploy the Cloud Service Broker." + value = { + ecr_user = { + username = module.csb_broker[0].ecr_user_username + access_key_id_curr = module.csb_broker[0].ecr_user_access_key_id_curr + secret_access_key_curr = module.csb_broker[0].ecr_user_secret_access_key_curr + access_key_id_prev = module.csb_broker[0].ecr_user_access_key_id_prev + secret_access_key_prev = module.csb_broker[0].ecr_user_secret_access_key_prev + } + rds = { + host = module.csb_broker[0].rds_host + port = module.csb_broker[0].rds_port + url = module.csb_broker[0].rds_url + name = module.csb_broker[0].rds_name + username = module.csb_broker[0].rds_username + password = module.csb_broker[0].rds_password + } + broker_user = { + access_key_id_curr = module.csb_iam.access_key_id_curr + secret_access_key_curr = module.csb_iam.secret_access_key_curr + access_key_id_prev = module.csb_iam.access_key_id_prev + secret_access_key_prev = module.csb_iam.secret_access_key_prev + } + } +} diff --git a/terraform/stacks/main/stack.tf b/terraform/stacks/main/stack.tf index 7390424d2..a8cf0c5a4 100644 --- a/terraform/stacks/main/stack.tf +++ b/terraform/stacks/main/stack.tf @@ -469,7 +469,9 @@ resource "random_password" "csb_rds_password" { module "csb_broker" { count = var.stack_description == "development" ? 1 : 0 - source = "../../modules/csb/broker" + source = "../../modules/csb/broker" + remote_state_bucket = var.remote_state_bucket + remote_state_region = var.aws_default_region rds_password = random_password.csb_rds_password.result rds_subnet_group = module.stack.rds_subnet_group @@ -480,4 +482,5 @@ module "csb_broker" { rds_instance_type = var.csb_rds_instance_type stack_description = var.stack_description + ecr_stack_name = var.ecr_stack_name } diff --git a/terraform/stacks/main/variables.tf b/terraform/stacks/main/variables.tf index b5f9e95ce..90d258085 100644 --- a/terraform/stacks/main/variables.tf +++ b/terraform/stacks/main/variables.tf @@ -289,3 +289,9 @@ variable "csb_rds_instance_type" { type = string default = "db.t3.small" } + +variable "ecr_stack_name" { + type = string + description = "The name of the stack that configures ECR." + default = "ecr" +}