Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCM-5085: init and compliance #5

Merged
merged 11 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/actions/terraform_testing/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Terraform Testing
description: |
Executes a series of code quality, consistency and security tests against the
Terraform codebase in the repo

inputs:
folder:
description: The project folder name
required: true
terraform_project:
description: The terraform project name to use for terraform planning
required: true
component:
description: The terraform component name to use for terraform planning
required: true
environment:
description: The terraform environment name to use for terraform planning
default: dev
required: true
group:
description: The terraform group name to use for terraform planning
default: target-env
required: true
region:
description: The terraform region code to use for terraform planning
default: eu-west-2
required: true

runs:
using: composite
steps:
- name: Dynamically generate a corresponding tfvars file
shell: bash
run: |-
cd ./${{ inputs.folder }}/terraform && ./bin/generate_target_env_tfvars.sh "${{ inputs.folder }}" "${{ inputs.environment }}"
- name: Ensure all Terraform files are named appropriately
shell: bash
run: |-
cd ./${{ inputs.folder }}/terraform && \
./bin/test_filenames.sh
- name: Ensure all mandatory fields are present in the TFVARS file
shell: bash
run: |-
cd ./${{ inputs.folder }}/terraform && \
./bin/test_mandatory_tfvars.sh "${{ inputs.component }}"
- name: Wait for the environment to be available
shell: bash
run: |-
cd ./${{ inputs.folder }}/terraform && \
./bin/wait_for_lock.sh ${{ inputs.terraform_project }} ${{ inputs.component }} ${{ inputs.environment }}
- name: Run Terraform Compliance against the plan file
shell: bash
run: |-
cd ./${{ inputs.folder }}/terraform && \
./bin/terratest.sh \
--region "${{ inputs.region }}" \
--project "${{ inputs.terraform_project }}" \
--component "${{ inputs.component }}" \
--environment "${{ inputs.environment }}" \
--group "${{ inputs.group }}" \
--action "plan"
4 changes: 4 additions & 0 deletions .github/workflows/cicd-1-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
pull_request:
types: [opened, reopened]

concurrency:
group: notify-web-ui-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
metadata:
name: "Set CI/CD metadata"
Expand Down
95 changes: 77 additions & 18 deletions .github/workflows/cicd-3-deploy.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
name: "CI/CD deploy"
run-name: CI/CD Deploy ${{ inputs.deployEnvironment }} << ${{ github.ref_name }}

on:
workflow_dispatch:
inputs:
deployEnvironment:
description: Name of the environment to deploy
required: true
default: dev
type: string
terraformAction:
description: Terraform Action
required: true
default: apply
type: string
tag:
description: "This is the tag that is oging to be deployed"
required: true
default: "latest"

env:
PROJECT_DIR: infrastructure
PROJECT_NAME: notify-web-gateway
DEFAULT_ENVIRONMENT: dev
CI_PIPELINE_IID: ${{ github.run_number }}
CI_COMMIT_REF_NAME: ${{ github.ref_name }}
CI_COMMIT_BRANCH: ${{ github.ref_name }}
AWS_REGION: eu-west-2
DEPLOY_ENVIRONMENT: ${{ inputs.deployEnvironment }}
TERRAFORM_ACTION: ${{ inputs.terraformAction }}

permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout

concurrency:
group: notify-web-ui-${{ github.workflow }}-${{ github.event.inputs.deployEnvironment }}
cancel-in-progress: false

