diff --git a/.github/workflows/build-and-deploy-dev-waf.yml b/.github/workflows/build-and-deploy-dev-waf.yml index 74f0a3da3..684545a5a 100644 --- a/.github/workflows/build-and-deploy-dev-waf.yml +++ b/.github/workflows/build-and-deploy-dev-waf.yml @@ -23,8 +23,7 @@ jobs: CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' CF_ORG: '${{ secrets.CF_ORG }}' PROJECT: '${{ secrets.PROJECT }}' - run: | - source ./scripts/pipeline/cloud-gov-login.sh + run: source ./scripts/pipeline/cloud-gov-login.sh - name: Check version id: version env: @@ -35,7 +34,7 @@ jobs: source ./scripts/pipeline/cloud-gov-waf-version.sh outputs: current_nginx_version: ${{ steps.version.outputs.current_nginx_version }} - nginx_version: ${{ steps.version.outputs.nginx_version }} + new_nginx_version: ${{ steps.version.outputs.new_nginx_version }} cloudgov_wf_version: ${{ steps.version.outputs.cloudgov_wf_version }} cloudgov_bp_version: ${{ steps.version.outputs.cloudgov_bp_version }} update: ${{ steps.version.outputs.update }} @@ -50,81 +49,101 @@ jobs: - name: Set env.BRANCH run: echo "BRANCH=dev" >> $GITHUB_ENV - name: Install basic dependancies - run: | - ./scripts/pipeline/deb-basic-deps.sh + run: ./scripts/pipeline/deb-basic-deps.sh - name: Install Cloudfoundry CLI run: ./scripts/pipeline/deb-cf-install.sh - name: Cloud.gov login - id: cglogin env: CF_USER: '${{ secrets.CF_USER }}' CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' CF_ORG: '${{ secrets.CF_ORG }}' PROJECT: '${{ secrets.PROJECT }}' + TF_BASTION: '${{ secrets.TF_BASTION }}' + TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}' run: | source ./scripts/pipeline/cloud-gov-login.sh + cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1 + - name: Start Bastion + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' + run: | + cf start "${TF_BASTION}" >/dev/null 2>&1 + ./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${TF_BASTION}" + - name: Cloud.gov bastion git checkout + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' + run: | + declare -a commands=("rm -rf px-benefit-finder" "git clone https://github.com/GSA/px-benefit-finder.git && cd px-benefit-finder && git checkout ${BRANCH}") + for command in "${commands[@]}"; do + ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "${command}" 1 + done - name: Build nginx WAF Plugin env: ubuntu_version: '${{ vars.UBUNTU_VERSION }}' modsecurity_nginx_version: '${{ vars.MODSECURITY_NGINX_VERSION }}' - nginx_version: ${{ needs.checkVersion.outputs.nginx_version }} + new_nginx_version: ${{ needs.checkVersion.outputs.new_nginx_version }} + TF_BASTION: '${{ secrets.TF_BASTION }}' run: | - CWD=$(pwd) - cd "${CWD}/infra/applications/nginx-waf/.docker" || exit 1 - make - cd "${CWD}" || exit 1 - - name: Configure Terraform Provider + source ./scripts/pipeline/terraform-build-waf-plugin.sh + - name: Configure Terraform env: - bucket_name: ${{ secrets.BUCKET_NAME_TERRAFORM }} CF_USER: '${{ secrets.CF_USER }}' CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' CF_ORG: '${{ secrets.CF_ORG }}' PROJECT: '${{ secrets.PROJECT }}' + TF_BASTION: '${{ secrets.TF_BASTION }}' run: | CWD=$(pwd) - cf target -s "${PROJECT}-dev" >/dev/null 2>&1 - source ./scripts/local/cloud-gov-s3-creds.sh - cd "${CWD}/infra" || exit 1 - echo "cloudgov_password=\"${{ secrets.CF_PASSWORD }}\"" >> terraform.tfvars - echo "cloudgov_username=\"${{ secrets.CF_USER }}\"" >> terraform.tfvars - envsubst < provider.tf.tmpl > provider.tf - cd "${CWD}" || exit 1 - - uses: hashicorp/setup-terraform@v3 + cd terraform/infra + envsubst < terraform.tfvars.tmpl > terraform.tfvars + ${CWD}/scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "terraform.tfvars" "px-benefit-finder/terraform/infra" + cd "${CWD}" - name: Terraform Init env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} + TF_BASTION: '${{ secrets.TF_BASTION }}' id: init - run: terraform init - working-directory: 'infra' + run : ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "tofu -chdir=px-benefit-finder/terraform/infra init" 1 - name: Terraform Validate + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' id: validate - run: TF_WORKSPACE=${BRANCH} terraform validate -no-color + run : | + stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh ${TF_BASTION} "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra validate -no-color" 1)) + stdout=$(echo $stdout | sed '$ d') + echo ${stdout} + echo "stdout=${stdout}" >> $GITHUB_OUTPUT - name: Terraform Plan env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} + TF_BASTION: '${{ secrets.TF_BASTION }}' id: plan - run: TF_WORKSPACE=${BRANCH} terraform plan -no-color - working-directory: 'infra' + run : | + stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra plan -no-color" 1) + stdout=$(echo $stdout | sed '$ d') + echo ${stdout} + echo "stdout=${stdout}" >> $GITHUB_OUTPUT - name: Terraform Apply - id: apply env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} - run: TF_WORKSPACE=${BRANCH} terraform apply -auto-approve - working-directory: 'infra' + TF_BASTION: '${{ secrets.TF_BASTION }}' + id: apply + run : | + stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra apply -auto-approve" 1) + stdout=$(echo $stdout | sed '$ d') + echo ${stdout} + echo "stdout=${stdout}" >> $GITHUB_OUTPUT - name: Create Issue For Review id: issue uses: dacbd/create-issue-action@main with: token: ${{ secrets.ADD_TO_PROJECT_PAT }} assignees: ${{ vars.TECH_LEAD }} - title: "Update to NGINX v${{ needs.checkVersion.outputs.nginx_version }} (dev)" + title: "Update to NGINX v${{ needs.checkVersion.outputs.new_nginx_version }} (dev)" body: | ## Automated update of NGINX WAF This is an automated build and deploy of the NGINX WAF application in dev. Build pack: ${{ needs.checkVersion.outputs.cloudgov_wf_version }} => ${{ needs.checkVersion.outputs.cloudgov_bp_version }} - NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.nginx_version }} + NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.new_nginx_version }} #### Terraform Initialization ⚙`${{ steps.init.outcome }}` #### Terraform Validation 🤖`${{ steps.validate.outcome }}` @@ -154,4 +173,32 @@ jobs: GH_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }} ISSUE_NUMBER: ${{ steps.issue.outputs.number }} run: source ./scripts/pipeline/github-update-issue-status.sh - \ No newline at end of file + stopBastion: + name: Stop Bastion + runs-on: ubuntu-latest + needs: updateWAF + if: ${{ always() }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set env.BRANCH + run: echo "BRANCH=dev" >> $GITHUB_ENV + - name: Install basic dependancies + run: ./scripts/pipeline/deb-basic-deps.sh + - name: Install Cloudfoundry CLI + run: ./scripts/pipeline/deb-cf-install.sh + - name: Cloud.gov login + env: + CF_USER: '${{ secrets.CF_USER }}' + CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' + CF_ORG: '${{ secrets.CF_ORG }}' + PROJECT: '${{ secrets.PROJECT }}' + TF_BASTION: '${{ secrets.TF_BASTION }}' + TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}' + run: | + source ./scripts/pipeline/cloud-gov-login.sh + cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1 + - name: Stop Bastion + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' + run: cf stop "${TF_BASTION}" >/dev/null 2>&1 \ No newline at end of file diff --git a/.github/workflows/build-and-deploy-main-waf.yml b/.github/workflows/build-and-deploy-main-waf.yml index f487716be..d10ba3ced 100644 --- a/.github/workflows/build-and-deploy-main-waf.yml +++ b/.github/workflows/build-and-deploy-main-waf.yml @@ -2,7 +2,7 @@ name: Update Main WAF on: schedule: - - cron: '30 5 * * 4' + - cron: '0 5 * * 4' jobs: checkVersion: @@ -23,8 +23,7 @@ jobs: CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' CF_ORG: '${{ secrets.CF_ORG }}' PROJECT: '${{ secrets.PROJECT }}' - run: | - source ./scripts/pipeline/cloud-gov-login.sh + run: source ./scripts/pipeline/cloud-gov-login.sh - name: Check version id: version env: @@ -35,9 +34,9 @@ jobs: source ./scripts/pipeline/cloud-gov-waf-version.sh outputs: current_nginx_version: ${{ steps.version.outputs.current_nginx_version }} - nginx_version: ${{ steps.version.outputs.nginx_version }} - cloudgov_wf_version: ${{ steps.version.outputs.cloudgov_wf_version }} - cloudgov_bp_version: ${{ steps.version.outputs.cloudgov_bp_version }} + new_nginx_version: ${{ steps.version.outputs.new_nginx_version }} + current_bp_version: ${{ steps.version.outputs.current_bp_version }} + new_bp_version: ${{ steps.version.outputs.new_bp_version }} update: ${{ steps.version.outputs.update }} updateWAF: name: Update WAF @@ -50,83 +49,104 @@ jobs: - name: Set env.BRANCH run: echo "BRANCH=main" >> $GITHUB_ENV - name: Install basic dependancies - run: | - ./scripts/pipeline/deb-basic-deps.sh + run: ./scripts/pipeline/deb-basic-deps.sh - name: Install Cloudfoundry CLI run: ./scripts/pipeline/deb-cf-install.sh - name: Cloud.gov login - id: cglogin env: CF_USER: '${{ secrets.CF_USER }}' CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' CF_ORG: '${{ secrets.CF_ORG }}' PROJECT: '${{ secrets.PROJECT }}' + TF_BASTION: '${{ secrets.TF_BASTION }}' + TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}' run: | source ./scripts/pipeline/cloud-gov-login.sh + cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1 + - name: Start Bastion + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' + run: | + cf start "${TF_BASTION}" >/dev/null 2>&1 + ./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${TF_BASTION}" + - name: Cloud.gov bastion git checkout + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' + run: | + declare -a commands=("rm -rf px-benefit-finder" "git clone https://github.com/GSA/px-benefit-finder.git && cd px-benefit-finder && git checkout ${BRANCH}") + for command in "${commands[@]}"; do + ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "${command}" 1 + done - name: Build nginx WAF Plugin env: ubuntu_version: '${{ vars.UBUNTU_VERSION }}' modsecurity_nginx_version: '${{ vars.MODSECURITY_NGINX_VERSION }}' - nginx_version: ${{ needs.checkVersion.outputs.nginx_version }} + new_nginx_version: ${{ needs.checkVersion.outputs.new_nginx_version }} + TF_BASTION: '${{ secrets.TF_BASTION }}' run: | - CWD=$(pwd) - cd "${CWD}/infra/applications/nginx-waf/.docker" || exit 1 - make - cd "${CWD}" || exit 1 - - name: Configure Terraform Provider + source ./scripts/pipeline/terraform-build-waf-plugin.sh + - name: Configure Terraform env: - bucket_name: ${{ secrets.BUCKET_NAME_TERRAFORM }} CF_USER: '${{ secrets.CF_USER }}' CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' CF_ORG: '${{ secrets.CF_ORG }}' PROJECT: '${{ secrets.PROJECT }}' + TF_BASTION: '${{ secrets.TF_BASTION }}' run: | CWD=$(pwd) - cf target -s "${PROJECT}-dev" >/dev/null 2>&1 - source ./scripts/local/cloud-gov-s3-creds.sh - cd "${CWD}/infra" || exit 1 - echo "cloudgov_password=\"${{ secrets.CF_PASSWORD }}\"" >> terraform.tfvars - echo "cloudgov_username=\"${{ secrets.CF_USER }}\"" >> terraform.tfvars - envsubst < provider.tf.tmpl > provider.tf - cd "${CWD}" || exit 1 - - uses: hashicorp/setup-terraform@v3 + cd terraform/infra + envsubst < terraform.tfvars.tmpl > terraform.tfvars + ${CWD}/scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "terraform.tfvars" "px-benefit-finder/terraform/infra" + cd "${CWD}" - name: Terraform Init env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} + TF_BASTION: '${{ secrets.TF_BASTION }}' id: init - run: terraform init - working-directory: 'infra' + run : ./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "tofu -chdir=px-benefit-finder/terraform/infra init" 1 - name: Terraform Validate + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' id: validate - run: TF_WORKSPACE=${BRANCH} terraform validate -no-color + run : | + stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh ${TF_BASTION} "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra validate -no-color" 1)) + stdout=$(echo $stdout | sed '$ d') + echo ${stdout} + echo "stdout=${stdout}" >> $GITHUB_OUTPUT - name: Terraform Plan env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} + TF_BASTION: '${{ secrets.TF_BASTION }}' id: plan - run: TF_WORKSPACE=${BRANCH} terraform plan -no-color - working-directory: 'infra' + run : | + stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra plan -no-color" 1) + stdout=$(echo $stdout | sed '$ d') + echo ${stdout} + echo "stdout=${stdout}" >> $GITHUB_OUTPUT - name: Terraform Apply - id: apply env: - GITHUB_TOKEN: ${{ secrets.CR_PAT }} - run: TF_WORKSPACE=${BRANCH} terraform apply -auto-approve - working-directory: 'infra' + TF_BASTION: '${{ secrets.TF_BASTION }}' + id: apply + run : | + stdout=$(./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "TF_WORKSPACE=${BRANCH} tofu -chdir=px-benefit-finder/terraform/infra apply -auto-approve" 1) + stdout=$(echo $stdout | sed '$ d') + echo ${stdout} + echo "stdout=${stdout}" >> $GITHUB_OUTPUT - name: Create Issue For Review + id: issue uses: dacbd/create-issue-action@main with: token: ${{ secrets.ADD_TO_PROJECT_PAT }} assignees: ${{ vars.TECH_LEAD }} - title: "Update to NGINX v${{ needs.checkVersion.outputs.nginx_version }} (main)" + title: "Update to NGINX v${{ needs.checkVersion.outputs.new_nginx_version }} (main)" body: | ## Automated update of NGINX WAF This is an automated build and deploy of the NGINX WAF application in main. - Build pack: ${{ needs.checkVersion.outputs.cloudgov_wf_version }} => ${{ needs.checkVersion.outputs.cloudgov_bp_version }} - NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.nginx_version }} + Build pack: ${{ needs.checkVersion.outputs.current_bp_version }} => ${{ needs.checkVersion.outputs.new_bp_version }} + NGINX: ${{ needs.checkVersion.outputs.current_nginx_version }} => ${{ needs.checkVersion.outputs.new_nginx_version }} - #### Terraform Initialization ⚙️`${{ steps.init.outcome }}` - #### Terraform Validation ✅`${{ steps.validate.outcome }}` + #### Terraform Initialization ⚙`${{ steps.init.outcome }}` + #### Terraform Validation 🤖`${{ steps.validate.outcome }}`
Validation Output @@ -136,7 +156,7 @@ jobs:
- #### Terraform Plan ✏️`${{ steps.plan.outcome }}` + #### Terraform Plan 📅`${{ steps.plan.outcome }}`
Show Plan @@ -145,3 +165,40 @@ jobs: ```
+ + #### Terraform Apply 🚀`${{ steps.apply.outcome }}` + - name: Update Project Status + env: + GH_PROJECT_NUMBER: ${{ secrets.GH_PROJECT_NUMBER }} + GH_TOKEN: ${{ secrets.ADD_TO_PROJECT_PAT }} + ISSUE_NUMBER: ${{ steps.issue.outputs.number }} + run: source ./scripts/pipeline/github-update-issue-status.sh + stopBastion: + name: Stop Bastion + runs-on: ubuntu-latest + needs: updateWAF + if: ${{ always() }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set env.BRANCH + run: echo "BRANCH=main" >> $GITHUB_ENV + - name: Install basic dependancies + run: ./scripts/pipeline/deb-basic-deps.sh + - name: Install Cloudfoundry CLI + run: ./scripts/pipeline/deb-cf-install.sh + - name: Cloud.gov login + env: + CF_USER: '${{ secrets.CF_USER }}' + CF_PASSWORD: '${{ secrets.CF_PASSWORD }}' + CF_ORG: '${{ secrets.CF_ORG }}' + PROJECT: '${{ secrets.PROJECT }}' + TF_BASTION: '${{ secrets.TF_BASTION }}' + TF_BACKEND_SPACE: '${{ secrets.TF_BACKEND_SPACE }}' + run: | + source ./scripts/pipeline/cloud-gov-login.sh + cf target -s "${TF_BACKEND_SPACE}" >/dev/null 2>&1 + - name: Stop Bastion + env: + TF_BASTION: '${{ secrets.TF_BASTION }}' + run: cf stop "${TF_BASTION}" >/dev/null 2>&1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index f325820e4..43b6d7de6 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ backups/*.sql.gz *.Identifier *.gz **.log + +**restore.txt +**backup.txt \ No newline at end of file diff --git a/benefit-finder/package-lock.json b/benefit-finder/package-lock.json index b6084e5c1..c812c1828 100644 --- a/benefit-finder/package-lock.json +++ b/benefit-finder/package-lock.json @@ -1,12 +1,12 @@ { "name": "benefit-finder", - "version": "0.3.1.beta.1", + "version": "0.4.0.beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "benefit-finder", - "version": "0.3.1.beta.1", + "version": "0.4.0.beta.1", "hasInstallScript": true, "dependencies": { "@uswds/uswds": "^3.8.1", diff --git a/benefit-finder/package.json b/benefit-finder/package.json index 825fa6c47..f2decd28a 100644 --- a/benefit-finder/package.json +++ b/benefit-finder/package.json @@ -1,6 +1,6 @@ { "name": "benefit-finder", - "version": "0.3.1.beta.1", + "version": "0.4.0.beta.1", "private": true, "engines": { "node": "20.x.x" diff --git a/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap b/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap index 7b444b0a4..af7a2506a 100644 --- a/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap +++ b/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap @@ -12,7 +12,7 @@ exports[`loads intro 1`] = `
value - if try(value.deployed, false) && - try(data.cloudfoundry_space.this[0].id, null) != null - } - name_or_id = format(local.env.name_pattern, each.key) - space = try(data.cloudfoundry_space.this[0].id, null) -} - -data "cloudfoundry_domain" "external" { - domain = "${split(".", local.env.external_domain)[1]}.${split(".", local.env.external_domain)[2]}" - sub_domain = split(".", local.env.external_domain)[0] -} - -data "cloudfoundry_domain" "internal" { - domain = split(".", local.env.internal_domain)[1] - sub_domain = split(".", local.env.internal_domain)[0] -} - -data "cloudfoundry_org" "this" { - name = local.env.organization -} - -data "cloudfoundry_space" "this" { - count = terraform.workspace != local.env.bootstrap_workspace ? 1 : 0 - name = try(local.env.space, terraform.workspace) - org = data.cloudfoundry_org.this.id -} - - -data "cloudfoundry_service" "this" { - for_each = { - for key, value in try(local.env.services, {}) : key => value - if value.service_type != "user-provided" && - try(data.cloudfoundry_space.this[0].id, null) != null - } - - name = each.value.service_type - space = try(data.cloudfoundry_space.this[0].id, null) -} - -# data "cloudfoundry_space" "egress_proxy" { -# count = terraform.workspace != local.env.egress.workspace && terraform.workspace != local.env.bootstrap_workspace ? 1 : 0 -# name = local.env.egress.space -# org = data.cloudfoundry_org.this.id -# } - -# data "cloudfoundry_app" "egress_proxy" { -# count = terraform.workspace != local.env.egress.workspace && terraform.workspace != local.env.bootstrap_workspace ? 1 : 0 -# name_or_id = format(local.env.egress.name_pattern, local.env.egress.name) -# space = data.cloudfoundry_space.egress_proxy[0].id -# } - -# data "cloudfoundry_route" "egress_proxy" { -# count = terraform.workspace != local.env.egress.workspace && terraform.workspace != local.env.bootstrap_workspace ? 1 : 0 -# domain = data.cloudfoundry_domain.internal.id -# hostname = data.cloudfoundry_app.egress_proxy[0].name -# } diff --git a/infra/locals.tf b/infra/locals.tf deleted file mode 100755 index 4971fa65d..000000000 --- a/infra/locals.tf +++ /dev/null @@ -1,901 +0,0 @@ -locals { - - ## The name of the project. Used to name most applications and services. - ## Default naming convention: ${local.project}-application-name-${terraform.workspace} - project = "benefit-finder" - - ## The full name of the project. If their isn't a longer name, this can be set to - ## local.project. - project_full = "${local.project}" - - ## The name of the bootstrap/init workspace. This is used for various things, such as - ## creating global pipeline variables that aren't project specific. - bootstrap_workspace = "bootstrap" - - ## The names of the project's production workspaces. This is used to adjust - ## settings dynamically throughout this configuration file. - production_workspaces = ["main", "stage"] - - ## "Common" applications and services that are deployed to every space. - globals = { - apps = { - ## Nginx Web Application Firewall (WAF). - waf = { - - ## Should the application have access to the internet? - allow_egress = true - - ## Buildpacks to use with this application. - ## List buildpacks avalible with: cf buildpacks - buildpacks = [ - "https://github.com/cloudfoundry/apt-buildpack", - "nginx_buildpack" - ] - - ## Command to run when container starts. - command = "./start" - - ## Ephemeral disk storage. - disk_quota = 1024 - - ## Should SSH be enabled? - enable_ssh = true - - ## Environmental variables. Avoid sensitive variables. - environment = { - - ## IP addresses allowed to connected to the CMS. - ALLOWED_IPS_CMS = base64encode( - jsonencode([ - "allow 0.0.0.0/0;" - # ## Benefit-Finder Dev - # "allow 72.76.74.75/32;", - # "allow 98.164.54.242/32;", - # "allow 75.176.62.86/32;", - # "allow 70.21.206.246/32;", - # "allow 100.16.185.127/32;", - # "allow 75.176.62.86/32;", - # "allow 2603:6080:2840:284:80b4:2ae:80f8:9652;", - # ## GSA VPN pool - # "allow 50.81.160.164;", - # "allow 100.36.151.190;", - # "allow 52.222.122.97/32;", - # "allow 52.222.123.172/32;", - # "allow 159.142.0.0/16;", - - # # SiteImprove: - # "allow 13.58.165.213;", - # "allow 18.116.191.222;", - # "allow 18.116.197.208;", - # "allow 18.189.206.159;", - # "allow 18.190.68.80;", - # "allow 18.216.137.252;", - # "allow 18.223.191.8;", - # "allow 3.13.121.241;", - # "allow 3.133.38.181;", - # "allow 3.135.49.180;", - # "allow 3.136.111.218;", - # "allow 3.138.54.100;", - # "allow 18.219.35.44;", - # "allow 18.157.140.51;", - # "allow 18.159.218.224;", - # "allow 18.196.205.2;", - # "allow 18.198.120.55;", - # "allow 3.124.26.114;", - # "allow 3.125.99.135;", - # "allow 3.64.159.177;", - # "allow 3.66.247.32;", - # "allow 3.68.122.244;", - # "allow 52.57.167.198;", - # "allow 35.158.180.204;", - # "allow 18.192.147.131;", - # "allow 52.58.146.230;", - # "allow 185.229.145.22;", - # "allow 3.129.126.175;", - - # # Salesforce GovCloud: - # "allow 96.43.153.8;", - # "allow 96.43.152.8;", - # "allow 52.61.131.34;", - # "allow 52.61.135.34;", - - # # US-East IPs from Salesforce Public Cloud:" - # "allow 3.225.240.254/32;", - # "allow 18.204.28.162/32;", - # "allow 18.214.12.209/32;", - # "allow 34.202.86.120/32;", - # "allow 34.204.111.166/32;", - # "allow 52.44.156.44/32;", - - # # US-West IPs from Salesforce Public Cloud: - # "allow 44.233.69.21/32;", - # "allow 44.237.79.66/32;", - # "allow 52.36.20.11/32;", - # "allow 35.80.213.208/32;", - # "allow 35.161.141.162/32;", - # "allow 44.234.249.148/32;", - - # # Connections from Salesforce in the US:" - # "allow 3.225.151.145;", - # "allow 3.225.240.254/32;", - # "allow 18.204.28.162/32;", - # "allow 18.211.105.61;", - # "allow 34.197.58.108;", - # "allow 34.204.111.166/32;", - # "allow 52.3.16.30;", - # "allow 52.22.251.194;", - # "allow 52.70.135.185;", - ]) - ) - - ## The OWASP CRS rules for modsecurity. - CRS_RULES = "coreruleset-4.0.0.tar.gz" - - ## IP address that are denied access from the static website. - DENYED_IPS_STATIC = base64encode(jsonencode([])) - - ## The current environment the application is running in. - ENV = terraform.workspace - - ## Linux "Load Library Path", where system libraries are located. (i.e. libzip, gd, etc) - LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" - - ## Ubuntu patch for newer version of mod security. - MODSECURITY_UPDATE = "libmodsecurity3_3.0.9-1_amd64.deb" - - ## Domains that shouldn't be passed to the egress proxy server (i.e. apps.internal). - #no_proxy = var.no_proxy - } - - ## Timeout for health checks, in seconds. - health_check_timeout = 180 - - ## Type of health check. - ## Options: port, process, http - health_check_type = "port" - - ## Number of instances of application to deploy. - instances = 1 - - ## Labels to add to the application. - labels = { - environment = terraform.workspace - } - - ## Maximum amount of memory the application can use. - memory = 96 - - ## Addional network policies to add to the application. - ## Format: name of the application and the port it is listening on. - network_policies = { - drupal = 61443 - } - - ## Port the application uses. - port = 80 - - ## Can the application be accessed outside of cloud.gov? - public_route = true - - ## The source file should be a directory or a zip file. - source = "./applications/nginx-waf" - - ## Templates take templated files and fill them in with sensitive data. - ## The proxy-to-static.conf has the S3 bucket written to it during - ## the 'terraform apply' command, before it the files are zipped up and - ## uploaded to cloud.gov. - templates = [ - { - source = "${path.cwd}/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl" - destination = "${path.cwd}/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf" - }, - { - source = "${path.cwd}/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl" - destination = "${path.cwd}/applications/nginx-waf/nginx/snippets/proxy-to-static.conf" - }, - { - source = "${path.cwd}/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl" - destination = "${path.cwd}/applications/nginx-waf/nginx/snippets/proxy-to-app.conf" - } - ] - } - } - - ## Services to deploy in this environment. - services = { - - ## S3 storage for backups. - backup = { - ## Applications to bind to this service. - applications = [] - - ## Should a service key be generated for other applications to use? - service_key = true - - ## The size of the instance to deploy. - service_plan = "basic" - - ## The type of service to be deployed. - service_type = "s3" - - ## Tags to add to the service. - tags = [ - terraform.workspace - ] - } - - ## MySQL RDS database. - mysql = { - - ## Applications to bind to this service. - applications = ["cms"] - - ## The size of the instance to deploy. - service_plan = contains(local.production_workspaces, terraform.workspace) ? "micro-mysql" : "micro-mysql" - - ## The type of service to be deployed. - service_type = "aws-rds" - - ## Tags to add to the service. - tags = [ - terraform.workspace - ] - } - - ## Credentials and other sensitive variables. - secrets = { - ## Applications to bind to this service. - applications = ["cms", "waf"] - - ## Credentials that should be added to the json blob. - credentials = [ - "ca_certificate", - "ca_key", - "cron_key", - "hash_salt", - "HASH_SALT", - "newrelic_key", - "proxy_password", - "proxy_username", - "proxy_uri", - "static_bucket", - "static_fips_endpoint", - "static_access_key_id", - "static_secret_access_key", - "storage_bucket", - "storage_fips_endpoint", - "storage_access_key_id", - "storage_secret_access_key" - ] - - ## The type of service to be deployed. - service_type = "user-provided" - - ## Tags to add to the service. - tags = [ - terraform.workspace - ] - } - - ## S3 storage for public files for Drupal. - ## Typically "sites/default/files/" - storage = { - - ## Applications to bind to this service. - applications = ["cms", "waf"] - - ## Should a service key be generated for other applications to use? - service_key = true - - ## The size of the instance to deploy. - service_plan = "basic-public-sandbox" - - ## The type of service to be deployed. - service_type = "s3" - - ## Tags to add to the service. - tags = [ - terraform.workspace - ] - } - - # S3 storage for the statically generated site. - static = { - - ## Applications to bind to this service. - applications = ["waf", "cms"] - - ## Should a service key be generated for other applications to use? - service_key = true - - ## The size of the instance to deploy. - service_plan = "basic-public-sandbox" - - ## The type of service to be deployed. - service_type = "s3" - - ## Tags to add to the service. - tags = [ - terraform.workspace - ] - } - } - - ## Variables to store in CircleCI as pipeline variables. - circleci_variables = [ - "cf_space", - "cron_key", - "cms_uri", - "ssg_uri", - "drupal_instances", - "drupal_memory", - "drupal_port", - "hash_salt", - "waf_name", - "waf_external_endpoint" - ] - } - - ## Settings for the egress proxy. - # egress = { - - # ## The name of the proxy. - # name = "caddy" - - # ## The naming convention/pattern for systems in the proxy space. The space could be named something - # ## besides dmz, such as public, so that can be specified here. The '%s' is replaced with the name of - # ## the application or service. - # name_pattern = "${local.project}-%s-dmz" - - # ## The mTLS port the proxy application uses. - # ## Cloudfoundry will automatically redirect connections on this port to local port 8080. - # port = var.mtls_port - - # ## The name of the space the proxy is deployed in. - # space = "${local.project}-dmz" - - # ## The terraform workspace the proxy is deployed with. - # workspace = "dmz" - # } - - ## The mTLS port the proxy application uses. - ## Cloudfoundry will automatically redirect connections on this port to local port 8080. - mtls_port = var.mtls_port - - ## Any applications that are external to this Terraform infrastucture. - ## These are saved to CircleCI variables. - ## In this case, the Drupal application is deployed via a manifest.yml in the Drupal - ## Github repostitory. - external_applications = { - dev = { - cms = { - - ## How many instances of the application to run. - instances = 1 - - ## Port is the application listening on. - port = var.mtls_port - - ## How much memory should it be using? - memory = 512 - } - } - main = { - cms = { - - ## How many instances of the application to run. - instances = 1 - - ## Port is the application listening on. - port = var.mtls_port - - ## How much memory should it be using? - memory = 512 - - enable_ssh = false - } - } - # stage = { - # drupal = { - - # ## How many instances of the application to run. - # instances = 1 - - # ## Port is the application listening on. - # port = var.mtls_port - - # ## How much memory should it be using? - # memory = 512 - # } - # } - # test = { - # drupal = { - - # ## How many instances of the application to run. - # instances = 1 - - # ## Port is the application listening on. - # port = var.mtls_port - - # ## How much memory should it be using? - # memory = 512 - - # } - # } - } - - ## The various environment settings to be deployed. - envs = { - - ## Every environment gets settings in 'all'. - all = { - - ## The API URL for cloud.gov. - api_url = "https://api.fr.cloud.gov" - - ## Copy the bootstrap workspace name from above to be passed to modules. - bootstrap_workspace = local.bootstrap_workspace - - # // 1825 days or 5 years - # certificate_authority_validity = 43800 - - # // Allow renewal one month before experation. - # certificate_authority_renewal = 43080 - - ## These values are defaults values when options aren't configured in the application block. - defaults = { - - ## The default size of the containers ephemeral disk. - disk_quota = 2048 - - ## Is SSH enabled on the container by default? - enable_ssh = true - - ## The default health check timeout. - health_check_timeout = 60 - - ## Default method of performing a health check. - ## Valid options: "port", "process", or "http" - ## https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html - health_check_type = "port" - - ## Default number of application instances to deploy. - instances = 1 - - ## Default amount of memory to use memory to use for an application. - memory = 64 - - port = 8080 - - ## The default cloudfoundry stack to deploy. - ## https://docs.cloudfoundry.org/devguide/deploy-apps/stacks.html - stack = "cflinuxfs4" - - ## Is the application stopped by default? - stopped = false - - ## Default CloudFoundry deployment strategy. - ## Valid optons: "none", "standard", or "blue-green". - ## https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html - strategy = "none" - - ## Default wait time for an application to start. - timeout = 300 - } - - ## Configuration settings for the egress proxy application. - # egress = local.egress - - ## External application based on the Terraform workspace being used. - external_applications = try(local.external_applications[terraform.workspace], {}) - - ## The domain name for applications accessable external of cloud.gov. - external_domain = "app.cloud.gov" - - ## The domain name for applications accessable inside of cloud.gov. - internal_domain = "apps.internal" - - ## The naming convention/pattern for deployed systems and subsystems. - ## %s is replaced with the name of the system. - name_pattern = "${local.project}-%s-${terraform.workspace}" - - ## The name of the cloud.gov organization. - organization = "gsa-tts-usagov" - - ## Passwords that are generated for workspaces. By default, it's an empty map. - ## If one is defined below in a workspace's settings, it will supersed this one. - passwords = {} - - ## CircleCI global configuration. - circleci = { - ## The name of the organization in CircleCI - organization = "GSA" - ## The name of the project in CircleCI. Typically the repo name. - project = "${local.project_full}" - ## The name of the version control system. The provider supports: - ## - github - ## - bitbucket - vcs_type = "github" - - ## Scheduled pipeline definitions. - schedules = { - - ## Runs the pipeline that builds the static site every 30 minutes. - # build-static = { - # name = "${local.project}-static-${terraform.workspace}" - # description = "Build static files for ${terraform.workspace} environment." - # ignore_workspace = ["bootstrap", "dmz"] - # organization = "GSA" - # project = "px-${local.project_full}" - # per_hour = 2 - # hours_of_day = ["*"] - # days_of_week = ["*"] - - # parameters = { - # branch = terraform.workspace - # build_static = true - # } - # } - - ## Runs the pipeline that runs 'drush cron' once an hour. - # cron = { - # name = "${local.project}-cron-${terraform.workspace}" - # description = "The 'drush cron' job for ${terraform.workspace} environment." - # ignore_workspace = ["bootstrap", "dmz"] - # organization = "usagov" - # project = "${local.project_full}-drupal" - # per_hour = 1 - # hours_of_day = ["*"] - # days_of_week = ["*"] - # parameters = { - # branch = terraform.workspace - # drush_cron = true - # } - # } - - ## Runs the pipeline that backups the database, user uploaded content, and terraform state files once an hour. - # scheduled-backup = { - # name = "${local.project}-scheduled-backup-${terraform.workspace}" - # description = "A scheduled backup job for ${terraform.workspace} environment." - # ignore_workspace = ["bootstrap", "dmz"] - # organization = "usagov" - # project = "${local.project_full}-drupal" - # per_hour = 1 - # hours_of_day = ["1", "16"] - # days_of_week = ["*"] - # parameters = { - # branch = terraform.workspace - # scheduled_backup = true - # } - # } - } - } - - ## A copy of the project name, so it gets added to this setting object. - project = local.project - - ## The name of the current Cloud.gov space. - space = "${local.project}-${terraform.workspace}" - } - - ## - ## - ## The bootstrap workspace. - ## Used to initialize gobal/project wide settings. - ## - ## - - bootstrap = { - - ## Username and password that gets generated for the egress proxy to allow egress. - # passwords = { - # proxy_username = { - # length = 16 - # special = false - # } - # proxy_password = { - # length = 48 - # special = false - # } - # } - - ## Sensitive variables to store in the pipeline. - circleci_variables = { - ## The variable set for egress proxy sensitive variables. - bootstrap = { - variables = [ - #"backend_aws_bucket_name", - #"backend_aws_bucket_region", - #"backup_aws_bucket_name", - #"backup_aws_bucket_region", - "cloudgov_password", - "cloudgov_username", - "circleci_token", - "cf_org", - "drupal_port", - "newrelic_key", - "no_proxy", - "project", - "proxy_password", - "proxy_username" - ] - } - } - } - - ## - ## - ## The DMZ workspace. - ## - ## - - # dmz = { - # ## Applications to deploy to this workspace. - # apps = { - - # ## The Caddy egress proxy. - # caddy = { - # buildpacks = [ - # #"https://github.com/cloudfoundry/apt-buildpack", - # "binary_buildpack" - # ] - # command = "./start" - # disk_quota = 256 - # enable_ssh = true - # environment = { - # ENV = terraform.workspace - # LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" - - # ## List of domains that applications are allowed to connect to. - # PROXY_ALLOW = join(" ", - # [ - # "*.amazonaws.com", - # "*.s3-us-gov-west-1.amazonaws.com", - # "*.drupal.org", - # "*.github.com", - # "*.packagist.org", - # "*.newrelic.com", - # "*.githubusercontent.com" - # ] - # ) - - # ## List of domains that applications are denied to connect to. - # PROXY_DENY = join(" ", - # [ - # "*.yahoo.com" - # ] - # ) - - # } - # ## How long until the health check times out. - # health_check_timeout = 180 - - # ## The type of health check. - # health_check_type = "port" - - # ## The number of instances to deploy. - # instances = 1 - - # ## How to tag the application. - # labels = { - # environment = terraform.workspace - # } - - # ## The ammount of memory, in MB, should the applcation use. - # memory = 64 - - # ## The port the application should listen on. - # port = 8080 - - # ## Is the application routable from the internet? - # public_route = false - - # ## The source file should be a directory or a zip file. - # source = "./applications/caddy-proxy" - - # ## Templates take templated files and fill them in with sensitive data. - # ## The Caddyfile has the proxy username and password written to it during - # ## the 'terraform apply' command, before it the files are zipped up and - # ## uploaded to cloud.gov. - # templates = [ - # { - # source = "${path.cwd}/applications/caddy-proxy/Caddyfile.tmpl" - # destination = "${path.cwd}/applications/caddy-proxy/Caddyfile" - # } - # ] - # } - # } - - # ## Variables that are globally used in every space. - # circleci_variables = { - # caddy = { - # global = true - # variables = [ - # "proxy_name", - # "proxy_port", - # "proxy_space", - # "caddy_internal_endpoint" - # ] - # } - # } - # } - - ################################# - ## - ## ____ - ## | _ \ _____ __ - ## | | | |/ _ \ \ / / - ## | |_| | __/\ V / - ## |____/ \___| \_/ - ## - ################################# - - dev = merge( - { - ## Applications to deploy. - apps = { - waf = local.globals.apps.waf - } - services = { - backup = local.globals.services.backup - mysql = local.globals.services.mysql - secrets = local.globals.services.secrets - static = local.globals.services.static - storage = local.globals.services.storage - } - circleci_variables = local.globals.circleci_variables - }, - { - ## The space to deploy to the application to. - space = "${local.project}-dev" - - ## Passwords that need to be generated for this environment. - ## These will actually use the sha256 result from the random module. - passwords = { - hash_salt = { - length = 32 - } - cron_key = { - length = 32 - } - } - } - ) - - ################################# - ## - ## ____ _ - ## | _ \ _ __ ___ __| | - ## | |_) | '__/ _ \ / _` | - ## | __/| | | (_) | (_| | - ## |_| |_| \___/ \__,_| - ## - ################################# - - - main = merge( - { - ## Applications to deploy. - apps = { - waf = merge( - local.globals.apps.waf, - { - instances = 1 - } - ) - } - services = { - backup = local.globals.services.backup - mysql = local.globals.services.mysql - secrets = local.globals.services.secrets - static = local.globals.services.static - storage = local.globals.services.storage - } - circleci_variables = local.globals.circleci_variables - }, - { - ## The space to deploy to the application to. - space = "${local.project}-${terraform.workspace}" - - ## Passwords that need to be generated for this environment. - ## These will actually use the sha256 result from the random module. - passwords = { - hash_salt = { - length = 32 - } - cron_key = { - length = 32 - } - } - } - ) - - ################################# - ## - ## ____ _ - ## / ___|| |_ __ _ __ _ ___ - ## \___ \| __/ _` |/ _` |/ _ \ - ## ___) | || (_| | (_| | __/ - ## |____/ \__\__,_|\__, |\___| - ## |___/ - ## - ################################# - - # stage = merge( - # { - # ## Applications to deploy. - # apps = { - # waf = merge( - # local.globals.apps.waf, - # { - # instances = 2 - # } - # ) - # } - # services = { - # mysql = local.globals.services.mysql - # secrets = local.globals.services.secrets - # static = local.globals.services.static - # storage = local.globals.services.storage - # } - # circleci_variables = local.globals.circleci_variables - # }, - # { - # ## Passwords that need to be generated for this environment. - # ## These will actually use the sha256 result from the random module. - # passwords = { - # hash_salt = { - # length = 32 - # } - # cron_key = { - # length = 32 - # } - # } - # } - # ) - - ################################# - ## - ## _____ _ - ## |_ _|__ ___| |_ - ## | |/ _ \/ __| __| - ## | | __/\__ \ |_ - ## |_|\___||___/\__| - ## - ################################# - - # test = merge( - # { - # ## Applications to deploy. - # apps = { - # waf = local.globals.apps.waf - # } - # services = { - # mysql = local.globals.services.mysql - # secrets = local.globals.services.secrets - # static = local.globals.services.static - # storage = local.globals.services.storage - # } - # circleci_variables = local.globals.circleci_variables - # }, - # { - # ## The space to deploy to the application to. - # space = "${local.project}-dev" - - # ## Passwords that need to be generated for this environment. - # ## These will actually use the sha256 result from the random module. - # passwords = { - # hash_salt = { - # length = 32 - # } - # cron_key = { - # length = 32 - # } - # } - # } - # ) - } - - ## Map of the 'all' environement and the current workspace settings. - env = merge(try(local.envs.all, {}), try(local.envs[terraform.workspace], {})) -} diff --git a/infra/locals_dynamic.tf b/infra/locals_dynamic.tf deleted file mode 100644 index 9bdcf7d64..000000000 --- a/infra/locals_dynamic.tf +++ /dev/null @@ -1,160 +0,0 @@ -locals { - - ## Map of merged external and internal applications. - apps_merged = merge( - try(local.env.apps, {}), - try(local.external_applications[terraform.workspace], {}) - ) - - ## Map of application routes that will be created. - domains = merge( - merge( - flatten([ - for key, value in try(local.apps_merged, {}) : { - "${key}_internal_endpoint" = "${format(local.env.name_pattern, key)}.${local.env.internal_domain}" - } if !try(value.public_route, false) - ]) - ...), - merge( - flatten([ - for key, value in try(local.apps_merged, {}) : { - "${key}_external_endpoint" = "${format(local.env.name_pattern, key)}.${local.env.external_domain}" - } if try(value.public_route, false) - ]) - ...) - ) - - ## Additional environment variables that applications might need. - extra_variables = { - all = merge( - { - # ca_certificate = module.certificates.certificate.base64 - # ca_key = module.certificates.key.base64 - cf_space = local.env.space - # cms_uri = terraform.workspace == "prod" ? "https://cms..gov" : format( - # "https://cms-%s..gov", - # terraform.workspace - # ) - cms_uri = terraform.workspace == "main" ? "https://bf-cms-main.bxdev.net" : "https://bf-cms-dev.bxdev.net" - - drupal_instances = try( - local.external_applications[terraform.workspace].drupal.instances, - local.env.defaults.instances - ) - drupal_memory = try( - local.external_applications[terraform.workspace].drupal.memory, - local.env.defaults.memory - ) - drupal_port = try( - local.external_applications[terraform.workspace].drupal.port, - local.env.defaults.port - ) - #newrelic_key = var.newrelic_key - # proxy_uri = format( - # "https://%s:%s@%s.%s:%s", - # var.proxy_username, - # var.proxy_password, - # format(local.egress.name_pattern, local.egress.name), - # local.env.internal_domain, - # var.mtls_port - # ) - # ssg_uri = terraform.workspace == "prod" ? "https://ssg..gov" : format( - # "https://ssg-%s..gov", - # terraform.workspace - # ) - ssg_uri = terraform.workspace == "main" ? "https://bf-static-main.bxdev.net" : "https://bf-static-dev.bxdev.net" - # sso_x509_cert = var.sso_x509_cert - waf_name = format(local.env.name_pattern, "waf") - }, - merge( - flatten([ - for key, value in module.random.results : { - "${key}" = value.result - } - ]) - ...) - ) - bootstrap = { - # may be needed for bootstrap but variables have been removed - #backend_aws_bucket_name = var.backend_aws_bucket_name - #backend_aws_bucket_region = var.backend_aws_bucket_region - #backup_aws_bucket_name = var.backup_aws_bucket_name - #backup_aws_bucket_region = var.backup_aws_bucket_region - //circleci_token = var.circleci_token - cloudgov_password = var.cloudgov_password - cloudgov_username = var.cloudgov_username - cf_org = local.env.organization - drupal_port = var.mtls_port - # no_proxy = ".${local.env.internal_domain}" - project = local.project - # dmz_space = local.env.egress.space - proxy_credentials = jsonencode( - merge( - flatten([ - for key, value in module.random.results : { - "${key}" = value.result - } if try(regex("^(?:\\w+_)(proxy_password|proxy_username)$", key), null) != null - ]) - ...) - ) - }, - dev = { - drupal_internal_endpoint = "benefit-finder-cms-dev.apps.internal" - }, - main = { - drupal_internal_endpoint = "benefit-finder-cms-main.apps.internal" - } - # dmz = { - # proxy_password = var.proxy_password, - # proxy_port = local.env.egress.port - # proxy_username = var.proxy_username - # proxy_name = format( - # local.env.egress.name_pattern, - # local.env.egress.name - # ) - # proxy_space = local.env.space - # } - } - - ## Map of service instances and secrets merged together. - services = { - instance = merge( - module.services.results.instance, - module.secrets.results.instance - ) - user_provided = merge( - module.services.results.user_provided, - module.secrets.results.user_provided - ) - service_key = merge( - module.services.results.service_key, - module.secrets.results.service_key - ) - } - - ## Map of service credentials (i.e. S3 bucket credentials). - service_keys = merge( - flatten([ - for key, value in try(local.env.services, {}) : [ - for k, v in try(module.services.results.service_key[key].credentials, {}) : { - "${key}_${k}" = v - } - ] if try(module.services.results.service_key[key].credentials, null) != null - ]) - ...) - - ## Merging of the various credentials and environmental variables. - secrets = merge( - try(local.extra_variables[terraform.workspace], {}), - try(local.extra_variables.all, {}), - try(local.service_keys, {}), - local.domains - ) - - ## List of the workspaces defined in the configuration above. - workspaces = flatten([ - for key, value in local.envs : [ - key - ] if key != "all" || key != local.bootstrap_workspace - ]) -} diff --git a/infra/modules/application/README.md b/infra/modules/application/README.md deleted file mode 100644 index ad519ed49..000000000 --- a/infra/modules/application/README.md +++ /dev/null @@ -1,138 +0,0 @@ - -# CloudFoundry Application Module - -## Requirements - -| Name | Version | -|------|---------| -| [archive](#requirement\_archive) | 2.4.0 | -| [cloudfoundry](#requirement\_cloudfoundry) | 0.51.2 | -| [local](#requirement\_local) | 2.4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [archive](#provider\_archive) | 2.4.0 | -| [cloudfoundry](#provider\_cloudfoundry) | 0.51.2 | -| [local](#provider\_local) | 2.4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [cloudfoundry_app.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/app) | resource | -| [cloudfoundry_network_policy.egress_proxy](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/network_policy) | resource | -| [cloudfoundry_network_policy.ingress_proxy](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/network_policy) | resource | -| [cloudfoundry_route.external](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/route) | resource | -| [cloudfoundry_route.internal](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/route) | resource | -| [local_sensitive_file.this](https://registry.terraform.io/providers/hashicorp/local/2.4.0/docs/resources/sensitive_file) | resource | -| [archive_file.this](https://registry.terraform.io/providers/hashicorp/archive/2.4.0/docs/data-sources/file) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [cloudfoundry](#input\_cloudfoundry) | The settings object for Cloudfoundry. | `any` | n/a | yes | -| [env](#input\_env) | The settings object for this environment. | `any` | n/a | yes | -| [secrets](#input\_secrets) | Sensitive credentials to be used to set application environmental variables. | `map` | `{}` | no | -| [services](#input\_services) | Services generated from the service module. | `any` |
{
"instance": null,
"service_key": null,
"user_provided": null
}
| no | - -## Outputs - -| Name | Description | -|------|-------------| -| [apps](#output\_apps) | A `map` of [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource outputs. The key is the app name. | -| [external\_endpoints](#output\_external\_endpoints) | A map of external URL's (app.cloud.gov) to used to reach an application. The key is the app name. | -| [internal\_endpoints](#output\_internal\_endpoints) | A map of internal URL's (apps.internal) to used to reach an application. The key is the app name. | - -## Example - -```terraform -module "applications" { - source = "./modules/application" - - cloudfoundry = local.cloudfoundry - env = local.env - secrets = local.secrets - services = local.services -} -``` - -## Variables - -### cloudfoundry - -A variable that contains a `map(string)` of data lookups for pre-existing resources from Cloud.gov. This includes thing such as the organization and space ids. These are defined in `data.tf` in the root directory. - -### env - -A mixed type `object` variable that contains application settings. It is passed as an `any` type to allow optional variables to be ommitted from the object. It is defined in `locals.tf`, in the root directory. The object `local.env[terraform.workspace].apps` stores the values for the specific application that is to be deployed. - -Valid options are the attributes for the [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource. - -### secrets - -A variable that has secrets and other credentials that the application uses. The `local.secrets` variable is generated in `locals_dynamic.tf`, as it merges a variety of credentials from the random and services modules. - -### services - -A variable that contains a `map(map(string))` of the services deployed in the environment. `local.services` is generated in `locals_dynamic.tf`, due to needing to be generated after the creation of the services, after the instance id are known. The services are then bound to the application. - -See the [service module](../service/readme.MD) for more information. - -## Usage - -Here is an example of how to define an application in `locals.tf`. - -```terraform -locals { - env = { - workspace1 = { - apps = { - application1 = { - buildpacks = [ - "staticfile_buildpack" - ] - command = "./start" - disk_quota = 256 - enable_ssh = true - environment = { - environment = terraform.workspace - LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" - } - health_check_timeout = 180 - health_check_type = "port" - instances = 1 - labels = { - environment = terraform.workspace - } - memory = 64 - port = 8080 - public_route = false - - source = "/path/to/application/directory" - - templates = [ - { - source = "${path.cwd}/path/to/templates/template.tmpl" - destination = "${path.cwd}}/path/to/templates/file" - } - ] - } - } - } - } -} -``` - -## Additional Notes - -- Buildpacks - - Valid built-in Cloud.gov buildpacks can be found by running `cf buildpacks` from the CLI. - - External buildpacks, such as the `apt-buildpack` by referencing the URL to the buildpack repository: [https://github.com/cloudfoundry/apt-buildpack](https://github.com/cloudfoundry/apt-buildpack). - \ No newline at end of file diff --git a/infra/modules/application/variables.tf b/infra/modules/application/variables.tf deleted file mode 100755 index 082a65f93..000000000 --- a/infra/modules/application/variables.tf +++ /dev/null @@ -1,25 +0,0 @@ -variable "cloudfoundry" { - description = "The settings object for Cloudfoundry." - type = any -} - -variable "env" { - description = "The settings object for this environment." - type = any -} - -variable "secrets" { - description = "Sensitive credentials to be used to set application environmental variables." - type = map - default = {} -} - -variable "services" { - description = "Services generated from the service module." - type = any - default = { - instance = null - user_provided = null - service_key = null - } -} \ No newline at end of file diff --git a/infra/modules/certificate/.terraform-docs/footer.md b/infra/modules/certificate/.terraform-docs/footer.md deleted file mode 100755 index eeabe85c5..000000000 --- a/infra/modules/certificate/.terraform-docs/footer.md +++ /dev/null @@ -1,10 +0,0 @@ -## Usage - -```terraform -module "certificates" { - source = "./modules/certificates" - - project_name = local.env.project - workspace = local.env.bootstrap_workspace -} -``` diff --git a/infra/modules/certificate/.terraform-docs/header.md b/infra/modules/certificate/.terraform-docs/header.md deleted file mode 100755 index af1e49904..000000000 --- a/infra/modules/certificate/.terraform-docs/header.md +++ /dev/null @@ -1,7 +0,0 @@ -# CloudFoundry Certificates Module - -## Introduction - -This module is used to create self-signed certificates. - -The outputs of this module are `certificate` and `key`. This is a `map` with they keys `certificate` and `base64`, which is the base64 encoded value of the certificate value. \ No newline at end of file diff --git a/infra/modules/certificate/README.md b/infra/modules/certificate/README.md deleted file mode 100644 index bcdec91a6..000000000 --- a/infra/modules/certificate/README.md +++ /dev/null @@ -1,57 +0,0 @@ - -# CloudFoundry Certificates Module - -## Introduction - -This module is used to create self-signed certificates. - -The outputs of this module are `certificate` and `key`. This is a `map` with they keys `certificate` and `base64`, which is the base64 encoded value of the certificate value. - -## Requirements - -| Name | Version | -|------|---------| -| [tls](#requirement\_tls) | 4.0.4 | - -## Providers - -| Name | Version | -|------|---------| -| [tls](#provider\_tls) | 4.0.4 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [tls_private_key.this](https://registry.terraform.io/providers/hashicorp/tls/4.0.4/docs/resources/private_key) | resource | -| [tls_self_signed_cert.this](https://registry.terraform.io/providers/hashicorp/tls/4.0.4/docs/resources/self_signed_cert) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [project\_name](#input\_project\_name) | The project name. | `string` | n/a | yes | -| [workspace](#input\_workspace) | Workspace to generate certificates in. | `string` | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| [certificate](#output\_certificate) | The CA certificate generated by the module. | -| [key](#output\_key) | The CA key generated by the module. | - -## Usage - -```terraform -module "certificates" { - source = "./modules/certificates" - - project_name = local.env.project - workspace = local.env.bootstrap_workspace -} -``` - \ No newline at end of file diff --git a/infra/modules/certificate/main.tf b/infra/modules/certificate/main.tf deleted file mode 100644 index 27b55d6c4..000000000 --- a/infra/modules/certificate/main.tf +++ /dev/null @@ -1,33 +0,0 @@ -locals { - is_deployment_workspace = terraform.workspace == var.workspace ? true : false -} - -resource "tls_private_key" "this" { - count = !local.is_deployment_workspace ? 1 : 0 - algorithm = "RSA" - rsa_bits = 4096 -} - -resource "tls_self_signed_cert" "this" { - count = !local.is_deployment_workspace ? 1 : 0 - private_key_pem = tls_private_key.this[0].private_key_pem - - is_ca_certificate = true - - subject { - country = "US" - province = "Washington" - locality = "DC" - common_name = "${var.project_name} CA" - organization = "${var.project_name}" - organizational_unit = "${var.project_name}" - } - - validity_period_hours = try(var.env.certificate_authority_validity, 8766) - early_renewal_hours = try(var.env.certificate_authority_renewal, 1461) - allowed_uses = [ - "digital_signature", - "cert_signing", - "crl_signing", - ] -} diff --git a/infra/modules/certificate/outputs.tf b/infra/modules/certificate/outputs.tf deleted file mode 100755 index 201dbe588..000000000 --- a/infra/modules/certificate/outputs.tf +++ /dev/null @@ -1,15 +0,0 @@ -output "certificate" { - description = "The CA certificate generated by the module." - value = { - certificate = local.is_deployment_workspace ? tls_self_signed_cert.this[0].cert_pem : null - base64 = local.is_deployment_workspace ? base64encode(tls_self_signed_cert.this[0].cert_pem) : null - } -} - -output "key" { - description = "The CA key generated by the module." - value = { - certificate = local.is_deployment_workspace ? tls_private_key.this[0].private_key_pem : null - base64 = local.is_deployment_workspace ? base64encode(tls_private_key.this[0].private_key_pem) : null - } -} \ No newline at end of file diff --git a/infra/modules/certificate/providers.tf b/infra/modules/certificate/providers.tf deleted file mode 100644 index a27a4fac0..000000000 --- a/infra/modules/certificate/providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - tls = { - source = "hashicorp/tls" - version = "4.0.4" - } - } -} diff --git a/infra/modules/certificate/variables.tf b/infra/modules/certificate/variables.tf deleted file mode 100755 index c1a0a965b..000000000 --- a/infra/modules/certificate/variables.tf +++ /dev/null @@ -1,14 +0,0 @@ -variable "env" { - description = "The settings object for this environment." - type = any -} - -variable "project_name" { - description = "The project name." - type = string -} - -variable "workspace" { - description = "Workspace to generate certificates in." - type = string -} \ No newline at end of file diff --git a/infra/modules/random/variables.tf b/infra/modules/random/variables.tf deleted file mode 100755 index bf8c4279a..000000000 --- a/infra/modules/random/variables.tf +++ /dev/null @@ -1,22 +0,0 @@ -variable "expiration" { - type = number - description = "A number, in days, when the password should be rotated. If none is set, the password will not rotate." - default = 0 -} - -variable "names" { - type = list(string) - description = "List of unique names for the multiple resources." - default = [] -} - -variable "passwords" { - type = any - description = "A map of objects with password settings." -} - -variable "per_workspace" { - type = bool - description = "Generate a password for each workspace." - default = false -} \ No newline at end of file diff --git a/infra/modules/service/README.md b/infra/modules/service/README.md deleted file mode 100644 index ecf6e52d4..000000000 --- a/infra/modules/service/README.md +++ /dev/null @@ -1,88 +0,0 @@ - -# CloudFoundry Service Module - -## Requirements - -| Name | Version | -|------|---------| -| [cloudfoundry](#requirement\_cloudfoundry) | 0.51.2 | - -## Providers - -| Name | Version | -|------|---------| -| [cloudfoundry](#provider\_cloudfoundry) | 0.51.2 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [cloudfoundry_service_instance.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/service_instance) | resource | -| [cloudfoundry_service_key.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/service_key) | resource | -| [cloudfoundry_user_provided_service.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/0.51.2/docs/resources/user_provided_service) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [cloudfoundry](#input\_cloudfoundry) | Cloudfoundry settings. | `any` | n/a | yes | -| [env](#input\_env) | The settings map for this environment. | `any` | n/a | yes | -| [passwords](#input\_passwords) | Sensitive strings to be added to the apps environmental variables. | `map` | `{}` | no | -| [secrets](#input\_secrets) | Sensitive strings to be added to the apps environmental variables. | `map` | `{}` | no | -| [skip\_service\_instances](#input\_skip\_service\_instances) | Allows the skipping of service instances. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | -| [skip\_user\_provided\_services](#input\_skip\_user\_provided\_services) | Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [name](#output\_name) | n/a | -| [results](#output\_results) | n/a | - -## Examples - -### Basic -```terraform -module "services" { - source = "./modules/service" - - cloudfoundry = local.cloudfoundry - env = local.env -} -``` - -### Advanced - -This advanced example will first generate service instances, such as RDS, along with other defined services, except for the `user defined` services. `User defined` services are useful for providing variables at runtime to applications. The issue is that until a service, such as RDS is deployed, their isn't a username and password created for that instance. - -The first step is to initalize any services that are not `user defined`, but setting `skip_user_provided_services` to `true`. - -```terraform -module "services" { - source = "./modules/service" - - cloudfoundry = local.cloudfoundry - env = local.env - - skip_user_provided_services = true -} -``` - -After the services are generated, another module block can be defined, which will pass a merged `map(string)` called `secrets`, that have the various information that is to be added to the `user defined` service. Setting the `skip_service_instances` to `true` will prevent the module from trying to redploy any non `user defined` service. - -```terraform -module "secrets" { - source = "./modules/service" - - cloudfoundry = local.cloudfoundry - env = local.env - - secrets = local.secrets - skip_service_instances = true -} -``` - \ No newline at end of file diff --git a/infra/modules/service/variables.tf b/infra/modules/service/variables.tf deleted file mode 100755 index 3ecec7f35..000000000 --- a/infra/modules/service/variables.tf +++ /dev/null @@ -1,33 +0,0 @@ -variable "cloudfoundry" { - description = "Cloudfoundry settings." - type = any -} - -variable "env" { - description = "The settings map for this environment." - type = any -} - -variable "passwords" { - description = "Sensitive strings to be added to the apps environmental variables." - type = map - default = {} -} - -variable "skip_service_instances" { - description = "Allows the skipping of service instances. Useful to inject service secrets into a user provided secret." - type = bool - default = false -} - -variable "skip_user_provided_services" { - description = "Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret." - type = bool - default = false -} - -variable "secrets" { - description = "Sensitive strings to be added to the apps environmental variables." - type = map - default = {} -} \ No newline at end of file diff --git a/infra/terraform.tfvars.tmpl b/infra/terraform.tfvars.tmpl deleted file mode 100644 index a32c21261..000000000 --- a/infra/terraform.tfvars.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -backend_aws_bucket_name="$backend_aws_bucket_name" -backend_aws_bucket_region="$backend_aws_bucket_region" - -#circleci_token="$circleci_token" - -cloudgov_password="$cloudgov_password" -cloudgov_username="$cloudgov_username" - -newrelic_key="$newrelic_key" - -proxy_username="$proxy_username" -proxy_password="$proxy_password" \ No newline at end of file diff --git a/infra/variables.tf b/infra/variables.tf deleted file mode 100755 index 2828f864a..000000000 --- a/infra/variables.tf +++ /dev/null @@ -1,78 +0,0 @@ -# variable "circleci_token" { -# description = "CircleCI token." -# type = string -# sensitive = true -# } - -variable "cloudgov_username" { - description = "The username for the cloudfoundry account." - type = string - sensitive = true -} - -variable "cloudgov_password" { - description = "The password for the cloud.gov account." - type = string - sensitive = true -} - -variable "mtls_port" { - description = "The default port to direct traffic to. Envoy proxy listens on 61443 and redirects to 8080, which the application should listen on." - type = number - default = 61443 -} - -# variable "newrelic_key" { -# description = "The API key for New Relic." -# type = string -# sensitive = true -# } - -# variable "no_proxy" { -# description = "URIs that shouldn't be using the proxy to communicate." -# type = string -# default = "apps.internal,localhost,127.0.0.1" -# } - -# variable "proxy_password" { -# description = "The proxy password." -# type = string -# sensitive = true -# } - -# variable "proxy_username" { -# description = "The proxy username." -# type = string -# sensitive = true -# } - -# variable "sso_x509_cert" { -# description = "x509 cert used for Secure Auth SSO." -# type = string -# sensitive = true -# } - -# may be needed for bootstrap but variables have been removed -#variable "backup_aws_bucket_name" { -#description = "The S3 bucket used for persistent file backups." -#type = string -#sensitive = true -#} - -#variable "backup_aws_region_name" { -#description = "The S3 region used for persistent file backups." -#type = string -#sensitive = true -#} - -#variable "backend_aws_bucket_name" { -#description = "The S3 bucket used for persistent file backends." -#type = string -#sensitive = true -#} - -#variable "backend_aws_region_name" { -#description = "The S3 region used for persistent file backends." -#type = string -#sensitive = true -#} diff --git a/scripts/local/cloud-gov-s3-creds.sh b/scripts/local/cloud-gov-s3-creds.sh index 16b15d383..811ee52a0 100755 --- a/scripts/local/cloud-gov-s3-creds.sh +++ b/scripts/local/cloud-gov-s3-creds.sh @@ -11,7 +11,7 @@ if [ -z "${bucket_name}" ]; then return fi -echo "Getting bucket credentials..." +echo "Deleting old credentials..." { service_key="${bucket_name}-${user}-key" @@ -33,4 +33,27 @@ echo "Creating key..." export AWS_BUCKET=${aws_bucket_name} export AWS_DEFAULT_REGION=${aws_bucket_region} export AWS_SECRET_ACCESS_KEY=${aws_secret_key} -} >/dev/null 2>&1 \ No newline at end of file + + cat >~/current_creds.sh << EOT +export AWS_ACCESS_KEY_ID=${aws_access_key} +export AWS_BUCKET=${aws_bucket_name} +export AWS_DEFAULT_REGION=${aws_bucket_region} +export AWS_SECRET_ACCESS_KEY=${aws_secret_key} +EOT +chmod +x ~/current_creds.sh +} >/dev/null 2>&1 + +declare eror >/dev/null 2>&1 + +[ -z "${AWS_ACCESS_KEY_ID}" ] && eror=true + +if [ -n "${eror}" ]; then + echo -e "Error setting AWS credentials." + echo -e "Please ensure you're logged in to Cloud.gov." + echo -e "You can check by running: cf target -s " + + ## `CI` is set to true in GitHub action pipelines. So if it's empty, just return so it doesn't exit the terminal. + [ -z "${CI}" ] && return + + exit 1 +fi \ No newline at end of file diff --git a/scripts/local/setup-benefit-finder-test.sh b/scripts/local/setup-benefit-finder-test.sh old mode 100644 new mode 100755 diff --git a/scripts/local/setup-usagov-benefit-finder.sh b/scripts/local/setup-usagov-benefit-finder.sh old mode 100644 new mode 100755 diff --git a/scripts/pipeline/cloud-gov-remote-command.sh b/scripts/pipeline/cloud-gov-remote-command.sh index 63fb1c098..ea20fc900 100755 --- a/scripts/pipeline/cloud-gov-remote-command.sh +++ b/scripts/pipeline/cloud-gov-remote-command.sh @@ -1,13 +1,33 @@ #!/bin/bash -application=$1 +APP_NAME=$1 command=$2 +show_output=$3 -drush_path=/var/www/vendor/bin/ +APP_GUID=$(cf app "${APP_NAME}" --guid) +bin_path="/var/www/vendor/bin/:/home/vcap/deps/0/bin/" -[ -z "${application}" ] || [ -z "${command}" ] && echo "Command error! Valid format: ${0} " && exit 1 +[ -z "${APP_NAME}" ] || [ -z "${command}" ] && echo "Command error! Valid format: ${0} " && exit 1 -echo "Running command: '$(echo "${command}" | cut -d' ' -f1,2)'..." -{ - cf ssh "${application}" -c "PATH=\$PATH:${drush_path} ${command}" -} >/dev/null 2>&1 \ No newline at end of file +ssh_config=/tmp/ssh_config +ssh_passwd="/tmp/ssh_password" + +cat >${ssh_config} < ${ssh_passwd} + +if [ -z "${show_output}" ]; then + echo "Running command: '$(echo "${command}" | cut -d' ' -f1,2)'..." + { + sshpass -f "${ssh_passwd}" ssh -F "${ssh_config}" "ssh.fr.cloud.gov" "touch ~/.bashrc && source ~/.bashrc && PATH=\$PATH:${bin_path} ${command}" + } >/dev/null 2>&1 +else + sshpass -f "${ssh_passwd}" ssh -F "${ssh_config}" "ssh.fr.cloud.gov" "touch ~/.bashrc && source ~/.bashrc && PATH=\$PATH:${bin_path} ${command}" +fi \ No newline at end of file diff --git a/scripts/pipeline/cloud-gov-scp-file.sh b/scripts/pipeline/cloud-gov-scp-file.sh new file mode 100755 index 000000000..9aa62f1cb --- /dev/null +++ b/scripts/pipeline/cloud-gov-scp-file.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +APP_NAME=$1 +LOCAL=$2 +REMOTE=$3 + +APP_GUID=$(cf app "${APP_NAME}" --guid) + +ssh_config=/tmp/ssh_config +ssh_passwd="/tmp/ssh_password" + +cat >${ssh_config} < ${ssh_passwd} + +if [ -d "${LOCAL}" ]; then + echo "Uploading folder '${LOCAL}'..." + sshpass -f "${ssh_passwd}" scp -F ${ssh_config} -r "${LOCAL}" "ssh.fr.cloud.gov:${REMOTE}" +else + echo "Uploading file '${LOCAL}'..." + sshpass -f "${ssh_passwd}" scp -F ${ssh_config} "${LOCAL}" "ssh.fr.cloud.gov:${REMOTE}" +fi + +echo "Upload complete." \ No newline at end of file diff --git a/scripts/pipeline/cloud-gov-waf-version.sh b/scripts/pipeline/cloud-gov-waf-version.sh index fa29249f8..4c613a65b 100755 --- a/scripts/pipeline/cloud-gov-waf-version.sh +++ b/scripts/pipeline/cloud-gov-waf-version.sh @@ -12,28 +12,42 @@ else unzip pup_v0.4.0_linux_amd64.zip -d /usr/local/bin fi -declare CLOUDGOV_WF_VERSION -CLOUDGOV_WF_VERSION=$(cf app "${PROJECT}-waf-${BRANCH}" | grep nginx_buildpack | xargs | awk '{print $2}') +declare CURRENT_BP_VERSION +CURRENT_BP_VERSION=$(cf app "${PROJECT}-waf-${BRANCH}" | grep nginx_buildpack | xargs | awk '{print $2}') -declare CLOUDGOV_BP_VERSION -CLOUDGOV_BP_VERSION=$(cf buildpacks | grep nginx | grep cflinuxfs4 | awk '{print $NF}' | grep -Eo '[0-9]\.[0-9]?(.[0-9]+)') +declare NEW_BP_VERSION +NEW_BP_VERSION=$(cf buildpacks | grep nginx | grep cflinuxfs4 | awk '{print $NF}' | grep -Eo '[0-9]\.[0-9]?(.[0-9]+)') -bp_version=$(version "${CLOUDGOV_BP_VERSION}") -waf_version=$(version "${CLOUDGOV_WF_VERSION}") +new_bp_version=$(version "${NEW_BP_VERSION}") +current_bp_version=$(version "${CURRENT_BP_VERSION}") -if [ "${bp_version}" -ne "${waf_version}" ]; then +if [ "${new_bp_version}" -ne "${current_bp_version}" ]; then echo "New version of buildpack found!" + + curl -Ls "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${CURRENT_BP_VERSION}" > /tmp/current_bp_version declare current_nginx_version - current_nginx_version=$(curl -s "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${CLOUDGOV_WF_VERSION}" | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text == "cflinuxfs4") | .children[].text' | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}') + current_nginx_version=$(cat /tmp/current_bp_version | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text == "cflinuxfs4") | .children[].text' | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}') + + curl -Ls "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${NEW_BP_VERSION}" > /tmp/new_nginx_version + + declare default_nginx_binary_version + default_nginx_binary_version=$(cat /tmp/new_nginx_version | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text | contains(".x")) | .children[].text' | grep -v nginx | sed 's/.\{1\}$//') + + declare new_nginx_version + new_nginx_version=$(cat /tmp/new_nginx_version | pup 'table json{}' | jq -r ".[].children[].children[] | select(.children[].text == \"nginx\") | select(.children[].text == \"cflinuxfs4\") | select(.children[].text | contains(\"${default_nginx_binary_version}\")) | .children[].text" | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}') - declare nginx_version - nginx_version=$(curl -s "https://github.com/cloudfoundry/nginx-buildpack/releases/tag/v${CLOUDGOV_BP_VERSION}" | pup 'table json{}' | jq -r '.[].children[].children[] | select(.children[].text == "nginx") | select(.children[].text == "cflinuxfs4") | .children[].text' | tr '\n' ' ' | sed -E 's/cflinuxfs4 /cflinuxfs4\n/g' | sort -r | head -n 1 | awk '{print $2}') - echo "nginx_version=${nginx_version}" >> "${GITHUB_OUTPUT}" + echo "new_nginx_version=${new_nginx_version}" >> "${GITHUB_OUTPUT}" echo "current_nginx_version=${current_nginx_version}" >> "${GITHUB_OUTPUT}" - echo "cloudgov_wf_version=${CLOUDGOV_WF_VERSION}" >> "${GITHUB_OUTPUT}" - echo "cloudgov_bp_version=${CLOUDGOV_BP_VERSION}" >> "${GITHUB_OUTPUT}" + echo "current_bp_version=${CURRENT_BP_VERSION}" >> "${GITHUB_OUTPUT}" + echo "new_bp_version=${NEW_BP_VERSION}" >> "${GITHUB_OUTPUT}" echo "update=true" >> "${GITHUB_OUTPUT}" else echo "Running latest version of the buildpack!" -fi \ No newline at end of file +fi + + echo "new_nginx_version=${new_nginx_version}" + echo "current_nginx_version=${current_nginx_version}" + echo "current_bp_version=${CURRENT_BP_VERSION}" + echo "new_bp_version=${NEW_BP_VERSION}" + echo "update=true" \ No newline at end of file diff --git a/scripts/pipeline/cloud-gov-wait-for-app-start.sh b/scripts/pipeline/cloud-gov-wait-for-app-start.sh new file mode 100755 index 000000000..176a7f81a --- /dev/null +++ b/scripts/pipeline/cloud-gov-wait-for-app-start.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +app_name=$1 + +while : ; do + app_status=$( cf app "${app_name}" | grep "#0" | awk '{print $2}' ) + if [ "${app_status}" == "running" ]; then + break + else + echo "waiting for application to start..." + sleep 2 + fi +done \ No newline at end of file diff --git a/scripts/pipeline/deb-basic-deps.sh b/scripts/pipeline/deb-basic-deps.sh index 51e5c9346..4824710a2 100755 --- a/scripts/pipeline/deb-basic-deps.sh +++ b/scripts/pipeline/deb-basic-deps.sh @@ -1,10 +1,10 @@ #!/bin/bash -apps="ca-certificates coreutils curl gnupg gettext jq wget" +apps="ca-certificates coreutils curl gnupg gettext jq sshpass wget" ## To work for rootless and root images. echo "Installing basic dependencies..." -{ +#{ if [ "$(whoami)" != "root" ]; then sudo apt-get update sudo apt-get install -y $apps @@ -12,4 +12,4 @@ echo "Installing basic dependencies..." apt-get update apt-get install -y $apps fi -} >/dev/null 2>&1 \ No newline at end of file +#} >/dev/null 2>&1 \ No newline at end of file diff --git a/scripts/pipeline/github-update-issue-status.sh b/scripts/pipeline/github-update-issue-status.sh index 903100141..ba8e97abe 100755 --- a/scripts/pipeline/github-update-issue-status.sh +++ b/scripts/pipeline/github-update-issue-status.sh @@ -1,5 +1,15 @@ #!/bin/bash +home="/home/vcap" +PATH="${PATH}:${home}/deps/0/bin/" + +VERSION=$(curl "https://api.github.com/repos/cli/cli/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/' | cut -c2-) +curl -sSL "https://github.com/cli/cli/releases/download/v${VERSION}/gh_${VERSION}_linux_amd64.tar.gz" -o "gh_${VERSION}_linux_amd64.tar.gz" + +tar xvf "gh_${VERSION}_linux_amd64.tar.gz" +cp "gh_${VERSION}_linux_amd64/bin/gh" ${home}/deps/0/bin/ + + ## Field configuration options. gh_status_option="QA" gh_domain_option="DevOps" diff --git a/scripts/pipeline/terraform-build-waf-plugin.sh b/scripts/pipeline/terraform-build-waf-plugin.sh new file mode 100755 index 000000000..334c6d2ee --- /dev/null +++ b/scripts/pipeline/terraform-build-waf-plugin.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +[ -z "${new_nginx_version}" ] && echo "NGINX version not set!" && exit 1 +[ -z "${modsecurity_nginx_version}" ] && echo "Modsecurity version not set!" && exit 1 +[ -z "${ubuntu_version}" ] && echo "Ubuntu version not set!" && exit 1 + +## The current root path. +CWD=$(pwd) + +## Path to the WAF application. +APP_PATH=terraform/applications/nginx-waf + +## Change directory to the Dockerfile path. +cd "${APP_PATH}/.docker/" || exit 1 + +## Run make, which builds the module and moves it to '../modules/'. +make + +## Change directory back to the root path. +cd "${CWD}" || exit 1 + +## Make sure the module directory on the bastion exists. +./scripts/pipeline/cloud-gov-remote-command.sh "${TF_BASTION}" "mkdir -p px-benefit-finder/${APP_PATH}/modules/" + +## SCP the module to the bastion. +./scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "${APP_PATH}/modules/" "px-benefit-finder/${APP_PATH}/" \ No newline at end of file diff --git a/scripts/pipeline/terraform-scp-tfvars.sh b/scripts/pipeline/terraform-scp-tfvars.sh new file mode 100755 index 000000000..ba8ffa4e0 --- /dev/null +++ b/scripts/pipeline/terraform-scp-tfvars.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +TF_PATH="terraform/infra" + +cd "./${TF_PATH}" || exit 1 + +## Update terraform.tfvars with pipeline user/password. +envsubst < terraform.tfvars.tmpl > terraform.tfvars + +cd "${CWD}" || exit 1 + +## SCP the file to the bastion. +./scripts/pipeline/cloud-gov-scp-file.sh "${TF_BASTION}" "./${TF_PATH}/terraform.tfvars" "px-benefit-finder/${TF_PATH}" \ No newline at end of file diff --git a/infra/.gitignore b/terraform/.gitignore similarity index 93% rename from infra/.gitignore rename to terraform/.gitignore index 419387d71..15b963fdd 100755 --- a/infra/.gitignore +++ b/terraform/.gitignore @@ -1,9 +1,9 @@ ## Terraform -terraform.tfstate.d -**terraform* +**.terraform* +**terraform.tfstate.d* **.tfvars* **tfplan* -provider.tf +terraform.tfstate* ## Other files/folders *.bak diff --git a/infra/README.md b/terraform/README.md similarity index 73% rename from infra/README.md rename to terraform/README.md index a02d508d4..00b6356ef 100644 --- a/infra/README.md +++ b/terraform/README.md @@ -3,12 +3,13 @@ - Applications - [Caddy Proxy](applications/caddy-proxy/README.md) - [Web Application Firewall](applications/nginx-waf/README.md) + - [Terraform Bastion](applications/tf-bastion/README.md) - [Scripts](docs/scripts.MD) - Terraform - modules - [application](modules/application/README.md) - - [certificate](modules/certificate/README.md) - - [circleci](modules/circleci/README.md) + - [certificate](modules/circleci/README.md) + - [circleci](modules/github/README.md) - [random](modules/random/README.md) - [service](modules/service/README.md) - [locals.tf](docs/locals.tf.MD) diff --git a/infra/applications/caddy-proxy/.docker/Dockerfile b/terraform/applications/caddy-proxy/.docker/Dockerfile similarity index 100% rename from infra/applications/caddy-proxy/.docker/Dockerfile rename to terraform/applications/caddy-proxy/.docker/Dockerfile diff --git a/infra/applications/caddy-proxy/.docker/Makefile b/terraform/applications/caddy-proxy/.docker/Makefile similarity index 100% rename from infra/applications/caddy-proxy/.docker/Makefile rename to terraform/applications/caddy-proxy/.docker/Makefile diff --git a/infra/applications/caddy-proxy/Caddyfile.tmpl b/terraform/applications/caddy-proxy/Caddyfile.tmpl similarity index 100% rename from infra/applications/caddy-proxy/Caddyfile.tmpl rename to terraform/applications/caddy-proxy/Caddyfile.tmpl diff --git a/infra/applications/caddy-proxy/README.md b/terraform/applications/caddy-proxy/README.md similarity index 100% rename from infra/applications/caddy-proxy/README.md rename to terraform/applications/caddy-proxy/README.md diff --git a/infra/applications/caddy-proxy/start b/terraform/applications/caddy-proxy/start similarity index 100% rename from infra/applications/caddy-proxy/start rename to terraform/applications/caddy-proxy/start diff --git a/infra/applications/nginx-waf/.docker/Dockerfile b/terraform/applications/nginx-waf/.docker/Dockerfile similarity index 100% rename from infra/applications/nginx-waf/.docker/Dockerfile rename to terraform/applications/nginx-waf/.docker/Dockerfile diff --git a/infra/applications/nginx-waf/.docker/Makefile b/terraform/applications/nginx-waf/.docker/Makefile similarity index 79% rename from infra/applications/nginx-waf/.docker/Makefile rename to terraform/applications/nginx-waf/.docker/Makefile index 61dc210d8..0019a5269 100644 --- a/infra/applications/nginx-waf/.docker/Makefile +++ b/terraform/applications/nginx-waf/.docker/Makefile @@ -1,5 +1,5 @@ nginx-waf-with-modsecurity: Dockerfile - docker build --platform linux/amd64 --tag nginx-modsecurity --build-arg=modsecurity_nginx_version=${modsecurity_nginx_version} --build-arg=nginx_version=${nginx_version} --build-arg=ubuntu_version=${ubuntu_version} . + docker build --platform linux/amd64 --tag nginx-modsecurity --build-arg=modsecurity_nginx_version=${modsecurity_nginx_version} --build-arg=nginx_version=${new_nginx_version} --build-arg=ubuntu_version=${ubuntu_version} . docker create --name nginx-vol nginx-modsecurity mkdir -p ../modules docker cp nginx-vol:/ngx_http_modsecurity_module.so ../modules diff --git a/infra/applications/nginx-waf/README.md b/terraform/applications/nginx-waf/README.md similarity index 100% rename from infra/applications/nginx-waf/README.md rename to terraform/applications/nginx-waf/README.md diff --git a/infra/applications/nginx-waf/apt.yml b/terraform/applications/nginx-waf/apt.yml similarity index 100% rename from infra/applications/nginx-waf/apt.yml rename to terraform/applications/nginx-waf/apt.yml diff --git a/infra/applications/nginx-waf/entrypoint b/terraform/applications/nginx-waf/entrypoint similarity index 100% rename from infra/applications/nginx-waf/entrypoint rename to terraform/applications/nginx-waf/entrypoint diff --git a/infra/applications/nginx-waf/init b/terraform/applications/nginx-waf/init similarity index 95% rename from infra/applications/nginx-waf/init rename to terraform/applications/nginx-waf/init index d02f680fb..a8651d6da 100755 --- a/infra/applications/nginx-waf/init +++ b/terraform/applications/nginx-waf/init @@ -33,7 +33,7 @@ source ${home}/.bashrc echo "Configuring Nginx..." ## Remove the comment from the load module now that the buildpack has started. -sed -i 's/^#load_module./load_module /' ${app_path}/nginx.conf +[ -f "/home/vcap/app/modules/ngx_http_modsecurity_module.so" ] && sed -i 's/^#load_module./load_module /' ${app_path}/nginx.conf ## Remove the comment for the reverse proxy configuration. sed -i 's/^ #include./include /' ${app_path}/nginx.conf @@ -54,7 +54,7 @@ rm -rf /tmp/owasp-crs if [ -n "${MODSECURITY_UPDATE}" ]; then modsecurity_version=$(echo "${MODSECURITY_UPDATE}" | cut -d '_' -f2 | cut -d '-' -f1) echo "Updating libmodsecurity..." - current_path=$(pwd) + #current_path=$(pwd) dpkg-deb -R "${app_path}/packages/${MODSECURITY_UPDATE}" ${home}/deps/0/apt/ ln -s "${home}/deps/0/apt/usr/lib/x86_64-linux-gnu/libmodsecurity.so.${modsecurity_version}" "libmodsecurity.so.${modsecurity_version}" diff --git a/infra/applications/nginx-waf/mime.types b/terraform/applications/nginx-waf/mime.types similarity index 100% rename from infra/applications/nginx-waf/mime.types rename to terraform/applications/nginx-waf/mime.types diff --git a/infra/applications/nginx-waf/modsecurity.conf b/terraform/applications/nginx-waf/modsecurity.conf similarity index 100% rename from infra/applications/nginx-waf/modsecurity.conf rename to terraform/applications/nginx-waf/modsecurity.conf diff --git a/infra/applications/nginx-waf/modsecurity/crs-setup.conf b/terraform/applications/nginx-waf/modsecurity/crs-setup.conf similarity index 100% rename from infra/applications/nginx-waf/modsecurity/crs-setup.conf rename to terraform/applications/nginx-waf/modsecurity/crs-setup.conf diff --git a/infra/applications/nginx-waf/modsecurity/mod-sec-rules.conf b/terraform/applications/nginx-waf/modsecurity/mod-sec-rules.conf similarity index 100% rename from infra/applications/nginx-waf/modsecurity/mod-sec-rules.conf rename to terraform/applications/nginx-waf/modsecurity/mod-sec-rules.conf diff --git a/infra/applications/nginx-waf/modsecurity/modsecurity-override.conf b/terraform/applications/nginx-waf/modsecurity/modsecurity-override.conf similarity index 100% rename from infra/applications/nginx-waf/modsecurity/modsecurity-override.conf rename to terraform/applications/nginx-waf/modsecurity/modsecurity-override.conf diff --git a/infra/applications/nginx-waf/modsecurity/modsecurity.conf b/terraform/applications/nginx-waf/modsecurity/modsecurity.conf similarity index 100% rename from infra/applications/nginx-waf/modsecurity/modsecurity.conf rename to terraform/applications/nginx-waf/modsecurity/modsecurity.conf diff --git a/infra/applications/nginx-waf/modsecurity/modsecurity.conf-recommended b/terraform/applications/nginx-waf/modsecurity/modsecurity.conf-recommended similarity index 100% rename from infra/applications/nginx-waf/modsecurity/modsecurity.conf-recommended rename to terraform/applications/nginx-waf/modsecurity/modsecurity.conf-recommended diff --git a/infra/applications/nginx-waf/modsecurity/unicode.mapping b/terraform/applications/nginx-waf/modsecurity/unicode.mapping similarity index 100% rename from infra/applications/nginx-waf/modsecurity/unicode.mapping rename to terraform/applications/nginx-waf/modsecurity/unicode.mapping diff --git a/terraform/applications/nginx-waf/modules/ngx_http_modsecurity_module.so b/terraform/applications/nginx-waf/modules/ngx_http_modsecurity_module.so new file mode 100755 index 000000000..8f323a72b Binary files /dev/null and b/terraform/applications/nginx-waf/modules/ngx_http_modsecurity_module.so differ diff --git a/infra/applications/nginx-waf/nginx.conf b/terraform/applications/nginx-waf/nginx.conf similarity index 100% rename from infra/applications/nginx-waf/nginx.conf rename to terraform/applications/nginx-waf/nginx.conf diff --git a/infra/applications/nginx-waf/nginx/conf.d/default.conf b/terraform/applications/nginx-waf/nginx/conf.d/default.conf similarity index 100% rename from infra/applications/nginx-waf/nginx/conf.d/default.conf rename to terraform/applications/nginx-waf/nginx/conf.d/default.conf diff --git a/infra/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf b/terraform/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf similarity index 100% rename from infra/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf rename to terraform/applications/nginx-waf/nginx/dynamic/deny-by-domain.conf diff --git a/infra/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh b/terraform/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh similarity index 100% rename from infra/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh rename to terraform/applications/nginx-waf/nginx/dynamic/deny_domain_by_ip.sh diff --git a/infra/applications/nginx-waf/nginx/dynamic/domains-deny.list b/terraform/applications/nginx-waf/nginx/dynamic/domains-deny.list similarity index 100% rename from infra/applications/nginx-waf/nginx/dynamic/domains-deny.list rename to terraform/applications/nginx-waf/nginx/dynamic/domains-deny.list diff --git a/infra/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl similarity index 100% rename from infra/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl rename to terraform/applications/nginx-waf/nginx/snippets/ip-restrict-cms.conf.tmpl diff --git a/infra/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl similarity index 100% rename from infra/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl rename to terraform/applications/nginx-waf/nginx/snippets/ip-restrict-static.conf.tmpl diff --git a/infra/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf b/terraform/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf similarity index 100% rename from infra/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf rename to terraform/applications/nginx-waf/nginx/snippets/owasp-modsecurity-main.conf diff --git a/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf new file mode 100755 index 000000000..b10699004 --- /dev/null +++ b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf @@ -0,0 +1,24 @@ +set $cf_forwarded_host "$host"; +set $cf_forwarded_uri "$request_uri"; + +set $cf_destination_host "benefit-finder-cms-main.apps.internal"; +set $cf_destination_port "61443"; + +set $base_host "$cf_forwarded_host"; +if ($cf_forwarded_host ~* ^(.*)-waf-(.*)\.app\.cloud\.gov$) { + set $base_host "$1-cms-$2"; +} + +proxy_http_version 1.1; +proxy_set_header Connection ""; +proxy_redirect off; +proxy_connect_timeout 300; +chunked_transfer_encoding off; + +proxy_set_header Host $cf_forwarded_host; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Real-IP $remote_addr; + +# Use XX-CF-APP-INSTANCE on the original request if you wish to target an instance +proxy_set_header X-CF-APP-INSTANCE $http_xx_cf_app_instance; +proxy_pass https://$cf_destination_host:$cf_destination_port$cf_forwarded_uri; diff --git a/infra/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl similarity index 92% rename from infra/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl rename to terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl index 8d515425e..b62bb7bd9 100644 --- a/infra/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl +++ b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl @@ -1,7 +1,7 @@ set $cf_forwarded_host "$host"; set $cf_forwarded_uri "$request_uri"; -set $cf_destination_host "${drupal_internal_endpoint}"; +set $cf_destination_host "${cms_internal_endpoint}"; set $cf_destination_port "61443"; set $base_host "$cf_forwarded_host"; diff --git a/terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf new file mode 100755 index 000000000..40f2b5fa8 --- /dev/null +++ b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf @@ -0,0 +1,14 @@ +proxy_http_version 1.1; +proxy_set_header Connection \"\"; +proxy_set_header Authorization ''; +proxy_set_header Host cg-3c6758a1-641a-4fc9-8b26-c3c9673e93dc.s3-fips.us-gov-west-1.amazonaws.com; +proxy_hide_header x-amz-id-2; +proxy_hide_header x-amz-request-id; +proxy_hide_header x-amz-meta-server-side-encryption; +proxy_hide_header x-amz-server-side-encryption; +proxy_hide_header Set-Cookie; +proxy_ignore_headers Set-Cookie; +proxy_intercept_errors on; +#add_header Cache-Control max-age=31536000; +add_header Cache-Control max-age=60; +proxy_pass https://cg-3c6758a1-641a-4fc9-8b26-c3c9673e93dc.s3-fips.us-gov-west-1.amazonaws.com; diff --git a/infra/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl similarity index 100% rename from infra/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl rename to terraform/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl diff --git a/terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf new file mode 100755 index 000000000..79c573c3c --- /dev/null +++ b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf @@ -0,0 +1,14 @@ +proxy_http_version 1.1; +proxy_set_header Connection \"\"; +proxy_set_header Authorization ''; +proxy_set_header Host cg-a0d91117-fbbe-426a-8e74-dad2148151c5.s3-fips.us-gov-west-1.amazonaws.com; +proxy_hide_header x-amz-id-2; +proxy_hide_header x-amz-request-id; +proxy_hide_header x-amz-meta-server-side-encryption; +proxy_hide_header x-amz-server-side-encryption; +proxy_hide_header Set-Cookie; +proxy_ignore_headers Set-Cookie; +proxy_intercept_errors on; +#add_header Cache-Control max-age=31536000; +add_header Cache-Control max-age=60; +proxy_pass https://cg-a0d91117-fbbe-426a-8e74-dad2148151c5.s3-fips.us-gov-west-1.amazonaws.com; diff --git a/infra/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl b/terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl similarity index 100% rename from infra/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl rename to terraform/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl diff --git a/infra/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz b/terraform/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz similarity index 100% rename from infra/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz rename to terraform/applications/nginx-waf/packages/coreruleset-4.0.0.tar.gz diff --git a/infra/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb b/terraform/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb similarity index 100% rename from infra/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb rename to terraform/applications/nginx-waf/packages/libmodsecurity3_3.0.9-1_amd64.deb diff --git a/infra/applications/nginx-waf/public/index.html b/terraform/applications/nginx-waf/public/index.html similarity index 100% rename from infra/applications/nginx-waf/public/index.html rename to terraform/applications/nginx-waf/public/index.html diff --git a/infra/applications/nginx-waf/start b/terraform/applications/nginx-waf/start similarity index 100% rename from infra/applications/nginx-waf/start rename to terraform/applications/nginx-waf/start diff --git a/terraform/applications/tf-bastion/apt.yml b/terraform/applications/tf-bastion/apt.yml new file mode 100755 index 000000000..5f6bd5ca8 --- /dev/null +++ b/terraform/applications/tf-bastion/apt.yml @@ -0,0 +1,6 @@ +--- +packages: + - curl + - gettext + - git + - wget diff --git a/terraform/applications/tf-bastion/start b/terraform/applications/tf-bastion/start new file mode 100755 index 000000000..4a8365fa8 --- /dev/null +++ b/terraform/applications/tf-bastion/start @@ -0,0 +1,35 @@ +#!/bin/bash + +home="/home/vcap" +#app_path="${home}/app" + +rm /home/vcap/deps/0/bin/tofu +wget -q "https://github.com/opentofu/opentofu/releases/download/v${OPENTOFU_VERSION}/tofu_${OPENTOFU_VERSION}_amd64.deb" +dpkg-deb -R "tofu_${OPENTOFU_VERSION}_amd64.deb" ${home}/deps/0/apt/ +ln -s "${home}/deps/0/apt/usr/bin/tofu" "${home}/deps/0/bin/tofu" +rm -f "tofu_${OPENTOFU_VERSION}_amd64.deb" + +PG_CONN_STR=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.uri') +PGDATABASE=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.db_name') +PGHOST=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.host') +PGPASSWORD=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.password') +PGPORT=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.port') +PGUSER=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[].credentials.username') + +echo "export PATH=${PATH}:${home}/deps/0/bin" > "${home}/exports.sh" +echo "alias terraform=tofu" >> "${home}/exports.sh" +echo "alias tf=tofu" >> "${home}/exports.sh" + +{ + echo "export PG_CONN_STR=${PG_CONN_STR}" + echo "export PGDATABASE=${PGDATABASE}" + echo "export PGHOST=${PGHOST}" + echo "export PGPASSWORD=${PGPASSWORD}" + echo "export PGPORT=${PGPORT}" + echo "export PGUSER=${PGUSER}" +} > "${home}/exports.sh" + +sed -i '1s/^/source exports.sh\n/' "${home}/.bashrc" + +while : ; do sleep 500 ; done + diff --git a/terraform/bootstrap/.terraform.lock.hcl b/terraform/bootstrap/.terraform.lock.hcl new file mode 100644 index 000000000..03908252d --- /dev/null +++ b/terraform/bootstrap/.terraform.lock.hcl @@ -0,0 +1,136 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/cloudfoundry-community/cloudfoundry" { + version = "0.53.1" + constraints = "~> 0.5" + hashes = [ + "h1:o6nGtINonmkgsX810QianzlX+y+aJ7WzYRwAvhQu5qE=", + "zh:017a55cdbd444ccf8fe45a3c7cdbc08ddf4f0f13550fcd457c31df9b2cfdb767", + "zh:100e9bd10868547193134082427abebad9db6359f6139a882192232e8e6911e3", + "zh:34467f6504e8527bd3e18e372d5386a43f2bffd88abf54bb72d51f04ab3e4e23", + "zh:3a278f5f71e39d29c7db999e2a34e8135b79cee4f36510b0f2c2dfec47997cf1", + "zh:3be1fbe17382c91561b1985d372606d802513d94bae6368e1bafd8dd49494737", + "zh:3f12bd7a629d547c706c380d9499ff39eab7b8824a14662aa446f230304bdd3a", + "zh:404acaa9ad7f95e83baf2332be54c065c21053bf304e80ac41ae49719462b184", + "zh:5ac5f6159d1e0c989e739cf16aa8dede6cee3562a6262bf9f2c6b53f4da866fe", + "zh:7a440ee173e69fa153ea4baea47adfca34d7171ffc83e7a1c0ec319d28998cbc", + "zh:87e2200bf66443671e249108d1cfa4aa13a31b9fdf445cec88364db8ea6be623", + "zh:b1b20b2b751df7765225cee5b01290b06e245e50faa8053495c2ef5ebe316998", + "zh:c8ddda9cf7dff40d762ea4dc22941c993ae8e9b2388c8d421f43254a56c98482", + "zh:d6ce83f0077a9f6262ffa1f7d777e2b72feac7ea7c8735aa39a5f86b4f3f7084", + "zh:d74126b9189ab4ca137ca634eaa25c571491bdd2456ccd0f3276a6d49163e412", + "zh:db5d415346e03eac0c5e025f9c10afdebfff35487e8a8383b3c4cd867c422fe2", + ] +} + +provider "registry.opentofu.org/hashicorp/archive" { + version = "2.4.2" + hashes = [ + "h1:tZcueUOGqjDRRzW9b6BMwV++XRqABodQjgC/K3bRoXM=", + "zh:0fee4f61bc999b5174a1268295e04c91c3f6be0160022cb53943b6ec0a3f1055", + "zh:10a895ee751beec68727d3dc6bf8e670f499618bb4b02649544be2c73e89603e", + "zh:1118373dfc03cf524273573e3aff9c99e0bb7128ab3ce0be211fd30e3928dfb8", + "zh:19c1b4c785f1d864e4fcaec7d96045437494efc333f1e661ea9994cd5c969cdb", + "zh:23f0aa399394ce8aa918a6a16ca9f5451d9d5b021e1b08929eb7972f65cb27da", + "zh:27d5daeec1819019a4b94c4980c09626e9cf71de3f54128a621fddb1b94b9ece", + "zh:56244088a96ff9e3a04b23de0ce2fcfa92c1a5fe6c91c6357cceda4d6d441c17", + "zh:578fcb23e8ebde3c5be6c5c67377b5e0c404cc807a74d7087e70c8fb3bb59b92", + "zh:9709d108559da5066f24a6d28be661b65a02e908f89b91fe42fc493962a5f466", + "zh:ff2a6df5d22bda78ca284756801ba7c86504e4bf0b48b31c8f5af44eefd9d0e8", + ] +} + +provider "registry.opentofu.org/hashicorp/github" { + version = "6.2.1" + hashes = [ + "h1:uDerb9YJo3vAO+wKw+Z064InX5aXom+nKLDry2eGf14=", + "zh:172aa5141c525174f38504a0d2e69d0d16c0a0b941191b7170fe6ae4d7282e30", + "zh:1a098b731fa658c808b591d030cc17cc7dfca1bf001c3c32e596f8c1bf980e9f", + "zh:245d6a1c7e632d8ae4bdd2da2516610c50051e81505cf420a140aa5fa076ea90", + "zh:43c61c230fb4ed26ff1b04b857778e65be3d8f80292759abbe2a9eb3c95f6d97", + "zh:59bb7dd509004921e4322a196be476a2f70471b462802f09d03d6ce96f959860", + "zh:5cb2ab8035d015c0732107c109210243650b6eb115e872091b0f7b98c2763777", + "zh:69d2a6acfcd686f7e859673d1c8a07fc1fc1598a881493f19d0401eb74c0f325", + "zh:77f36d3f46911ace5c50dee892076fddfd64a289999a5099f8d524c0143456d1", + "zh:87df41097dfcde72a1fbe89caca882af257a4763c2e1af669c74dcb8530f9932", + "zh:899dbe621f32d58cb7c6674073a6db8328a9db66eecfb0cc3fc13299fd4e62e7", + "zh:ad2eb7987f02f7dd002076f65a685730705d04435313b5cf44d3a6923629fb29", + "zh:b2145ae7134dba893c7f74ad7dfdc65fdddf6c7b1d0ce7e2f3baa96212322fd8", + "zh:bd6bae3ac5c3f96ad9219d3404aa006ef1480e9041d4c95df1808737e37d911b", + "zh:e89758b20ae59f1b9a6d32c107b17846ddca9634b868cf8f5c927cbb894b1b1f", + ] +} + +provider "registry.opentofu.org/hashicorp/local" { + version = "2.5.1" + hashes = [ + "h1:87L+rpGao062xifb1VuG9YVFwp9vbDP6G2fgfYxUkQs=", + "zh:031c2c2070672b7e78e0aa15560839278dc57fe7cf1e58a617ac13c67b31d5fb", + "zh:1ef64ea4f8382cd538a76f3d319f405d18130dc3280f1c16d6aaa52a188ecaa4", + "zh:422ce45691b2f384dbd4596fdc8209d95cb43d85a82aaa0173089d38976d6e96", + "zh:7415fbd8da72d9363ba55dd8115837714f9534f5a9a518ec42268c2da1b9ed2f", + "zh:92aa22d071339c8ef595f18a9f9245c287266c80689f5746b26e10eaed04d542", + "zh:9cd0d99f5d3be835d6336c19c4057af6274e193e677ecf6370e5b0de12b4aafe", + "zh:a8c1525b389be5809a97f02aa7126e491ba518f97f57ed3095a3992f2134bb8f", + "zh:b336fa75f72643154b07c09b3968e417a41293358a54fe03efc0db715c5451e6", + "zh:c66529133599a419123ad2e42874afbd9aba82bd1de2b15cc68d2a1e665d4c8e", + "zh:c7568f75ba6cb7c3660b69eaab8b0e4278533bd9a7a4c33ee6590cc7e69743ea", + ] +} + +provider "registry.opentofu.org/hashicorp/random" { + version = "3.6.2" + hashes = [ + "h1:cwVnVdQqyli6MhRE74KtT70s6MepGHFFQu+oKcbETP4=", + "zh:1f27612f7099441526d8af59f5b4bdcc35f46915df5d243043d7337ea5a3e38a", + "zh:2a58e66502825db8b4b96116c04bd0323bca1cf1f5752bdd8f9c26feb84d3b1e", + "zh:4f0a4fa479e29de0c3c90146fd58799c097f7a55401cb00560dd4e9b1e6fad9d", + "zh:9c93c0fe6ef685513734527e0c8078636b2cc07591427502a7260f4744b1af1d", + "zh:a466ff5219beb77fb3b18a3d7e7fe30e7edd4d95c8e5c87f4f4e3fe3eeb8c2d7", + "zh:ab33e6176d0c757ddb31e40e01a941e6918ad10f7a786c8e8e4f35e5cff81c96", + "zh:b6eabf377a1c12cb3f9ddd97aacdd5b49c1646dc959074124f81d40fcd216d7e", + "zh:ccec5d03d0d1c0f354be299cdd6a417b2700f1a6781df36bcce77246b2f57e50", + "zh:d2a7945eeb691fdd2b1474da76ddc2d1655e2aedbb14b57f06d4f5123d47adf9", + "zh:ed62351f4ad9d1469c6798b77dee5f63b18b29c473620a0046ba3d4f111b621d", + ] +} + +provider "registry.opentofu.org/hashicorp/time" { + version = "0.11.2" + hashes = [ + "h1:qxivXg2too+UTcA8O3ZOdajC1lpTfKHXA/CCcVOgpsc=", + "zh:534d56fac2daa5c15737fa1907b7afe2d91474197a0ec272d3f864d2a0a87743", + "zh:67c7765dc3b5ec19b6df32c29620de4b3e7846f5aa1a6b1b0e15394d87d0f875", + "zh:9502412f028d17051ea0550f5015d0515da78bc938f415191d9171742481330c", + "zh:982984971fe6cb87600f118fbf754e7f60f7ad343735a4f99d632bf148319cee", + "zh:9b0bedf8781be6c414f314989246a3b9a79302845db57dbe497d768cd56ee73e", + "zh:a35244d464dcad8940060c49b66a2f653d2dfc4e471c2dd59e2118a17a279427", + "zh:c8e3534b8cdff62f88a9882aed8c6dc4484639662bff2ea4e89499f1209b9ba9", + "zh:e78c88613bb6eab52d94bb7592d4df3a79bf69b9f1e50a86f9d9174db7e5251c", + "zh:fb28ffd2b641a449bd5526bef6e547894b4c5ddac0cef05ee03881a6c53eac39", + "zh:fcfcd35ff1178cbf11034e2eaf4254f9aadd8531f75d1b15b5e245715183d49b", + ] +} + +provider "registry.opentofu.org/integrations/github" { + version = "6.2.1" + constraints = "~> 6.0" + hashes = [ + "h1:uDerb9YJo3vAO+wKw+Z064InX5aXom+nKLDry2eGf14=", + "zh:172aa5141c525174f38504a0d2e69d0d16c0a0b941191b7170fe6ae4d7282e30", + "zh:1a098b731fa658c808b591d030cc17cc7dfca1bf001c3c32e596f8c1bf980e9f", + "zh:245d6a1c7e632d8ae4bdd2da2516610c50051e81505cf420a140aa5fa076ea90", + "zh:43c61c230fb4ed26ff1b04b857778e65be3d8f80292759abbe2a9eb3c95f6d97", + "zh:59bb7dd509004921e4322a196be476a2f70471b462802f09d03d6ce96f959860", + "zh:5cb2ab8035d015c0732107c109210243650b6eb115e872091b0f7b98c2763777", + "zh:69d2a6acfcd686f7e859673d1c8a07fc1fc1598a881493f19d0401eb74c0f325", + "zh:77f36d3f46911ace5c50dee892076fddfd64a289999a5099f8d524c0143456d1", + "zh:87df41097dfcde72a1fbe89caca882af257a4763c2e1af669c74dcb8530f9932", + "zh:899dbe621f32d58cb7c6674073a6db8328a9db66eecfb0cc3fc13299fd4e62e7", + "zh:ad2eb7987f02f7dd002076f65a685730705d04435313b5cf44d3a6923629fb29", + "zh:b2145ae7134dba893c7f74ad7dfdc65fdddf6c7b1d0ce7e2f3baa96212322fd8", + "zh:bd6bae3ac5c3f96ad9219d3404aa006ef1480e9041d4c95df1808737e37d911b", + "zh:e89758b20ae59f1b9a6d32c107b17846ddca9634b868cf8f5c927cbb894b1b1f", + ] +} diff --git a/terraform/bootstrap/README.md b/terraform/bootstrap/README.md new file mode 100644 index 000000000..c309a92dd --- /dev/null +++ b/terraform/bootstrap/README.md @@ -0,0 +1,52 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | > 1.7 | +| [cloudfoundry](#requirement\_cloudfoundry) | ~> 0.5 | + +## Providers + +| Name | Version | +|------|---------| +| [cloudfoundry](#provider\_cloudfoundry) | 0.53.1 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [applications](#module\_applications) | ../modules/application | n/a | +| [github](#module\_github) | ../modules/github | n/a | +| [random](#module\_random) | ../modules/random | n/a | +| [services](#module\_services) | ../modules/service | n/a | + +## Resources + +| Name | Type | +|------|------| +| [cloudfoundry_app.external_applications](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/app) | data source | +| [cloudfoundry_domain.external](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/domain) | data source | +| [cloudfoundry_domain.internal](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/domain) | data source | +| [cloudfoundry_org.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/org) | data source | +| [cloudfoundry_service.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/service) | data source | +| [cloudfoundry_space.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/data-sources/space) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cloudgov\_organization](#input\_cloudgov\_organization) | The organization for the cloud.gov account. | `string` | n/a | yes | +| [cloudgov\_password](#input\_cloudgov\_password) | The password for the cloud.gov account. | `string` | n/a | yes | +| [cloudgov\_space](#input\_cloudgov\_space) | The organization for the cloud.gov account. | `string` | n/a | yes | +| [cloudgov\_username](#input\_cloudgov\_username) | The username for the cloudfoundry account. | `string` | n/a | yes | +| [github\_organization](#input\_github\_organization) | The organization to use with GitHub. | `string` | `"GSA"` | no | +| [github\_token](#input\_github\_token) | The token used authenticate with GitHub. | `string` | n/a | yes | +| [mtls\_port](#input\_mtls\_port) | The default port to direct traffic to. Envoy proxy listens on 61443 and redirects to 8080, which the application should listen on. | `number` | `61443` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [name](#output\_name) | n/a | + \ No newline at end of file diff --git a/terraform/bootstrap/data.tf b/terraform/bootstrap/data.tf new file mode 100755 index 000000000..ddac76697 --- /dev/null +++ b/terraform/bootstrap/data.tf @@ -0,0 +1,51 @@ +locals { + cloudfoundry = { + external_applications = try(data.cloudfoundry_app.external_applications, null) + domain_external = try(data.cloudfoundry_domain.external, null) + domain_internal = try(data.cloudfoundry_domain.internal, null) + organization = try(data.cloudfoundry_org.this, null) + services = try(data.cloudfoundry_service.this, null) + space = try(data.cloudfoundry_space.this, null) + } +} + +data "cloudfoundry_app" "external_applications" { + for_each = { + for key, value in try(local.env.external_applications, []) : value.name => value + if try(value.deployed, false) && + try(data.cloudfoundry_space.this.id, null) != null + } + name_or_id = format(local.env.name_pattern, each.key) + space = try(data.cloudfoundry_space.this.id, null) +} + +data "cloudfoundry_domain" "external" { + //domain = "${split(".", local.env.external_domain)[1]}.${split(".", local.env.external_domain)[2]}" + domain = join(",", slice(split(".", local.env.external_domain), 0, 0)) + sub_domain = split(".", local.env.external_domain)[0] +} + +data "cloudfoundry_domain" "internal" { + domain = join(",", slice(split(".", local.env.external_domain), 0, 0)) + sub_domain = split(".", local.env.internal_domain)[0] +} + +data "cloudfoundry_org" "this" { + name = local.env.organization +} + +data "cloudfoundry_space" "this" { + name = try(format(local.space_pattern, local.production_space), terraform.workspace) + org = data.cloudfoundry_org.this.id +} + + +data "cloudfoundry_service" "this" { + for_each = { + for key, value in try(local.env.services, {}) : key => value + if value.service_type != "user-provided" && try(data.cloudfoundry_space.this.id, null) != null + } + + name = each.value.service_type + space = try(data.cloudfoundry_space.this.id, null) +} \ No newline at end of file diff --git a/terraform/bootstrap/locals.tf b/terraform/bootstrap/locals.tf new file mode 100644 index 000000000..488c7d13e --- /dev/null +++ b/terraform/bootstrap/locals.tf @@ -0,0 +1,231 @@ +locals { + + ## The name of the project. Used to name most applications and services. + ## Default naming convention: ${local.project}-application-name-${terraform.workspace} + project = "benefit-finder" + + ## The full name of the project. If their isn't a longer name, this can be set to + ## local.project. + project_full = "${local.project}" + + production_space = "main" + + repository = "GSA/px-benefit-finder" + + space_pattern = "${local.project}-%s" + +## The various environment settings to be deployed. + envs = { + + ## Every environment gets settings in 'all'. + all = { + + ## The API URL for cloud.gov. + api_url = "https://api.fr.cloud.gov" + + ## These values are defaults values when options aren't configured in the application block. + defaults = { + + ## The default size of the containers ephemeral disk. + disk_quota = 2048 + + ## Is SSH enabled on the container by default? + enable_ssh = true + + ## The default health check timeout. + health_check_timeout = 60 + + ## Default method of performing a health check. + ## Valid options: "port", "process", or "http" + ## https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html + health_check_type = "port" + + ## Default number of application instances to deploy. + instances = 1 + + ## Default amount of memory to use memory to use for an application. + memory = 64 + + port = 8080 + + ## The default cloudfoundry stack to deploy. + ## https://docs.cloudfoundry.org/devguide/deploy-apps/stacks.html + stack = "cflinuxfs4" + + ## Is the application stopped by default? + stopped = false + + ## Default CloudFoundry deployment strategy. + ## Valid optons: "none", "standard", or "blue-green". + ## https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html + strategy = "none" + + ## Default wait time for an application to start. + timeout = 300 + } + + ## Configuration settings for the egress proxy application. + # egress = local.egress + + ## External application based on the Terraform workspace being used. + external_applications = {} + + ## The domain name for applications accessable external of cloud.gov. + external_domain = "app.cloud.gov" + + ## The domain name for applications accessable inside of cloud.gov. + internal_domain = "apps.internal" + + ## The naming convention/pattern for deployed systems and subsystems. + ## %s is replaced with the name of the system. + name_pattern = "${local.project}-%s-${terraform.workspace}" + + ## The name of the cloud.gov organization. + organization = "gsa-tts-usagov" + + ## Passwords that are generated for workspaces. By default, it's an empty map. + ## If one is defined below in a workspace's settings, it will supersed this one. + passwords = { + test = {length = 32} + } + + ## A copy of the project name, so it gets added to this setting object. + project = local.project + + ## The name of the current Cloud.gov space. + space = "${local.project}-${terraform.workspace}" + } + + ## + ## + ## The bootstrap workspace. + ## Used to initialize gobal/project wide applications/services. + ## + ## + + bootstrap = { + secrets = { + PGDATABASE = { + encrypted = false + key = "db_name" + } + PGHOST = { + encrypted = false + key = "host" + } + PGPASSWORD = { + encrypted = false + key = "password" + } + PGPORT = { + encrypted = false + key = "port" + } + PG_CONN_STR = { + encrypted = false + key = "uri" + } + PGUSER = { + encrypted = false + key = "username" + } + } + services = { + terraform-backend = { + ## Applications to bind to this service. + applications = [ "tf-bastion" ] + + ## The size of the instance to deploy. + service_plan = "micro-psql" + + ## The type of service to be deployed. + service_type = "aws-rds" + + ## Tags to add to the service. + tags = [ + terraform.workspace + ] + } + } + space = "${local.project}-main" + } + + main = { + apps = { + tf-bastion = { + + ## Should the application have access to the internet? + allow_egress = true + + ## Buildpacks to use with this application. + ## List buildpacks avalible with: cf buildpacks + buildpacks = [ + "https://github.com/cloudfoundry/apt-buildpack", + "binary_buildpack" + ] + + ## Command to run when container starts. + command = "./start" + + ## Ephemeral disk storage. + disk_quota = 1024 + + ## Should SSH be enabled? + enable_ssh = true + + ## Environmental variables. Avoid sensitive variables. + environment = { + CF_ORG = var.cloudgov_organization + CF_PASSWORD = var.cloudgov_password + CF_SPACE = var.cloudgov_space + CF_USER = var.cloudgov_username + OPENTOFU_VERSION = "1.7.1" + } + + ## Timeout for health checks, in seconds. + health_check_timeout = 180 + + ## Type of health check. + ## Options: port, process, http + health_check_type = "process" + + ## Number of instances of application to deploy. + instances = 1 + + ## Labels to add to the application. + labels = { + environment = "main" + } + + ## Maximum amount of memory the application can use. + memory = 64 + + ## Addional network policies to add to the application. + ## Format: name of the application and the port it is listening on. + network_policies = {} + + ## Port the application uses. + #port = 0 + + ## Can the application be accessed outside of cloud.gov? + public_route = false + + ## The source file should be a directory or a zip file. + source = "../applications/tf-bastion" + + space = "main" + + ## Templates take templated files and fill them in with sensitive data. + templates = [] + } + } + } + } + + ## Map of the 'all' environement and the current workspace settings. + env = merge(try(local.envs.all, {}), try(local.envs.bootstrap, {})) +} + +output "name" { + value = local.env.passwords +} \ No newline at end of file diff --git a/terraform/bootstrap/main.tf b/terraform/bootstrap/main.tf new file mode 100755 index 000000000..47d5ba8e0 --- /dev/null +++ b/terraform/bootstrap/main.tf @@ -0,0 +1,59 @@ +locals { + ## Merging of the various credentials and environmental variables. + secrets = merge( + flatten( + [ + for service_key, service_value in try(local.env.services, {}) : [ + for key, value in try(module.services.results.service_key[service_key].credentials, {}) : { + "${key}" = value + } + ] if try(module.services.results.service_key[service_key].credentials, null) != null + ] + ) + ...) +} + +module "random" { + source = "../modules/random" + names = [ "dev" ] + passwords = local.env.passwords +} + +## The instanced services (i.e. RDS, S3, etc.) get created first. +## This allows their credentials to be injected into "user-provided" services (JSON blobs), if needed. +module "services" { + source = "../modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env + skip_user_provided_services = true +} + +# module "secrets" { +# source = "../modules/service" + +# cloudfoundry = local.cloudfoundry +# env = local.env +# skip_service_instances = true +# secrets = local.secrets +# } + +module "applications" { + #for_each = local.cloudfoundry.spaces + source = "../modules/application" + + cloudfoundry = local.cloudfoundry + env = merge(local.envs.all, local.envs.bootstrap, local.envs[local.production_space]) + secrets = local.secrets + services = module.services.results +} + +module "github" { + source = "../modules/github" + + env = local.env + github_organization = var.github_organization + github_token = var.github_token + repository = local.repository + secrets = local.secrets +} diff --git a/terraform/bootstrap/provider.tf b/terraform/bootstrap/provider.tf new file mode 100644 index 000000000..e27de2fa1 --- /dev/null +++ b/terraform/bootstrap/provider.tf @@ -0,0 +1,21 @@ +terraform { + required_providers { + cloudfoundry = { + source = "cloudfoundry-community/cloudfoundry" + version = "~> 0.5" + } + } + required_version = "> 1.7" +} + +provider "cloudfoundry" { + api_url = local.env.api_url + user = var.cloudgov_username + password = var.cloudgov_password +} + +# Configure the GitHub Provider +provider "github" { + owner = var.github_organization + token = var.github_token +} \ No newline at end of file diff --git a/terraform/bootstrap/variables.tf b/terraform/bootstrap/variables.tf new file mode 100644 index 000000000..69420fc97 --- /dev/null +++ b/terraform/bootstrap/variables.tf @@ -0,0 +1,40 @@ +variable "cloudgov_username" { + description = "The username for the cloudfoundry account." + type = string + sensitive = true +} + +variable "cloudgov_password" { + description = "The password for the cloud.gov account." + type = string + sensitive = true +} + +variable "cloudgov_organization" { + description = "The organization for the cloud.gov account." + type = string + sensitive = true +} + +variable "cloudgov_space" { + description = "The organization for the cloud.gov account." + type = string + sensitive = true +} + +variable "github_organization" { + description = "The organization to use with GitHub." + type = string + default = "GSA" +} +variable "github_token" { + description = "The token used authenticate with GitHub." + type = string + sensitive = true +} + +variable "mtls_port" { + description = "The default port to direct traffic to. Envoy proxy listens on 61443 and redirects to 8080, which the application should listen on." + type = number + default = 61443 +} \ No newline at end of file diff --git a/infra/docs/locals.tf.MD b/terraform/docs/locals.tf.MD similarity index 100% rename from infra/docs/locals.tf.MD rename to terraform/docs/locals.tf.MD diff --git a/infra/docs/scripts.MD b/terraform/docs/scripts.MD similarity index 100% rename from infra/docs/scripts.MD rename to terraform/docs/scripts.MD diff --git a/infra/.terraform-docs.yaml b/terraform/infra/.terraform-docs.yaml similarity index 100% rename from infra/.terraform-docs.yaml rename to terraform/infra/.terraform-docs.yaml diff --git a/infra/.terraform-docs/footer.md b/terraform/infra/.terraform-docs/footer.md similarity index 100% rename from infra/.terraform-docs/footer.md rename to terraform/infra/.terraform-docs/footer.md diff --git a/infra/.terraform-docs/header.md b/terraform/infra/.terraform-docs/header.md similarity index 100% rename from infra/.terraform-docs/header.md rename to terraform/infra/.terraform-docs/header.md diff --git a/terraform/infra/.terraform.lock.hcl b/terraform/infra/.terraform.lock.hcl new file mode 100644 index 000000000..913e3ce15 --- /dev/null +++ b/terraform/infra/.terraform.lock.hcl @@ -0,0 +1,93 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/cloudfoundry-community/cloudfoundry" { + version = "0.51.2" + constraints = "~> 0.5, 0.51.2" + hashes = [ + "h1:DAAWn0QmE75d6agoavWvchV6Ec5yOsxprPMMU7Q+xfM=", + "zh:2c15c7fbc8f15f6c21935d21c1eb8bab3e1454aec3476bc6fcda2d59bbd235a5", + "zh:3efe88cd4c40f1e90d71ceb94088d3ec2260ff01e4a4d722182c042b958c61f0", + "zh:41ea39daf091516f08cf6a5bc1efb88f19ac17ab8c146bc503d5a44c3c0fdd5a", + "zh:5287f2aade8821211426c8eed0a9dacaf41ab0be5a39ecf1526be2f5b71ab5a2", + "zh:7438d2dca479ace7720125c02c24660d4e928ee8b0ebd1514e2841b95d3563ef", + "zh:7583edf26c3160c4271c5ea473e799bca1f65da249d2e2e96dca69f2dce82c40", + "zh:8003fd57163a259d8005c7efa79d1d4d1cd3d98c26f82c58fa84f1e27c0f50d1", + "zh:8a5c05e59f4078193db1ceabd5350863cea869791e8ee5765472c0f36579cdcd", + "zh:8c0ccf62206c242b116ade68fee48c425b897c53f3da40d233c9c9a4a5fb514f", + "zh:9cc6ba428f1cd8c9a2d9bb3333dd16944cafcd2d2ca5af6fc040e4207cef4ea6", + "zh:a0fd393db027f03bde2b0056bfb04ce54827394eb31498b20e9beb3cb8e198b3", + "zh:a3d5cce15f8f611494c510f3a2dde2bd2841ffb2351541ed7f3177ea17f47a35", + "zh:bcf6edbebeabb36bf9254a2b6cdd3ea5e2f7d26836bd1392e6867a901d34c1ee", + "zh:e5a909abf388aa15af09ea1c9b6ec6fea5dd27ba4cefdf247b3afa90bb97cd3c", + "zh:fc2a42a8dbc41e216f63359087bfdaa02fe98717f4b793436e356013097c907a", + ] +} + +provider "registry.opentofu.org/hashicorp/archive" { + version = "2.4.2" + hashes = [ + "h1:tZcueUOGqjDRRzW9b6BMwV++XRqABodQjgC/K3bRoXM=", + "zh:0fee4f61bc999b5174a1268295e04c91c3f6be0160022cb53943b6ec0a3f1055", + "zh:10a895ee751beec68727d3dc6bf8e670f499618bb4b02649544be2c73e89603e", + "zh:1118373dfc03cf524273573e3aff9c99e0bb7128ab3ce0be211fd30e3928dfb8", + "zh:19c1b4c785f1d864e4fcaec7d96045437494efc333f1e661ea9994cd5c969cdb", + "zh:23f0aa399394ce8aa918a6a16ca9f5451d9d5b021e1b08929eb7972f65cb27da", + "zh:27d5daeec1819019a4b94c4980c09626e9cf71de3f54128a621fddb1b94b9ece", + "zh:56244088a96ff9e3a04b23de0ce2fcfa92c1a5fe6c91c6357cceda4d6d441c17", + "zh:578fcb23e8ebde3c5be6c5c67377b5e0c404cc807a74d7087e70c8fb3bb59b92", + "zh:9709d108559da5066f24a6d28be661b65a02e908f89b91fe42fc493962a5f466", + "zh:ff2a6df5d22bda78ca284756801ba7c86504e4bf0b48b31c8f5af44eefd9d0e8", + ] +} + +provider "registry.opentofu.org/hashicorp/local" { + version = "2.5.1" + hashes = [ + "h1:87L+rpGao062xifb1VuG9YVFwp9vbDP6G2fgfYxUkQs=", + "zh:031c2c2070672b7e78e0aa15560839278dc57fe7cf1e58a617ac13c67b31d5fb", + "zh:1ef64ea4f8382cd538a76f3d319f405d18130dc3280f1c16d6aaa52a188ecaa4", + "zh:422ce45691b2f384dbd4596fdc8209d95cb43d85a82aaa0173089d38976d6e96", + "zh:7415fbd8da72d9363ba55dd8115837714f9534f5a9a518ec42268c2da1b9ed2f", + "zh:92aa22d071339c8ef595f18a9f9245c287266c80689f5746b26e10eaed04d542", + "zh:9cd0d99f5d3be835d6336c19c4057af6274e193e677ecf6370e5b0de12b4aafe", + "zh:a8c1525b389be5809a97f02aa7126e491ba518f97f57ed3095a3992f2134bb8f", + "zh:b336fa75f72643154b07c09b3968e417a41293358a54fe03efc0db715c5451e6", + "zh:c66529133599a419123ad2e42874afbd9aba82bd1de2b15cc68d2a1e665d4c8e", + "zh:c7568f75ba6cb7c3660b69eaab8b0e4278533bd9a7a4c33ee6590cc7e69743ea", + ] +} + +provider "registry.opentofu.org/hashicorp/random" { + version = "3.6.1" + hashes = [ + "h1:egGGMQ18ihxoFBTgL/6aRL2N5/0bTI738Mg+TTsvBHA=", + "zh:1208af24d1f66e858740812dd5da12e8951b1ca75cc6edb1975ba22bfdeefb1b", + "zh:19137e9b4d3c15e1d99d2352888b98ec0e69bd5b2e89049150379d7bbd115063", + "zh:26613834a1a8ac60390c7a4cbd4cb794b01dfe237d2b0c10f132f3e434a21e03", + "zh:2cbe4425918f3f401609d89e6381f7d120493d637a3d103d827f0c0fd00b1600", + "zh:44ef27a972540435efa88f323280f96d6ac77934079225e7fcc3560cc28aae59", + "zh:8c5d4ca7d1ce007f7c055807cde77aad4685eb807ff802c93ffbec8589068f17", + "zh:9a4fa908d6af48805c862cd4f3a1031d552b96d863a94263e390ac92915d74a9", + "zh:ba396849f0f6d488784f6039095634e1c84e67e31375f3d17218fcf8ce952cb8", + "zh:cb695db8798957bd64ce411f061307e39cb2baa69668b4d42ccf010db47d2e39", + "zh:d02704bf99a93dc0b1ca00bd6051df9c431fbe17cd662a1ab58db1b96264a26f", + ] +} + +provider "registry.opentofu.org/hashicorp/time" { + version = "0.11.1" + hashes = [ + "h1:+S9YvR/HeCxFGMS3ITjOFqlWrR6DdarWWowT9Cz18/M=", + "zh:048c56f9f810f67a7460363a26bf3ef939d64f0d7b7342b9e7f24cc85ee1491b", + "zh:49f949cc5cb50fbb65f7b4578b79fbe02b6bafe9e3f5f1c2936114dd445b84b3", + "zh:553174a4fa88f6e186800d7ee155a6b5b4c6c81793643f1a20eab26becc7f823", + "zh:5cae304e21f77091d4b50389c655afd5e4e2e8d4cd9c06de139a31b8e7d343a9", + "zh:7aae20832bd9885f034831aa44db3a6ffcec034a2d5a2815d92c42c40c14ca1d", + "zh:93d715610dce777474b5eff1d7dbe797e72ca0b679cd8636efb3aa45d1cb589e", + "zh:bd29e04645775851eb10e7f3b39104ae57ca3632dec4ae07328d33d4182e7fb5", + "zh:d6ad6a4d52a6989b8452466f2ec3dbcdb00cc44a96bd1ca618d91a5d74895f49", + "zh:e68cfad3ec526631410fa9406938d624fd56b9ab065c76525cb3f731d106fbfe", + "zh:ffee8aa6b7ce56f4b8fdc0c492404be0041137a278388eb1d1180b637fb5b3de", + ] +} diff --git a/infra/.tflint.hcl b/terraform/infra/.tflint.hcl similarity index 100% rename from infra/.tflint.hcl rename to terraform/infra/.tflint.hcl diff --git a/infra/TERRAFORM.md b/terraform/infra/TERRAFORM.md similarity index 100% rename from infra/TERRAFORM.md rename to terraform/infra/TERRAFORM.md diff --git a/terraform/infra/data.tf b/terraform/infra/data.tf new file mode 100755 index 000000000..15b30a745 --- /dev/null +++ b/terraform/infra/data.tf @@ -0,0 +1,52 @@ +locals { + cloudfoundry = { + external_applications = try(data.cloudfoundry_app.external_applications, null) + domain_external = try(data.cloudfoundry_domain.external, null) + domain_internal = try(data.cloudfoundry_domain.internal, null) + organization = try(data.cloudfoundry_org.this, null) + services = try(data.cloudfoundry_service.this, null) + space = try(data.cloudfoundry_space.this, null) + } +} + +data "cloudfoundry_app" "external_applications" { + for_each = { + for key, value in try(local.env.external_applications, []) : key => value + if try(value.deployed, false) && + try(data.cloudfoundry_space.this.id, null) != null + } + name_or_id = format(local.env.name_pattern, each.key) + space = try(data.cloudfoundry_space.this.id, null) +} + +data "cloudfoundry_domain" "external" { + //domain = "${split(".", local.env.external_domain)[1]}.${split(".", local.env.external_domain)[2]}" + domain = join(",", slice(split(".", local.env.external_domain), 0, 0)) + sub_domain = split(".", local.env.external_domain)[0] +} + +data "cloudfoundry_domain" "internal" { + domain = join(",", slice(split(".", local.env.external_domain), 0, 0)) + sub_domain = split(".", local.env.internal_domain)[0] +} + +data "cloudfoundry_org" "this" { + name = local.env.organization +} + +data "cloudfoundry_space" "this" { + name = try(local.env.space, terraform.workspace) + org = data.cloudfoundry_org.this.id +} + + +data "cloudfoundry_service" "this" { + for_each = { + for key, value in try(local.env.services, {}) : key => value + if value.service_type != "user-provided" && + try(data.cloudfoundry_space.this.id, null) != null + } + + name = each.value.service_type + space = try(data.cloudfoundry_space.this.id, null) +} diff --git a/terraform/infra/dynamic.tf b/terraform/infra/dynamic.tf new file mode 100644 index 000000000..6404b22ad --- /dev/null +++ b/terraform/infra/dynamic.tf @@ -0,0 +1,45 @@ +locals { + + ## Map of service instances and secrets merged together. + services = { + instance = merge( + module.services.results.instance, + module.secrets.results.instance + ) + user_provided = merge( + module.services.results.user_provided, + module.secrets.results.user_provided + ) + service_key = merge( + module.services.results.service_key, + module.secrets.results.service_key + ) + } + + ## Merging of the various credentials and environmental variables. + secrets = merge( + merge( + flatten([ + for app in try(local.env.services, []) : [ + for key, value in try(module.services.results.service_key[app.name].credentials, {}) : { + "${app.name}_${key}" = value + } + ] if try(module.services.results.service_key[app.name].credentials, null) != null + ]) + ...), + merge( + flatten([ + for key, value in try(module.random.results, {}) : { + "${key}" = value.result + } + ]) + ...) + ) + + ## List of the workspaces defined in the configuration above. + workspaces = flatten([ + for key, value in local.envs : [ + key + ] + ]) +} diff --git a/terraform/infra/locals.tf b/terraform/infra/locals.tf new file mode 100755 index 000000000..cdd047297 --- /dev/null +++ b/terraform/infra/locals.tf @@ -0,0 +1,442 @@ +locals { + + ## The name of the project. Used to name most applications and services. + ## Default naming convention: ${local.project}-application-name-${terraform.workspace} + project = "benefit-finder" + + ## The full name of the project. If their isn't a longer name, this can be set to + ## local.project. + project_full = "${local.project}" + + ## The names of the project's production workspaces. This is used to adjust + ## settings dynamically throughout this configuration file. + production_workspaces = ["main", "dev"] + + cms_fqdn = "https://bf-cms-${terraform.workspace}.bxdev.net" + static_fqdn = "https://bf-static-${terraform.workspace}.bxdev.net" + + tf_backend = { + type = "pg" + name_pattern_psql = "${local.project}-terraform-backend-bootstrap" + name_pattern_secrets = "${local.project}--pg-secrets-bootstrap " + } + + ## "Common" applications and services that are deployed to every space. + globals = { + apps = { + ## Nginx Web Application Firewall (WAF). + waf = { + + ## Should the application have access to the internet? + allow_egress = true + + ## Buildpacks to use with this application. + ## List buildpacks avalible with: cf buildpacks + buildpacks = [ + "https://github.com/cloudfoundry/apt-buildpack", + "nginx_buildpack" + ] + + ## Command to run when container starts. + command = "./start" + + ## Ephemeral disk storage. + disk_quota = 1024 + + ## Should SSH be enabled? + enable_ssh = true + + ## Environmental variables. Avoid sensitive variables. + environment = { + + ## IP addresses allowed to connected to the CMS. + ALLOWED_IPS_CMS = base64encode( + jsonencode([ + "allow 0.0.0.0/0;" + ]) + ) + + ## The OWASP CRS rules for modsecurity. + CRS_RULES = "coreruleset-4.0.0.tar.gz" + + ## IP address that are denied access from the static website. + DENYED_IPS_STATIC = base64encode(jsonencode([])) + + ## The current environment the application is running in. + ENV = terraform.workspace + + ## Linux "Load Library Path", where system libraries are located. (i.e. libzip, gd, etc) + LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" + + ## Ubuntu patch for newer version of mod security. + MODSECURITY_UPDATE = "libmodsecurity3_3.0.9-1_amd64.deb" + + ## Domains that shouldn't be passed to the egress proxy server (i.e. apps.internal). + #no_proxy = var.no_proxy + } + + ## Timeout for health checks, in seconds. + health_check_timeout = 180 + + ## Type of health check. + ## Options: port, process, http + health_check_type = "port" + + ## Number of instances of application to deploy. + instances = 1 + + ## Labels to add to the application. + labels = { + environment = terraform.workspace + } + + ## Maximum amount of memory the application can use. + memory = 96 + + ## Addional network policies to add to the application. + ## Format: name of the application and the port it is listening on. + network_policies = { + drupal = 61443 + } + + ## Port the application uses. + port = 80 + + ## Can the application be accessed outside of cloud.gov? + public_route = true + + ## The source file should be a directory or a zip file. + source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf" + + ## Templates take templated files and fill them in with sensitive data. + ## The proxy-to-static.conf has the S3 bucket written to it during + ## the 'terraform apply' command, before it the files are zipped up and + ## uploaded to cloud.gov. + templates = [ + { + source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf.tmpl" + destination = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-storage.conf" + }, + { + source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-static.conf.tmpl" + destination = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-static.conf" + }, + { + source = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-app.conf.tmpl" + destination = "${path.cwd}/${var.terraform_working_dir}/applications/nginx-waf/nginx/snippets/proxy-to-app.conf" + } + ] + } + } + + ## Services to deploy in this environment. + services = { + + ## S3 storage for backups. + "backup" = { + + ## Applications to bind to this service. + applications = [] + + ## Should a service key be generated for other applications to use? + service_key = true + + ## The size of the instance to deploy. + service_plan = "basic" + + ## The type of service to be deployed. + service_type = "s3" + + ## Tags to add to the service. + tags = [ + terraform.workspace + ] + }, + + ## MySQL RDS database. + "mysql" = { + + ## Applications to bind to this service. + applications = ["cms"] + + ## The size of the instance to deploy. + service_plan = contains(local.production_workspaces, terraform.workspace) ? "micro-mysql" : "micro-mysql" + + ## The type of service to be deployed. + service_type = "aws-rds" + + ## Tags to add to the service. + tags = [ + terraform.workspace + ] + }, + + ## Credentials and other sensitive variables. + "secrets" = { + + ## Applications to bind to this service. + applications = ["cms", "waf"] + + ## Credentials that should be added to the json blob. + credentials = [ + "cron_key", + "hash_salt", + "static_bucket", + "static_fips_endpoint", + "static_access_key_id", + "static_secret_access_key", + "storage_bucket", + "storage_fips_endpoint", + "storage_access_key_id", + "storage_secret_access_key" + ] + + ## The type of service to be deployed. + service_type = "user-provided" + + ## Tags to add to the service. + tags = [ + terraform.workspace + ] + }, + + ## S3 storage for public files for Drupal. + ## Typically "sites/default/files/" + "storage" = { + + ## Applications to bind to this service. + applications = ["cms", "waf"] + + ## Should a service key be generated for other applications to use? + service_key = true + + ## The size of the instance to deploy. + service_plan = "basic-public-sandbox" + + ## The type of service to be deployed. + service_type = "s3" + + ## Tags to add to the service. + tags = [ + terraform.workspace + ] + }, + + # S3 storage for the statically generated site. + "static" = { + + ## Applications to bind to this service. + applications = ["waf", "cms"] + + ## Should a service key be generated for other applications to use? + service_key = true + + ## The size of the instance to deploy. + service_plan = "basic-public-sandbox" + + ## The type of service to be deployed. + service_type = "s3" + + ## Tags to add to the service. + tags = [ + terraform.workspace + ] + } + } + } + + ## The mTLS port the proxy application uses. + ## Cloudfoundry will automatically redirect connections on this port to local port 8080. + mtls_port = var.mtls_port + + ## Any applications that are external to this Terraform infrastucture. + ## In this case, the Drupal application is deployed via a manifest.yml in the Drupal + ## Github repostitory. + external_applications = { + cms = { + + environement = "dev" + + ## Port is the application listening on. + port = var.mtls_port + }, + cms = { + + environement = "main" + + ## Port is the application listening on. + port = var.mtls_port + } + } + + ## The various environment settings to be deployed. + envs = { + + ## Every environment gets settings in 'all'. + all = { + + ## The API URL for cloud.gov. + api_url = "https://api.fr.cloud.gov" + + ## These values are defaults values when options aren't configured in the application block. + defaults = { + + ## The default size of the containers ephemeral disk. + disk_quota = 2048 + + ## Is SSH enabled on the container by default? + enable_ssh = true + + ## The default health check timeout. + health_check_timeout = 60 + + ## Default method of performing a health check. + ## Valid options: "port", "process", or "http" + ## https://docs.cloudfoundry.org/devguide/deploy-apps/healthchecks.html + health_check_type = "port" + + ## Default number of application instances to deploy. + instances = 1 + + ## Default amount of memory to use memory to use for an application. + memory = 64 + + port = 8080 + + ## The default cloudfoundry stack to deploy. + ## https://docs.cloudfoundry.org/devguide/deploy-apps/stacks.html + stack = "cflinuxfs4" + + ## Is the application stopped by default? + stopped = false + + ## Default CloudFoundry deployment strategy. + ## Valid optons: "none", "standard", or "blue-green". + ## https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html + strategy = "none" + + ## Default wait time for an application to start. + timeout = 300 + } + + ## Configuration settings for the egress proxy application. + # egress = local.egress + + ## External application based on the Terraform workspace being used. + external_applications = try(local.external_applications, []) + + ## The domain name for applications accessable external of cloud.gov. + external_domain = "app.cloud.gov" + + ## The domain name for applications accessable inside of cloud.gov. + internal_domain = "apps.internal" + + ## The naming convention/pattern for deployed systems and subsystems. + ## %s is replaced with the name of the system. + name_pattern = "${local.project}-%s-${terraform.workspace}" + + ## The name of the cloud.gov organization. + organization = "gsa-tts-usagov" + + ## Passwords that are generated for workspaces. By default, it's an empty map. + ## If one is defined below in a workspace's settings, it will supersed this one. + passwords = { + hash_salt = { + length = 32 + } + cron_key = { + length = 32 + } + } + + ## A copy of the project name, so it gets added to this setting object. + project = local.project + + ## The name of the current Cloud.gov space. + space = "${local.project}-${terraform.workspace}" + } + + ################################# + ## + ## ____ + ## | _ \ _____ __ + ## | | | |/ _ \ \ / / + ## | |_| | __/\ V / + ## |____/ \___| \_/ + ## + ################################# + + dev = merge( + { + ## Applications to deploy. + apps = local.globals.apps + services = local.globals.services + }, + { + ## The space to deploy to the application to. + space = "${local.project}-dev" + + ## Passwords that need to be generated for this environment. + ## These will actually use the sha256 result from the random module. + passwords = { + hash_salt = { + length = 32 + } + cron_key = { + length = 32 + } + } + } + ) + + ################################# + ## + ## ____ _ + ## | _ \ _ __ ___ __| | + ## | |_) | '__/ _ \ / _` | + ## | __/| | | (_) | (_| | + ## |_| |_| \___/ \__,_| + ## + ################################# + + + main = merge( + { + ## Applications to deploy. + apps = local.globals.apps + services = local.globals.services + }, + { + ## The space to deploy to the application to. + space = "${local.project}-${terraform.workspace}" + + ## Passwords that need to be generated for this environment. + ## These will actually use the sha256 result from the random module. + passwords = [ + { + name = "hash_salt" + length = 32 + }, + { + name = "cron_key" + length = 32 + } + ] + } + ) + } + + ## Map of the 'all' environement and the current workspace settings. + env = merge(try(local.envs.all, {}), try(local.envs[terraform.workspace], {})) + + service_bindings = merge( + flatten( + [ + for key, value in try(local.env.services, {}) : { + #svc_value.name => svc_value + "${key}" = value + } + ] + ) + ...) +} diff --git a/infra/main.tf b/terraform/infra/main.tf similarity index 63% rename from infra/main.tf rename to terraform/infra/main.tf index f7d75d31b..a43f11cd7 100755 --- a/infra/main.tf +++ b/terraform/infra/main.tf @@ -1,12 +1,5 @@ -# module "certificates" { -# source = "./modules/certificate" -# project_name = local.env.project -# workspace = local.env.bootstrap_workspace -# env = local.env -# } - module "random" { - source = "./modules/random" + source = "../modules/random" names = local.workspaces passwords = local.env.passwords } @@ -14,7 +7,7 @@ module "random" { ## The instanced services (i.e. RDS, S3, etc.) get created first. ## This allows their credentials to be injected into "user-provided" services (JSON blobs), if needed. module "services" { - source = "./modules/service" + source = "../modules/service" cloudfoundry = local.cloudfoundry env = local.env @@ -26,7 +19,7 @@ module "services" { } module "secrets" { - source = "./modules/service" + source = "../modules/service" cloudfoundry = local.cloudfoundry env = local.env @@ -39,7 +32,7 @@ module "secrets" { } module "applications" { - source = "./modules/application" + source = "../modules/application" cloudfoundry = local.cloudfoundry env = local.env @@ -47,10 +40,14 @@ module "applications" { services = local.services } -# module "circleci" { -# source = "./modules/circleci" - -# env = local.env -# secrets = local.secrets -# schedules = local.env.circleci.schedules -# } +# output "name" { +# value = merge( +# flatten( +# [ +# for service in try(local.env.services, {}) : { +# "${service.name}" = service +# } +# ] +# ) +# ...) +# } \ No newline at end of file diff --git a/infra/provider.tf.tmpl b/terraform/infra/provider.tf old mode 100755 new mode 100644 similarity index 57% rename from infra/provider.tf.tmpl rename to terraform/infra/provider.tf index fe787c702..cc8f39a48 --- a/infra/provider.tf.tmpl +++ b/terraform/infra/provider.tf @@ -5,17 +5,11 @@ terraform { version = "~> 0.5" } } - required_version = "1.8.2" + required_version = "> 1.7" } terraform { - backend "s3" { - bucket = "$AWS_BUCKET" - key = "terraform" - region = "$AWS_DEFAULT_REGION" - access_key = "$AWS_ACCESS_KEY_ID" - secret_key = "$AWS_SECRET_ACCESS_KEY" - } + backend "pg" { } } provider "cloudfoundry" { diff --git a/infra/scripts/cloudgov-aws-creds.sh b/terraform/infra/scripts/cloudgov-aws-creds.sh similarity index 100% rename from infra/scripts/cloudgov-aws-creds.sh rename to terraform/infra/scripts/cloudgov-aws-creds.sh diff --git a/infra/scripts/cloudgov-create-service-account.sh b/terraform/infra/scripts/cloudgov-create-service-account.sh similarity index 100% rename from infra/scripts/cloudgov-create-service-account.sh rename to terraform/infra/scripts/cloudgov-create-service-account.sh diff --git a/infra/scripts/egress-network-policy.sh b/terraform/infra/scripts/egress-network-policy.sh similarity index 100% rename from infra/scripts/egress-network-policy.sh rename to terraform/infra/scripts/egress-network-policy.sh diff --git a/infra/scripts/init.sh b/terraform/infra/scripts/init.sh similarity index 100% rename from infra/scripts/init.sh rename to terraform/infra/scripts/init.sh diff --git a/terraform/infra/terraform.tfvars.tmpl b/terraform/infra/terraform.tfvars.tmpl new file mode 100644 index 000000000..4b0a21f22 --- /dev/null +++ b/terraform/infra/terraform.tfvars.tmpl @@ -0,0 +1,2 @@ +cloudgov_password="$CF_PASSWORD" +cloudgov_username="$CF_USER" \ No newline at end of file diff --git a/terraform/infra/variables.tf b/terraform/infra/variables.tf new file mode 100755 index 000000000..cf03767a5 --- /dev/null +++ b/terraform/infra/variables.tf @@ -0,0 +1,23 @@ +variable "cloudgov_username" { + description = "The username for the cloudfoundry account." + type = string + sensitive = true +} + +variable "cloudgov_password" { + description = "The password for the cloud.gov account." + type = string + sensitive = true +} + +variable "terraform_working_dir" { + description = "Working directory for Terraform." + type = string + default = "px-benefit-finder/terraform" +} + +variable "mtls_port" { + description = "The default port to direct traffic to. Envoy proxy listens on 61443 and redirects to 8080, which the application should listen on." + type = number + default = 61443 +} \ No newline at end of file diff --git a/infra/modules/application/.terraform-docs.yaml b/terraform/modules/application/.terraform-docs.yaml similarity index 100% rename from infra/modules/application/.terraform-docs.yaml rename to terraform/modules/application/.terraform-docs.yaml diff --git a/infra/modules/application/.terraform-docs/footer.md b/terraform/modules/application/.terraform-docs/footer.md similarity index 100% rename from infra/modules/application/.terraform-docs/footer.md rename to terraform/modules/application/.terraform-docs/footer.md diff --git a/infra/modules/application/.terraform-docs/header.md b/terraform/modules/application/.terraform-docs/header.md similarity index 100% rename from infra/modules/application/.terraform-docs/header.md rename to terraform/modules/application/.terraform-docs/header.md diff --git a/terraform/modules/application/README.md b/terraform/modules/application/README.md new file mode 100644 index 000000000..22607fe81 --- /dev/null +++ b/terraform/modules/application/README.md @@ -0,0 +1,137 @@ + +# CloudFoundry Application Module + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | > 1.7 | +| [cloudfoundry](#requirement\_cloudfoundry) | ~> 0.5 | + +## Providers + +| Name | Version | +|------|---------| +| [archive](#provider\_archive) | n/a | +| [cloudfoundry](#provider\_cloudfoundry) | ~> 0.5 | +| [local](#provider\_local) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [cloudfoundry_app.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) | resource | +| [cloudfoundry_network_policy.egress_proxy](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/network_policy) | resource | +| [cloudfoundry_network_policy.ingress_proxy](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/network_policy) | resource | +| [cloudfoundry_route.external](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/route) | resource | +| [cloudfoundry_route.internal](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/route) | resource | +| [local_sensitive_file.this](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/sensitive_file) | resource | +| [archive_file.this](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cloudfoundry](#input\_cloudfoundry) | Cloudfoundry settings. |
object(
{
domain_external = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
domain_internal = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
external_applications = optional(
map(
object(
{
name = string
environement = string
port = optional(number, 61443)
}
)
),{}
)
organization = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
}
)
services = map(
object(
{
id = string
name = string
service_broker_guid = string
service_broker_name = string
service_plans = map(string)
space = string
}
)
)
space = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
org = string
org_name = string
quota = string
}
)
}
)
| n/a | yes | +| [env](#input\_env) | The settings object for this environment. |
object({
api_url = optional(string, "https://api.fr.cloud.gov")
apps = optional(
map(
object({
allow_egress = optional(bool, true)
buildpacks = list(string)
command = optional(string, "entrypoint.sh")
disk_quota = optional(number, 1024)
enable_ssh = optional(bool, false)
environment = optional(map(string), {})
health_check_timeout = optional(number, 180)
health_check_type = optional(string, "port")
instances = optional(number, 1)
labels = optional(map(string), {})
memory = optional(number, 96)
network_policies = optional(map(number),{})
port = optional(number, 80)
public_route = optional(bool, false)
space = optional(string ,null)
source = optional(string, null)
templates = list(map(string))
})
), {}
)
bootstrap_workspace = optional(string, "bootstrap")
defaults = object(
{
disk_quota = optional(number, 2048)
enable_ssh = optional(bool, true)
health_check_timeout = optional(number, 60)
health_check_type = optional(string, "port")
instances = optional(number, 1)
memory = optional(number, 64)
port = optional(number, 8080)
stack = optional(string, "cflinuxfs4")
stopped = optional(bool, false)
strategy = optional(string, "none")
timeout = optional(number, 300)
}
)
external_applications = optional(
map(
object({
enable_ssh = optional(bool, false)
instances = optional(number, 1)
memory = optional(number, 96)
port = optional(number, 61443)
})
), {}
)
external_domain = optional(string, "app.cloud.gov")
internal_domain = optional(string, "apps.internal")
name_pattern = string
organization = optional(string, "gsa-tts-usagov")
passwords = optional(
list(
object(
{
length = optional(number, 32)
}
)
), []
)
project = string
secrets = optional(
map(
object(
{
encrypted = bool
key = string
}
)
), {}
)
services = optional(
map(
object(
{
applications = optional(list(string), [])
environement = optional(string, "dev")
service_key = optional(bool, true)
service_plan = optional(string, "basic")
service_type = optional(string, "s3")
tags = optional(list(string), [])
}
)
), {}
)
space = string
})
| n/a | yes | +| [secrets](#input\_secrets) | Sensitive credentials to be used to set application environmental variables. | `map(string)` | `{}` | no | +| [services](#input\_services) | Services generated from the service module. |
object(
{
instance = map(
object(
{
annotations = optional(string, null)
id = optional(string, null)
json_params = optional(string, null)
labels = optional(map(string), {})
name = optional(string, null)
recursive_delete = optional(bool, null)
replace_on_params_change = optional(bool, false)
replace_on_service_plan_change = optional(bool, false)
service_plan = optional(string, null)
space = optional(string, null)
tags = optional(list(string), null)
}
)
)
user_provided = map(
object(
{
annotations = optional(string, null)
id = optional(string, null)
json_params = optional(string, null)
labels = optional(map(string), {})
name = optional(string, null)
recursive_delete = optional(bool, null)
replace_on_params_change = optional(bool, false)
replace_on_service_plan_change = optional(bool, false)
service_plan = optional(string, null)
space = optional(string, null)
tags = optional(list(string), null)
}
)
)
service_key = map(
object(
{
name = optional(string, null)
service_instance = optional(string, null)
params = optional(map(string), null)
params_json = optional(string, null)
credentials = optional(map(string), {})
}
)
)
}
)
| `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [apps](#output\_apps) | A `map` of [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource outputs. The key is the app name. | +| [external\_endpoints](#output\_external\_endpoints) | A map of external URL's (app.cloud.gov) to used to reach an application. The key is the app name. | +| [internal\_endpoints](#output\_internal\_endpoints) | A map of internal URL's (apps.internal) to used to reach an application. The key is the app name. | + +## Example + +```terraform +module "applications" { + source = "./modules/application" + + cloudfoundry = local.cloudfoundry + env = local.env + secrets = local.secrets + services = local.services +} +``` + +## Variables + +### cloudfoundry + +A variable that contains a `map(string)` of data lookups for pre-existing resources from Cloud.gov. This includes thing such as the organization and space ids. These are defined in `data.tf` in the root directory. + +### env + +A mixed type `object` variable that contains application settings. It is passed as an `any` type to allow optional variables to be ommitted from the object. It is defined in `locals.tf`, in the root directory. The object `local.env[terraform.workspace].apps` stores the values for the specific application that is to be deployed. + +Valid options are the attributes for the [cloudfoundry\_app](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/app) resource. + +### secrets + +A variable that has secrets and other credentials that the application uses. The `local.secrets` variable is generated in `locals_dynamic.tf`, as it merges a variety of credentials from the random and services modules. + +### services + +A variable that contains a `map(map(string))` of the services deployed in the environment. `local.services` is generated in `locals_dynamic.tf`, due to needing to be generated after the creation of the services, after the instance id are known. The services are then bound to the application. + +See the [service module](../service/readme.MD) for more information. + +## Usage + +Here is an example of how to define an application in `locals.tf`. + +```terraform +locals { + env = { + workspace1 = { + apps = { + application1 = { + buildpacks = [ + "staticfile_buildpack" + ] + command = "./start" + disk_quota = 256 + enable_ssh = true + environment = { + environment = terraform.workspace + LD_LIBRARY_PATH = "/home/vcap/deps/0/lib/" + } + health_check_timeout = 180 + health_check_type = "port" + instances = 1 + labels = { + environment = terraform.workspace + } + memory = 64 + port = 8080 + public_route = false + + source = "/path/to/application/directory" + + templates = [ + { + source = "${path.cwd}/path/to/templates/template.tmpl" + destination = "${path.cwd}}/path/to/templates/file" + } + ] + } + } + } + } +} +``` + +## Additional Notes + +- Buildpacks + - Valid built-in Cloud.gov buildpacks can be found by running `cf buildpacks` from the CLI. + - External buildpacks, such as the `apt-buildpack` by referencing the URL to the buildpack repository: [https://github.com/cloudfoundry/apt-buildpack](https://github.com/cloudfoundry/apt-buildpack). + \ No newline at end of file diff --git a/terraform/modules/application/data.tf b/terraform/modules/application/data.tf new file mode 100644 index 000000000..d820d7325 --- /dev/null +++ b/terraform/modules/application/data.tf @@ -0,0 +1,29 @@ +locals { + + ## Create a single list of external service names. Multiple applications + ## could reference the same service, but the GUID only needs to be looked up once. + services_external = distinct( + flatten( + [ + for key, value in try(var.env.apps, {}) : [ + try(var.env.apps[key].services_external, []) + ] + ] + ) + ) +} + +output "name" { + value = data.cloudfoundry_service_instance.this +} + +## Lookup up service instance GUID's for existing services. +## These can be externally deployed services or services deployed from different code sources. +## The GUID can then be refrenced by data.cloudfoundry_service_instance.this["service-name"].id +data "cloudfoundry_service_instance" "this" { + for_each = { + for key, value in local.services_external : value => value + } + name_or_id = each.value + space = try(var.cloudfoundry.space.id, null) +} \ No newline at end of file diff --git a/infra/modules/application/main.tf b/terraform/modules/application/main.tf similarity index 66% rename from infra/modules/application/main.tf rename to terraform/modules/application/main.tf index 26fb0fbb7..4e9c76c90 100755 --- a/infra/modules/application/main.tf +++ b/terraform/modules/application/main.tf @@ -1,36 +1,41 @@ locals { - - apps = merge( - try(var.env.apps, {}), - try(var.env.external_applications, {}) - ) - domains = merge( merge( flatten([ - for key, value in try(local.apps, {}) : { - "${key}_internal_endpoint" = "${format(var.env.name_pattern, key)}.${var.env.internal_domain}" - } if !try(value.public_route, false) + for key, value in try(var.env.apps, {}) : { + "${key}_internal_endpoint" = try(value.public_route, false) ? "${format(var.env.name_pattern, key)}.${var.env.external_domain}" : "${format(var.env.name_pattern, key)}.${var.env.internal_domain}" + } ]) ...), - merge( + merge( flatten([ - for key, value in try(local.apps, {}) : { - "${key}_external_endpoint" = "${format(var.env.name_pattern, key)}.${var.env.external_domain}" - } if try(value.public_route, false) + for key, value in try(var.env.external_applications, {}) : { + "${key}_internal_endpoint" = try(value.public_route, false) ? "${format(var.env.name_pattern, key)}.${var.env.external_domain}" : "${format(var.env.name_pattern, key)}.${var.env.internal_domain}" + } ]) ...) ) service_keys = merge( flatten([ - for key, value in try(var.env.services, {}) : [ - for k, v in try(var.services.service_key[key].credentials, {}) : { - "${key}_${k}" = v - } - ] if try(var.services.service_key[key].credentials, null) != null + for service_key, service_value in try(var.env.services, {}) : [ + for key, value in try(var.services.service_key[service_key].credentials, {}) : { + "${service_key}_${key}" = value + } if try(var.services.service_key[service_key].credentials, null) != null + ] ]) ...) + + service_bindings = merge( + flatten( + [ + for key, value in try(var.env.services, {}) : { + #svc_value.name => svc_value + "${key}" = value + } + ] + ) + ...) } resource "local_sensitive_file" "this" { @@ -50,8 +55,8 @@ resource "local_sensitive_file" "this" { each.value.source, merge( var.secrets, - local.service_keys, - local.domains + local.domains, + local.service_keys ) ) filename = each.value.destination @@ -60,8 +65,7 @@ resource "local_sensitive_file" "this" { data "archive_file" "this" { for_each = { for key, value in try(var.env.apps, {}) : key => value - if try(value.source, null) != null && - !endswith(try(value.source, ""), ".zip") + if try(value.source, null) != null && !endswith(try(value.source, ""), ".zip") } type = "zip" @@ -69,12 +73,13 @@ data "archive_file" "this" { output_path = "/tmp/${var.env.project}-${each.key}-${terraform.workspace}.zip" depends_on = [ - local_sensitive_file .this + local_sensitive_file.this ] } resource "cloudfoundry_app" "this" { - for_each = { for key, value in try(var.env.apps, {}) : key => value + for_each = { + for key, value in try(var.env.apps, {}) : key => value } buildpack = try(each.value.buildpack, null) @@ -85,7 +90,7 @@ resource "cloudfoundry_app" "this" { docker_image = try(each.value.docker_image, null) enable_ssh = try(each.value.enable_ssh, try(var.env.defaults.enable_ssh, true)) environment = try(each.value.environment, {}) - health_check_http_endpoint = try(each.value.health_check_http_endpoint, try(var.env.defaults.health_check_http_endpoint, "/")) + health_check_http_endpoint = try(each.value.health_check_http_endpoint, try(var.env.defaults.health_check_http_endpoint, null)) health_check_invocation_timeout = try(each.value.health_check_invocation_timeout, try(var.env.defaults.health_check_invocation_timeout, 5)) health_check_timeout = try(each.value.health_check_timeout, try(var.env.defaults.health_check_timeout, 180)) health_check_type = try(each.value.health_check_type, try(var.env.defaults.health_check_type, "port")) @@ -104,8 +109,7 @@ resource "cloudfoundry_app" "this" { dynamic "service_binding" { for_each = { for svc_key, svc_value in try(var.env.services, {}) : svc_key => svc_value - if contains(svc_value.applications, each.key) && - svc_value.service_type != "user-provided" + if contains(svc_value.applications, each.key) && svc_value.service_type != "user-provided" } content { service_instance = var.services.instance[service_binding.key].id @@ -127,6 +131,16 @@ resource "cloudfoundry_app" "this" { } } + ## Bind any external services, not deployed by the root code calling this module. + dynamic "service_binding" { + for_each = try(local.services_external, []) + content { + service_instance = data.cloudfoundry_service_instance.this[service_binding.value].id + params_json = try(var.env.services[service_binding.value].params_json, null) + params = try(var.env.services[service_binding.value].params, {}) + } + } + depends_on = [ data.archive_file.this, ] diff --git a/infra/modules/application/networking.tf b/terraform/modules/application/networking.tf similarity index 81% rename from infra/modules/application/networking.tf rename to terraform/modules/application/networking.tf index a23094aa1..b10803fe4 100755 --- a/infra/modules/application/networking.tf +++ b/terraform/modules/application/networking.tf @@ -3,7 +3,8 @@ locals { } resource "cloudfoundry_network_policy" "ingress_proxy" { - for_each = {for key, value in try(var.env.apps, {}) : key => value + for_each = { + for key, value in try(var.env.apps, []) : value.name => value if try(value.network_policy, null) != null && try(var.cloudfoundry.external_applications[value.network_policy.name].id, null) != null } @@ -16,14 +17,15 @@ resource "cloudfoundry_network_policy" "ingress_proxy" { } resource "cloudfoundry_network_policy" "egress_proxy" { - for_each = { for key, value in try(var.env.apps, {}) : key => value + for_each = { + for key, value in try(var.env.apps, []) : value.name => value if try(var.cloudfoundry.egress_app.id, null) != null && terraform.workspace != try(var.env.egress.workspace, null) } policy { source_app = cloudfoundry_app.this[each.key].id - destination_app = var.cloudfoundry.egress_app.id + destination_app = try(var.cloudfoundry.egress_app.id, null) port = try(var.env.egress.mtls_port, 61443) protocol = try(var.env.egress.protocol, "tcp") } diff --git a/infra/modules/application/output.tf b/terraform/modules/application/outputs.tf similarity index 100% rename from infra/modules/application/output.tf rename to terraform/modules/application/outputs.tf diff --git a/infra/modules/application/providers.tf b/terraform/modules/application/providers.tf similarity index 69% rename from infra/modules/application/providers.tf rename to terraform/modules/application/providers.tf index 2be1ef144..d106004d3 100644 --- a/infra/modules/application/providers.tf +++ b/terraform/modules/application/providers.tf @@ -2,8 +2,8 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.51.2" + version = "~> 0.5" } } - required_version = "> 1.4" + required_version = "> 1.7" } diff --git a/infra/modules/application/routes.tf b/terraform/modules/application/routes.tf similarity index 59% rename from infra/modules/application/routes.tf rename to terraform/modules/application/routes.tf index 8c8116890..f240b6284 100755 --- a/infra/modules/application/routes.tf +++ b/terraform/modules/application/routes.tf @@ -1,10 +1,11 @@ resource "cloudfoundry_route" "external" { for_each = { for key, value in try(var.env.apps, {}) : key => value - if value.public_route + if value.public_route && try(value.port, -1) != -1 } domain = var.cloudfoundry.domain_external.id - space = var.cloudfoundry.space.id + #space = var.cloudfoundry.space.id + space = try(var.cloudfoundry.spaces[each.value.space].id, var.cloudfoundry.space.id) hostname = format(var.env.name_pattern, each.key) port = try(cloudfoundry_app.this[each.key].port, null) @@ -15,12 +16,14 @@ resource "cloudfoundry_route" "external" { } resource "cloudfoundry_route" "internal" { - for_each = { for key, value in try(var.env.apps, {}) : key => value - if !value.public_route + for_each = { + for key, value in try(var.env.apps, {}) : key => value + if !value.public_route && try(value.port, -1) != -1 } domain = var.cloudfoundry.domain_internal.id - space = var.cloudfoundry.space.id + #space = var.cloudfoundry.space.id + space = try(var.cloudfoundry.spaces[each.value.space].id, var.cloudfoundry.space.id) hostname = format(var.env.name_pattern, each.key) port = try(cloudfoundry_app.this[each.key].port, null) diff --git a/terraform/modules/application/variables.tf b/terraform/modules/application/variables.tf new file mode 100755 index 000000000..e3747b8b0 --- /dev/null +++ b/terraform/modules/application/variables.tf @@ -0,0 +1,235 @@ +variable "cloudfoundry" { + description = "Cloudfoundry settings." + type = object( + { + domain_external = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + domain_internal = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + external_applications = optional( + map( + object( + { + name = string + environement = string + port = optional(number, 61443) + } + ) + ),{} + ) + organization = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + } + ) + services = map( + object( + { + id = string + name = string + service_broker_guid = string + service_broker_name = string + service_plans = map(string) + space = string + } + ) + ) + space = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + org = string + org_name = string + quota = string + } + ) + } + ) +} + +variable "env" { + description = "The settings object for this environment." + type = object({ + api_url = optional(string, "https://api.fr.cloud.gov") + apps = optional( + map( + object({ + allow_egress = optional(bool, true) + buildpacks = list(string) + command = optional(string, "entrypoint.sh") + disk_quota = optional(number, 1024) + enable_ssh = optional(bool, false) + environment = optional(map(string), {}) + health_check_timeout = optional(number, 180) + health_check_type = optional(string, "port") + instances = optional(number, 1) + labels = optional(map(string), {}) + memory = optional(number, 96) + network_policies = optional(map(number),{}) + port = optional(number, -1) + public_route = optional(bool, false) + services_external = optional(list(string), []) + space = optional(string ,null) + source = optional(string, null) + templates = list(map(string)) + }) + ), {} + ) + bootstrap_workspace = optional(string, "bootstrap") + defaults = object( + { + disk_quota = optional(number, 2048) + enable_ssh = optional(bool, true) + health_check_timeout = optional(number, 60) + health_check_type = optional(string, "port") + instances = optional(number, 1) + memory = optional(number, 64) + port = optional(number, 8080) + stack = optional(string, "cflinuxfs4") + stopped = optional(bool, false) + strategy = optional(string, "none") + timeout = optional(number, 300) + } + ) + external_applications = optional( + map( + object({ + enable_ssh = optional(bool, false) + instances = optional(number, 1) + memory = optional(number, 96) + port = optional(number, 61443) + }) + ), {} + ) + external_domain = optional(string, "app.cloud.gov") + internal_domain = optional(string, "apps.internal") + name_pattern = string + organization = optional(string, "gsa-tts-usagov") + passwords = optional( + map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ), {} + ) + project = string + secrets = optional( + map( + object( + { + encrypted = bool + key = string + } + ) + ), {} + ) + services = optional( + map( + object( + { + applications = optional(list(string), []) + environement = optional(string, "dev") + service_key = optional(bool, true) + service_plan = optional(string, "basic") + service_type = optional(string, "s3") + tags = optional(list(string), []) + } + ) + ), {} + ) + space = string + }) +} + +variable "secrets" { + description = "Sensitive credentials to be used to set application environmental variables." + type = map(string) + default = {} +} + +variable "services" { + description = "Services generated from the service module." + type = object( + { + instance = map( + object( + { + annotations = optional(string, null) + id = optional(string, null) + json_params = optional(string, null) + labels = optional(map(string), {}) + name = optional(string, null) + recursive_delete = optional(bool, null) + replace_on_params_change = optional(bool, false) + replace_on_service_plan_change = optional(bool, false) + service_plan = optional(string, null) + space = optional(string, null) + tags = optional(list(string), null) + } + ) + ) + user_provided = map( + object( + { + annotations = optional(string, null) + id = optional(string, null) + json_params = optional(string, null) + labels = optional(map(string), {}) + name = optional(string, null) + recursive_delete = optional(bool, null) + replace_on_params_change = optional(bool, false) + replace_on_service_plan_change = optional(bool, false) + service_plan = optional(string, null) + space = optional(string, null) + tags = optional(list(string), null) + } + ) + ) + service_key = map( + object( + { + name = optional(string, null) + service_instance = optional(string, null) + params = optional(map(string), null) + params_json = optional(string, null) + credentials = optional(map(string), {}) + } + ) + ) + } + ) + default = null +} \ No newline at end of file diff --git a/infra/modules/certificate/.terraform-docs.yaml b/terraform/modules/circleci/.terraform-docs.yaml similarity index 100% rename from infra/modules/certificate/.terraform-docs.yaml rename to terraform/modules/circleci/.terraform-docs.yaml diff --git a/infra/modules/circleci/.terraform-docs/footer.md b/terraform/modules/circleci/.terraform-docs/footer.md similarity index 100% rename from infra/modules/circleci/.terraform-docs/footer.md rename to terraform/modules/circleci/.terraform-docs/footer.md diff --git a/infra/modules/circleci/.terraform-docs/header.md b/terraform/modules/circleci/.terraform-docs/header.md similarity index 100% rename from infra/modules/circleci/.terraform-docs/header.md rename to terraform/modules/circleci/.terraform-docs/header.md diff --git a/infra/modules/circleci/README.md b/terraform/modules/circleci/README.md similarity index 100% rename from infra/modules/circleci/README.md rename to terraform/modules/circleci/README.md diff --git a/infra/modules/circleci/main.tf b/terraform/modules/circleci/main.tf similarity index 100% rename from infra/modules/circleci/main.tf rename to terraform/modules/circleci/main.tf diff --git a/infra/modules/circleci/providers.tf b/terraform/modules/circleci/providers.tf similarity index 80% rename from infra/modules/circleci/providers.tf rename to terraform/modules/circleci/providers.tf index e8acaedf6..3c44cb3ed 100644 --- a/infra/modules/circleci/providers.tf +++ b/terraform/modules/circleci/providers.tf @@ -5,5 +5,5 @@ terraform { version = "0.8.2" } } - required_version = "> 1.4" + required_version = "> 1.7" } diff --git a/infra/modules/circleci/variables.tf b/terraform/modules/circleci/variables.tf similarity index 100% rename from infra/modules/circleci/variables.tf rename to terraform/modules/circleci/variables.tf diff --git a/infra/modules/circleci/.terraform-docs.yaml b/terraform/modules/github/.terraform-docs.yaml similarity index 100% rename from infra/modules/circleci/.terraform-docs.yaml rename to terraform/modules/github/.terraform-docs.yaml diff --git a/terraform/modules/github/.terraform-docs/footer.md b/terraform/modules/github/.terraform-docs/footer.md new file mode 100755 index 000000000..e69de29bb diff --git a/terraform/modules/github/.terraform-docs/header.md b/terraform/modules/github/.terraform-docs/header.md new file mode 100755 index 000000000..70ad68afb --- /dev/null +++ b/terraform/modules/github/.terraform-docs/header.md @@ -0,0 +1 @@ +# Github Secrets and Variables diff --git a/terraform/modules/github/README.md b/terraform/modules/github/README.md new file mode 100644 index 000000000..82bd599ae --- /dev/null +++ b/terraform/modules/github/README.md @@ -0,0 +1,42 @@ + +# Github Secrets and Variables + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | > 1.7 | +| [github](#requirement\_github) | ~> 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [github](#provider\_github) | ~> 6.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_actions_secret.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_secret) | resource | +| [github_actions_variable.this](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_variable) | resource | +| [github_repository.this](https://registry.terraform.io/providers/integrations/github/latest/docs/data-sources/repository) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [env](#input\_env) | The settings object for this environment. |
object({
api_url = optional(string, "https://api.fr.cloud.gov")
apps = optional(
map(
object({
allow_egress = optional(bool, true)
buildpacks = list(string)
command = optional(string, "entrypoint.sh")
disk_quota = optional(number, 1024)
enable_ssh = optional(bool, false)
environment = optional(map(string), {})
health_check_timeout = optional(number, 180)
health_check_type = optional(string, "port")
instances = optional(number, 1)
labels = optional(map(string), {})
memory = optional(number, 96)
network_policies = optional(map(number),{})
port = optional(number, 80)
public_route = optional(bool, false)
source = optional(string, null)
templates = list(map(string))
})
), {}
)
bootstrap_workspace = optional(string, "bootstrap")
defaults = object(
{
disk_quota = optional(number, 2048)
enable_ssh = optional(bool, true)
health_check_timeout = optional(number, 60)
health_check_type = optional(string, "port")
instances = optional(number, 1)
memory = optional(number, 64)
port = optional(number, 8080)
stack = optional(string, "cflinuxfs4")
stopped = optional(bool, false)
strategy = optional(string, "none")
timeout = optional(number, 300)
}
)
external_applications = optional(
map(
object(
{
name = string
environement = string
port = optional(number, 61443)
}
)
),{}
)
external_domain = optional(string, "app.cloud.gov")
internal_domain = optional(string, "apps.internal")
name_pattern = string
organization = optional(string, "gsa-tts-usagov")
passwords = optional(
list(
object(
{
length = optional(number, 32)
}
)
), []
)
project = string
secrets = optional(
map(
object(
{
encrypted = bool
key = string
}
)
), {}
)
services = optional(
map(
object(
{
applications = optional(list(string), [])
environement = optional(string, "dev")
service_key = optional(bool, true)
service_plan = optional(string, "basic")
service_type = optional(string, "s3")
tags = optional(list(string), [])
}
)
), {}
)
space = string
})
| n/a | yes | +| [github\_organization](#input\_github\_organization) | The organization to use with GitHub. | `string` | `"GSA"` | no | +| [github\_token](#input\_github\_token) | The token used authenticate with GitHub. | `string` | n/a | yes | +| [repository](#input\_repository) | The GitHub respository. | `string` | n/a | yes | +| [secrets](#input\_secrets) | Secrets to create in the respository. | `map(string)` | `{}` | no | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/terraform/modules/github/main.tf b/terraform/modules/github/main.tf new file mode 100644 index 000000000..57722f76f --- /dev/null +++ b/terraform/modules/github/main.tf @@ -0,0 +1,18 @@ +data "github_repository" "this" { + full_name = var.repository +} + +resource "github_actions_secret" "this" { + for_each = { for key, value in try(var.env.secrets, []) : key => value } + repository = data.github_repository.this.name + secret_name = each.key + plaintext_value = !try(each.value.encrypted, false) ? try(var.secrets[each.value.key], null) : null + encrypted_value = try(each.value.encrypted, false) ? try(var.secrets[each.value.key], null) : null +} + +resource "github_actions_variable" "this" { + for_each = { for key, value in try(var.env.variables, []) : key => value } + repository = data.github_repository.this.name + variable_name = each.key + value = each.value.value +} diff --git a/terraform/modules/github/provider.tf b/terraform/modules/github/provider.tf new file mode 100644 index 000000000..a4f1bf27c --- /dev/null +++ b/terraform/modules/github/provider.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } + required_version = "> 1.7" +} + +# Configure the GitHub Provider +provider "github" { + owner = var.github_organization + token = var.github_token +} \ No newline at end of file diff --git a/terraform/modules/github/variables.tf b/terraform/modules/github/variables.tf new file mode 100644 index 000000000..fcac3b27e --- /dev/null +++ b/terraform/modules/github/variables.tf @@ -0,0 +1,126 @@ +variable "env" { + description = "The settings object for this environment." + type = object({ + api_url = optional(string, "https://api.fr.cloud.gov") + apps = optional( + map( + object({ + allow_egress = optional(bool, true) + buildpacks = list(string) + command = optional(string, "entrypoint.sh") + disk_quota = optional(number, 1024) + enable_ssh = optional(bool, false) + environment = optional(map(string), {}) + health_check_timeout = optional(number, 180) + health_check_type = optional(string, "port") + instances = optional(number, 1) + labels = optional(map(string), {}) + memory = optional(number, 96) + network_policies = optional(map(number),{}) + port = optional(number, 80) + public_route = optional(bool, false) + source = optional(string, null) + templates = list(map(string)) + }) + ), {} + ) + bootstrap_workspace = optional(string, "bootstrap") + defaults = object( + { + disk_quota = optional(number, 2048) + enable_ssh = optional(bool, true) + health_check_timeout = optional(number, 60) + health_check_type = optional(string, "port") + instances = optional(number, 1) + memory = optional(number, 64) + port = optional(number, 8080) + stack = optional(string, "cflinuxfs4") + stopped = optional(bool, false) + strategy = optional(string, "none") + timeout = optional(number, 300) + } + ) + external_applications = optional( + map( + object( + { + name = string + environement = string + port = optional(number, 61443) + } + ) + ),{} + ) + external_domain = optional(string, "app.cloud.gov") + internal_domain = optional(string, "apps.internal") + name_pattern = string + organization = optional(string, "gsa-tts-usagov") + passwords = optional( + map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ), {} + ) + project = string + secrets = optional( + map( + object( + { + encrypted = bool + key = string + } + ) + ), {} + ) + services = optional( + map( + object( + { + applications = optional(list(string), []) + environement = optional(string, "dev") + service_key = optional(bool, true) + service_plan = optional(string, "basic") + service_type = optional(string, "s3") + tags = optional(list(string), []) + } + ) + ), {} + ) + space = string + }) +} + +variable "github_organization" { + description = "The organization to use with GitHub." + type = string + default = "GSA" +} +variable "github_token" { + description = "The token used authenticate with GitHub." + type = string + sensitive = true +} + +variable "repository" { + description = "The GitHub respository." + type = string +} + +variable "secrets" { + default = {} + description = "Secrets to create in the respository." + type = map(string) +} diff --git a/infra/modules/random/.terraform-docs.yaml b/terraform/modules/random/.terraform-docs.yaml similarity index 100% rename from infra/modules/random/.terraform-docs.yaml rename to terraform/modules/random/.terraform-docs.yaml diff --git a/infra/modules/random/.terraform-docs/footer.md b/terraform/modules/random/.terraform-docs/footer.md similarity index 100% rename from infra/modules/random/.terraform-docs/footer.md rename to terraform/modules/random/.terraform-docs/footer.md diff --git a/infra/modules/random/.terraform-docs/header.md b/terraform/modules/random/.terraform-docs/header.md similarity index 100% rename from infra/modules/random/.terraform-docs/header.md rename to terraform/modules/random/.terraform-docs/header.md diff --git a/infra/modules/random/README.md b/terraform/modules/random/README.md similarity index 67% rename from infra/modules/random/README.md rename to terraform/modules/random/README.md index 2ef777eae..2f99e8a5e 100644 --- a/infra/modules/random/README.md +++ b/terraform/modules/random/README.md @@ -7,17 +7,14 @@ This module generates random credentials and hashes that can be used in various ## Requirements -| Name | Version | -|------|---------| -| [random](#requirement\_random) | 3.5.0 | -| [time](#requirement\_time) | 0.9.1 | +No requirements. ## Providers | Name | Version | |------|---------| -| [random](#provider\_random) | 3.5.0 | -| [time](#provider\_time) | 0.9.1 | +| [random](#provider\_random) | n/a | +| [time](#provider\_time) | n/a | ## Modules @@ -27,20 +24,19 @@ No modules. | Name | Type | |------|------| -| [random_password.multiple](https://registry.terraform.io/providers/hashicorp/random/3.5.0/docs/resources/password) | resource | -| [random_password.single](https://registry.terraform.io/providers/hashicorp/random/3.5.0/docs/resources/password) | resource | -| [time_rotating.multiple](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/rotating) | resource | -| [time_rotating.single](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/rotating) | resource | -| [time_static.multiple](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/static) | resource | -| [time_static.single](https://registry.terraform.io/providers/hashicorp/time/0.9.1/docs/resources/static) | resource | +| [random_password.multiple](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [random_password.single](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [time_rotating.multiple](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource | +| [time_rotating.single](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/rotating) | resource | +| [time_static.multiple](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource | +| [time_static.single](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [experation](#input\_experation) | A number, in days, when the password should be rotated. If none is set, the password will not rotate. | `number` | `0` | no | | [names](#input\_names) | List of unique names for the multiple resources. | `list(string)` | `[]` | no | -| [passwords](#input\_passwords) | A map of objects with password settings. | `any` | n/a | yes | +| [passwords](#input\_passwords) | A map of objects with password settings. |
map(
object(
{
experation_days = optional(number, 0)
length = number
lower = optional(bool, false)
min_lower = optional(number, 0)
min_numeric = optional(number, 0)
min_special = optional(number, 0)
min_upper = optional(number, 0)
numeric = optional(bool, true)
override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?")
special = optional(bool, true)
upper = optional(bool, true)
}
)
)
| n/a | yes | | [per\_workspace](#input\_per\_workspace) | Generate a password for each workspace. | `bool` | `false` | no | ## Outputs diff --git a/infra/modules/random/main.tf b/terraform/modules/random/main.tf similarity index 91% rename from infra/modules/random/main.tf rename to terraform/modules/random/main.tf index 997d6d074..0c63e2417 100755 --- a/infra/modules/random/main.tf +++ b/terraform/modules/random/main.tf @@ -13,7 +13,7 @@ locals { resource "time_rotating" "single" { for_each = { for key, value in var.passwords : key => value - if var.expiration > 0 + if try(value.expiration, 0) > 0 } rotation_days = each.value.expiration_days } @@ -21,7 +21,7 @@ resource "time_rotating" "single" { resource "time_static" "single" { for_each = { for key, value in var.passwords : key => value - if !var.per_workspace && var.expiration == 0 + if !var.per_workspace && try(value.expiration, 0) == 0 } } @@ -50,7 +50,7 @@ resource "random_password" "single" { resource "time_rotating" "multiple" { for_each = { for key, value in local.passwords : key => value - if var.per_workspace && var.expiration > 0 + if var.per_workspace && try(value.expiration, 0) > 0 } rotation_days = each.value.expiration_days } @@ -58,7 +58,7 @@ resource "time_rotating" "multiple" { resource "time_static" "multiple" { for_each = { for key, value in local.passwords : key => value - if var.per_workspace && var.expiration == 0 + if var.per_workspace && try(value.expiration, 0) == 0 } } diff --git a/infra/modules/random/outputs.tf b/terraform/modules/random/outputs.tf similarity index 100% rename from infra/modules/random/outputs.tf rename to terraform/modules/random/outputs.tf diff --git a/terraform/modules/random/variables.tf b/terraform/modules/random/variables.tf new file mode 100755 index 000000000..337e6a84f --- /dev/null +++ b/terraform/modules/random/variables.tf @@ -0,0 +1,32 @@ +variable "names" { + type = list(string) + description = "List of unique names for the multiple resources." + default = [] +} + +variable "passwords" { + description = "A map of objects with password settings." + type = map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ) +} + +variable "per_workspace" { + type = bool + description = "Generate a password for each workspace." + default = false +} \ No newline at end of file diff --git a/infra/modules/service/.terraform-docs.yaml b/terraform/modules/service/.terraform-docs.yaml similarity index 100% rename from infra/modules/service/.terraform-docs.yaml rename to terraform/modules/service/.terraform-docs.yaml diff --git a/infra/modules/service/.terraform-docs/footer.md b/terraform/modules/service/.terraform-docs/footer.md similarity index 100% rename from infra/modules/service/.terraform-docs/footer.md rename to terraform/modules/service/.terraform-docs/footer.md diff --git a/infra/modules/service/.terraform-docs/header.md b/terraform/modules/service/.terraform-docs/header.md similarity index 100% rename from infra/modules/service/.terraform-docs/header.md rename to terraform/modules/service/.terraform-docs/header.md diff --git a/terraform/modules/service/README.md b/terraform/modules/service/README.md new file mode 100644 index 000000000..a212a6c4e --- /dev/null +++ b/terraform/modules/service/README.md @@ -0,0 +1,87 @@ + +# CloudFoundry Service Module + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | > 1.7 | +| [cloudfoundry](#requirement\_cloudfoundry) | ~> 0.5 | + +## Providers + +| Name | Version | +|------|---------| +| [cloudfoundry](#provider\_cloudfoundry) | ~> 0.5 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [cloudfoundry_service_instance.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/service_instance) | resource | +| [cloudfoundry_service_key.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/service_key) | resource | +| [cloudfoundry_user_provided_service.this](https://registry.terraform.io/providers/cloudfoundry-community/cloudfoundry/latest/docs/resources/user_provided_service) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cloudfoundry](#input\_cloudfoundry) | Cloudfoundry settings. |
object(
{
domain_external = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
domain_internal = object(
{
domain = string
id = string
internal = bool
name = string
org = string
sub_domain = string
}
)
external_applications = map(string)
organization = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
}
)
services = map(
object(
{
id = string
name = string
service_broker_guid = string
service_broker_name = string
service_plans = map(string)
space = string
}
)
)
space = object(
{
annotations = map(string)
id = string
labels = map(string)
name = string
org = string
org_name = string
quota = string
}
)
}
)
| n/a | yes | +| [env](#input\_env) | The settings object for this environment. |
object({
api_url = optional(string, "https://api.fr.cloud.gov")
apps = optional(
map(
object({
allow_egress = optional(bool, true)
buildpacks = list(string)
command = optional(string, "entrypoint.sh")
disk_quota = optional(number, 1024)
enable_ssh = optional(bool, false)
environment = optional(map(string), {})
health_check_timeout = optional(number, 180)
health_check_type = optional(string, "port")
instances = optional(number, 1)
labels = optional(map(string), {})
memory = optional(number, 96)
network_policies = optional(map(number),{})
port = optional(number, 80)
public_route = optional(bool, false)
source = optional(string, null)
templates = list(map(string))
})
), {}
)
bootstrap_workspace = optional(string, "bootstrap")
defaults = object(
{
disk_quota = optional(number, 2048)
enable_ssh = optional(bool, true)
health_check_timeout = optional(number, 60)
health_check_type = optional(string, "port")
instances = optional(number, 1)
memory = optional(number, 64)
port = optional(number, 8080)
stack = optional(string, "cflinuxfs4")
stopped = optional(bool, false)
strategy = optional(string, "none")
timeout = optional(number, 300)
}
)
external_applications = optional(
map(
object(
{
environement = string
port = optional(number, 61443)
}
)
),{}
)
external_domain = optional(string, "app.cloud.gov")
internal_domain = optional(string, "apps.internal")
name_pattern = string
organization = optional(string, "gsa-tts-usagov")
passwords = optional(
list(
object(
{
length = optional(number, 32)
}
)
), []
)
project = string
secrets = optional(
map(
object(
{
encrypted = bool
key = string
}
)
), {}
)
services = optional(
map(
object(
{
applications = optional(list(string), [])
environement = optional(string, "dev")
service_key = optional(bool, true)
service_plan = optional(string, "basic")
service_type = optional(string, "s3")
tags = optional(list(string), [])
}
)
), {}
)
space = string
})
| n/a | yes | +| [secrets](#input\_secrets) | Sensitive strings to be added to the apps environmental variables. | `map` | `{}` | no | +| [skip\_service\_instances](#input\_skip\_service\_instances) | Allows the skipping of service instances. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | +| [skip\_user\_provided\_services](#input\_skip\_user\_provided\_services) | Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret. | `bool` | `false` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [results](#output\_results) | n/a | + +## Examples + +### Basic +```terraform +module "services" { + source = "./modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env +} +``` + +### Advanced + +This advanced example will first generate service instances, such as RDS, along with other defined services, except for the `user defined` services. `User defined` services are useful for providing variables at runtime to applications. The issue is that until a service, such as RDS is deployed, their isn't a username and password created for that instance. + +The first step is to initalize any services that are not `user defined`, but setting `skip_user_provided_services` to `true`. + +```terraform +module "services" { + source = "./modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env + + skip_user_provided_services = true +} +``` + +After the services are generated, another module block can be defined, which will pass a merged `map(string)` called `secrets`, that have the various information that is to be added to the `user defined` service. Setting the `skip_service_instances` to `true` will prevent the module from trying to redploy any non `user defined` service. + +```terraform +module "secrets" { + source = "./modules/service" + + cloudfoundry = local.cloudfoundry + env = local.env + + secrets = local.secrets + skip_service_instances = true +} +``` + \ No newline at end of file diff --git a/infra/modules/service/main.tf b/terraform/modules/service/main.tf similarity index 85% rename from infra/modules/service/main.tf rename to terraform/modules/service/main.tf index 31b5d3f06..ea18b7c88 100755 --- a/infra/modules/service/main.tf +++ b/terraform/modules/service/main.tf @@ -1,15 +1,15 @@ locals { credentials = merge( flatten([ - for key, value in try(var.env.services, {}) : { + for key, value in try(var.env.services,{}) : { "${key}" = { applications = value.applications service_type = value.service_type tags = value.tags credentials = merge( [ - for name in try(value.credentials, []) : { - try(nonsensitive("${name}"),"${name}") = try(try(nonsensitive(var.secrets[name]), var.secrets[name]), null) + for name in try(value.credentials, {}) : { + name = try(var.secrets[name], null) } ] ...) @@ -20,9 +20,6 @@ locals { ...) } -output "name" { - value = local.credentials -} resource "cloudfoundry_service_key" "this" { for_each = { @@ -53,7 +50,9 @@ resource "cloudfoundry_service_instance" "this" { } resource "cloudfoundry_user_provided_service" "this" { - for_each = local.credentials + for_each = { + for key, value in local.credentials : key => value + } name = format(var.env.name_pattern, each.key) space = var.cloudfoundry.space.id diff --git a/infra/modules/service/output.tf b/terraform/modules/service/output.tf similarity index 100% rename from infra/modules/service/output.tf rename to terraform/modules/service/output.tf diff --git a/infra/modules/service/providers.tf b/terraform/modules/service/providers.tf similarity index 69% rename from infra/modules/service/providers.tf rename to terraform/modules/service/providers.tf index 2be1ef144..d106004d3 100644 --- a/infra/modules/service/providers.tf +++ b/terraform/modules/service/providers.tf @@ -2,8 +2,8 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.51.2" + version = "~> 0.5" } } - required_version = "> 1.4" + required_version = "> 1.7" } diff --git a/terraform/modules/service/variables.tf b/terraform/modules/service/variables.tf new file mode 100755 index 000000000..4c633c9bb --- /dev/null +++ b/terraform/modules/service/variables.tf @@ -0,0 +1,182 @@ +variable "cloudfoundry" { + description = "Cloudfoundry settings." + type = object( + { + domain_external = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + domain_internal = object( + { + domain = string + id = string + internal = bool + name = string + org = string + sub_domain = string + } + ) + external_applications = map(string) + organization = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + } + ) + services = map( + object( + { + id = string + name = string + service_broker_guid = string + service_broker_name = string + service_plans = map(string) + space = string + } + ) + ) + space = object( + { + annotations = map(string) + id = string + labels = map(string) + name = string + org = string + org_name = string + quota = string + } + ) + } + ) +} + +variable "env" { + description = "The settings object for this environment." + type = object({ + api_url = optional(string, "https://api.fr.cloud.gov") + apps = optional( + map( + object({ + allow_egress = optional(bool, true) + buildpacks = list(string) + command = optional(string, "entrypoint.sh") + disk_quota = optional(number, 1024) + enable_ssh = optional(bool, false) + environment = optional(map(string), {}) + health_check_timeout = optional(number, 180) + health_check_type = optional(string, "port") + instances = optional(number, 1) + labels = optional(map(string), {}) + memory = optional(number, 96) + network_policies = optional(map(number),{}) + port = optional(number, 80) + public_route = optional(bool, false) + source = optional(string, null) + templates = list(map(string)) + }) + ), {} + ) + bootstrap_workspace = optional(string, "bootstrap") + defaults = object( + { + disk_quota = optional(number, 2048) + enable_ssh = optional(bool, true) + health_check_timeout = optional(number, 60) + health_check_type = optional(string, "port") + instances = optional(number, 1) + memory = optional(number, 64) + port = optional(number, 8080) + stack = optional(string, "cflinuxfs4") + stopped = optional(bool, false) + strategy = optional(string, "none") + timeout = optional(number, 300) + } + ) + external_applications = optional( + map( + object( + { + environement = string + port = optional(number, 61443) + } + ) + ),{} + ) + external_domain = optional(string, "app.cloud.gov") + internal_domain = optional(string, "apps.internal") + name_pattern = string + organization = optional(string, "gsa-tts-usagov") + passwords = optional( + map( + object( + { + experation_days = optional(number, 0) + length = number + lower = optional(bool, false) + min_lower = optional(number, 0) + min_numeric = optional(number, 0) + min_special = optional(number, 0) + min_upper = optional(number, 0) + numeric = optional(bool, true) + override_special = optional(string, "!@#$%&*()-_=+[]{}<>:?") + special = optional(bool, true) + upper = optional(bool, true) + } + ) + ), {} + ) + project = string + secrets = optional( + map( + object( + { + encrypted = bool + key = string + } + ) + ), {} + ) + services = optional( + map( + object( + { + applications = optional(list(string), []) + environement = optional(string, "dev") + service_key = optional(bool, true) + service_plan = optional(string, "basic") + service_type = optional(string, "s3") + tags = optional(list(string), []) + } + ) + ), {} + ) + space = string + }) +} + +variable "skip_service_instances" { + description = "Allows the skipping of service instances. Useful to inject service secrets into a user provided secret." + type = bool + default = false +} + +variable "skip_user_provided_services" { + description = "Allows the skipping of user provided services. Useful to inject service secrets into a user provided secret." + type = bool + default = false +} + +variable "secrets" { + default = {} + description = "Sensitive strings to be added to the apps environmental variables." + type = map + sensitive = true +} \ No newline at end of file