diff --git a/.github/workflows/tf-config.yml b/.github/workflows/tf-config.yml new file mode 100644 index 00000000..85c18e14 --- /dev/null +++ b/.github/workflows/tf-config.yml @@ -0,0 +1,64 @@ +name: tf-config +run-name: tf-config ${{ (inputs.apply || (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'schedule') && 'apply' || 'plan' }} + +on: + push: + paths: + - .github/workflows/tf-config.yml + - terraform/services/config/** + schedule: + - cron: "12 14 * * 1-5" + workflow_dispatch: + inputs: + apply: + required: false + type: boolean + description: "Apply the terraform?" + +env: + TENV_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +defaults: + run: + working-directory: ./terraform/services/config + +jobs: + check-fmt: + runs-on: codebuild-cdap-${{github.run_id}}-${{github.run_attempt}} + steps: + - uses: actions/checkout@v4 + - uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2 + - uses: cmsgov/cdap/actions/setup-tenv@8343fb96563ce4b74c4dececee9b268f42bd4a40 + - run: tofu fmt -check -diff -recursive . + + plan-apply: + needs: check-fmt + permissions: + contents: read + id-token: write + runs-on: codebuild-cdap-${{github.run_id}}-${{github.run_attempt}} + strategy: + fail-fast: false + matrix: + app: [cdap] + env: [test, prod] + include: + - app: cdap + env: mgmt + steps: + - uses: actions/checkout@v4 + - uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2 + - uses: cmsgov/cdap/actions/setup-tenv@8343fb96563ce4b74c4dececee9b268f42bd4a40 + with: + role-to-assume: arn:aws:iam::${{ contains(fromJSON('["dev", "test"]'), matrix.env) && secrets.NON_PROD_ACCOUNT || secrets.PROD_ACCOUNT }}:role/delegatedadmin/developer/${{ matrix.app }}-${{ matrix.env }}-github-actions + aws-region: ${{ vars.AWS_REGION }} + - uses: nhedger/setup-sops@v2 + with: + version: "latest" + - run: tofu init -backend-config=../../backends/${{ matrix.app }}-${{ matrix.env }}.s3.tfbackend + - run: tofu plan -out=tf.plan + env: + TF_VAR_app: ${{ matrix.app }} + TF_VAR_env: ${{ matrix.env }} + - if: inputs.apply || (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'schedule' + run: tofu apply -auto-approve tf.plan \ No newline at end of file diff --git a/terraform/modules/platform/main.tf b/terraform/modules/platform/main.tf index 2a8432b2..acb03df7 100644 --- a/terraform/modules/platform/main.tf +++ b/terraform/modules/platform/main.tf @@ -1,7 +1,7 @@ locals { app = var.app env = var.env - established_envs = ["test", "dev", "sandbox", "prod"] + established_envs = ["test", "dev", "sandbox", "prod", "mgmt"] root_module = var.root_module parent_env = one([for x in local.established_envs : x if can(regex("${x}$$", local.env))]) sdlc_env = contains(["sandbox", "prod"], local.parent_env) ? "production" : "non-production" @@ -22,6 +22,7 @@ locals { "test" = "bucket-access-logs-20250409172631068600000001" "sandbox" = "bucket-access-logs-20250411172631068600000001" "prod" = "bucket-access-logs-20250411172631068600000001" + "mgmt" = "bucket-access-logs-20250411172631068600000001" } aws_iam_role_names = [ diff --git a/terraform/modules/platform/variables.tf b/terraform/modules/platform/variables.tf index c1acd7f2..41088478 100644 --- a/terraform/modules/platform/variables.tf +++ b/terraform/modules/platform/variables.tf @@ -2,8 +2,8 @@ variable "app" { description = "The short name for the delivery team or ADO." type = string validation { - condition = contains(["ab2d", "bcda", "dpc"], var.app) - error_message = "Invalid short var.app (application). Must be one of ab2d, bcda, or dpc." + condition = contains(["ab2d", "bcda", "dpc", "cdap"], var.app) + error_message = "Invalid short var.app (application). Must be one of ab2d, bcda, dpc or cdap." } } @@ -11,8 +11,8 @@ variable "env" { description = "The solution's environment name." type = string validation { - condition = one([for x in ["test", "dev", "sandbox", "prod"] : x if can(regex("^${x}$$|^([a-z0-9]+[a-z0-9-])+([^--])-${x}$$", var.env))]) != null - error_message = "Invalid environment/workspace name. Must end in one of test, dev, sandbox, or prod." + condition = one([for x in ["test", "dev", "sandbox", "prod", "mgmt"] : x if can(regex("^${x}$$|^([a-z0-9]+[a-z0-9-])+([^--])-${x}$$", var.env))]) != null + error_message = "Invalid environment/workspace name. Must end in one of test, dev, sandbox, prod or mgmt." } } diff --git a/terraform/modules/sops/main.tf b/terraform/modules/sops/main.tf index b21afcb8..6876ee6c 100644 --- a/terraform/modules/sops/main.tf +++ b/terraform/modules/sops/main.tf @@ -3,7 +3,7 @@ locals { is_ephemeral_env = var.platform.is_ephemeral_env env = var.platform.env parent_env = var.platform.parent_env - env_key_arn = var.platform.kms_alias_primary.id + env_key_arn = var.platform.kms_alias_primary.target_key_arn # Local Variables with Input Variable Overrides sopsw_values_dir = coalesce(var.sopsw_values_dir, "${path.root}/values") diff --git a/terraform/modules/sops/variables.tf b/terraform/modules/sops/variables.tf index 78604425..6cecbace 100644 --- a/terraform/modules/sops/variables.tf +++ b/terraform/modules/sops/variables.tf @@ -1,6 +1,14 @@ variable "platform" { description = "Object that describes standardized platform values." - type = any + type = object({ + app = string, + parent_env = string,env = string, + kms_alias_primary = object({ + target_key_arn = string, + }), + service = string, + is_ephemeral_env = string + }) } variable "sopsw_values_file_extension" { diff --git a/terraform/services/config/README.md b/terraform/services/config/README.md new file mode 100644 index 00000000..6a29fd18 --- /dev/null +++ b/terraform/services/config/README.md @@ -0,0 +1,63 @@ +# CDAP Config Root Module + +This root module is responsible for configuring the sops-enabled strategy for storing sensitive and nonsensitive configuration in AWS SSM Parameter Store. +The _parent environment_ specific configuration values are located in the `values` directory. + +Usage: +```hcl +# declare the `db` module, defining the desired input variables +module "db" { + source = "github.com/CMSgov/cdap//terraform/modules/aurora" + + backup_retention_period = module.platform.is_ephemeral_env ? 1 : 7 + deletion_protection = !module.platform.is_ephemeral_env + password = module.platform.ssm.core.database_password.value + username = module.platform.ssm.core.database_user.value + platform = module.platform + +} + +# use the `db` module's output to write parameter to SSM parameter store: +resource "aws_ssm_parameter" "writer_endpoint" { + name = "/cdap/writer_endpoint" + value = "${module.db.aurora_cluster.endpoint}:${module.db.aurora_cluster.port}" +type = "String" +} +``` + + + +## Requirements + +| Name | Version | +|------|---------| +| [aws](#requirement\_aws) | ~> 5 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [platform](#module\_platform) | github.com/CMSgov/cdap//terraform/modules/platform | ff2ef539fb06f2c98f0e3ce0c8f922bdacb96d66 | +| [sops](#module\_sops) | github.com/CMSgov/cdap//terraform/modules/sops | ff2ef539fb06f2c98f0e3ce0c8f922bdacb96d66 | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [region](#input\_region) | n/a | `string` | `"us-east-1"` | no | +| [secondary\_region](#input\_secondary\_region) | n/a | `string` | `"us-west-2"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [edit](#output\_edit) | n/a | + diff --git a/terraform/services/config/main.tf b/terraform/services/config/main.tf new file mode 100644 index 00000000..68d4cbb2 --- /dev/null +++ b/terraform/services/config/main.tf @@ -0,0 +1,32 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5" + } + } +} + +module "platform" { + source = "github.com/CMSgov/cdap//terraform/modules/platform?ref=plt-1358_sops" + providers = { aws = aws, aws.secondary = aws.secondary } + + app = local.app + env = var.env + root_module = "https://github.com/CMSgov/cdap/tree/terraform/services/config" + service = local.service +} + +locals { + default_tags = module.platform.default_tags + service = "config" +} + +module "sops" { + source = "github.com/CMSgov/cdap//terraform/modules/sops?ref=ff2ef539fb06f2c98f0e3ce0c8f922bdacb96d66" + platform = module.platform +} + +output "edit" { + value = module.sops.sopsw +} diff --git a/terraform/services/config/tofu.tf b/terraform/services/config/tofu.tf new file mode 100644 index 00000000..998dde33 --- /dev/null +++ b/terraform/services/config/tofu.tf @@ -0,0 +1,37 @@ +locals { + app = "cdap" +} + +variable "region" { + default = "us-east-1" + nullable = false + type = string +} + +variable "secondary_region" { + default = "us-west-2" + nullable = false + type = string +} + +provider "aws" { + region = var.region + default_tags { + tags = local.default_tags + } +} + +provider "aws" { + alias = "secondary" + + region = var.secondary_region + default_tags { + tags = local.default_tags + } +} + +terraform { + backend "s3" { + key = "config/terraform.tfstate" + } +} diff --git a/terraform/services/config/values/mgmt.sopsw.yaml b/terraform/services/config/values/mgmt.sopsw.yaml new file mode 100644 index 00000000..aa3f4b26 --- /dev/null +++ b/terraform/services/config/values/mgmt.sopsw.yaml @@ -0,0 +1,17 @@ +/cdap/account/security_events_slack_renotify_after_days: 30 +/cdap/account/security_events_slack_severity_list: CRITICAL,HIGH,MEDIUM +/cdap/sensitive/account/security_events_slack_webhook_url: ENC[AES256_GCM,data:z9MLEAlb76u6MZ+GWcWcfnRtax1J677k47tabDmwCqAGN7H2BrmTnkIs1fAhl9dShaL5qZrq78s0sY9b2hCAGIWWaUenVbGGpWBuZvh7rw==,iv:kbgCH76ryIbnU40SWd/Wgg+hULSgGsTO4LLWIPoDE68=,tag:NmwrTB/oVI0WEhL9R5eV2g==,type:str] +/cdap/sensitive/bucket-access-logs-bucket: ENC[AES256_GCM,data:TjwtktvWlh7Gt7JrTuxZganUT3AotzmEAeYKkpn5GutLcIT1/KSpTV1kjMxV,iv:fSMld0pXjqKabcq+8CK7kG018tspwVAS30ngYFepJKw=,tag:dMsUtxHVSpiHb9U+ebUbNg==,type:str] +/cdap/sensitive/mgmt-vpc/cidr: ENC[AES256_GCM,data:uNKE6Nckt24ZWHDHEWjU,iv:yVvl1HbK7ljy6lgZdGUkfi0CeIHPnd2uof9tVB1z008=,tag:9tQ1atj1Vwkgw6j1FQ8p5w==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-a: ENC[AES256_GCM,data:FlVrW4HMpGxShfezY7k=,iv:5pZNFGbdfyrGCti7cL/7pfm4S3i5VpnESEO5Rglqw7E=,tag:NZIPNuaSZD/NSD6Q2sE2PQ==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-b: ENC[AES256_GCM,data:N1zEW1bym0cRrT5b,iv:+i6TbqeQLVdZRGUb/O0FUDSEXHsuxxW8hEJbQJYy8gU=,tag:dypP4NS1W0h0c+SeMmuI+g==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-c: ENC[AES256_GCM,data:dE2gJAAstO0VcCol,iv:C5g6vtQbu6AQUmtobCrnZmFcBc4Pn6EZmex1YhQqXA8=,tag:gsAznB9ygxhNdpK73HbAwQ==,type:str] +sops: + kms: + - arn: arn:aws:kms:us-east-1:${ACCOUNT_ID}:key/e32dffdb-97e7-4b64-b5cb-f6dc4e6fabca + created_at: "2025-10-03T17:27:53Z" + enc: AQICAHiXhc+HhELIyRKOpc5vBWQJB9/2XFW+CxWFIfUyci0r/wGkXSt3AG0b8bCJ0pVuEmyuAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM+S5DlWnhTDkMvmOxAgEQgDvMXlly/I5Vb2ah1KX2fSbY3mMOxA92rK4MU/rsyUN2oR8WXebzzW+ooNY1pEdGE4FMUmLUrU5qbcUoPg== + aws_profile: "" + encrypted_regex: sensitive + mac_only_encrypted: true + version: 3.11.0 diff --git a/terraform/services/config/values/prod.sopsw.yaml b/terraform/services/config/values/prod.sopsw.yaml new file mode 100644 index 00000000..aa3f4b26 --- /dev/null +++ b/terraform/services/config/values/prod.sopsw.yaml @@ -0,0 +1,17 @@ +/cdap/account/security_events_slack_renotify_after_days: 30 +/cdap/account/security_events_slack_severity_list: CRITICAL,HIGH,MEDIUM +/cdap/sensitive/account/security_events_slack_webhook_url: ENC[AES256_GCM,data:z9MLEAlb76u6MZ+GWcWcfnRtax1J677k47tabDmwCqAGN7H2BrmTnkIs1fAhl9dShaL5qZrq78s0sY9b2hCAGIWWaUenVbGGpWBuZvh7rw==,iv:kbgCH76ryIbnU40SWd/Wgg+hULSgGsTO4LLWIPoDE68=,tag:NmwrTB/oVI0WEhL9R5eV2g==,type:str] +/cdap/sensitive/bucket-access-logs-bucket: ENC[AES256_GCM,data:TjwtktvWlh7Gt7JrTuxZganUT3AotzmEAeYKkpn5GutLcIT1/KSpTV1kjMxV,iv:fSMld0pXjqKabcq+8CK7kG018tspwVAS30ngYFepJKw=,tag:dMsUtxHVSpiHb9U+ebUbNg==,type:str] +/cdap/sensitive/mgmt-vpc/cidr: ENC[AES256_GCM,data:uNKE6Nckt24ZWHDHEWjU,iv:yVvl1HbK7ljy6lgZdGUkfi0CeIHPnd2uof9tVB1z008=,tag:9tQ1atj1Vwkgw6j1FQ8p5w==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-a: ENC[AES256_GCM,data:FlVrW4HMpGxShfezY7k=,iv:5pZNFGbdfyrGCti7cL/7pfm4S3i5VpnESEO5Rglqw7E=,tag:NZIPNuaSZD/NSD6Q2sE2PQ==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-b: ENC[AES256_GCM,data:N1zEW1bym0cRrT5b,iv:+i6TbqeQLVdZRGUb/O0FUDSEXHsuxxW8hEJbQJYy8gU=,tag:dypP4NS1W0h0c+SeMmuI+g==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-c: ENC[AES256_GCM,data:dE2gJAAstO0VcCol,iv:C5g6vtQbu6AQUmtobCrnZmFcBc4Pn6EZmex1YhQqXA8=,tag:gsAznB9ygxhNdpK73HbAwQ==,type:str] +sops: + kms: + - arn: arn:aws:kms:us-east-1:${ACCOUNT_ID}:key/e32dffdb-97e7-4b64-b5cb-f6dc4e6fabca + created_at: "2025-10-03T17:27:53Z" + enc: AQICAHiXhc+HhELIyRKOpc5vBWQJB9/2XFW+CxWFIfUyci0r/wGkXSt3AG0b8bCJ0pVuEmyuAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM+S5DlWnhTDkMvmOxAgEQgDvMXlly/I5Vb2ah1KX2fSbY3mMOxA92rK4MU/rsyUN2oR8WXebzzW+ooNY1pEdGE4FMUmLUrU5qbcUoPg== + aws_profile: "" + encrypted_regex: sensitive + mac_only_encrypted: true + version: 3.11.0 diff --git a/terraform/services/config/values/test.sopsw.yaml b/terraform/services/config/values/test.sopsw.yaml new file mode 100644 index 00000000..6fbc46f2 --- /dev/null +++ b/terraform/services/config/values/test.sopsw.yaml @@ -0,0 +1,16 @@ +/cdap/account/security_events_slack_renotify_after_days: 30 +/cdap/account/security_events_slack_severity_list: CRITICAL,HIGH,MEDIUM +/cdap/sensitive/account/security_events_slack_webhook_url: ENC[AES256_GCM,data:J70QSI23I0d1OnvD+n2ncd/ii0XiZ3NRcQqVwCLCBd7UxRIKgJ1YWqCZ9xV2Y8s/4fy+hi8iVvIkd0WdnC1Q9Adwh2rvRKKWM6lS59EYlA==,iv:/ZIY4WGOsxo2pR/t2FYADYcUeEq5eutP43KkNeTa3q0=,tag:HYuxPAStpJZ0t39j/xHLYg==,type:str] +/cdap/sensitive/bucket-access-logs-bucket: ENC[AES256_GCM,data:I8m2zMO44IB1FnAuK08G99390eF5NjLcKTVvY4T5oD2O6/Apt3oSV3GAUldn,iv:DQUyoHG1Gplx4YuogWcG9kZnK3XTZXC+N5Ayzyb6LDM=,tag:wQpSTHVaItOGb3PC6tHw/w==,type:str] +/cdap/sensitive/mgmt-vpc/cidr: ENC[AES256_GCM,data:bKqvpJcIqTqG9DkzO1/T,iv:ul/XIzIZ3BnERohYmuM7fWXWTYI0b45T4F2lUFHkvIw=,tag:LEpy1QiHUoGlTg+mKUaQtg==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-a: ENC[AES256_GCM,data:dAMCs1t1aqfpzibaswE=,iv:g6q2MfSpDZeaSSkpkdwZhGD0ZCdlvrJpHoGVTD1+FfU=,tag:7Z5gSQuDlI0wjBiafCJmDA==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-b: ENC[AES256_GCM,data:tfr8BWh6eKGNsUkx,iv:oAHGGbq9ttxnJ99I70Z7IXIgKRSPkvqIkAmVOyBzWus=,tag:iZ1A8L7mUQLJvJ6uWsoF6A==,type:str] +/cdap/mgmt/public_nat_ipv4/sensitive/cdap-east-mgmt-c: ENC[AES256_GCM,data:nIVBfKDdYIzOObjj,iv:5E2wwUCOtBtmjpAnWOBZxUJVIqci3o2ncBNQmzEOEPM=,tag:bygAmJUYfbP4tLcFRu94XQ==,type:str] +sops: + kms: + - arn: arn:aws:kms:us-east-1:${ACCOUNT_ID}:key/e32dffdb-97e7-4b64-b5cb-f6dc4e6fabca + created_at: "2025-10-03T17:38:55Z" + enc: AQICAHiXhc+HhELIyRKOpc5vBWQJB9/2XFW+CxWFIfUyci0r/wGgF/ZOMV/LMrvJSRhtedM1AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM8xXrZN9mTFGkvKjtAgEQgDsOpxt5mGlXEpdiTlnAjnt/AcOMpA8coSH4NHWfC2Tsw/VES55XVAaWQYXuOOdKnIJznMlUqzqBVrfvTw== + aws_profile: "" + encrypted_regex: sensitive + version: 3.11.0 diff --git a/terraform/services/config/variables.tf b/terraform/services/config/variables.tf new file mode 100644 index 00000000..1d0d9afc --- /dev/null +++ b/terraform/services/config/variables.tf @@ -0,0 +1,8 @@ +variable "env" { + description = "The application environment (test, prod, mgmt)" + type = string + validation { + condition = contains(["test", "prod", "mgmt"], var.env) + error_message = "Valid value for env is test, prod, or mgmt." + } +}