jobs:
metadata:
name: "Set CI/CD metadata"
Expand All @@ -22,6 +52,10 @@ jobs:
terraform_version: ${{ steps.variables.outputs.terraform_version }}
version: ${{ steps.variables.outputs.version }}
tag: ${{ steps.variables.outputs.tag }}
aws_region: ${{ steps.variables.outputs.aws_region }}
terraform_action: ${{ steps.variables.outputs.terraform_action }}
ci_pipeline_iid: ${{ steps.variables.outputs.ci_pipeline_iid }}
deploy_environment: ${{ steps.variables.outputs.deploy_environment }}
steps:
- name: "Checkout code"
uses: actions/checkout@v4
Expand All @@ -38,6 +72,18 @@ jobs:
# TODO: Get the version, but it may not be the .version file as this should come from the CI/CD Pull Request Workflow
echo "version=$(head -n 1 .version 2> /dev/null || echo unknown)" >> $GITHUB_OUTPUT
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
echo "project_dir=infrastructure" >> $GITHUB_OUTPUT
echo "default_environment=dev" >> $GITHUB_OUTPUT
echo "ci_pipeline_iid=${{ github.run_number }}" >> $GITHUB_OUTPUT
echo "ci_commit_ref_name=${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "ci_commit_branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "aws_region=eu-west-2" >> $GITHUB_OUTPUT
echo "terraform_action=${{ inputs.terraformAction }}" >> $GITHUB_OUTPUT
deploy_environment=${{ inputs.deployEnvironment }}
if [[ $deploy_environment == de-* ]]; then
deploy_environment="dynamic"
fi
echo "deploy_environment=$deploy_environment" >> $GITHUB_OUTPUT
- name: "List variables"
run: |
export BUILD_DATETIME="${{ steps.variables.outputs.build_datetime }}"
Expand All @@ -48,30 +94,43 @@ jobs:
export TERRAFORM_VERSION="${{ steps.variables.outputs.terraform_version }}"
export VERSION="${{ steps.variables.outputs.version }}"
export TAG="${{ steps.variables.outputs.tag }}"
export PROJECT_DIR="${{ steps.variables.outputs.project_dir }}"
export PROJECT_NAME="${{ steps.variables.outputs.project_name }}"
export DEFAULT_ENVIRONMENT="${{ steps.variables.outputs.default_environment }}"
export CI_PIPELINE_IID="${{ steps.variables.outputs.ci_pipeline_iid }}"
export CI_COMMIT_REF_NAME="${{ steps.variables.outputs.ci_commit_ref_name }}"
export CI_COMMIT_BRANCH="${{ steps.variables.outputs.ci_commit_branch }}"
export AWS_REGION="${{ steps.variables.outputs.aws_region }}"
export DEPLOY_ENVIRONMENT="${{ steps.variables.outputs.deploy_environment }}"
export TERRAFORM_ACTION="${{ steps.variables.outputs.terraform_action }}"
make list-variables
deploy:
name: "Deploy to an environment"
environment: ${{ needs.metadata.outputs.deploy_environment }}
runs-on: ubuntu-latest
needs: [metadata]
timeout-minutes: 10
env:
TERRAFORM_ACTION: ${{ needs.metadata.outputs.terraform_action }}
CI_PIPELINE_IID: ${{ needs.metadata.outputs.ci_pipeline_iid }}
steps:
- name: "Checkout code"
uses: actions/checkout@v4
# TODO: More jobs or/and steps here
# success:
# name: "Success notification"
# runs-on: ubuntu-latest
# needs: [deploy]
# steps:
# - name: "Check prerequisites for notification"
# id: check
# run: echo "secret_exist=${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL != '' }}" >> $GITHUB_OUTPUT
# - name: "Notify on deployment to an environment"
# if: steps.check.outputs.secret_exist == 'true'
# uses: nhs-england-tools/notify-msteams-action@v0.0.4
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# teams-webhook-url: ${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL }}
# message-title: "Notification title"
# message-text: "This is a notification body"
# link: ${{ github.event.pull_request.html_url }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: ${{ vars.AWS_DEPLOY_ROLE }}
role-session-name: deployInfra
aws-region: ${{ needs.metadata.outputs.aws_region }}
- name: Terraform Plan
if: >-
always() &&
!cancelled()
run: |
cd infrastructure/terraform && ./deploy.sh plan
- name: Terraform Apply
if: >-
always() &&
!cancelled()
run: |
cd infrastructure/terraform && ./deploy.sh ${TERRAFORM_ACTION}
29 changes: 29 additions & 0 deletions .github/workflows/stage-1-commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ on:
required: true
type: string

env:
AWS_REGION: eu-west-2
TERM: xterm-256color

permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout

jobs:
scan-secrets:
name: "Scan secrets"
Expand Down Expand Up @@ -86,6 +94,27 @@ jobs:
uses: actions/checkout@v4
- name: "Lint Terraform"
uses: ./.github/actions/lint-terraform
terraform-compliance:
name: "Terraform Compliance"
runs-on: ubuntu-latest
timeout-minutes: 2
environment: dev
steps:
- name: "Checkout code"
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: ${{ vars.AWS_DEPLOY_ROLE }}
role-session-name: deployInfra
aws-region: ${{ env.AWS_REGION }}
- name: "Terraform Compliance"
uses: ./.github/actions/terraform_testing
with:
folder: infrastructure
terraform_project: notify-web-gateway
component: web-ui
environment: dev
count-lines-of-code:
name: "Count lines of code"
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ config:: # Configure development environment (main) @Configuration
# TODO: Use only 'make' targets that are specific to this project, e.g. you may not need to install Node.js
make _install-dependencies

deploy_local:
./cd_utils/local_package_and_deploy.sh $(filter-out $@,$(MAKECMDGOALS))

# ==============================================================================

${VERBOSE}.SILENT: \
Expand Down
52 changes: 52 additions & 0 deletions cd_utils/local_package_and_deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash

set -euo pipefail

tf_project="${1:-notify-web-gateway}"
environment="${2:-dev}"
action="${3:-plan}"
rebuild="${4:-no_rebuild}"
component="web-ui"
project="nhs-notify-web-gateway"


if [[ "${environment:0:3}" == "de-" ]]; then
tfvars_environment="dynamic"
else
tfvars_environment="${environment}"
fi

