diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml new file mode 100644 index 000000000..288064a77 --- /dev/null +++ b/.github/workflows/deploy-prod.yml @@ -0,0 +1,87 @@ +name: Deploy to production environment + +on: + push: + branches: [ production ] + +permissions: + contents: read + +# deploy-prod and deploy-demo will run in parallel now. +# TODO: Research if we want to serialize them +# by moving the jobs into a single file similar to +# https://github.com/GSA/usnotify-ssb/blob/main/.github/workflows/apply.yml +jobs: + deploy: + runs-on: ubuntu-latest + environment: production + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 2 + + - name: Check for changes to Terraform + id: changed-terraform-files + uses: tj-actions/changed-files@v34 + with: + files: | + terraform/production + terraform/shared + .github/workflows/deploy-prod.yml + - name: Terraform init + if: steps.changed-terraform-files.outputs.any_changed == 'true' + working-directory: terraform/production + env: + AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }} + run: terraform init + - name: Terraform apply + if: steps.changed-terraform-files.outputs.any_changed == 'true' + working-directory: terraform/production + env: + AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }} + TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }} + TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} + run: terraform apply -auto-approve -input=false + + - uses: ./.github/actions/setup-project + - name: Install application dependencies + run: make bootstrap + + - name: Create requirements.txt because Cloud Foundry does a weird pipenv thing + run: pipenv requirements > requirements.txt + + - name: Deploy to cloud.gov + uses: 18f/cg-deploy-action@main + env: + DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }} + SECRET_KEY: ${{ secrets.SECRET_KEY }} + ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }} + NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} + with: + cf_username: ${{ secrets.CLOUDGOV_USERNAME }} + cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} + cf_org: gsa-tts-benefits-studio-prototyping + cf_space: notify-production + push_arguments: >- + --vars-file deploy-config/production.yml + --var DANGEROUS_SALT="$DANGEROUS_SALT" + --var SECRET_KEY="$SECRET_KEY" + --var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET" + --var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY" + + - name: Check for changes to egress config + id: changed-egress-config + uses: tj-actions/changed-files@v34 + with: + files: | + deploy-config/egress_proxy/notify-api-production.*.acl + .github/actions/deploy-proxy/action.yml + .github/workflows/deploy-prod.yml + - name: Deploy egress proxy + if: steps.changed-egress-config.outputs.any_changed == 'true' + uses: ./.github/actions/deploy-proxy + with: + cf_space: notify-production + app: notify-api-production diff --git a/.github/workflows/terraform-production.yml b/.github/workflows/terraform-production.yml index e48000438..afb10dcfb 100644 --- a/.github/workflows/terraform-production.yml +++ b/.github/workflows/terraform-production.yml @@ -2,7 +2,7 @@ name: Run Terraform plan in production on: pull_request: - branches: [ production-disabled-for-now ] + branches: [ production ] paths: [ 'terraform/**' ] defaults: diff --git a/deploy-config/egress_proxy/notify-api-production.allow.acl b/deploy-config/egress_proxy/notify-api-production.allow.acl new file mode 100644 index 000000000..a6e4a2f65 --- /dev/null +++ b/deploy-config/egress_proxy/notify-api-production.allow.acl @@ -0,0 +1,4 @@ +email.us-gov-west-1.amazonaws.com +sns.us-gov-west-1.amazonaws.com +gov-collector.newrelic.com +egress-proxy-notify-api-production.apps.internal diff --git a/deploy-config/egress_proxy/notify-api-production.deny.acl b/deploy-config/egress_proxy/notify-api-production.deny.acl new file mode 100644 index 000000000..e69de29bb diff --git a/deploy-config/egress_proxy/notify-api-production.deploy.acl b/deploy-config/egress_proxy/notify-api-production.deploy.acl new file mode 100644 index 000000000..e5a3a541d --- /dev/null +++ b/deploy-config/egress_proxy/notify-api-production.deploy.acl @@ -0,0 +1 @@ +Update this file to force a re-deploy of the egress proxy even when notify-api-production..acl haven't changed diff --git a/docs/deploying.md b/docs/deploying.md index 2763a57a5..916a5279f 100644 --- a/docs/deploying.md +++ b/docs/deploying.md @@ -1,19 +1,20 @@ # Deploying -We deploy automatically to cloud.gov for demo and staging environments. +We deploy automatically to cloud.gov for production, demo, and staging environments. Deployment to staging runs via the [base deployment action](../.github/workflows/deploy.yml) on GitHub, which pulls credentials from GitHub's secrets store in the staging environment. Deployment to demo runs via the [demo deployment action](../.github/workflows/deploy-demo.yml) on GitHub, which pulls credentials from GitHub's secrets store in the demo environment. +Deployment to production runs via the [production deployment action](../.github/workflows/deploy-prod.yml) on GitHub, which pulls credentials from GitHub's secrets store in the production environment. + The [action that we use](https://github.com/18F/cg-deploy-action) deploys using [a rolling strategy](https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html), so all deployments should have zero downtime. -The API has 2 deployment environments: +The API has 3 deployment environments: - Staging, which deploys from `main` - Demo, which deploys from `production` - -In the future, we will add a Production deploy environment, which will deploy in parallel to Demo. +- Production, which deploys from `production` Configurations for these are located in [the `deploy-config` folder](../deploy-config/). diff --git a/docs/infra-overview.md b/docs/infra-overview.md index e7dd0768e..8707f9797 100644 --- a/docs/infra-overview.md +++ b/docs/infra-overview.md @@ -102,6 +102,24 @@ We are using [New Relic](https://one.newrelic.com/nr1-core?account=3389907) for These steps are required for new cloud.gov environments. Local development borrows SES & SNS infrastructure from the `notify-staging` cloud.gov space, so these steps are not required for new developers. +### Steps to do a clean prod deploy to cloud.gov + +Steps for deploying production from scratch. These can be updated for a new cloud.gov environment by subbing out `prod` or `production` for your desired environment within the steps. + +1. Deploy API app + 1. Update `terraform-production.yml` and `deploy-prod.yml` to point to the correct space and git branch. + 1. Ensure that the `domain` module is commented out in `terraform/production/main.tf` + 1. Run CI/CD pipeline on the `production` branch by opening a PR from `main` to `production` + 1. Create any necessary DNS records (check `notify-api-ses-production` service credentials for instructions) within https://github.com/18f/dns + 1. Follow the `Steps to prepare SES` below + 1. (Optional) if using a public API route, uncomment the `domain` module and re-trigger a deploy +1. Deploy Admin app + 1. Update `terraform-production.yml` and `deploy-prod.yml` to point to the correct space and git branch. + 1. Ensure that the `api_network_route` and `domain` modules are commented out in `terraform/production/main.tf` + 1. Run CI/CD pipeline on the `production` branch by opening a PR from `main` to `production` + 1. Uncomment the `api_network_route` and `domain` modules and re-trigger a deploy + 1. Create DNS records for `domain` module within https://github.com/18f/dns + ### Steps to prepare SES 1. After the first deploy of the application with the SSB-brokered SES service completes: diff --git a/terraform/production/main.tf b/terraform/production/main.tf index afe132ad4..49e7840d8 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -72,9 +72,10 @@ module "sns_sms" { ########################################################################### # The following lines need to be commented out for the initial `terraform apply` # It can be re-enabled after: +# TODO: decide on public API domain name # 1) the app has first been deployed # 2) the route has been manually created by an OrgManager: -# `cf create-domain TKTK-org-name TKTK-production-domain-name` +# `cf create-domain gsa-tts-benefits-studio-prototyping api.notify.gov` ########################################################################### # module "domain" { # source = "github.com/18f/terraform-cloudgov//domain?ref=v0.2.0" @@ -85,5 +86,5 @@ module "sns_sms" { # name = "${local.app_name}-domain-${local.env}" # recursive_delete = local.recursive_delete # cdn_plan_name = "domain" -# domain_name = "TKTK-production-domain-name" +# domain_name = "api.notify.gov" # }