From ebeae51793fe17ff387736049f19d4077a447b7a Mon Sep 17 00:00:00 2001 From: josielsouzanordcloud <90701149+josielsouzanordcloud@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:53:46 +0000 Subject: [PATCH] feat: add relay-namespace module for Azure Relay with private endpoint (#268) - Add azurerm_relay_namespace resource - Add optional private endpoint configuration - Add diagnostic settings integration - Add README with usage examples and naming constraints feat: add relay-hybrid-connection module for Azure Relay Hybrid Connections (#269) Adds a new Terraform module to create Azure Relay Hybrid Connections and Authorization Rules. The module is designed to work with an existing relay namespace created by the relay-namespace module. Add support for excluding paths from Entra ID authentication (#270) Allow container apps to specify paths (e.g., /healthcheck, /sha) that bypass authentication via the new auth_excluded_paths variable. Updated tfdocs feat: added alerts for Key Vault secrets nearing or past expiry (#271) feat: added alerts for key kault certificates nearing or past expiry (#272) fix: added default values for secret and certificate expiry alerts (#273) test: add Terraform module testing and security scanning - Add Terratest tests for managed-identity module - Valid inputs validation - Invalid inputs validation (name format) - Tests run offline (no Azure credentials required) - Add tflint integration (advisory) - Scans all modules for best practice violations - Reports issues in workflow summary - Non-blocking (178 existing issues baselined) - Add tfsec with GitHub Code Scanning (advisory) - Security scanning for Terraform misconfigurations - Results appear in Security tab, not public artifacts - Non-blocking (7 existing issues baselined) - Clean up workflow - Remove irrelevant jobs (unit tests, coverage, lint) - Update CodeQL action to v4 - Fix Go cache restore issues feat: integrate terraform quality tools and streamline local workflow - Add targets for terraform-lint, terraform-security, and terraform-test to Makefiles - Update CI/CD pipelines to dynamically use tool versions from .tool-versions - Clean up redundant boilerplate make targets to reduce clutter - Fix managed-identity linting issues by adding missing versions.tf - Disable unused Docker make includes to simplify the developer interface fix wrong line endings or no final newline removed redundant root All plugins are already installed, added to the linting loop and add check of exist tf file in a folder fix traiting whitespaces Amend managed identity testing to remove redundant provider remove comments --- .github/workflows/cicd-1-pull-request.yaml | 11 +- .github/workflows/stage-1-commit.yaml | 4 + .github/workflows/stage-2-test.yaml | 137 ++++++++---- .github/workflows/stage-4-acceptance.yaml | 4 + .tool-versions | 7 +- infrastructure/modules/key-vault/tfdocs.md | 124 +++++++++++ .../modules/managed-identity/README.md | 109 ++++++++++ .../modules/managed-identity/tests/go.mod | 40 ++++ .../modules/managed-identity/tests/go.sum | 72 +++++++ .../tests/managed_identity_test.go | 202 ++++++++++++++++++ .../modules/managed-identity/versions.tf | 10 + scripts/init.mk | 2 +- scripts/terraform/terraform.mk | 96 +++++++++ scripts/tests/lint.sh | 5 + scripts/tests/security.sh | 5 + scripts/tests/test.mk | 53 +---- scripts/tests/unit.sh | 19 +- 17 files changed, 789 insertions(+), 111 deletions(-) create mode 100644 infrastructure/modules/managed-identity/README.md create mode 100644 infrastructure/modules/managed-identity/tests/go.mod create mode 100644 infrastructure/modules/managed-identity/tests/go.sum create mode 100644 infrastructure/modules/managed-identity/tests/managed_identity_test.go create mode 100644 infrastructure/modules/managed-identity/versions.tf create mode 100755 scripts/tests/lint.sh create mode 100755 scripts/tests/security.sh diff --git a/.github/workflows/cicd-1-pull-request.yaml b/.github/workflows/cicd-1-pull-request.yaml index 004b11a5..1af7da19 100644 --- a/.github/workflows/cicd-1-pull-request.yaml +++ b/.github/workflows/cicd-1-pull-request.yaml @@ -22,6 +22,7 @@ jobs: nodejs_version: ${{ steps.variables.outputs.nodejs_version }} python_version: ${{ steps.variables.outputs.python_version }} terraform_version: ${{ steps.variables.outputs.terraform_version }} + golang_version: ${{ steps.variables.outputs.golang_version }} version: ${{ steps.variables.outputs.version }} does_pull_request_exist: ${{ steps.pr_exists.outputs.does_pull_request_exist }} docker_file_exists: ${{ steps.check_compose.outputs.docker_file_exists }} @@ -37,9 +38,10 @@ jobs: echo "build_datetime=$datetime" >> $GITHUB_OUTPUT echo "build_timestamp=$(date --date=$datetime -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT echo "build_epoch=$(date --date=$datetime -u +'%s')" >> $GITHUB_OUTPUT - echo "nodejs_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - echo "python_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT - echo "terraform_version=$(grep "^terraform" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT + echo "nodejs_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ' || echo 20.11.0)" >> $GITHUB_OUTPUT + echo "python_version=$(grep "^python" .tool-versions | cut -f2 -d' ' || echo 3.12.1)" >> $GITHUB_OUTPUT + echo "terraform_version=$(grep "^terraform" .tool-versions | cut -f2 -d' ' || echo 1.13.0)" >> $GITHUB_OUTPUT + echo "golang_version=$(grep "^golang" .tool-versions | cut -f2 -d' ' || echo 1.22.5)" >> $GITHUB_OUTPUT echo "version=$(head -n 1 .version 2> /dev/null || echo unknown)" >> $GITHUB_OUTPUT - name: "Check if pull request exists for this branch" id: pr_exists @@ -87,6 +89,7 @@ jobs: nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}" python_version: "${{ needs.metadata.outputs.python_version }}" terraform_version: "${{ needs.metadata.outputs.terraform_version }}" + golang_version: "${{ needs.metadata.outputs.golang_version }}" version: "${{ needs.metadata.outputs.version }}" secrets: inherit test-stage: # Recommended maximum execution time is 5 minutes @@ -100,6 +103,7 @@ jobs: nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}" python_version: "${{ needs.metadata.outputs.python_version }}" terraform_version: "${{ needs.metadata.outputs.terraform_version }}" + golang_version: "${{ needs.metadata.outputs.golang_version }}" version: "${{ needs.metadata.outputs.version }}" secrets: inherit build-stage: # Recommended maximum execution time is 3 minutes @@ -126,5 +130,6 @@ jobs: nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}" python_version: "${{ needs.metadata.outputs.python_version }}" terraform_version: "${{ needs.metadata.outputs.terraform_version }}" + golang_version: "${{ needs.metadata.outputs.golang_version }}" version: "${{ needs.metadata.outputs.version }}" secrets: inherit diff --git a/.github/workflows/stage-1-commit.yaml b/.github/workflows/stage-1-commit.yaml index a97d49ee..e56eb93d 100644 --- a/.github/workflows/stage-1-commit.yaml +++ b/.github/workflows/stage-1-commit.yaml @@ -27,6 +27,10 @@ on: description: "Terraform version, set by the CI/CD pipeline workflow" required: true type: string + golang_version: + description: "Go version, set by the CI/CD pipeline workflow" + required: true + type: string version: description: "Version of the software, set by the CI/CD pipeline workflow" required: true diff --git a/.github/workflows/stage-2-test.yaml b/.github/workflows/stage-2-test.yaml index b10c159f..099850d9 100644 --- a/.github/workflows/stage-2-test.yaml +++ b/.github/workflows/stage-2-test.yaml @@ -27,71 +27,136 @@ on: description: "Terraform version, set by the CI/CD pipeline workflow" required: true type: string + golang_version: + description: "Go version, set by the CI/CD pipeline workflow" + required: true + type: string version: description: "Version of the software, set by the CI/CD pipeline workflow" required: true type: string jobs: - test-unit: - name: "Unit tests" - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: "Checkout code" - uses: actions/checkout@v4 - - name: "Run unit test suite" - run: | - make test-unit - - name: "Save the result of fast test suite" - run: | - echo "Nothing to save" - test-lint: - name: "Linting" + terraform-lint: + name: "Terraform lint (tflint)" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 steps: - name: "Checkout code" uses: actions/checkout@v4 - - name: "Run linting" - run: | - make test-lint - - name: "Save the linting result" + - name: Setup TFLint + uses: terraform-linters/setup-tflint@v4 + with: + tflint_version: latest + - name: "Run TFLint on modules" + id: tflint + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "Nothing to save" - test-coverage: - name: "Test coverage" - needs: [test-unit] + echo "## TFLint Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + total_issues=0 + for module_dir in $(find infrastructure/modules -mindepth 1 -maxdepth 1 -type d); do + module_name=$(basename "$module_dir") + # Only lint directories containing .tf files + if ls "$module_dir"/*.tf > /dev/null 2>&1; then + echo "=== Linting $module_dir ===" + # Init tflint for the module (downloads plugins if needed) + tflint --init --chdir="$module_dir" > /dev/null 2>&1 || true + + # Capture output and count issues + output=$(tflint --chdir="$module_dir" --format=compact 2>&1 || true) + issue_count=$(echo "$output" | grep -c ":" || echo "0") + + if [ "$issue_count" -gt 0 ] && [ -n "$output" ]; then + total_issues=$((total_issues + issue_count)) + echo "### ⚠️ $module_name ($issue_count issues)" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "$output" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + else + echo "### ✅ $module_name" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + fi + done + echo "---" >> $GITHUB_STEP_SUMMARY + echo "**Total issues: $total_issues**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [ $total_issues -gt 0 ]; then + echo "> **Note:** TFLint issues are advisory only. Please address these issues to improve code quality." >> $GITHUB_STEP_SUMMARY + fi + # Always exit 0 - this job is advisory only + exit 0 + terraform-security: + name: "Terraform security scan" runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 + permissions: + contents: read + security-events: write steps: - name: "Checkout code" uses: actions/checkout@v4 - - name: "Run test coverage check" + - name: "Run tfsec with SARIF output" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - make test-coverage - - name: "Save the coverage check result" + # Install tfsec + curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash + # Run tfsec and output SARIF format + tfsec infrastructure/ --format sarif --out tfsec-results.sarif --soft-fail + - name: "Upload SARIF to GitHub Code Scanning" + uses: github/codeql-action/upload-sarif@v4 + if: always() + with: + sarif_file: tfsec-results.sarif + category: terraform-security + - name: "Generate summary" + if: always() run: | - echo "Nothing to save" + echo "## Terraform Security Scan" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Security findings are uploaded to the **Security** tab → **Code scanning alerts**." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "> **Note:** Findings are informational and do not block merges." >> $GITHUB_STEP_SUMMARY + echo "> To make blocking, enable 'Require code scanning results' in branch protection rules." >> $GITHUB_STEP_SUMMARY unit-test-terraform-modules: name: "Unit test terraform modules" - needs: [test-unit] runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 steps: - name: "Checkout code" uses: actions/checkout@v4 - name: Install Terraform uses: hashicorp/setup-terraform@v3 with: - terraform_version: 1.12.2 - - name: "run the tests" + terraform_version: ${{ inputs.terraform_version }} + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ inputs.golang_version }} + cache: false # Disable cache to avoid tar restore errors + - name: "Run module tests" run: | - cd tests/modules - go test -v + # Find all module test directories and run tests + failed=0 + for test_dir in $(find infrastructure/modules -type d -name "tests"); do + if ls "$test_dir"/*_test.go 1> /dev/null 2>&1; then + echo "=== Running tests in $test_dir ===" + cd "$test_dir" + go mod tidy + if ! go test -v ./...; then + failed=1 + fi + cd - > /dev/null + fi + done + if [ $failed -eq 1 ]; then + exit 1 + fi perform-static-analysis: name: "Perform static analysis" - needs: [test-unit] runs-on: ubuntu-latest permissions: id-token: write diff --git a/.github/workflows/stage-4-acceptance.yaml b/.github/workflows/stage-4-acceptance.yaml index d554f98a..f182af4d 100644 --- a/.github/workflows/stage-4-acceptance.yaml +++ b/.github/workflows/stage-4-acceptance.yaml @@ -27,6 +27,10 @@ on: description: "Terraform version, set by the CI/CD pipeline workflow" required: true type: string + golang_version: + description: "Go version, set by the CI/CD pipeline workflow" + required: true + type: string version: description: "Version of the software, set by the CI/CD pipeline workflow" required: true diff --git a/.tool-versions b/.tool-versions index b850c185..9b07693d 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,7 +1,10 @@ # This file is for you! Please, updated to the versions agreed by your team. -terraform 1.9.2 -pre-commit 3.6.0 +terraform 1.13.0 +pre-commit 4.5.1 +tflint 0.60.0 +tfsec 1.28.13 +golang 1.22.5 # ============================================================================== # The section below is reserved for Docker image versions. diff --git a/infrastructure/modules/key-vault/tfdocs.md b/infrastructure/modules/key-vault/tfdocs.md index 0bb170a1..16917d22 100644 --- a/infrastructure/modules/key-vault/tfdocs.md +++ b/infrastructure/modules/key-vault/tfdocs.md @@ -60,6 +60,62 @@ Type: `string` The following input variables are optional (have default values): +### [action\_group\_id](#input\_action\_group\_id) + +Description: The ID of the Action Group to use for alerts. + +Type: `string` + +Default: `null` + +### [certificate\_expired\_alert](#input\_certificate\_expired\_alert) + +Description: n/a + +Type: + +```hcl +object({ + evaluation_frequency = string + window_duration = string + threshold = number + }) +``` + +Default: + +```json +{ + "evaluation_frequency": "PT15M", + "threshold": 1, + "window_duration": "PT1H" +} +``` + +### [certificate\_near\_expiry\_alert](#input\_certificate\_near\_expiry\_alert) + +Description: n/a + +Type: + +```hcl +object({ + evaluation_frequency = string + window_duration = string + threshold = number + }) +``` + +Default: + +```json +{ + "evaluation_frequency": "P1D", + "threshold": 1, + "window_duration": "P1D" +} +``` + ### [disk\_encryption](#input\_disk\_encryption) Description: Should the disk encryption be enabled @@ -68,6 +124,14 @@ Type: `bool` Default: `true` +### [enable\_alerting](#input\_enable\_alerting) + +Description: Whether monitoring and alerting is enabled for the Key Vault. + +Type: `bool` + +Default: `false` + ### [enable\_rbac\_authorization](#input\_enable\_rbac\_authorization) Description: n/a @@ -108,6 +172,62 @@ Type: `list(string)` Default: `[]` +### [resource\_group\_name\_monitoring](#input\_resource\_group\_name\_monitoring) + +Description: The name of the resource group in which to create the Monitoring resources for the Key Vault. Changing this forces a new resource to be created. + +Type: `string` + +Default: `null` + +### [secret\_expired\_alert](#input\_secret\_expired\_alert) + +Description: n/a + +Type: + +```hcl +object({ + evaluation_frequency = string + window_duration = string + threshold = number + }) +``` + +Default: + +```json +{ + "evaluation_frequency": "PT15M", + "threshold": 1, + "window_duration": "PT1H" +} +``` + +### [secret\_near\_expiry\_alert](#input\_secret\_near\_expiry\_alert) + +Description: n/a + +Type: + +```hcl +object({ + evaluation_frequency = string + window_duration = string + threshold = number + }) +``` + +Default: + +```json +{ + "evaluation_frequency": "P1D", + "threshold": 1, + "window_duration": "P1D" +} +``` + ### [sku\_name](#input\_sku\_name) Description: Type of the Key Vault's SKU. @@ -253,4 +373,8 @@ Description: n/a The following resources are used by this module: - [azurerm_key_vault.keyvault](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault) (resource) +- [azurerm_monitor_scheduled_query_rules_alert_v2.kv_certificate_expired](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_scheduled_query_rules_alert_v2) (resource) +- [azurerm_monitor_scheduled_query_rules_alert_v2.kv_certificate_near_expiry](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_scheduled_query_rules_alert_v2) (resource) +- [azurerm_monitor_scheduled_query_rules_alert_v2.kv_secret_expired](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_scheduled_query_rules_alert_v2) (resource) +- [azurerm_monitor_scheduled_query_rules_alert_v2.kv_secret_near_expiry](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_scheduled_query_rules_alert_v2) (resource) - [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) (data source) diff --git a/infrastructure/modules/managed-identity/README.md b/infrastructure/modules/managed-identity/README.md new file mode 100644 index 00000000..5d6ffb8b --- /dev/null +++ b/infrastructure/modules/managed-identity/README.md @@ -0,0 +1,109 @@ +# Managed Identity Module + +Creates an Azure User-Assigned Managed Identity. + +## Usage + +```hcl +module "managed_identity" { + source = "../../modules/managed-identity" + + resource_group_name = "rg-myapp-prod" + location = "uksouth" + uai_name = "mi-myapp-prod" + + tags = { + environment = "production" + managed_by = "terraform" + } +} +``` + +## Requirements + +| Name | Version | +|------|---------| +| terraform | >= 1.0 | +| azurerm | >= 3.0 | + +## Inputs + +| Name | Description | Type | Required | Default | +|------|-------------|------|----------|---------| +| `resource_group_name` | The name of the resource group in which to create the Identity | `string` | Yes | - | +| `location` | Azure region for the resource | `string` | Yes | - | +| `uai_name` | The name of the user assigned identity (3-128 chars, alphanumeric, hyphens, underscores) | `string` | Yes | - | +| `tags` | Resource tags to be applied | `map(string)` | No | `{}` | + +### Naming Constraints + +The `uai_name` must: +- Be between 3 and 128 characters +- Start with an alphanumeric character +- End with an alphanumeric character or underscore +- Contain only alphanumeric characters, hyphens, and underscores + +## Outputs + +| Name | Description | +|------|-------------| +| `id` | The resource ID of the User Assigned Identity | +| `name` | The name of the User Assigned Identity | +| `principal_id` | The Principal ID (Object ID) for the Service Principal associated with this Identity | +| `client_id` | The Client ID (Application ID) for the Service Principal associated with this Identity | + +## Example: Assigning to a Function App + +```hcl +module "managed_identity" { + source = "../../modules/managed-identity" + + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + uai_name = "mi-funcapp-prod" +} + +resource "azurerm_linux_function_app" "example" { + # ... other config ... + + identity { + type = "UserAssigned" + identity_ids = [module.managed_identity.id] + } +} +``` + +## Example: Granting Key Vault Access (RBAC) + +```hcl +module "managed_identity" { + source = "../../modules/managed-identity" + + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + uai_name = "mi-myapp-prod" +} + +resource "azurerm_role_assignment" "keyvault_secrets" { + scope = azurerm_key_vault.main.id + role_definition_name = "Key Vault Secrets User" + principal_id = module.managed_identity.principal_id +} +``` + +## Testing + +This module includes automated tests using Terratest. + +```bash +cd tests +go test -v ./... +``` + +Tests validate: +- Valid input configurations are accepted +- Invalid identity names are rejected by validation rules + +## Documentation + +Auto-generated documentation is available in [tfdocs.md](./tfdocs.md). diff --git a/infrastructure/modules/managed-identity/tests/go.mod b/infrastructure/modules/managed-identity/tests/go.mod new file mode 100644 index 00000000..037948a6 --- /dev/null +++ b/infrastructure/modules/managed-identity/tests/go.mod @@ -0,0 +1,40 @@ +module managed-identity-tests + +go 1.25.6 + +require ( + github.com/gruntwork-io/terratest v0.55.0 + github.com/stretchr/testify v1.11.1 +) + +require ( + github.com/agext/levenshtein v1.2.3 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter/v2 v2.2.3 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/hcl/v2 v2.22.0 // indirect + github.com/hashicorp/terraform-json v0.23.0 // indirect + github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/tmccombs/hcl2json v0.6.4 // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + github.com/zclconf/go-cty v1.15.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/infrastructure/modules/managed-identity/tests/go.sum b/infrastructure/modules/managed-identity/tests/go.sum new file mode 100644 index 00000000..bca54390 --- /dev/null +++ b/infrastructure/modules/managed-identity/tests/go.sum @@ -0,0 +1,72 @@ +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= +github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/gruntwork-io/terratest v0.55.0 h1:NgG6lm2dArdQ3KcOofw6PTfVRK1Flt7L3NNhFSBo72A= +github.com/gruntwork-io/terratest v0.55.0/go.mod h1:OE0Jsc8Wn5kw/QySLbBd53g9Gt+xfDyDKChwRHwkKvI= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter/v2 v2.2.3 h1:6CVzhT0KJQHqd9b0pK3xSP0CM/Cv+bVhk+jcaRJ2pGk= +github.com/hashicorp/go-getter/v2 v2.2.3/go.mod h1:hp5Yy0GMQvwWVUmwLs3ygivz1JSLI323hdIE9J9m7TY= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M= +github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= +github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI= +github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= +github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= +github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg= +github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw= +github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= +github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/infrastructure/modules/managed-identity/tests/managed_identity_test.go b/infrastructure/modules/managed-identity/tests/managed_identity_test.go new file mode 100644 index 00000000..b766e7e4 --- /dev/null +++ b/infrastructure/modules/managed-identity/tests/managed_identity_test.go @@ -0,0 +1,202 @@ +package test + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/gruntwork-io/terratest/modules/files" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" +) + +func setupTestModule(t *testing.T, vars map[string]any) string { + moduleDir := "../" + + tempDir, err := files.CopyTerraformFolderToTemp(moduleDir, t.Name()) + if err != nil { + tempDir = t.TempDir() + copyTerraformFiles(t, moduleDir, tempDir) + } + + // We only need to inject the provider block, as required_providers is now in versions.tf + providerContent := "provider \"azurerm\" {\nfeatures {}\n}\n" + providerPath := filepath.Join(tempDir, "provider_test.tf") + err = os.WriteFile(providerPath, []byte(providerContent), 0644) + if err != nil { + t.Fatalf("failed to write provider file: %v", err) + } + + writeVarsFile(t, tempDir, vars) + + return tempDir +} + +func writeVarsFile(t *testing.T, dir string, vars map[string]any) { + var content string + for k, v := range vars { + switch val := v.(type) { + case string: + content += fmt.Sprintf("%s = %q\n", k, val) + case map[string]string: + content += fmt.Sprintf("%s = {\n", k) + for mk, mv := range val { + content += fmt.Sprintf(" %s = %q\n", mk, mv) + } + content += "}\n" + case nil: + content += fmt.Sprintf("%s = {}\n", k) + default: + content += fmt.Sprintf("%s = %v\n", k, val) + } + } + + varsPath := filepath.Join(dir, "terraform.tfvars") + err := os.WriteFile(varsPath, []byte(content), 0644) + if err != nil { + t.Fatalf("failed to write tfvars file: %v", err) + } +} + +func copyTerraformFiles(t *testing.T, src, dst string) { + entries, err := os.ReadDir(src) + if err != nil { + t.Fatalf("failed to read source directory: %v", err) + } + + for _, entry := range entries { + if entry.IsDir() || filepath.Ext(entry.Name()) != ".tf" { + continue + } + + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + content, err := os.ReadFile(srcPath) + if err != nil { + t.Fatalf("failed to read file %s: %v", srcPath, err) + } + + err = os.WriteFile(dstPath, content, 0644) + if err != nil { + t.Fatalf("failed to write file %s: %v", dstPath, err) + } + } +} + +func TestManagedIdentity_ValidInputs(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + uaiName string + tags map[string]string + }{ + { + name: "standard_name", + uaiName: "mi-test-identity", + tags: map[string]string{"environment": "test"}, + }, + { + name: "minimum_length_name", + uaiName: "abc", + tags: nil, + }, + { + name: "name_with_underscores", + uaiName: "mi_test_identity", + tags: map[string]string{}, + }, + { + name: "name_with_mixed_separators", + uaiName: "mi-test_identity-01", + tags: map[string]string{"team": "platform", "cost_center": "12345"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + vars := map[string]any{ + "resource_group_name": "rg-test", + "location": "uksouth", + "uai_name": tc.uaiName, + "tags": tc.tags, + } + tempDir := setupTestModule(t, vars) + + terraformOptions := &terraform.Options{ + TerraformDir: tempDir, + NoColor: true, + } + + _, err := terraform.InitAndValidateE(t, terraformOptions) + assert.NoError(t, err, "Module should validate successfully with valid inputs") + }) + } +} + +// Variable validation rules are only evaluated during 'plan' or 'apply', not 'validate'. +func TestManagedIdentity_InvalidName(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + uaiName string + description string + }{ + { + name: "too_short_one_char", + uaiName: "a", + description: "single character should be rejected", + }, + { + name: "too_short_two_chars", + uaiName: "ab", + description: "two characters should be rejected", + }, + { + name: "starts_with_hyphen", + uaiName: "-mi-identity", + description: "name starting with hyphen should be rejected", + }, + { + name: "starts_with_underscore", + uaiName: "_mi-identity", + description: "name starting with underscore should be rejected", + }, + { + name: "contains_invalid_chars", + uaiName: "mi.test.identity", + description: "name containing dots should be rejected", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + vars := map[string]any{ + "resource_group_name": "rg-test", + "location": "uksouth", + "uai_name": tc.uaiName, + } + tempDir := setupTestModule(t, vars) + + terraformOptions := &terraform.Options{ + TerraformDir: tempDir, + NoColor: true, + } + + _, err := terraform.InitAndPlanE(t, terraformOptions) + + assert.Error(t, err, tc.description) + if err != nil { + assert.Contains(t, err.Error(), "User-Assigned Managed Identity name", + "Error message should mention the identity name validation") + } + }) + } +} diff --git a/infrastructure/modules/managed-identity/versions.tf b/infrastructure/modules/managed-identity/versions.tf new file mode 100644 index 00000000..58dfc8e6 --- /dev/null +++ b/infrastructure/modules/managed-identity/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.0" + } + } +} diff --git a/scripts/init.mk b/scripts/init.mk index 373f8a4f..8df7e3f6 100644 --- a/scripts/init.mk +++ b/scripts/init.mk @@ -1,6 +1,6 @@ # WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/nhs-england-tools/repository-template). Raise a PR instead. -include scripts/docker/docker.mk +# include scripts/docker/docker.mk include scripts/tests/test.mk -include scripts/terraform/terraform.mk diff --git a/scripts/terraform/terraform.mk b/scripts/terraform/terraform.mk index 5cfc4b5c..f798d3cc 100644 --- a/scripts/terraform/terraform.mk +++ b/scripts/terraform/terraform.mk @@ -1,3 +1,4 @@ + # This file is for you! Edit it to implement your own Terraform make targets. # ============================================================================== @@ -43,6 +44,95 @@ terraform-validate: # Validate Terraform configuration - optional: terraform_dir dir=$(or ${terraform_dir}, ${dir}) \ opts=$(or ${terraform_opts}, ${opts}) +terraform-lint: # Lint Terraform modules using tflint - optional: module=[name of the module to lint, e.g. 'managed-identity'] @Quality + echo "Running TFLint..." + make _install-dependency name="tflint" + if [ -n "${module}" ]; then \ + module_dir="infrastructure/modules/${module}"; \ + if [ ! -d "$$module_dir" ]; then echo "Error: Module directory $$module_dir not found"; exit 1; fi; \ + if ls "$$module_dir"/*.tf > /dev/null 2>&1; then \ + echo "=== Linting $$module_dir ==="; \ + tflint --init --chdir="$$module_dir" > /dev/null 2>&1 || true; \ + tflint --chdir="$$module_dir" --format=compact; \ + else \ + echo "Skipping $$module_dir (no .tf files)"; \ + fi; \ + else \ + total_issues=0; \ + for module_dir in $$(find infrastructure/modules -mindepth 1 -maxdepth 1 -type d); do \ + module_name=$$(basename "$$module_dir"); \ + if ls "$$module_dir"/*.tf > /dev/null 2>&1; then \ + echo "=== Linting $$module_dir ==="; \ + tflint --init --chdir="$$module_dir" > /dev/null 2>&1 || true; \ + output=$$(tflint --chdir="$$module_dir" --format=compact 2>&1 || true); \ + issue_count=$$(echo "$$output" | grep -c ":" || echo "0"); \ + if [ "$$issue_count" -gt 0 ] && [ -n "$$output" ]; then \ + total_issues=$$((total_issues + issue_count)); \ + echo "### ⚠️ $$module_name ($$issue_count issues)"; \ + echo "$$output"; \ + else \ + echo "### ✅ $$module_name"; \ + fi; \ + echo ""; \ + fi; \ + done; \ + echo "Total issues: $$total_issues"; \ + if [ $$total_issues -gt 0 ]; then \ + echo "> **Note:** TFLint issues are advisory only."; \ + fi; \ + fi + +terraform-security: # Run security scan using tfsec - optional: module=[name of the module to scan, e.g. 'managed-identity'] @Quality + echo "Running tfsec..." + make _install-dependency name="tfsec" + if [ -n "${module}" ]; then \ + module_dir="infrastructure/modules/${module}"; \ + if [ ! -d "$$module_dir" ]; then echo "Error: Module directory $$module_dir not found"; exit 1; fi; \ + echo "=== Scanning $$module_dir ==="; \ + tfsec "$$module_dir" --soft-fail; \ + else \ + tfsec infrastructure/ --soft-fail; \ + fi + +terraform-static-analysis: # Run static analysis using SonarScanner @Quality + echo "Running Static Analysis..." + ./scripts/reports/perform-static-analysis.sh + +terraform-test-modules: # Run Go unit tests for Terraform modules - optional: module=[name of the module to test, e.g. 'managed-identity'] @Testing + echo "Running Module Tests..." + make _install-dependency name="golang" + failed=0 + if [ -n "${module}" ]; then \ + test_dir="infrastructure/modules/${module}/tests"; \ + if [ ! -d "$$test_dir" ]; then echo "Error: Test directory $$test_dir not found"; exit 1; fi; \ + echo "=== Running tests in $$test_dir ==="; \ + cd "$$test_dir"; \ + go mod tidy; \ + if ! go test -v ./...; then failed=1; fi; \ + else \ + for test_dir in $$(find infrastructure/modules -type d -name "tests"); do \ + if ls "$$test_dir"/*_test.go 1> /dev/null 2>&1; then \ + echo "=== Running tests in $$test_dir ==="; \ + cd "$$test_dir"; \ + go mod tidy; \ + if ! go test -v ./...; then failed=1; fi; \ + cd - > /dev/null; \ + fi; \ + done; \ + fi; \ + if [ $$failed -eq 1 ]; then exit 1; fi + +terraform-test: # Run all Terraform quality checks and tests @Testing + make terraform-lint + make terraform-security + make terraform-test-modules + +terraform-install-tools: # Install all terraform related tools @Installation + make _install-dependency name="terraform" + make _install-dependency name="tflint" + make _install-dependency name="tfsec" + make _install-dependency name="golang" + 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}) \ @@ -77,6 +167,12 @@ ${VERBOSE}.SILENT: \ terraform-fmt \ terraform-init \ terraform-install \ + terraform-install-tools \ + terraform-lint \ terraform-plan \ + terraform-security \ terraform-shellscript-lint \ + terraform-static-analysis \ + terraform-test \ + terraform-test-modules \ terraform-validate \ diff --git a/scripts/tests/lint.sh b/scripts/tests/lint.sh new file mode 100755 index 00000000..989364b8 --- /dev/null +++ b/scripts/tests/lint.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -euo pipefail + +# Delegate to the Terraform-specific lint target +make terraform-lint diff --git a/scripts/tests/security.sh b/scripts/tests/security.sh new file mode 100755 index 00000000..4d1c6b51 --- /dev/null +++ b/scripts/tests/security.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -euo pipefail + +# Delegate to the Terraform-specific security target +make terraform-security diff --git a/scripts/tests/test.mk b/scripts/tests/test.mk index aab47c62..24fd2fb5 100644 --- a/scripts/tests/test.mk +++ b/scripts/tests/test.mk @@ -14,55 +14,14 @@ test-unit: # Run your unit tests from scripts/test/unit @Testing test-lint: # Lint your code from scripts/test/lint @Testing make _test name="lint" -test-coverage: # Evaluate code coverage from scripts/test/coverage @Testing - make _test name="coverage" - -test-accessibility: # Run your accessibility tests from scripts/test/accessibility @Testing - make _test name="accessibility" - -test-contract: # Run your contract tests from scripts/test/contract @Testing - make _test name="contract" - -test-integration: # Run your integration tests from scripts/test/integration @Testing - make _test name="integration" - -test-load: # Run all your load tests @Testing - make \ - test-capacity \ - test-soak \ - test-response-time - # You may wish to add more here, depending on your app - -test-capacity: # Test what load level your app fails at from scripts/test/capacity @Testing - make _test name="capacity" - -test-soak: # Test that resources don't get exhausted over time from scripts/test/soak @Testing - make _test name="soak" - -test-response-time: # Test your API response times from scripts/test/response-time @Testing - make _test name="response-time" - test-security: # Run your security tests from scripts/test/security @Testing make _test name="security" -test-ui: # Run your UI tests from scripts/test/ui @Testing - make _test name="ui" - -test-ui-performance: # Run UI render tests from scripts/test/ui-performance @Testing - make _test name="ui-performance" - test: # Run all the test tasks @Testing make \ test-unit \ test-lint \ - test-coverage \ - test-contract \ - test-security \ - test-ui \ - test-ui-performance \ - test-integration \ - test-accessibility \ - test-load + test-security _test: set -e @@ -76,16 +35,6 @@ _test: ${VERBOSE}.SILENT: \ _test \ test \ - test-accessibility \ - test-capacity \ - test-contract \ - test-coverage \ - test-soak \ - test-integration \ test-lint \ - test-load \ - test-response-time \ test-security \ - test-ui \ - test-ui-performance \ test-unit \ diff --git a/scripts/tests/unit.sh b/scripts/tests/unit.sh index c589be5b..ee6f37e4 100755 --- a/scripts/tests/unit.sh +++ b/scripts/tests/unit.sh @@ -1,20 +1,5 @@ #!/bin/bash - set -euo pipefail -cd "$(git rev-parse --show-toplevel)" - -# This file is for you! Edit it to call your unit test suite. Note that the same -# file will be called if you run it locally as if you run it on CI. - -# Replace the following line with something like: -# -# rails test:unit -# python manage.py test -# npm run test -# -# or whatever is appropriate to your project. You should *only* run your fast -# tests from here. If you want to run other test suites, see the predefined -# tasks in scripts/test.mk. - -echo "Unit tests are not yet implemented. See scripts/tests/unit.sh for more." +# Delegate to the Terraform-specific unit test target +make terraform-test-modules