diff --git a/.github/actions/lint-terraform/action.yaml b/.github/actions/lint-terraform/action.yaml index 28d990cd..6957a037 100644 --- a/.github/actions/lint-terraform/action.yaml +++ b/.github/actions/lint-terraform/action.yaml @@ -7,6 +7,11 @@ inputs: runs: using: "composite" steps: + - name: "Install Terraform binary" + shell: bash + run: | + asdf plugin add terraform || true + asdf install terraform || true - name: "Check Terraform format" shell: bash run: | @@ -14,8 +19,4 @@ runs: - name: "Validate Terraform" shell: bash run: | - stacks=${{ inputs.root-modules }} - for dir in $(find infrastructure/environments -maxdepth 1 -mindepth 1 -type d; echo ${stacks//,/$'\n'}); do - dir=$dir opts='-backend=false' make terraform-init - dir=$dir make terraform-validate - done + make terraform-validate-all diff --git a/.github/workflows/stage-1-commit.yaml b/.github/workflows/stage-1-commit.yaml index f46e2fba..b2e6157c 100644 --- a/.github/workflows/stage-1-commit.yaml +++ b/.github/workflows/stage-1-commit.yaml @@ -166,6 +166,8 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v6 + - name: "Setup ASDF" + uses: asdf-vm/actions/setup@b7bcd026f18772e44fe1026d729e1611cc435d47 # v4 - name: "Lint Terraform" uses: ./.github/actions/lint-terraform trivy-iac: diff --git a/infrastructure/terraform/components/reporting/backup_restore_testing_plan_main.tf b/infrastructure/terraform/components/reporting/backup_restore_testing_plan_main.tf index 3d1bde7d..626ec51d 100644 --- a/infrastructure/terraform/components/reporting/backup_restore_testing_plan_main.tf +++ b/infrastructure/terraform/components/reporting/backup_restore_testing_plan_main.tf @@ -1,6 +1,6 @@ resource "aws_backup_restore_testing_plan" "main" { count = var.enable_s3_backup ? 1 : 0 - name = replace("${local.csi}_restore_testing_plan", "-", "_") + name = replace("${local.csi}_restore_testing_plan", "-", "_") recovery_point_selection { algorithm = "LATEST_WITHIN_WINDOW" diff --git a/infrastructure/terraform/components/reporting/backup_restore_testing_selection_comms_backup_restore_selection_s3.tf b/infrastructure/terraform/components/reporting/backup_restore_testing_selection_comms_backup_restore_selection_s3.tf index e67a8d62..300ec563 100644 --- a/infrastructure/terraform/components/reporting/backup_restore_testing_selection_comms_backup_restore_selection_s3.tf +++ b/infrastructure/terraform/components/reporting/backup_restore_testing_selection_comms_backup_restore_selection_s3.tf @@ -1,5 +1,5 @@ resource "aws_backup_restore_testing_selection" "backup_restore_testing_selection_s3" { - count = var.enable_s3_backup ? 1 : 0 + count = var.enable_s3_backup ? 1 : 0 name = replace("${local.csi}_s3_backup_restore", "-", "_") restore_testing_plan_name = aws_backup_restore_testing_plan.main[0].name protected_resource_type = "S3" diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_core.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_core.tf index 35efb027..41493c5f 100644 --- a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_core.tf +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_core.tf @@ -7,7 +7,7 @@ resource "aws_cloudwatch_metric_alarm" "athena_workgroup_processed_bytes_core" { threshold_metric_id = "ad1" metric_query { - id = "m1" + id = "m1" metric { metric_name = "ProcessedBytes" namespace = "AWS/Athena" diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_housekeeping.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_housekeeping.tf index 006744d0..6e747c78 100644 --- a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_housekeeping.tf +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_housekeeping.tf @@ -7,7 +7,7 @@ resource "aws_cloudwatch_metric_alarm" "athena_workgroup_processed_bytes_houseke threshold_metric_id = "ad1" metric_query { - id = "m1" + id = "m1" metric { metric_name = "ProcessedBytes" namespace = "AWS/Athena" diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_ingestion.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_ingestion.tf index 83bb5308..b69f05b7 100644 --- a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_ingestion.tf +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_ingestion.tf @@ -7,7 +7,7 @@ resource "aws_cloudwatch_metric_alarm" "athena_workgroup_processed_bytes_ingesti threshold_metric_id = "ad1" metric_query { - id = "m1" + id = "m1" metric { metric_name = "ProcessedBytes" namespace = "AWS/Athena" diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_setup.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_setup.tf index f561be50..5bb700e1 100644 --- a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_setup.tf +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_setup.tf @@ -7,7 +7,7 @@ resource "aws_cloudwatch_metric_alarm" "athena_workgroup_processed_bytes_setup" threshold_metric_id = "ad1" metric_query { - id = "m1" + id = "m1" metric { metric_name = "ProcessedBytes" namespace = "AWS/Athena" diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_user.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_user.tf index f63e2977..d9fe7d15 100644 --- a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_user.tf +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_athena_workgroup_processed_bytes_user.tf @@ -7,7 +7,7 @@ resource "aws_cloudwatch_metric_alarm" "athena_workgroup_processed_bytes_user" { threshold_metric_id = "ad1" metric_query { - id = "m1" + id = "m1" metric { metric_name = "ProcessedBytes" namespace = "AWS/Athena" diff --git a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_degraded_latency.tf b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_degraded_latency.tf index 31554bbf..4e11b255 100644 --- a/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_degraded_latency.tf +++ b/infrastructure/terraform/components/reporting/cloudwatch_metric_alarm_degraded_latency.tf @@ -7,19 +7,19 @@ resource "aws_cloudwatch_metric_alarm" "degraded_latency" { treat_missing_data = "notBreaching" metric_query { - id = "degraded_latencies_count_max" - expression = <<-EOT + id = "degraded_latencies_count_max" + expression = <<-EOT SELECT MAX(DegradedLatenciesCount) FROM "Notify/Watchdog" WHERE environment='${var.environment}' GROUP BY environment, clientid, campaignid EOT return_data = false - period = 3600 + period = 3600 } metric_query { - id = "degraded_client_campaign_count" + id = "degraded_client_campaign_count" # Not particularly intuitive but needed to perform arithmetic on TS[] to count distinct series expression = "SUM(CEIL(degraded_latencies_count_max / (MAX(degraded_latencies_count_max) + 1)))" return_data = true diff --git a/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf b/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf index 47e5cc8f..a17e151e 100644 --- a/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf +++ b/infrastructure/terraform/components/reporting/ssm_maintenance_window_task_patch_task.tf @@ -7,7 +7,7 @@ resource "aws_ssm_maintenance_window_task" "patch_task_sunday" { task_type = "RUN_COMMAND" targets { - key = "WindowTargetIds" + key = "WindowTargetIds" values = [aws_ssm_maintenance_window_target.windows_instances_sunday[0].id] } diff --git a/scripts/config/pre-commit.yaml b/scripts/config/pre-commit.yaml index 29397e23..a0ed7510 100644 --- a/scripts/config/pre-commit.yaml +++ b/scripts/config/pre-commit.yaml @@ -13,7 +13,7 @@ repos: - id: mixed-line-ending - id: pretty-format-json args: ['--autofix'] - # - id: ... + exclude: '(^|/)package(-lock)?\.json$' - repo: local hooks: - id: sort-dictionary diff --git a/scripts/githooks/check-terraform-format.sh b/scripts/githooks/check-terraform-format.sh index 7255e512..569e54c6 100755 --- a/scripts/githooks/check-terraform-format.sh +++ b/scripts/githooks/check-terraform-format.sh @@ -29,11 +29,11 @@ function main() { # check_only=[do not format, run check only] function terraform-fmt() { - local opts= if is-arg-true "$check_only"; then - opts="-check" + make terraform-fmt-check + else + make terraform-fmt fi - opts=$opts make terraform-fmt } # ============================================================================== diff --git a/scripts/githooks/check-todos.sh b/scripts/githooks/check-todos.sh index 83b7a80e..4135cb2a 100755 --- a/scripts/githooks/check-todos.sh +++ b/scripts/githooks/check-todos.sh @@ -120,7 +120,7 @@ function search_todos() { # If the file is excluded, skip it if [ "$skip" = false ] && [ -f "$file" ]; then - file_todos=$(grep -nHiE '\bTODO\b' "$file" || true) + file_todos=$(grep -nHiE '\bTODO(:| )' "$file" || true) [ -n "$file_todos" ] && todos+="$file_todos\n" fi done @@ -136,7 +136,7 @@ function filter_todos_with_valid_jira_ticket() { while IFS= read -r line; do # Only lines with TODO but without a valid JIRA ticket - if grep -qnHiE '\bTODO\b' <<< "$line"; then + if grep -qnHiE '\bTODO(:| )' <<< "$line"; then if ! [[ "$line" =~ $jira_regex ]]; then todos_without_ticket+="$line\n" fi diff --git a/scripts/init.mk b/scripts/init.mk index e12255c3..416ff5e0 100644 --- a/scripts/init.mk +++ b/scripts/init.mk @@ -46,8 +46,8 @@ _install-dependency: # Install asdf dependency - mandatory: name=[listed in the asdf install ${name} $(or ${version},) _install-dependencies: # Install all the dependencies listed in .tool-versions - for plugin in $$(grep ^[a-z] .tool-versions | sed 's/[[:space:]].*//'); do - make _install-dependency name="$${plugin}" + for plugin in $$(grep '^[a-z]' .tool-versions | cut -f1 -d' '); do \ + $(MAKE) _install-dependency name=$${plugin}; \ done clean:: # Remove all generated and temporary files (common) @Operations diff --git a/scripts/setup/setup.sh b/scripts/setup/setup.sh index 2a008eff..fb88fbc2 100755 --- a/scripts/setup/setup.sh +++ b/scripts/setup/setup.sh @@ -25,7 +25,7 @@ function run-setup() { check-setup-status } -# Check the exit status of tfsec. +# Check the exit status of setup. function check-setup-status() { if [ $? -eq 0 ]; then diff --git a/scripts/terraform/terraform.mk b/scripts/terraform/terraform.mk index 07d3e672..bb8552ad 100644 --- a/scripts/terraform/terraform.mk +++ b/scripts/terraform/terraform.mk @@ -1,95 +1,173 @@ -# This file is for you! Edit it to implement your own Terraform make targets. +# Terraform Make Targets for TFScaffold +# NHS Notify standard for production infrastructure +# Requires infrastructure/terraform/bin/terraform.sh # ============================================================================== -# Custom implementation - implementation of a make target should not exceed 5 lines of effective code. -# In most cases there should be no need to modify the existing make targets. - -TF_ENV ?= dev -STACK ?= ${stack} -TERRAFORM_STACK ?= $(or ${STACK}, infrastructure/environments/${TF_ENV}) -dir ?= ${TERRAFORM_STACK} - -terraform-init: # Initialise Terraform - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform init command, default is none/empty] @Development - make _terraform cmd="init" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -terraform-plan: # Plan Terraform changes - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform plan command, default is none/empty] @Development - make _terraform cmd="plan" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -terraform-apply: # Apply Terraform changes - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform apply command, default is none/empty] @Development - make _terraform cmd="apply" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -terraform-destroy: # Destroy Terraform resources - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform destroy command, default is none/empty] @Development - make _terraform \ - cmd="destroy" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -terraform-fmt: # Format Terraform files - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform fmt command, default is '-recursive'] @Quality - make _terraform cmd="fmt" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -terraform-validate: # Validate Terraform configuration - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform validate command, default is none/empty] @Quality - make _terraform cmd="validate" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -clean:: # Remove Terraform files (terraform) - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set] @Operations - make _terraform cmd="clean" \ - dir=$(or ${terraform_dir}, ${dir}) \ - opts=$(or ${terraform_opts}, ${opts}) - -_terraform: # Terraform command wrapper - mandatory: cmd=[command to execute]; optional: dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], opts=[options to pass to the Terraform command, default is none/empty] - # 'TERRAFORM_STACK' is passed to the functions as environment variable - TERRAFORM_STACK=$(or ${TERRAFORM_STACK}, $(or ${terraform_stack}, $(or ${STACK}, ${stack}))) - dir=$(or ${dir}, ${TERRAFORM_STACK}) - . "scripts/terraform/terraform.lib.sh"; \ - terraform-${cmd} # 'dir' and 'opts' are accessible by the function as environment variables, if set +# TFScaffold Terraform Operations + +terraform-plan: # Plan Terraform changes - mandatory: component=[component_name], environment=[environment]; optional: project=[default: nhs], region=[default: eu-west-2], group=[default: dev], opts=[additional options] @Development + # Example: make terraform-plan component=mycomp environment=myenv group=mygroup + # Args: --project nhs --region eu-west-2 --component mycomp --environment myenv --group mygroup --action plan + make _terraform-scaffold action=plan \ + component=$(component) \ + environment=$(environment) \ + project=$(or ${project}, nhs) \ + region=$(or ${region}, eu-west-2) \ + group=$(or ${group}, dev) \ + opts=$(or ${opts}, ) + +terraform-plan-destroy: # Plan Terraform destroy - mandatory: component=[component_name], environment=[environment]; optional: project, region, group, opts @Development + # Example: make terraform-plan-destroy component=mycomp environment=myenv group=mygroup + # Args: --project nhs --region eu-west-2 --component mycomp --environment myenv --group mygroup --action plan-destroy + make _terraform-scaffold action=plan-destroy \ + component=$(component) \ + environment=$(environment) \ + project=$(or ${project}, nhs) \ + region=$(or ${region}, eu-west-2) \ + group=$(or ${group}, dev) \ + opts=$(or ${opts}, ) + +terraform-apply: # Apply Terraform changes - mandatory: component=[component_name], environment=[environment]; optional: project, region, group, build_id, opts @Development + # Example: make terraform-apply component=mycomp environment=myenv group=mygroup + # Args: --project nhs --region eu-west-2 --component mycomp --environment myenv --group mygroup --action apply + make _terraform-scaffold action=apply \ + component=$(component) \ + environment=$(environment) \ + project=$(or ${project}, nhs) \ + region=$(or ${region}, eu-west-2) \ + group=$(or ${group}, dev) \ + build_id=$(or ${build_id}, ) \ + opts=$(or ${opts}, ) + +terraform-destroy: # Destroy Terraform resources - mandatory: component=[component_name], environment=[environment]; optional: project, region, group, opts @Development + # Example: make terraform-destroy component=mycomp environment=myenv group=mygroup + # Args: --project nhs --region eu-west-2 --component mycomp --environment myenv --group mygroup --action destroy + make _terraform-scaffold action=destroy \ + component=$(component) \ + environment=$(environment) \ + project=$(or ${project}, nhs) \ + region=$(or ${region}, eu-west-2) \ + group=$(or ${group}, dev) \ + opts=$(or ${opts}, ) + +terraform-output: # Get Terraform outputs - mandatory: component=[component_name], environment=[environment]; optional: project, region, group @Development + # Example: make terraform-output component=mycomp environment=myenv group=mygroup + # Args: --project nhs --region eu-west-2 --component mycomp --environment myenv --group mygroup --action output + make _terraform-scaffold action=output \ + component=$(component) \ + environment=$(environment) \ + project=$(or ${project}, nhs) \ + region=$(or ${region}, eu-west-2) \ + group=$(or ${group}, dev) + +_terraform-scaffold: # Internal wrapper for terraform.sh - mandatory: action=[terraform action]; optional: component, environment, project, region, group, bootstrap, build_id, opts + cd infrastructure/terraform && \ + if [ "$(bootstrap)" = "true" ]; then \ + ./bin/terraform.sh \ + --bootstrap \ + --project $(project) \ + --region $(region) \ + --group $(group) \ + --action $(action) \ + $(if $(opts),-- $(opts),); \ + else \ + ./bin/terraform.sh \ + --project $(project) \ + --region $(region) \ + --component $(component) \ + --environment $(environment) \ + --group $(group) \ + $(if $(build_id),--build-id $(build_id),) \ + --action $(action) \ + $(if $(opts),-- $(opts),); \ + fi # ============================================================================== -# Quality checks - please DO NOT edit this section! - -terraform-shellscript-lint: # Lint all Terraform module shell scripts @Quality - for file in $$(find scripts/terraform -type f -name "*.sh"); do - file=$${file} scripts/shellscript-linter.sh +# Formatting and Validation + +terraform-fmt: # Format Terraform files in components/ and modules/ (excludes etc/) @Quality + # Example: make terraform-fmt + @cd infrastructure/terraform && \ + for dir in components modules; do \ + [ -d "$$dir" ] && terraform fmt -recursive "$$dir"; \ + done + +terraform-fmt-check: # Check Terraform formatting in components/ and modules/ (excludes etc/) @Quality + # Example: make terraform-fmt-check + @cd infrastructure/terraform && \ + for dir in components modules; do \ + [ -d "$$dir" ] && terraform fmt -check -recursive "$$dir"; \ + done + +terraform-validate: # Validate Terraform configuration - mandatory: component=[component_name] @Quality + # Example: make terraform-validate component=mycomp + # Note: Validation does not require environment/group as it checks syntax only + cd infrastructure/terraform/components/$(component) && \ + terraform init -backend=false && \ + terraform validate + +terraform-validate-all: # Validate all Terraform components @Quality + # Example: make terraform-validate-all + for dir in infrastructure/terraform/components/*; do \ + if [ -d "$$dir" ]; then \ + echo "Validating $$(basename $$dir)..."; \ + cd $$dir && \ + terraform init -backend=false && \ + terraform validate && \ + cd - > /dev/null; \ + fi; \ done -terraform-sec: # TFSEC check against Terraform files - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform fmt command, default is '-recursive'] @Quality - tfsec infrastructure/terraform \ - --force-all-dirs \ - --exclude-downloaded-modules \ - --config-file scripts/config/tfsec.yaml +terraform-sec: # Run Trivy IaC security scanning on Terraform code @Quality + # Example: make terraform-sec + ./scripts/terraform/trivy-scan.sh --mode iac infrastructure/terraform + +terraform-docs: # Generate Terraform documentation - optional: component=[specific component, or all if omitted] @Quality + # Example: make terraform-docs component=mycomp + # Example: make terraform-docs (generates for all components) + @if [ -n "$(component)" ]; then \ + ./scripts/terraform/terraform-docs.sh infrastructure/terraform/components/$(component); \ + else \ + for dir in infrastructure/terraform/components/* infrastructure/terraform/modules/*; do \ + if [ -d "$$dir" ]; then \ + ./scripts/terraform/terraform-docs.sh $$dir; \ + fi; \ + done; \ + fi -terraform-docs: # Terraform-docs check against Terraform files - optional: terraform_dir|dir=[path to a directory where the command will be executed, relative to the project's top-level directory, default is one of the module variables or the example directory, if not set], terraform_opts|opts=[options to pass to the Terraform fmt command, default is '-recursive'] @Quality - for dir in ./infrastructure/terraform/components/* ./infrastructure/terraform/modules/*; do \ - if [ -d "$$dir" ]; then \ - ./scripts/terraform/terraform-docs.sh $$dir; \ - fi \ - done +# ============================================================================== +# Cleanup + +clean:: # Remove Terraform build artifacts and cache @Operations + # Example: make clean + rm -rf infrastructure/terraform/components/*/build + rm -rf infrastructure/terraform/components/*/.terraform + rm -rf infrastructure/terraform/components/*/.terraform.lock.hcl + rm -rf infrastructure/terraform/bootstrap/.terraform + rm -rf infrastructure/terraform/bootstrap/.terraform.lock.hcl + rm -rf infrastructure/terraform/plugin-cache/* # ============================================================================== -# Configuration - please DO NOT edit this section! +# Installation -terraform-install: # Install Terraform @Installation +terraform-install: # Install Terraform using asdf @Installation + # Example: make terraform-install make _install-dependency name="terraform" # ============================================================================== ${VERBOSE}.SILENT: \ - _terraform \ + _terraform-scaffold \ clean \ terraform-apply \ terraform-destroy \ - terraform-fmt \ terraform-docs \ - terraform-init \ + terraform-fmt \ + terraform-fmt-check \ terraform-install \ + terraform-output \ terraform-plan \ - terraform-shellscript-lint \ + terraform-plan-destroy \ + terraform-sec \ terraform-validate \ + terraform-validate-all \ diff --git a/scripts/terraform/tfsec.sh b/scripts/terraform/tfsec.sh deleted file mode 100755 index 5d13e60a..00000000 --- a/scripts/terraform/tfsec.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash - -# WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/NHSDigital/nhs-notify-repository-template). Raise a PR instead. - -set -euo pipefail - -# TFSec command wrapper. It will run the command natively if TFSec is -# installed, otherwise it will run it in a Docker container. -# Run tfsec for security checks on Terraform code. -# -# Usage: -# $ ./tfsec.sh [directory] -# ============================================================================== - -function main() { - - cd "$(git rev-parse --show-toplevel)" - - local dir_to_scan=${1:-.} - - if command -v tfsec > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then - # shellcheck disable=SC2154 - run-tfsec-natively "$dir_to_scan" - else - run-tfsec-in-docker "$dir_to_scan" - fi -} - -# Run tfsec on the specified directory. -# Arguments: -# $1 - Directory to scan -function run-tfsec-natively() { - - local dir_to_scan="$1" - - echo "TFSec found locally, running natively" - - echo "Running TFSec on directory: $dir_to_scan" - tfsec \ - --force-all-dirs \ - --exclude-downloaded-modules \ - --config-file scripts/config/tfsec.yaml \ - --format text \ - "$dir_to_scan" - - check-tfsec-status -} - -# Check the exit status of tfsec. -function check-tfsec-status() { - - if [ $? -eq 0 ]; then - echo "TFSec completed successfully." - else - echo "TFSec found issues." - exit 1 - fi -} - -function run-tfsec-in-docker() { - - # shellcheck disable=SC1091 - source ./scripts/docker/docker.lib.sh - local dir_to_scan="$1" - - # shellcheck disable=SC2155 - local image=$(name=aquasec/tfsec docker-get-image-version-and-pull) - # shellcheck disable=SC2086 - echo "TFSec not found locally, running in Docker Container" - echo "Running TFSec on directory: $dir_to_scan" - docker run --rm --platform linux/amd64 \ - --volume "$PWD":/workdir \ - --workdir /workdir \ - "$image" \ - --concise-output \ - --force-all-dirs \ - --exclude-downloaded-modules \ - --config-file scripts/config/tfsec.yaml \ - --format text \ - --soft-fail \ - "$dir_to_scan" - check-tfsec-status -} -# ============================================================================== - -function is-arg-true() { - - if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then - return 0 - else - return 1 - fi -} - -# ============================================================================== - -is-arg-true "${VERBOSE:-false}" && set -x - -main "$@" - -exit 0 diff --git a/scripts/tests/test.mk b/scripts/tests/test.mk index aab47c62..a1f16907 100644 --- a/scripts/tests/test.mk +++ b/scripts/tests/test.mk @@ -65,12 +65,14 @@ test: # Run all the test tasks @Testing test-load _test: - set -e - script="./scripts/tests/${name}.sh" - if [ -e "$${script}" ]; then - exec $${script} - else - echo "make test-${name} not implemented: $${script} not found" >&2 + set -e; \ + script="./scripts/tests/${name}.sh"; \ + if [ -e "$${script}" ]; then \ + exec $${script}; \ + else \ + echo "test-${name}: Not currently implemented"; \ + echo "Create $${script} to implement this test target"; \ + exit 0; \ fi ${VERBOSE}.SILENT: \