if [[ ! "${action}" == "plan" && ! "${action}" == "apply" && ! "${action}" == "state" && ! "${action}" == "show" && ! "${action}" == "import" ]]; then
echo "ERROR: second arg should be 'plan' or 'apply' or 'state' or 'show' or 'import'" && exit 1
fi
if [[ "${rebuild}" == "rebuild" ]]; then
pnpm package --output-logs new-only
./ci_utils/publish_packages.sh
fi

if [[ $? != 0 ]]; then
echo -e "Rebuild failed. Aborting."
exit 1
fi

# ./cd_utils/generate_target_env.sh "${project}" "${tfvars_environment}" "${project}/terraform"
pushd "infrastructure/terraform"
./bin/generate_target_env_tfvars.sh "${project}" "${environment}"
# ./bin/download_static_content.sh

if [ "${action}" == "state" ] || [ "${action}" == "import" ]; then
echo -e "We need to provide some arguments for in a format like e.g. ::: -- rm -dry-run aws_s3_bucket.bucket_name"
read -p "Please provide additional arguments for this action:: " arg5
fi

./bin/terrawrap.sh --project ${tf_project} --component ${component} --environment ${environment} --group target-env --action ${action} ${arg5:-} && \
rm -rf "./static_content" && \
popd

if [[ $? == 0 ]]; then
echo -e "Local ${action} successful"
exit 0
else
echo -e "Rebuild failed. Aborting."
exit 2
fi
7 changes: 6 additions & 1 deletion infrastructure/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ crash.*.log
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*_output.*
*.tfvars.json
**/plugin-cache/**/*

# don't track target-env.tfvars and dynamic.tfvars that are dynamically created
group_target-env.tfvars
*de-*.tfvars

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
Expand Down
Empty file removed infrastructure/modules/.gitkeep
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash

set -euo pipefail # safe scripting

declare red="\033[0;31m";
declare yellow="\033[0;33m";
declare cyan="\033[0;36m";
declare nc="\033[0m"; # No Color
declare bold="\033[1m";

echo -e "\n${yellow}Before running this process you should assume the ${cyan}NHSNotify-Admin SSO profile${yellow} in the account in which you want to create the NOTIFYDeployRole iam role${nc}"
echo -e "\n${red}CONFIRM:${cyan} Are you SURE you are in the right AWS account?${nc}\n"

read -r confirmation

if [[ ! "${confirmation^^}" =~ ^(Y|YES)$ ]]; then
echo -e "\nAborted [ ${red}FAIL${nc} ]\n"
exit 1
fi

if [[ "$(uname)" == "Darwin" ]]; then
TAIL_CMD="tail -n +2"
else
TAIL_CMD="tail +2"
fi

account_id=$(aws sts get-caller-identity | jq '.Account' | sed 's/\"//g')
HOST=$(curl https://vstoken.actions.githubusercontent.com/.well-known/openid-configuration \
| jq -r '.jwks_uri | split("/")[2]')
thumbprint_list=$( echo | openssl s_client -servername $HOST -showcerts -connect $HOST:443 2> /dev/null \
| sed -n -e '/BEGIN/h' -e '/BEGIN/,/END/H' -e '$x' -e '$p' | $TAIL_CMD \
| openssl x509 -fingerprint -noout \
| sed -e "s/.*=//" -e "s/://g" \
| tr "ABCDEF" "abcdef" )

aws iam create-open-id-connect-provider --url https://token.actions.githubusercontent.com --client-id-list sts.amazonaws.com --thumbprint-list ${thumbprint_list}

echo -e "\n${yellow}Provide Repo Name${nc}"
read -r repo_name

if [[ -z "${repo_name^^}" ]]; then
echo -e "\nAborted [ ${red}FAIL${nc} ]\n"
exit 1
fi

document="{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Federated\":\"arn:aws:iam::${account_id}:oidc-provider/token.actions.githubusercontent.com\"},\"Action\":\"sts:AssumeRoleWithWebIdentity\",\"Condition\":{\"StringEquals\":{\"token.actions.githubusercontent.com:aud\":\"sts.amazonaws.com\"},\"StringLike\":{\"token.actions.githubusercontent.com:sub\":\"repo:NHSDigital/${repo_name}:*\"}}}]}"


if ! aws iam create-role \
--role-name NOTIFYDeployRole \
--assume-role-policy-document "${document}" \
--output yaml
then
echo -e "\nCouldn't create role [ ${red}FAIL${nc} ]\n"
exit 1
fi

if ! aws iam attach-role-policy \
--role-name NOTIFYDeployRole \
--policy-arn "arn:aws:iam::aws:policy/AdministratorAccess"
then
echo -e "\nCouldn't attach managed policy [ ${red}FAIL${nc} ]\n"
exit 1
fi

echo -e "\nRole created [ ${yellow}OK${nc} ]\n"
exit 0
Loading
Loading