diff --git a/.github/actions/deploy-environment/action.yml b/.github/actions/deploy-environment/action.yml new file mode 100644 index 0000000000..7908325377 --- /dev/null +++ b/.github/actions/deploy-environment/action.yml @@ -0,0 +1,44 @@ +name: Deploy environment +description: Deploys an application environment + +inputs: + environment: + description: The name of the environment + required: true + docker-image: + description: The Docker image to deploy + required: true + azure-credentials: + description: JSON object containing a service principal that can read from Azure Key Vault + required: true + pull-request-number: + description: The pull request number which triggered this deploy. + required: false + +runs: + using: composite + + steps: + - uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.6.4 + terraform_wrapper: false + + - uses: DFE-Digital/github-actions/set-kubelogin-environment@master + with: + azure-credentials: ${{ inputs.azure-credentials }} + + - name: Terraform Apply + shell: bash + run: | + make ci ${{ inputs.environment }} terraform-apply-aks + env: + DOCKER_IMAGE_TAG: ${{ inputs.docker-image }} + PR_NUMBER: ${{ inputs.pull-request-number }} + + - name: Extract Terraform outputs + shell: bash + id: set_outputs + run: | + output=$(terraform -chdir=terraform/application output -raw url) + echo "APP_URL=$output" >> $GITHUB_ENV diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml new file mode 100644 index 0000000000..578f8103bb --- /dev/null +++ b/.github/workflows/build_and_deploy.yml @@ -0,0 +1,76 @@ +name: Build and deploy to AKS cluster + +on: + pull_request: + types: [labeled, opened, reopened, synchronize] + +concurrency: deploy-${{ github.ref }} + +permissions: + packages: write + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} + outputs: + docker-image-tag: ${{ steps.build-image.outputs.tag }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build and push docker image + id: build-image + uses: DFE-Digital/github-actions/build-docker-image@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + context: . + + deploy_review: + name: Deploy to review environment + concurrency: deploy_review_${{ github.event.pull_request.number }} + runs-on: ubuntu-latest + needs: [build] + environment: + name: review-aks + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: ./.github/actions/deploy-environment + id: deploy + with: + environment: review-aks + docker-image: ${{ needs.build.outputs.docker-image-tag }} + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + pull-request-number: ${{ github.event.pull_request.number }} + + - uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Seed review app + shell: bash + if: github.event.number != '' + run: | + make ci review-aks get-cluster-credentials + kubectl exec -n srtl-development deployment/claim-additional-payments-for-teaching-review-${{ github.event.pull_request.number }} -- sh -c "cd /app && bundle exec rake db:schema:load db:seed" + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + + - name: Post comment to Pull Request ${{ github.event.number }} + if: ${{ github.event_name == 'pull_request' }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: aks + message: | + ### Deployments + + | Journey | URL | + | ------------------- | ------------------------------------ | + | Additional Payments | <${{ env.APP_URL }}/additional-payments/claim> | + | Student Loans | <${{ env.APP_URL }}/student-loans/claim> | + | Admin | <${{ env.APP_URL }}/admin> | diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9ae159b3ee..a0201b248a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,6 +24,33 @@ jobs: - name: Linting - Standardrb run: bin/rails standard + lint-dfe-analytics: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:11-alpine + ports: + - '5432:5432' + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + env: + RAILS_ENV: test + DFE_TEACHERS_PAYMENT_SERVICE_DATABASE_USERNAME: postgres + DFE_TEACHERS_PAYMENT_SERVICE_DATABASE_PASSWORD: password + DFE_TEACHERS_PAYMENT_SERVICE_DATABASE_HOST: localhost + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: Prepare DB + run: bin/rails db:prepare + - name: Linting - DfE::Analytics + run: bin/rails dfe:analytics:check + test: runs-on: ubuntu-latest strategy: diff --git a/.github/workflows/delete_review_app.yml b/.github/workflows/delete_review_app.yml new file mode 100644 index 0000000000..1bc80f7e1c --- /dev/null +++ b/.github/workflows/delete_review_app.yml @@ -0,0 +1,34 @@ +name: Delete review app on AKS + +on: + pull_request: + branches: + - master + types: + - closed + +jobs: + delete-review-app: + name: Delete review app ${{ github.event.pull_request.number }} + concurrency: deploy_review_${{ github.event.pull_request.number }} + runs-on: ubuntu-latest + if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} + environment: review-aks + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.6.4 + terraform_wrapper: false + + - uses: DFE-Digital/github-actions/set-kubelogin-environment@master + with: + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Terraform destroy + run: | + make ci review-aks terraform-destroy-aks + env: + PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/.gitignore b/.gitignore index dba304aef0..1b70e2dfe8 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,10 @@ coverage/ # Editor specific files .vscode/ .rspec-local + +.terraform +terraform/application/vendor +terraform/domains/environment_domains/vendor +terraform.tfstate* +bin/terrafile +bin/konduit.sh diff --git a/.tool-versions b/.tool-versions index 4dee0780cc..cf8eeee53c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -terraform 1.2.4 +terraform 1.6.4 ruby 3.2.0 nodejs 16.17.0 diff --git a/Gemfile.lock b/Gemfile.lock index 0e880dd3dd..7cd2e94878 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -129,9 +129,9 @@ GEM delayed_job (>= 3.0, < 5) diff-lcs (1.5.1) docile (1.4.0) - dotenv (3.1.0) - dotenv-rails (3.1.0) - dotenv (= 3.1.0) + dotenv (3.1.2) + dotenv-rails (3.1.2) + dotenv (= 3.1.2) railties (>= 6.1) erubi (1.12.0) et-orbi (1.2.7) @@ -218,7 +218,7 @@ GEM html-attributes-utils (0.9.2) activesupport (>= 6.1.4.4) httpclient (2.8.3) - i18n (1.14.4) + i18n (1.14.5) concurrent-ruby (~> 1.0) jbuilder (2.11.5) actionview (>= 5.0.0) @@ -257,10 +257,10 @@ GEM rack (>= 2.1.4.1) marcel (1.0.3) matrix (0.4.2) - method_source (1.0.0) + method_source (1.1.0) mini_mime (1.1.5) - mini_portile2 (2.8.5) - minitest (5.22.2) + mini_portile2 (2.8.6) + minitest (5.22.3) msgpack (1.7.2) multi_json (1.15.0) multipart-post (2.3.0) @@ -275,10 +275,10 @@ GEM net-smtp (0.4.0.1) net-protocol nio4r (2.7.0) - nokogiri (1.16.2) + nokogiri (1.16.5) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.2-x86_64-darwin) + nokogiri (1.16.5-x86_64-darwin) racc (~> 1.4) notifications-ruby-client (5.4.0) jwt (>= 1.5, < 3) @@ -320,7 +320,7 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.7.3) - rack (2.2.8.1) + rack (2.2.9) rack-mini-profiler (3.3.1) rack (>= 1.2.0) rack-oauth2 (1.21.3) @@ -369,7 +369,7 @@ GEM thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) - rake (13.1.0) + rake (13.2.1) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) @@ -434,7 +434,7 @@ GEM sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (4.19.0) + selenium-webdriver (4.20.1) base64 (~> 0.2) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) @@ -523,7 +523,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.13) + zeitwerk (2.6.14) PLATFORMS ruby diff --git a/Makefile b/Makefile index 534932a096..b362747ed7 100644 --- a/Makefile +++ b/Makefile @@ -34,12 +34,13 @@ production: $(eval CONTAINER_NAME=s118p01conttfstate) $(eval DEPLOY_ENV=production) -.PHONY: review_aks -review_aks: test-cluster +.PHONY: review-aks +review-aks: test-cluster $(if ${PR_NUMBER},,$(error Missing PR_NUMBER)) $(eval ENVIRONMENT=review-${PR_NUMBER}) $(eval export TF_VAR_environment=${ENVIRONMENT}) $(eval include global_config/review.sh) + echo https://claim-additional-payments-for-teaching-review-$(PR_NUMBER).test.teacherservices.cloud will be created in aks set-azure-account: az account set -s ${AZ_SUBSCRIPTION} @@ -55,27 +56,63 @@ terraform-init: set-azure-account -backend-config=container_name=${CONTAINER_NAME} \ ${BACKEND_KEY} +terraform-init-aks: composed-variables bin/terrafile set-azure-account-aks + $(if ${DOCKER_IMAGE_TAG}, , $(eval DOCKER_IMAGE_TAG=master)) + + ./bin/terrafile -p terraform/application/vendor/modules -f terraform/application/config/$(CONFIG)_Terrafile + terraform -chdir=terraform/application init -upgrade -reconfigure \ + -backend-config=resource_group_name=${RESOURCE_GROUP_NAME} \ + -backend-config=storage_account_name=${STORAGE_ACCOUNT_NAME} \ + -backend-config=key=${ENVIRONMENT}_kubernetes.tfstate + + $(eval export TF_VAR_azure_resource_prefix=${AZURE_RESOURCE_PREFIX}) + $(eval export TF_VAR_config_short=${CONFIG_SHORT}) + $(eval export TF_VAR_service_name=${SERVICE_NAME}) + $(eval export TF_VAR_service_short=${SERVICE_SHORT}) + $(eval export TF_VAR_docker_image=${DOCKER_REPOSITORY}:${DOCKER_IMAGE_TAG}) + terraform-plan: terraform-init terraform -chdir=azure/terraform plan \ -var="input_container_version=${IMAGE_TAG}" \ -var-file workspace_variables/${DEPLOY_ENV}.tfvars.json +terraform-plan-aks: terraform-init-aks + terraform -chdir=terraform/application plan -var-file "config/${CONFIG}.tfvars.json" + terraform-apply: terraform-init terraform -chdir=azure/terraform apply \ -var="input_container_version=${IMAGE_TAG}" \ -var-file workspace_variables/${DEPLOY_ENV}.tfvars.json +terraform-apply-aks: terraform-init-aks + terraform -chdir=terraform/application apply -var-file "config/${CONFIG}.tfvars.json" ${AUTO_APPROVE} + terraform-destroy: terraform-init terraform -chdir=azure/terraform destroy \ -var="input_container_version=${IMAGE_TAG}" \ -var-file workspace_variables/${DEPLOY_ENV}.tfvars.json +terraform-destroy-aks: terraform-init-aks + terraform -chdir=terraform/application destroy -var-file "config/${CONFIG}.tfvars.json" ${AUTO_APPROVE} + +domains: + $(eval include global_config/domains.sh) + composed-variables: $(eval RESOURCE_GROUP_NAME=${AZURE_RESOURCE_PREFIX}-${SERVICE_SHORT}-${CONFIG_SHORT}-rg) $(eval KEYVAULT_NAMES='("${AZURE_RESOURCE_PREFIX}-${SERVICE_SHORT}-${CONFIG_SHORT}-app-kv", "${AZURE_RESOURCE_PREFIX}-${SERVICE_SHORT}-${CONFIG_SHORT}-inf-kv")') $(eval STORAGE_ACCOUNT_NAME=${AZURE_RESOURCE_PREFIX}${SERVICE_SHORT}${CONFIG_SHORT}tfsa) $(eval LOG_ANALYTICS_WORKSPACE_NAME=${AZURE_RESOURCE_PREFIX}-${SERVICE_SHORT}-${CONFIG_SHORT}-log) +ci: + $(eval AUTO_APPROVE=-auto-approve) + $(eval SKIP_AZURE_LOGIN=true) + $(eval SKIP_CONFIRM=true) + +bin/terrafile: ## Install terrafile to manage terraform modules + curl -sL https://github.com/coretech/terrafile/releases/download/v${TERRAFILE_VERSION}/terrafile_${TERRAFILE_VERSION}_$$(uname)_x86_64.tar.gz \ + | tar xz -C ./bin terrafile + set-what-if: $(eval WHAT_IF=--what-if) @@ -96,6 +133,46 @@ deploy-arm-resources: arm-deployment ## Validate ARM resource deployment. Usage: validate-arm-resources: set-what-if arm-deployment ## Validate ARM resource deployment. Usage: make domains validate-arm-resources +domains-infra-init: bin/terrafile domains composed-variables set-azure-account-aks + ./bin/terrafile -p terraform/domains/infrastructure/vendor/modules -f terraform/domains/infrastructure/config/zones_Terrafile + + terraform -chdir=terraform/domains/infrastructure init -reconfigure -upgrade \ + -backend-config=resource_group_name=${RESOURCE_GROUP_NAME} \ + -backend-config=storage_account_name=${STORAGE_ACCOUNT_NAME} \ + -backend-config=key=domains_infrastructure.tfstate + +domains-infra-plan: domains domains-infra-init ## Terraform plan for DNS infrastructure (DNS zone and front door). Usage: make domains-infra-plan + terraform -chdir=terraform/domains/infrastructure plan -var-file config/zones.tfvars.json + +domains-infra-apply: domains domains-infra-init ## Terraform apply for DNS infrastructure (DNS zone and front door). Usage: make domains-infra-apply + terraform -chdir=terraform/domains/infrastructure apply -var-file config/zones.tfvars.json ${AUTO_APPROVE} + +domains-init: bin/terrafile domains composed-variables set-azure-account-aks + ./bin/terrafile -p terraform/domains/environment_domains/vendor/modules -f terraform/domains/environment_domains/config/${CONFIG}_Terrafile + + terraform -chdir=terraform/domains/environment_domains init -upgrade -reconfigure \ + -backend-config=resource_group_name=${RESOURCE_GROUP_NAME} \ + -backend-config=storage_account_name=${STORAGE_ACCOUNT_NAME} \ + -backend-config=key=${ENVIRONMENT}.tfstate + +domains-plan: domains-init ## Terraform plan for DNS environment domains. Usage: make development domains-plan + terraform -chdir=terraform/domains/environment_domains plan -var-file config/${CONFIG}.tfvars.json + +domains-apply: domains-init ## Terraform apply for DNS environment domains. Usage: make development domains-apply + terraform -chdir=terraform/domains/environment_domains apply -var-file config/${CONFIG}.tfvars.json ${AUTO_APPROVE} + test-cluster: $(eval CLUSTER_RESOURCE_GROUP_NAME=s189t01-tsc-ts-rg) $(eval CLUSTER_NAME=s189t01-tsc-test-aks) + +production-cluster: + $(eval CLUSTER_RESOURCE_GROUP_NAME=s189p01-tsc-pd-rg) + $(eval CLUSTER_NAME=s189p01-tsc-production-aks) + +get-cluster-credentials: set-azure-account-aks + az aks get-credentials --overwrite-existing -g ${CLUSTER_RESOURCE_GROUP_NAME} -n ${CLUSTER_NAME} + kubelogin convert-kubeconfig -l $(if ${GITHUB_ACTIONS},spn,azurecli) + +bin/konduit.sh: + curl -s https://raw.githubusercontent.com/DFE-Digital/teacher-services-cloud/main/scripts/konduit.sh -o bin/konduit.sh \ + && chmod +x bin/konduit.sh diff --git a/README.md b/README.md index e458b03d0c..a7fc3f4b1f 100644 --- a/README.md +++ b/README.md @@ -280,21 +280,12 @@ https://test.additional-teaching-payment.education.gov.uk. The release process for Production is documented in [`docs/release-process.md`](docs/release-process.md) -### Heroku Review Apps - -Pull requests in the -[GitHub Repository](https://github.com/DFE-Digital/dfe-teachers-payment-service) -will automatically have a -[review app](https://devcenter.heroku.com/articles/github-integration-review-apps) -created in Heroku once CI has passed. - -For more information, see the [app's Heroku docs](docs/heroku.md) - ### Azure review apps -Pull requests automatically build a review app in Azure using the same -technologies as the production environment. Each one can be accessed via this -URL: `https://s118d02-app-pr--as.azurewebsites.net` +Adding the 'deploy' label to pull requests builds a review app in Azure using +the same technologies as the production environment. Each one can be accessed +via this URL: +`https://claim-additional-payments-for-teaching-review-.test.teacherservices.cloud` The review app is then destroyed when the PR is closed or merged. diff --git a/bin/.gitkeep b/bin/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/config/environments/review.rb b/config/environments/review.rb new file mode 100644 index 0000000000..8513ffef36 --- /dev/null +++ b/config/environments/review.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require Rails.root.join("config/environments/production") diff --git a/db/seeds.rb b/db/seeds.rb index 1b186806ed..527939817e 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -6,7 +6,7 @@ # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) -if Rails.env.development? || ENV["ENVIRONMENT_NAME"] == "review" +if Rails.env.development? || ENV["ENVIRONMENT_NAME"].start_with?("review") Journeys::Configuration.create!(routing_name: Journeys::TeacherStudentLoanReimbursement::ROUTING_NAME, current_academic_year: AcademicYear.current) Journeys::Configuration.create!(routing_name: Journeys::AdditionalPaymentsForTeaching::ROUTING_NAME, current_academic_year: AcademicYear.current) diff --git a/global_config/domains.sh b/global_config/domains.sh new file mode 100644 index 0000000000..a7b7641875 --- /dev/null +++ b/global_config/domains.sh @@ -0,0 +1,4 @@ +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-production +AZURE_RESOURCE_PREFIX=s189p01 +CONFIG_SHORT=dom +DISABLE_KEYVAULTS=true diff --git a/global_config/production.sh b/global_config/production.sh new file mode 100644 index 0000000000..0a122077b3 --- /dev/null +++ b/global_config/production.sh @@ -0,0 +1,6 @@ +CONFIG=production +ENVIRONMENT=production +CONFIG_SHORT=pd +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-production +AZURE_RESOURCE_PREFIX=s189p01 +ENABLE_KV_DIAGNOSTICS=true diff --git a/global_config/test.sh b/global_config/test.sh new file mode 100644 index 0000000000..ad8eae4807 --- /dev/null +++ b/global_config/test.sh @@ -0,0 +1,6 @@ +CONFIG=test +ENVIRONMENT=test +CONFIG_SHORT=ts +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-test +AZURE_RESOURCE_PREFIX=s189t01 +KV_PURGE_PROTECTION=false diff --git a/terraform/application/application.tf b/terraform/application/application.tf new file mode 100644 index 0000000000..681d651efe --- /dev/null +++ b/terraform/application/application.tf @@ -0,0 +1,39 @@ +module "application_configuration" { + source = "./vendor/modules/aks//aks/application_configuration" + + namespace = var.namespace + environment = var.environment + azure_resource_prefix = var.azure_resource_prefix + service_short = var.service_short + config_short = var.config_short + secret_key_vault_short = "app" + + is_rails_application = true + + config_variables = merge( + local.app_env_values, + { + ENVIRONMENT_NAME = var.environment + PGSSLMODE = local.postgres_ssl_mode + }) + secret_variables = { + DATABASE_URL = module.postgres.url + } +} + +module "web_application" { + source = "./vendor/modules/aks//aks/application" + + is_web = true + + namespace = var.namespace + environment = var.environment + service_name = var.service_name + + cluster_configuration_map = module.cluster_data.configuration_map + kubernetes_config_map_name = module.application_configuration.kubernetes_config_map_name + kubernetes_secret_name = module.application_configuration.kubernetes_secret_name + + docker_image = var.docker_image + command = var.startup_command +} diff --git a/terraform/application/cluster_data.tf b/terraform/application/cluster_data.tf new file mode 100644 index 0000000000..42778646b9 --- /dev/null +++ b/terraform/application/cluster_data.tf @@ -0,0 +1,4 @@ +module "cluster_data" { + source = "./vendor/modules/aks//aks/cluster_data" + name = var.cluster +} diff --git a/terraform/application/config/production.tfvars.json b/terraform/application/config/production.tfvars.json new file mode 100644 index 0000000000..25bc897d9a --- /dev/null +++ b/terraform/application/config/production.tfvars.json @@ -0,0 +1,9 @@ +{ + "cluster": "production", + "namespace": "srtl-production", + "environment": "production", + "enable_postgres_backup_storage": true, + "enable_monitoring": true, + "external_url": "https://claim-additional-teaching-payment.service.gov.uk/healthcheck", + "statuscake_contact_groups": [282453] +} diff --git a/terraform/application/config/production_Terrafile b/terraform/application/config/production_Terrafile new file mode 100644 index 0000000000..5b2b118f00 --- /dev/null +++ b/terraform/application/config/production_Terrafile @@ -0,0 +1,3 @@ +aks: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "stable" diff --git a/terraform/application/config/review.tfvars.json b/terraform/application/config/review.tfvars.json new file mode 100644 index 0000000000..bc91753a70 --- /dev/null +++ b/terraform/application/config/review.tfvars.json @@ -0,0 +1,8 @@ +{ + "cluster": "test", + "namespace": "srtl-development", + "config": "review", + "deploy_azure_backing_services": false, + "enable_postgres_ssl": false, + "startup_command": ["/bin/sh", "-c", "bundle exec rails server -b 0.0.0.0"] +} diff --git a/terraform/application/config/review_Terrafile b/terraform/application/config/review_Terrafile new file mode 100644 index 0000000000..65af53b11d --- /dev/null +++ b/terraform/application/config/review_Terrafile @@ -0,0 +1,3 @@ +aks: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "main" diff --git a/terraform/application/config/review_app_env.yml b/terraform/application/config/review_app_env.yml new file mode 100644 index 0000000000..c58668c4c8 --- /dev/null +++ b/terraform/application/config/review_app_env.yml @@ -0,0 +1,35 @@ +--- +DFE_SIGN_IN_API_CLIENT_ID: teacherpayments +DFE_SIGN_IN_API_SECRET: secret +DFE_SIGN_IN_API_ENDPOINT: https://example.com + +DQT_API_URL: https://teacher-qualifications-api.education.gov.uk/ +DQT_API_KEY: 1a2b3c4d5e6f7g8h9i0 + +DQT_BEARER_BASE_URL: https://login.microsoftonline.com/123456/oauth2/v2.0/token +DQT_BEARER_GRANT_TYPE: client_credentials +DQT_BEARER_SCOPE: https://test.dynamics.com/.default +DQT_BEARER_CLIENT_ID: 1234 +DQT_BEARER_CLIENT_SECRET: 5678 +DQT_BASE_URL: https://test-api-customerengagement.platform.education.gov.uk/dqt-crm/v1/ +DQT_SUBSCRIPTION_KEY: 09876432 + +ADMIN_ALLOWED_IPS: ::1,127.0.0.1 + +ENVIRONMENT_NAME: review + +ORDNANCE_SURVEY_API_BASE_URL: https://api.os.uk +# ORDNANCE_SURVEY_CLIENT_PARAMS: { "key": "api-key-value" } + +RUN_FLAKY_SPECS: true +RUN_JS_SPECS: true +RUN_SLOW_SPECS: true + +HMRC_API_BASE_URL: https://test-api.service.hmrc.gov.uk +HMRC_API_BANK_VALIDATION_ENABLED: false +HMRC_API_CLIENT_ID: test +HMRC_API_CLIENT_SECRET: test + +TID_SIGN_IN_ISSUER: https://preprod.teaching-identity.education.gov.uk/ +TID_SIGN_IN_API_ENDPOINT: https://preprod.teaching-identity.education.gov.uk:433 +TID_SIGN_IN_CLIENT_ID: claim diff --git a/terraform/application/config/test.tfvars.json b/terraform/application/config/test.tfvars.json new file mode 100644 index 0000000000..b64515add3 --- /dev/null +++ b/terraform/application/config/test.tfvars.json @@ -0,0 +1,5 @@ +{ + "cluster": "test", + "namespace": "srtl-test", + "environment": "test" +} diff --git a/terraform/application/config/test_Terrafile b/terraform/application/config/test_Terrafile new file mode 100644 index 0000000000..b4c222c13d --- /dev/null +++ b/terraform/application/config/test_Terrafile @@ -0,0 +1,3 @@ +aks: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "testing" diff --git a/terraform/application/database.tf b/terraform/application/database.tf new file mode 100644 index 0000000000..b8da6665df --- /dev/null +++ b/terraform/application/database.tf @@ -0,0 +1,15 @@ +module "postgres" { + source = "./vendor/modules/aks//aks/postgres" + + namespace = var.namespace + environment = var.environment + azure_resource_prefix = var.azure_resource_prefix + service_name = var.service_name + service_short = var.service_short + config_short = var.config_short + cluster_configuration_map = module.cluster_data.configuration_map + use_azure = var.deploy_azure_backing_services + azure_enable_monitoring = var.enable_monitoring + azure_enable_backup_storage = var.enable_postgres_backup_storage + server_version = "14" +} diff --git a/terraform/application/output.tf b/terraform/application/output.tf new file mode 100644 index 0000000000..989dbad146 --- /dev/null +++ b/terraform/application/output.tf @@ -0,0 +1,3 @@ +output "url" { + value = module.web_application.url +} diff --git a/terraform/application/secrets.tf b/terraform/application/secrets.tf new file mode 100644 index 0000000000..f3592a80a6 --- /dev/null +++ b/terraform/application/secrets.tf @@ -0,0 +1,8 @@ +module "infrastructure_secrets" { + source = "./vendor/modules/aks//aks/secrets" + + azure_resource_prefix = var.azure_resource_prefix + service_short = var.service_short + config_short = var.config_short + key_vault_short = "inf" +} diff --git a/terraform/application/statuscake.tf b/terraform/application/statuscake.tf new file mode 100644 index 0000000000..840f7a8ad6 --- /dev/null +++ b/terraform/application/statuscake.tf @@ -0,0 +1,12 @@ +# TODO: Uncomment when needed then follow these steps: https://github.com/DFE-Digital/teacher-services-cloud/blob/main/documentation/onboard-service.md#configure-statuscake-credentials + +# module "statuscake" { +# count = var.enable_monitoring ? 1 : 0 + +# source = "./vendor/modules/aks//monitoring/statuscake" + +# uptime_urls = compact([module.web_application.probe_url, var.external_url]) +# ssl_urls = compact([var.external_url]) + +# contact_groups = var.statuscake_contact_groups +# } diff --git a/terraform/application/terraform.tf b/terraform/application/terraform.tf new file mode 100644 index 0000000000..612bdf535e --- /dev/null +++ b/terraform/application/terraform.tf @@ -0,0 +1,46 @@ +terraform { + required_version = "= 1.6.4" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.82.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.24.0" + } + statuscake = { + source = "StatusCakeDev/statuscake" + version = "2.1.0" + } + } + backend "azurerm" { + container_name = "terraform-state" + } +} + +provider "azurerm" { + features {} + + skip_provider_registration = true +} + +provider "kubernetes" { + host = module.cluster_data.kubernetes_host + client_certificate = module.cluster_data.kubernetes_client_certificate + client_key = module.cluster_data.kubernetes_client_key + cluster_ca_certificate = module.cluster_data.kubernetes_cluster_ca_certificate + + dynamic "exec" { + for_each = module.cluster_data.azure_RBAC_enabled ? [1] : [] + content { + api_version = "client.authentication.k8s.io/v1beta1" + command = "kubelogin" + args = module.cluster_data.kubelogin_args + } + } +} + +provider "statuscake" { + api_token = module.infrastructure_secrets.map.STATUSCAKE-API-TOKEN +} diff --git a/terraform/application/variables.tf b/terraform/application/variables.tf new file mode 100644 index 0000000000..f602f3b136 --- /dev/null +++ b/terraform/application/variables.tf @@ -0,0 +1,60 @@ +variable "cluster" { + description = "AKS cluster where this app is deployed. Either 'test' or 'production'" +} +variable "namespace" { + description = "AKS namespace where this app is deployed" +} +variable "environment" { + description = "Name of the deployed environment in AKS" +} +variable "azure_resource_prefix" { + description = "Standard resource prefix. Usually s189t01 (test) or s189p01 (production)" +} +variable "config_short" { + description = "Short name of the environment configuration, e.g. dv, st, pd..." +} +variable "service_name" { + description = "Full name of the service. Lowercase and hyphen separated" +} +variable "service_short" { + description = "Short name to identify the service. Up to 6 charcters." +} +variable "deploy_azure_backing_services" { + default = true + description = "Deploy real Azure backing services like databases, as opposed to containers inside of AKS" +} +variable "enable_postgres_ssl" { + default = true + description = "Enforce SSL connection from the client side" +} +variable "enable_postgres_backup_storage" { + default = false + description = "Create a storage account to store database dumps" +} +variable "docker_image" { + description = "Docker image full name to identify it in the registry. Includes docker registry, repository and tag e.g.: ghcr.io/dfe-digital/teacher-pay-calculator:673f6309fd0c907014f44d6732496ecd92a2bcd0" +} +variable "startup_command" { + type = list(string) +} +variable "external_url" { + default = null + description = "Healthcheck URL for StatusCake monitoring" +} +variable "statuscake_contact_groups" { + default = [] + description = "ID of the contact group in statuscake web UI" +} +variable "enable_monitoring" { + default = false + description = "Enable monitoring and alerting" +} +variable "config" { + type = string +} + +locals { + postgres_ssl_mode = var.enable_postgres_ssl ? "require" : "disable" + app_env_values_from_yml = yamldecode(file("${path.module}/config/${var.config}_app_env.yml")) + app_env_values = merge(local.app_env_values_from_yml) +} diff --git a/terraform/domains/environment_domains/config/production.tfvars.json b/terraform/domains/environment_domains/config/production.tfvars.json new file mode 100644 index 0000000000..bb37dcca10 --- /dev/null +++ b/terraform/domains/environment_domains/config/production.tfvars.json @@ -0,0 +1,12 @@ +{ + "hosted_zone": { + "claim-additional-teaching-payment.service.gov.uk": { + "front_door_name": "#FRONT_DOOR_NAME#", + "resource_group_name": "#DOMAINS_RESOURCE_GROUP_NAME#", + "domains": ["apex"], + "cached_paths": ["/assets/*"], + "environment_short": "pd", + "origin_hostname": "claim-additional-payments-for-teaching-production.teacherservices.cloud" + } + } +} diff --git a/terraform/domains/environment_domains/config/production_Terrafile b/terraform/domains/environment_domains/config/production_Terrafile new file mode 100644 index 0000000000..58e60b3c88 --- /dev/null +++ b/terraform/domains/environment_domains/config/production_Terrafile @@ -0,0 +1,3 @@ +domains: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "stable" diff --git a/terraform/domains/environment_domains/config/test.tfvars.json b/terraform/domains/environment_domains/config/test.tfvars.json new file mode 100644 index 0000000000..ec5ad7b7c6 --- /dev/null +++ b/terraform/domains/environment_domains/config/test.tfvars.json @@ -0,0 +1,12 @@ +{ + "hosted_zone": { + "claim-additional-teaching-payment.service.gov.uk": { + "front_door_name": "#FRONT_DOOR_NAME#", + "resource_group_name": "#DOMAINS_RESOURCE_GROUP_NAME#", + "domains": ["test"], + "cached_paths": ["/assets/*"], + "environment_short": "test", + "origin_hostname": "claim-additional-payments-for-teaching-test.test.teacherservices.cloud" + } + } +} diff --git a/terraform/domains/environment_domains/config/test_Terrafile b/terraform/domains/environment_domains/config/test_Terrafile new file mode 100644 index 0000000000..dfce270ef6 --- /dev/null +++ b/terraform/domains/environment_domains/config/test_Terrafile @@ -0,0 +1,3 @@ +domains: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "testing" diff --git a/terraform/domains/environment_domains/main.tf b/terraform/domains/environment_domains/main.tf new file mode 100644 index 0000000000..96beb730ab --- /dev/null +++ b/terraform/domains/environment_domains/main.tf @@ -0,0 +1,13 @@ +# Used to create domains to be managed by front door. +module "domains" { + for_each = var.hosted_zone + source = "./vendor/modules/domains//domains/environment_domains" + zone = each.key + front_door_name = each.value.front_door_name + resource_group_name = each.value.resource_group_name + domains = each.value.domains + environment = each.value.environment_short + host_name = each.value.origin_hostname + null_host_header = try(each.value.null_host_header, false) + cached_paths = try(each.value.cached_paths, []) +} diff --git a/terraform/domains/environment_domains/terraform.tf b/terraform/domains/environment_domains/terraform.tf new file mode 100644 index 0000000000..0fb333955c --- /dev/null +++ b/terraform/domains/environment_domains/terraform.tf @@ -0,0 +1,19 @@ +terraform { + + required_version = "= 1.6.4" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.82.0" + } + } + backend "azurerm" { + container_name = "terraform-state" + } +} + +provider "azurerm" { + features {} + + skip_provider_registration = true +} diff --git a/terraform/domains/environment_domains/variables.tf b/terraform/domains/environment_domains/variables.tf new file mode 100644 index 0000000000..ad63e546fd --- /dev/null +++ b/terraform/domains/environment_domains/variables.tf @@ -0,0 +1,4 @@ +variable "hosted_zone" { + type = map(any) + default = {} +} diff --git a/terraform/domains/infrastructure/config/zones.tfvars.json b/terraform/domains/infrastructure/config/zones.tfvars.json new file mode 100644 index 0000000000..fe0910df0a --- /dev/null +++ b/terraform/domains/infrastructure/config/zones.tfvars.json @@ -0,0 +1,11 @@ +{ + "hosted_zone": { + "claim-additional-teaching-payment.service.gov.uk": { + "caa_records": {}, + "txt_records": {}, + "resource_group_name": "#DOMAINS_RESOURCE_GROUP_NAME#", + "front_door_name": "#FRONT_DOOR_NAME#" + } + }, + "deploy_default_records": false +} diff --git a/terraform/domains/infrastructure/config/zones_Terrafile b/terraform/domains/infrastructure/config/zones_Terrafile new file mode 100644 index 0000000000..58e60b3c88 --- /dev/null +++ b/terraform/domains/infrastructure/config/zones_Terrafile @@ -0,0 +1,3 @@ +domains: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "stable" diff --git a/terraform/domains/infrastructure/main.tf b/terraform/domains/infrastructure/main.tf new file mode 100644 index 0000000000..da091f5e16 --- /dev/null +++ b/terraform/domains/infrastructure/main.tf @@ -0,0 +1,5 @@ +module "domains_infrastructure" { + source = "./vendor/modules/domains//domains/infrastructure" + hosted_zone = var.hosted_zone + deploy_default_records = var.deploy_default_records +} diff --git a/terraform/domains/infrastructure/terraform.tf b/terraform/domains/infrastructure/terraform.tf new file mode 100644 index 0000000000..65b9162e88 --- /dev/null +++ b/terraform/domains/infrastructure/terraform.tf @@ -0,0 +1,19 @@ +terraform { + required_version = "= 1.6.4" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.82.0" + } + } + backend "azurerm" { + container_name = "terraform-state" + } +} + +provider "azurerm" { + features {} + + skip_provider_registration = true +} diff --git a/terraform/domains/infrastructure/variables.tf b/terraform/domains/infrastructure/variables.tf new file mode 100644 index 0000000000..8d91472066 --- /dev/null +++ b/terraform/domains/infrastructure/variables.tf @@ -0,0 +1,7 @@ +variable "hosted_zone" { + type = map(any) +} + +variable "deploy_default_records" { + default = true +}