diff --git a/.github/actions/deploy-environment-to-aks/action.yml b/.github/actions/deploy-environment-to-aks/action.yml new file mode 100644 index 0000000..aa785e0 --- /dev/null +++ b/.github/actions/deploy-environment-to-aks/action.yml @@ -0,0 +1,61 @@ +name: Deploy environment to AKS +description: Deploys an application environment to AKS + +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 key for the service principal authorised on the Azure subscription + required: true + pull-request-number: + description: The pull request number which triggered this deploy. If set, this will automatically seed the database. + required: false + current-commit-sha: + description: The commit sha for the current commit + required: true + +outputs: + url: + description: The base URL for the deployed environment + value: ${{ steps.apply-terraform.outputs.url }} + +runs: + using: composite + + steps: + - uses: hashicorp/setup-terraform@v1 + with: + terraform_version: 1.5.4 + terraform_wrapper: false + + - uses: DFE-Digital/github-actions/set-arm-environment-variables@master + with: + azure-credentials: ${{ inputs.azure-credentials }} + + - name: Apply Terraform + id: apply-terraform + shell: bash + run: | + make ci ${{ inputs.environment }} terraform-apply + cd terraform/application && echo "url=$(terraform output -raw url)" >> $GITHUB_OUTPUT + env: + TF_VAR_azure_sp_credentials_json: ${{ inputs.azure-credentials }} + TF_VAR_statuscake_api_token: ${{ inputs.statuscake-api-token }} + DOCKER_IMAGE: ${{ inputs.docker-image }} + PULL_REQUEST_NUMBER: ${{ inputs.pull-request-number }} + + - uses: Azure/login@v1 + with: + creds: ${{ inputs.azure-credentials }} + + - name: Seed database + if: ${{ inputs.pull-request-number != '' }} + shell: bash + run: | + az aks get-credentials --resource-group s189t01-tsc-ts-rg --name s189t01-tsc-test-aks + kubectl exec -n cpd-development deployment/cpd-tsh-review-${{ inputs.pull-request-number }} -- sh -c "cd /app && /usr/local/bin/bundle exec rails db:seed" + diff --git a/.github/workflows/aks_deploy.yml b/.github/workflows/aks_deploy.yml new file mode 100644 index 0000000..527e418 --- /dev/null +++ b/.github/workflows/aks_deploy.yml @@ -0,0 +1,299 @@ +name: "Deploy" + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +on: + workflow_dispatch: + inputs: + environment: + description: "Deploy environment" + required: true + default: review_aks + type: environment + options: + - review_aks + push: + branches: + - main + + pull_request: + branches: + - main + types: + - labeled + - synchronize + - reopened + - opened + +jobs: + rspec: + name: Run Rspec + + runs-on: ubuntu-latest + env: + GOOGLE_MAP_API_KEY: someapikey + + services: + postgres: + image: postgis/postgis:11-3.3-alpine + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1.2 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '16.20.0' + + - name: Update dependencies + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -y libproj-dev proj-bin + + - name: Set up ruby gem cache + uses: actions/cache@v3 + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + + - name: Install gems + run: | + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + + - name: Install yarn + run: npm install yarn -g + + - name: Yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + + - name: Set up yarn cache + uses: actions/cache@v3 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install node.js dependencies + run: yarn install + + - name: Set up test database + run: bin/rails db:create db:schema:load + env: + DATABASE_URL: postgis://postgres:password@localhost:5432/test + + - name: Run tests + run: bundle exec rake + env: + DATABASE_URL: postgis://postgres:password@localhost:5432/test + linting: + name: Run Rubocop + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1.2 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '16.20.0' + + - name: Set up ruby gem cache + uses: actions/cache@v3 + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + + - name: Update dependencies + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -y libproj-dev proj-bin + + - name: Install gems + run: | + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + + - name: Install node.js dependencies + run: yarn install + + - name: Run rubocop + run: bundle exec rubocop --format clang --parallel + + brakeman: + name: Run Brakeman + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1.2 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '16.20.0' + + - name: Set up ruby gem cache + uses: actions/cache@v3 + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + + - name: Update dependencies + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -y libproj-dev proj-bin + + - name: Install gems + run: | + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + + - name: Run brakeman + run: bundle exec brakeman + + docker: + name: Build and push Docker image + runs-on: ubuntu-latest + if: github.actor != 'dependabot[bot]' + outputs: + docker-image: ${{ steps.build-docker-image.outputs.image }} + steps: + - uses: actions/checkout@v3 + + - uses: DFE-Digital/github-actions/build-docker-image@master + id: build-docker-image + with: + docker-repository: ghcr.io/dfe-digital/teaching-school-hub-finder + github-token: ${{ secrets.GITHUB_TOKEN }} + + permit-merge: + name: Permit merge + needs: [linting, rspec, brakeman] + runs-on: ubuntu-latest + steps: + - run: "echo 'Linting and tests passed, this branch is ready to be merged'" + + deploy_review: + name: Deploy review + concurrency: deploy_review_${{ github.event.pull_request.number }} + if: github.actor != 'dependabot[bot]' && github.event_name == 'pull_request' + needs: [docker, linting] + runs-on: ubuntu-latest + environment: + name: review + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/deploy-environment-to-aks + id: deploy + with: + environment: review + docker-image: ${{ needs.docker.outputs.docker-image }} + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + pull-request-number: ${{ github.event.pull_request.number }} + current-commit-sha: ${{ github.event.pull_request.head.sha }} + + - name: Post sticky pull request comment + if: github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + message: | + Review app deployed to ${{ steps.deploy.outputs.url }} + + deploy_staging: + name: Deploy staging + needs: [docker, rspec, linting, brakeman] + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: + name: staging + outputs: + docker-image: ${{ needs.docker.outputs.docker-image }} + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/deploy-environment-to-aks + id: deploy + with: + environment: staging + docker-image: ${{ needs.docker.outputs.docker-image }} + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + current-commit-sha: ${{ github.sha }} + statuscake-api-token: ${{ secrets.STATUSCAKE_API_TOKEN }} + + deploy_sandbox: + name: Deploy sandbox + needs: [deploy_staging] + runs-on: ubuntu-latest + environment: + name: sandbox + + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/deploy-environment-to-aks + id: deploy + with: + environment: sandbox + docker-image: ${{ needs.deploy_staging.outputs.docker-image }} + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + current-commit-sha: ${{ github.sha }} + statuscake-api-token: ${{ secrets.STATUSCAKE_API_TOKEN }} + + deploy_production: + name: Deploy production + needs: [deploy_staging] + runs-on: ubuntu-latest + environment: + name: production + + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/deploy-environment-to-aks + id: deploy + with: + environment: production + docker-image: ${{ needs.deploy_staging.outputs.docker-image }} + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + current-commit-sha: ${{ github.sha }} + statuscake-api-token: ${{ secrets.STATUSCAKE_API_TOKEN }} diff --git a/.github/workflows/aks_destroy_review.yml b/.github/workflows/aks_destroy_review.yml new file mode 100644 index 0000000..f240f24 --- /dev/null +++ b/.github/workflows/aks_destroy_review.yml @@ -0,0 +1,61 @@ +name: Delete review + +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + delete-review-app: + name: Delete Review App ${{ github.event.pull_request.number }} + concurrency: deploy_review_${{ github.event.pull_request.number }} + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') }} + runs-on: ubuntu-latest + environment: review + steps: + - uses: actions/checkout@v3 + + - uses: Azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: hashicorp/setup-terraform@v2 + with: + terraform_version: 1.5.4 + terraform_wrapper: false + + - name: Set environment variables + run: | + state_file_name=terraform-${{ github.event.pull_request.number }}.tfstate + + echo "TF_STATE_FILE=$state_file_name" >> $GITHUB_ENV + + state_file_status=$(az storage blob list -c cpdecf-tfstate \ + --account-name "s189t01cpdecftfstatervsa" \ + --prefix $state_file_name --query "[].name" -o tsv) + + if [ -n "$state_file_status" ]; then + echo "TF_STATE_EXISTS=true" >> $GITHUB_ENV + fi + + - uses: DFE-Digital/github-actions/set-arm-environment-variables@master + if: env.TF_STATE_EXISTS == 'true' + with: + azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Destroy Terraform + if: env.TF_STATE_EXISTS == 'true' + id: destroy-terraform + shell: bash + run: make ci review terraform-destroy + env: + TF_VAR_azure_sp_credentials_json: ${{ secrets.AZURE_CREDENTIALS }} + TF_VAR_statuscake_api_token: ${{ secrets.STATUSCAKE_API_TOKEN }} + DOCKER_IMAGE: "ghcr.io/dfe-digital/early-careers-framework:no-tag" + PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + + - name: Delete Terraform state file + if: env.TF_STATE_EXISTS == 'true' + run: | + az storage blob delete -c cpdecf-tfstate --name ${{ env.TF_STATE_FILE }} \ + --account-name "s189t01cpdecftfstatervsa" diff --git a/.github/workflows/specs.yml b/.github/workflows/specs.yml deleted file mode 100644 index 200c7fc..0000000 --- a/.github/workflows/specs.yml +++ /dev/null @@ -1,159 +0,0 @@ -name: Run Rspec, Rubocop, and Brakeman - -on: [push] - -env: - RAILS_ENV: test - GOOGLE_MAP_API_KEY: notusedinspecs - SECRET_KEY_BASE: notusedinspecs - -jobs: - rspec: - name: Run Rspec - - runs-on: ubuntu-latest - - services: - postgres: - image: postgis/postgis:11-3.3-alpine - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - ports: - - 5432:5432 - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1.2 - - - name: Set up Node - uses: actions/setup-node@v3 - with: - node-version: '16.20.0' - - - name: Install dependencies - run: sudo apt-get install -y libproj-dev proj-bin - - - name: Set up ruby gem cache - uses: actions/cache@v3 - with: - path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: | - ${{ runner.os }}-gems- - - - name: Install gems - run: | - bundle config path vendor/bundle - bundle install --jobs 4 --retry 3 - - - name: Install yarn - run: npm install yarn -g - - - name: Yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: Set up yarn cache - uses: actions/cache@v3 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Install node.js dependencies - run: yarn install - - - name: Set up test database - run: bin/rails db:create db:schema:load - env: - DATABASE_URL: postgis://postgres:password@localhost:5432/test - - - name: Run tests - run: bundle exec rake - env: - DATABASE_URL: postgis://postgres:password@localhost:5432/test - linting: - name: Run Rubocop - - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1.2 - - - name: Set up Node - uses: actions/setup-node@v3 - with: - node-version: '16.20.0' - - - name: Set up ruby gem cache - uses: actions/cache@v3 - with: - path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: | - ${{ runner.os }}-gems- - - - name: Install dependencies - run: sudo apt-get install -y libproj-dev proj-bin - - - name: Install gems - run: | - bundle config path vendor/bundle - bundle install --jobs 4 --retry 3 - - - name: Install node.js dependencies - run: yarn install - - - name: Run rubocop - run: bundle exec rubocop --format clang --parallel - brakeman: - name: Run Brakeman - - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.1.2 - - - name: Set up Node - uses: actions/setup-node@v3 - with: - node-version: '16.20.0' - - - name: Set up ruby gem cache - uses: actions/cache@v3 - with: - path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: | - ${{ runner.os }}-gems- - - - name: Install dependencies - run: sudo apt-get install -y libproj-dev proj-bin - - - name: Install gems - run: | - bundle config path vendor/bundle - bundle install --jobs 4 --retry 3 - - - name: Run brakeman - run: bundle exec brakeman diff --git a/.gitignore b/.gitignore index e9e66ad..af4abb2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,9 @@ tmp/pids/* !tmp/pids/.keep /config/master.key + +.terraform +terraform/application/vendor +terraform/domains/environment_domains/vendor +terraform.tfstate* +bin/terrafile diff --git a/Dockerfile b/Dockerfile index adbf95e..b17cb8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,5 +72,5 @@ RUN apk add --no-cache proj-dev COPY --from=builder /app /app COPY --from=builder /usr/local/bundle/ /usr/local/bundle/ -CMD RAILS_ENV=production bundle exec rails db:migrate && \ - RAILS_ENV=production bundle exec rails server -b 0.0.0.0 +CMD bundle exec rails db:migrate && \ + bundle exec rails server -b 0.0.0.0 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f3ef3d8 --- /dev/null +++ b/Makefile @@ -0,0 +1,149 @@ +TERRAFILE_VERSION=0.8 +ARM_TEMPLATE_TAG=1.1.10 +RG_TAGS={"Product" : "Find a teaching school hub"} +REGION=UK South +SERVICE_NAME=teaching-school-hub-finder +SERVICE_SHORT=cpdtsh + +help: + @grep -E '^[a-zA-Z\._\-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: development +development: test-cluster + $(eval include global_config/development.sh) + +.PHONY: review +review: test-cluster ## Specify review AKS environment + # PULL_REQUEST_NUMBER is set by the GitHub action + $(if $(PULL_REQUEST_NUMBER), , $(error Missing environment variable "PULL_REQUEST_NUMBER")) + $(eval include global_config/review.sh) + $(eval export TF_VAR_pull_request_number=-$(PULL_REQUEST_NUMBER)) + $(eval backend_config=-backend-config="key=terraform-$(PULL_REQUEST_NUMBER).tfstate") + $(eval export TF_VAR_app_suffix=-$(PULL_REQUEST_NUMBER)) + $(eval export TF_VAR_uploads_storage_account_name=$(AZURE_RESOURCE_PREFIX)$(SERVICE_SHORT)rv$(PULL_REQUEST_NUMBER)sa) + +.PHONY: staging +staging: test-cluster + $(eval include global_config/staging.sh) + +.PHONY: sandbox +sandbox: production-cluster + $(eval include global_config/sandbox.sh) + +production: production-cluster + $(if $(or ${SKIP_CONFIRM}, ${CONFIRM_PRODUCTION}), , $(error Missing CONFIRM_PRODUCTION=yes)) + $(eval include global_config/production.sh) + +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-azure-account: + [ "${SKIP_AZURE_LOGIN}" != "true" ] && az account set -s ${AZURE_SUBSCRIPTION} || true + +terraform-init: composed-variables bin/terrafile set-azure-account + ./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 $(backend_config) + + $(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_IMAGE}) + +terraform-plan: terraform-init + terraform -chdir=terraform/application plan -var-file "config/${CONFIG}.tfvars.json" + +terraform-apply: terraform-init + terraform -chdir=terraform/application apply -var-file "config/${CONFIG}.tfvars.json" ${AUTO_APPROVE} + +set-what-if: + $(eval WHAT_IF=--what-if) + +arm-deployment: composed-variables set-azure-account + $(if ${DISABLE_KEYVAULTS},, $(eval KV_ARG=keyVaultNames=${KEYVAULT_NAMES})) + $(if ${ENABLE_KV_DIAGNOSTICS}, $(eval KV_DIAG_ARG=enableDiagnostics=${ENABLE_KV_DIAGNOSTICS} logAnalyticsWorkspaceName=${LOG_ANALYTICS_WORKSPACE_NAME}),) + + az deployment sub create --name "resourcedeploy-tsc-$(shell date +%Y%m%d%H%M%S)" \ + -l "${REGION}" --template-uri "https://raw.githubusercontent.com/DFE-Digital/tra-shared-services/${ARM_TEMPLATE_TAG}/azure/resourcedeploy.json" \ + --parameters "resourceGroupName=${RESOURCE_GROUP_NAME}" 'tags=${RG_TAGS}' \ + "tfStorageAccountName=${STORAGE_ACCOUNT_NAME}" "tfStorageContainerName=terraform-state" \ + ${KV_ARG} \ + ${KV_DIAG_ARG} \ + "enableKVPurgeProtection=${KV_PURGE_PROTECTION}" \ + ${WHAT_IF} + +deploy-arm-resources: arm-deployment ## Validate ARM resource deployment. Usage: make domains validate-arm-resources + +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 + ./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 (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 (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 + ./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 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 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 + az aks get-credentials --overwrite-existing -g ${CLUSTER_RESOURCE_GROUP_NAME} -n ${CLUSTER_NAME} + +aks-console: get-cluster-credentials + $(if $(PULL_REQUEST_NUMBER), $(eval export APP_ID=review-$(PULL_REQUEST_NUMBER)) , $(eval export APP_ID=$(CONFIG_LONG))) + kubectl -n ${NAMESPACE} exec -ti --tty deployment/teaching-school-hub-finder-${APP_ID} -- /bin/sh -c "cd /app && bundle exec rails c" + +aks-ssh: get-cluster-credentials + $(if $(PULL_REQUEST_NUMBER), $(eval export APP_ID=review-$(PULL_REQUEST_NUMBER)) , $(eval export APP_ID=$(CONFIG_LONG))) + kubectl -n ${NAMESPACE} exec -ti --tty deployment/teaching-school-hub-finder-${APP_ID} -- /bin/sh + +.PHONY: install-konduit +install-konduit: ## Install the konduit script, for accessing backend services + [ ! -f bin/konduit.sh ] \ + && curl -s https://raw.githubusercontent.com/DFE-Digital/teacher-services-cloud/master/scripts/konduit.sh -o bin/konduit.sh \ + && chmod +x bin/konduit.sh \ + || true diff --git a/README.md b/README.md index f815c55..a63b37a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # README +## About + +Live version of the service can be find here: https://find-a-teaching-school-hub.education.gov.uk/ + ## Setup ### Prerequisites diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index d9c7c8f..67a0e97 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -13,4 +13,8 @@ def accessibility def terms end + + def healthcheck + head :ok + end end diff --git a/config/database.yml b/config/database.yml index eb6b727..09a8ae6 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,19 +1,3 @@ -# PostgreSQL. Versions 9.3 and up are supported. -# -# Install the pg driver: -# gem install pg -# On macOS with Homebrew: -# gem install pg -- --with-pg-config=/usr/local/bin/pg_config -# On macOS with MacPorts: -# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config -# On Windows: -# gem install pg -# Choose the win32 build. -# Install PostgreSQL and put its /bin directory on your path. -# -# Configure Using Gemfile -# gem "pg" -# default: &default encoding: unicode url: <%= ENV['DATABASE_URL'] %> @@ -27,63 +11,18 @@ development: <<: *default database: teaching_school_hub_finder_development - # The specified database role being used to connect to postgres. - # To create additional roles in postgres see `$ createuser --help`. - # When left blank, postgres will use the default role. This is - # the same name as the operating system user running Rails. - #username: teaching_school_hub_finder - - # The password associated with the postgres role (username). - #password: - - # Connect on a TCP socket. Omitted by default since the client uses a - # domain socket that doesn't need configuration. Windows does not have - # domain sockets, so uncomment these lines. - #host: localhost - - # The TCP port the server listens on. Defaults to 5432. - # If your server runs on a different port number, change accordingly. - #port: 5432 +test: + <<: *default + database: teaching_school_hub_finder_test - # Schema search path. The server defaults to $user,public - #schema_search_path: myapp,sharedapp,public +review: + <<: *default - # Minimum log levels, in increasing order: - # debug5, debug4, debug3, debug2, debug1, - # log, notice, warning, error, fatal, and panic - # Defaults to warning. - #min_messages: notice +staging: + <<: *default -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. -test: +sandbox: <<: *default - database: teaching_school_hub_finder_test -# As with config/credentials.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password or a full connection URL as an environment -# variable when you boot the app. For example: -# -# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" -# -# If the connection URL is provided in the special DATABASE_URL environment -# variable, Rails will automatically merge its configuration values on top of -# the values provided in this file. Alternatively, you can specify a connection -# URL environment variable explicitly: -# -# production: -# url: <%= ENV["MY_APP_DATABASE_URL"] %> -# -# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full overview on how database connection configuration can be specified. -# production: <<: *default - # database: teaching_school_hub_finder_production - # username: teaching_school_hub_finder - # password: <%= ENV["TEACHING_SCHOOL_HUB_FINDER_DATABASE_PASSWORD"] %> - url: <%= ENV['DATABASE_URL'].gsub(/^postgres:/, 'postgis:') rescue "connectionerror" %> diff --git a/config/environments/review.rb b/config/environments/review.rb new file mode 100644 index 0000000..e474d6c --- /dev/null +++ b/config/environments/review.rb @@ -0,0 +1,6 @@ +require Rails.root.join("config/environments/production") + +Rails.application.configure do + config.log_level = :debug + config.ssl_options = { redirect: { exclude: ->(request) { request.path.include?("/healthcheck") } } } +end diff --git a/config/environments/sandbox.rb b/config/environments/sandbox.rb new file mode 100644 index 0000000..48025d7 --- /dev/null +++ b/config/environments/sandbox.rb @@ -0,0 +1,6 @@ +require Rails.root.join("config/environments/production") + +Rails.application.configure do + config.log_level = :info + config.ssl_options = { redirect: { exclude: ->(request) { request.path.include?("/healthcheck") } } } +end diff --git a/config/environments/staging.rb b/config/environments/staging.rb new file mode 100644 index 0000000..e474d6c --- /dev/null +++ b/config/environments/staging.rb @@ -0,0 +1,6 @@ +require Rails.root.join("config/environments/production") + +Rails.application.configure do + config.log_level = :debug + config.ssl_options = { redirect: { exclude: ->(request) { request.path.include?("/healthcheck") } } } +end diff --git a/config/routes.rb b/config/routes.rb index 2863251..81b2a86 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,6 +10,7 @@ get "/pages/cookies", to: "pages#cookies_policy" get "/pages/accessibility", to: "pages#accessibility" get "/pages/terms", to: "pages#terms" + get "/healthcheck", to: "pages#healthcheck" scope via: :all do get '/404', to: 'errors#not_found' diff --git a/db/migrate/20230626101457_enable_postgis.rb b/db/migrate/20230626101457_enable_postgis.rb index 7267b3d..e168994 100644 --- a/db/migrate/20230626101457_enable_postgis.rb +++ b/db/migrate/20230626101457_enable_postgis.rb @@ -1,5 +1,8 @@ class EnablePostgis < ActiveRecord::Migration[7.0] def change - enable_extension 'postgis' + # rubocop:disable Rails/ReversibleMigration + # enable_extension 'postgis' + execute "CREATE EXTENSION IF NOT EXISTS postgis" + # rubocop:enable Rails/ReversibleMigration end end diff --git a/global_config/domains.sh b/global_config/domains.sh new file mode 100644 index 0000000..a7b7641 --- /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 0000000..8f32358 --- /dev/null +++ b/global_config/production.sh @@ -0,0 +1,8 @@ +CONFIG=production +ENVIRONMENT=production +CONFIG_SHORT=pd +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-production +AZURE_RESOURCE_PREFIX=s189p01 +ENABLE_KV_DIAGNOSTICS=true +CONFIG_LONG=production +NAMESPACE=cpd-production diff --git a/global_config/review.sh b/global_config/review.sh new file mode 100644 index 0000000..e80d8af --- /dev/null +++ b/global_config/review.sh @@ -0,0 +1,8 @@ +CONFIG=review +ENVIRONMENT=review +CONFIG_SHORT=rv +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-test +AZURE_RESOURCE_PREFIX=s189t01 +KV_PURGE_PROTECTION=false +CONFIG_LONG=review +NAMESPACE=cpd-review diff --git a/global_config/sandbox.sh b/global_config/sandbox.sh new file mode 100644 index 0000000..95db8be --- /dev/null +++ b/global_config/sandbox.sh @@ -0,0 +1,5 @@ +CONFIG=sandbox +ENVIRONMENT=sandbox +CONFIG_SHORT=sb +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-production +AZURE_RESOURCE_PREFIX=s189p01 diff --git a/global_config/staging.sh b/global_config/staging.sh new file mode 100644 index 0000000..acfa75f --- /dev/null +++ b/global_config/staging.sh @@ -0,0 +1,6 @@ +CONFIG=staging +ENVIRONMENT=staging +CONFIG_SHORT=st +AZURE_SUBSCRIPTION=s189-teacher-services-cloud-test +AZURE_RESOURCE_PREFIX=s189t01 +KV_PURGE_PROTECTION=false diff --git a/terraform/application/.terraform.lock.hcl b/terraform/application/.terraform.lock.hcl new file mode 100644 index 0000000..41e2083 --- /dev/null +++ b/terraform/application/.terraform.lock.hcl @@ -0,0 +1,61 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.62.1" + constraints = "3.62.1" + hashes = [ + "h1:3dG3gNv/w2Sad5EOIWSG7JesKKtRhIiQJzgWOdqpaos=", + "zh:0fa21ac98474f3a2aeab1191628735ddf1fdb0767f935e8b0f7e259a335773cd", + "zh:45ca710ffc39d194e0a29ca6983117a180302f3e1babb425257a23a1f38bb974", + "zh:481fd2ace1cf52ebae940ec8c66d025d2582af3aaf67340b6afbfa7a19b86864", + "zh:4fd32a7442f052fdf48ce6379f001581312a3ca7a10da837ee1c4e46c2f882dc", + "zh:507fff425b941a0fd53a57a4b79763183d37fac7ba3d4aa46a74dadf13ee5ced", + "zh:a09964b92e227cbceec7bf62e92bb71638b74738e465e5dafd75899a3c098eab", + "zh:a64ac7583675b0e704c6d6d1de31595e99ac7c270fd3e180d5f50d3f81f18801", + "zh:b20f4dbbd39aab6a3833541704771794790485ce62a85730f1cd671cf064ea8a", + "zh:d03b5c537e40fae12a9dd7750f0e29820b54f58efb1be1c7005ffb0ad70c6876", + "zh:ed19a03b3da4dfc000d278ffe986c5b3ab7114ccbe1d59e8bd474f065cfad377", + "zh:f53bb11e7be3d109f8763cb90910d79044693be35968c06eb31aa48f5375db31", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.21.1" + constraints = "2.21.1" + hashes = [ + "h1:gP8IU3gFfXYRfGZr5Qws9JryZsOGsluAVpiAoZW7eo0=", + "zh:156a437d7edd6813e9cb7bdff16ebce28cec08b07ba1b0f5e9cec029a217bc27", + "zh:1a21c255d8099e303560e252579c54e99b5f24f2efde772c7e39502c62472605", + "zh:27b2021f86e5eaf6b9ee7c77d7a9e32bc496e59dd0808fb15a5687879736acf6", + "zh:31fa284c1c873a85c3b5cfc26cf7e7214d27b3b8ba7ea5134ab7d53800894c42", + "zh:4be9cc1654e994229c0d598f4e07487fc8b513337de9719d79b45ce07fc4e123", + "zh:5f684ed161f54213a1414ac71b3971a527c3a6bfbaaf687a7c8cc39dcd68c512", + "zh:6d58f1832665c256afb68110c99c8112926406ae0b64dd5f250c2954fc26928e", + "zh:9dadfa4a019d1e90decb1fab14278ee2dbefd42e8f58fe7fa567a9bf51b01e0e", + "zh:a68ce7208a1ef4502528efb8ce9f774db56c421dcaccd3eb10ae68f1324a6963", + "zh:acdd5b45a7e80bc9d254ad0c2f9cb4715104117425f0d22409685909a790a6dd", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:fb451e882118fe92e1cb2e60ac2d77592f5f7282b3608b878b5bdc38bbe4fd5b", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.5.1" + hashes = [ + "h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=", + "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", + "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", + "zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831", + "zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3", + "zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b", + "zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2", + "zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865", + "zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03", + "zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602", + "zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014", + ] +} diff --git a/terraform/application/application.tf b/terraform/application/application.tf new file mode 100644 index 0000000..c25ba2b --- /dev/null +++ b/terraform/application/application.tf @@ -0,0 +1,47 @@ +locals { + environment = "${var.environment}${var.app_suffix}" + service_name = "cpd-tsh" + domain = var.environment == "review" ? "cpd-tsh-${local.environment}-web.test.teacherservices.cloud" : var.domain +} + +module "application_configuration" { + source = "./vendor/modules/aks//aks/application_configuration" + + namespace = var.namespace + environment = local.environment + azure_resource_prefix = var.azure_resource_prefix + service_short = var.service_short + config_short = var.config_short + secret_key_vault_short = "app" + + # Delete for non rails apps + is_rails_application = true + + config_variables = { + ENVIRONMENT_NAME = var.environment + PGSSLMODE = local.postgres_ssl_mode + DOMAIN = local.domain + GOVUK_WEBSITE_ROOT = local.domain + GOVUK_APP_DOMAIN = local.domain + RAILS_ENV = var.environment + } + secret_variables = { + DATABASE_URL = module.postgres.url + } +} + +module "web_application" { + source = "./vendor/modules/aks//aks/application" + + is_web = true + + namespace = var.namespace + environment = local.environment + service_name = local.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 +} diff --git a/terraform/application/cluster_data.tf b/terraform/application/cluster_data.tf new file mode 100644 index 0000000..4277864 --- /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 0000000..469796a --- /dev/null +++ b/terraform/application/config/production.tfvars.json @@ -0,0 +1,6 @@ +{ + "cluster": "production", + "namespace": "cpd-production", + "environment": "production", + "enable_postgres_backup_storage" : true +} diff --git a/terraform/application/config/production_Terrafile b/terraform/application/config/production_Terrafile new file mode 100644 index 0000000..65af53b --- /dev/null +++ b/terraform/application/config/production_Terrafile @@ -0,0 +1,3 @@ +aks: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "main" diff --git a/terraform/application/config/review.tfvars.json b/terraform/application/config/review.tfvars.json new file mode 100644 index 0000000..df098ed --- /dev/null +++ b/terraform/application/config/review.tfvars.json @@ -0,0 +1,8 @@ +{ + "cluster": "test", + "namespace": "cpd-development", + "environment": "review", + "deploy_azure_backing_services": false, + "enable_postgres_ssl" : false, + "command": ["/bin/sh", "-c", "bundle exec rails db:environment:set RAILS_ENV=review && RAILS_ENV=review bundle exec rails db:schema:load && RAILS_ENV=review bundle exec rails db:seed && 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 0000000..65af53b --- /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/sandbox.tfvars.json b/terraform/application/config/sandbox.tfvars.json new file mode 100644 index 0000000..a398943 --- /dev/null +++ b/terraform/application/config/sandbox.tfvars.json @@ -0,0 +1,6 @@ +{ + "cluster": "production", + "namespace": "cpd-production", + "environment": "sandbox", + "enable_postgres_backup_storage" : true +} diff --git a/terraform/application/config/sandbox_Terrafile b/terraform/application/config/sandbox_Terrafile new file mode 100644 index 0000000..65af53b --- /dev/null +++ b/terraform/application/config/sandbox_Terrafile @@ -0,0 +1,3 @@ +aks: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "main" diff --git a/terraform/application/config/staging.tfvars.json b/terraform/application/config/staging.tfvars.json new file mode 100644 index 0000000..800f338 --- /dev/null +++ b/terraform/application/config/staging.tfvars.json @@ -0,0 +1,7 @@ +{ + "cluster": "test", + "namespace": "cpd-test", + "environment": "staging", + "deploy_azure_backing_services": false, + "enable_postgres_ssl" : false +} diff --git a/terraform/application/config/staging_Terrafile b/terraform/application/config/staging_Terrafile new file mode 100644 index 0000000..65af53b --- /dev/null +++ b/terraform/application/config/staging_Terrafile @@ -0,0 +1,3 @@ +aks: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "main" diff --git a/terraform/application/database.tf b/terraform/application/database.tf new file mode 100644 index 0000000..d7927eb --- /dev/null +++ b/terraform/application/database.tf @@ -0,0 +1,33 @@ +module "postgres" { + source = "./vendor/modules/aks//aks/postgres" + + namespace = var.namespace + environment = local.environment + azure_resource_prefix = var.azure_resource_prefix + service_name = local.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" + server_docker_image = "postgis/postgis:14-3.4" + azure_extensions = ["POSTGIS"] +} + + +module "redis-cache" { + source = "./vendor/modules/aks//aks/redis" + + namespace = var.namespace + environment = local.environment + azure_resource_prefix = var.azure_resource_prefix + service_short = var.service_short + config_short = var.config_short + service_name = local.service_name + cluster_configuration_map = module.cluster_data.configuration_map + use_azure = var.deploy_azure_backing_services + azure_enable_monitoring = var.enable_monitoring + azure_patch_schedule = [{ "day_of_week" : "Sunday", "start_hour_utc" : 01 }] +} diff --git a/terraform/application/output.tf b/terraform/application/output.tf new file mode 100644 index 0000000..4398f01 --- /dev/null +++ b/terraform/application/output.tf @@ -0,0 +1,3 @@ +output "url" { + value = "https://${module.web_application.hostname}/" +} diff --git a/terraform/application/secrets.tf b/terraform/application/secrets.tf new file mode 100644 index 0000000..f3592a8 --- /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 0000000..fc201ab --- /dev/null +++ b/terraform/application/statuscake.tf @@ -0,0 +1,10 @@ +//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 0000000..cb5ba38 --- /dev/null +++ b/terraform/application/terraform.tf @@ -0,0 +1,37 @@ +terraform { + required_version = "= 1.5.4" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.62.1" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.21.1" + } + // statuscake = { + // source = "StatusCakeDev/statuscake" + // version = "2.1.0" + // } + } + backend "azurerm" { + container_name = "terraform-state" + } +} + +provider "azurerm" { + features {} + + skip_provider_registration = true + subscription_id = try(local.azure_credentials.subscriptionId, null) + client_id = try(local.azure_credentials.clientId, null) + client_secret = try(local.azure_credentials.clientSecret, null) + tenant_id = try(local.azure_credentials.tenantId, null) +} + +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 +} diff --git a/terraform/application/variables.tf b/terraform/application/variables.tf new file mode 100644 index 0000000..a8c967c --- /dev/null +++ b/terraform/application/variables.tf @@ -0,0 +1,68 @@ +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_credentials_json" { + default = null + description = "JSON containing the service principal authentication key when running in automation" +} +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 "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 "domain" { + type = string + default = "" +} + +variable "app_suffix" { + type = string + default = "" +} + +locals { + azure_credentials = try(jsondecode(var.azure_credentials_json), null) + + postgres_ssl_mode = var.enable_postgres_ssl ? "require" : "disable" +} diff --git a/terraform/domains/environment_domains/config/development.tfvars.json b/terraform/domains/environment_domains/config/development.tfvars.json new file mode 100644 index 0000000..cf90824 --- /dev/null +++ b/terraform/domains/environment_domains/config/development.tfvars.json @@ -0,0 +1,16 @@ +{ + "hosted_zone": { + "teaching-school-hub-finder.education.gov.uk": { + "front_door_name": "s189p01-cpdtsh-domains-fd", + "resource_group_name": "s189p01-cpdtsh-domains-rg", + "domains": [ + "development" + ], + "cached_paths": [ + "/assets/*" + ], + "environment_short": "dv", + "origin_hostname": "teaching-school-hub-finder-development.test.teacherservices.cloud" + } + } +} diff --git a/terraform/domains/environment_domains/config/development_Terrafile b/terraform/domains/environment_domains/config/development_Terrafile new file mode 100644 index 0000000..dfce270 --- /dev/null +++ b/terraform/domains/environment_domains/config/development_Terrafile @@ -0,0 +1,3 @@ +domains: + source: "https://github.com/DFE-Digital/terraform-modules" + version: "testing" 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 0000000..f1f022a --- /dev/null +++ b/terraform/domains/environment_domains/config/production.tfvars.json @@ -0,0 +1,16 @@ +{ + "hosted_zone": { + "find-a-teaching-school-hub.education.gov.uk": { + "front_door_name": "s189p01-cpdtsh-dom-fd", + "resource_group_name": "s189p01-cpdtsh-dom-rg", + "domains": [ + "apex" + ], + "cached_paths": [ + "/assets/*" + ], + "environment_short": "pd", + "origin_hostname": "teaching-school-hub-finder-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 0000000..58e60b3 --- /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/staging.tfvars.json b/terraform/domains/environment_domains/config/staging.tfvars.json new file mode 100644 index 0000000..1206492 --- /dev/null +++ b/terraform/domains/environment_domains/config/staging.tfvars.json @@ -0,0 +1,16 @@ +{ + "hosted_zone": { + "teaching-school-hub-finder.education.gov.uk": { + "front_door_name": "s189p01-cpdtsh-dom-fd", + "resource_group_name": "s189p01-cpdtsh-dom-rg", + "domains": [ + "staging" + ], + "cached_paths": [ + "/assets/*" + ], + "environment_short": "dv", + "origin_hostname": "teaching-school-hub-finder-staging.test.teacherservices.cloud" + } + } +} diff --git a/terraform/domains/environment_domains/config/staging_Terrafile b/terraform/domains/environment_domains/config/staging_Terrafile new file mode 100644 index 0000000..dfce270 --- /dev/null +++ b/terraform/domains/environment_domains/config/staging_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 0000000..96beb73 --- /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 0000000..235983f --- /dev/null +++ b/terraform/domains/environment_domains/terraform.tf @@ -0,0 +1,19 @@ +terraform { + + required_version = "= 1.5.4" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.62.1" + } + } + 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 0000000..ad63e54 --- /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 0000000..8f3bc00 --- /dev/null +++ b/terraform/domains/infrastructure/config/zones.tfvars.json @@ -0,0 +1,15 @@ +{ + "hosted_zone": { + "find-a-teaching-school-hub.education.gov.uk": { + "caa_records": {}, + "txt_records": { + "_dmarc": { + "value": "v=DMARC1;p=quarantine;sp=quarantine;pct=100;fo=1;rua=mailto:dmarc-rua@dmarc.service.gov.uk,mailto:dmarc-rua@education.gov.uk;ruf=mailto:DMARC.Forensic@education.gov.uk" + } + }, + "resource_group_name": "s189p01-cpdtsh-dom-rg", + "front_door_name": "s189p01-cpdtsh-dom-fd" + } + }, + "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 0000000..58e60b3 --- /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 0000000..da091f5 --- /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 0000000..d385232 --- /dev/null +++ b/terraform/domains/infrastructure/terraform.tf @@ -0,0 +1,19 @@ +terraform { + required_version = "= 1.5.4" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.62.1" + } + } + 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 0000000..8d91472 --- /dev/null +++ b/terraform/domains/infrastructure/variables.tf @@ -0,0 +1,7 @@ +variable "hosted_zone" { + type = map(any) +} + +variable "deploy_default_records" { + default = true +}