From e6595a9e0a8028b4b711469eabf2b4d21b18b901 Mon Sep 17 00:00:00 2001 From: Matt White <16320656+matt-FFFFFF@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:23:09 +0100 Subject: [PATCH 1/5] feat: nee telemetry --- .github/workflows/module-version.yml | 67 ---------------------------- locals.telemetry.tf | 66 --------------------------- locals.version.tf.json | 5 --- main.telemetry.tf | 38 +++++++++------- terraform.tf | 8 +++- variables.tf | 1 + 6 files changed, 30 insertions(+), 155 deletions(-) delete mode 100644 .github/workflows/module-version.yml delete mode 100644 locals.telemetry.tf delete mode 100644 locals.version.tf.json diff --git a/.github/workflows/module-version.yml b/.github/workflows/module-version.yml deleted file mode 100644 index 1440e809..00000000 --- a/.github/workflows/module-version.yml +++ /dev/null @@ -1,67 +0,0 @@ ---- -name: module version - -on: - pull_request: - branches: - - main - paths: - - '**.tf' - - '**.tf.json' - workflow_dispatch: - -permissions: - contents: read - -jobs: - checkversion: - env: - SEMVER_REGEX: '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' - MODULE_VERSION_FILE: locals.version.tf.json - MODULE_JSON_QUERY: .locals.module_version - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - run: | - VER=$(curl --silent -L -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .name | sed s/^v//) - if echo "$VER" | grep -P -qv "$SEMVER_REGEX"; then - echo "Release version $VER is not a valid semantic version" - exit 1 - fi - echo LATEST_RELEASE="$VER" >> "$GITHUB_ENV" - name: Get latest release version - - - run: | - VER=$(jq -r '${{ env.MODULE_JSON_QUERY }}' < ${{ env.MODULE_VERSION_FILE }}) - if echo "$VER" | grep -P -qv "$SEMVER_REGEX"; then - echo "Module version $VER is not a valid semantic version" - exit 1 - fi - echo MODULE_VERSION="$VER" >> "$GITHUB_ENV" - name: Get module version - - - run: | - MODVERMAJOR=$(echo ${{ env.MODULE_VERSION }} | cut -d. -f1) - MODVERMINOR=$(echo ${{ env.MODULE_VERSION }} | cut -d. -f2) - MODVERPATCH=$(echo ${{ env.MODULE_VERSION }} | cut -d. -f3) - - RELVERMAJOR=$(echo ${{ env.LATEST_RELEASE }} | cut -d. -f1) - RELVERMINOR=$(echo ${{ env.LATEST_RELEASE }} | cut -d. -f2) - RELVERPATCH=$(echo ${{ env.LATEST_RELEASE }} | cut -d. -f3) - - if [ "$MODVERMAJOR" -lt "$RELVERMAJOR" ]; then - echo "Module version ${{ env.MODULE_VERSION }} is less than latest release ${{ env.LATEST_RELEASE }}" - exit 1 - fi - - if [ "$MODVERMAJOR" -eq "$RELVERMAJOR" ] && [ "$MODVERMINOR" -lt "$RELVERMINOR" ]; then - echo "Module version ${{ env.MODULE_VERSION }} is less than latest release ${{ env.LATEST_RELEASE }}" - exit 1 - fi - - if [ "$MODVERMAJOR" -eq "$RELVERMAJOR" ] && [ "$MODVERMINOR" -eq "$RELVERMINOR" ] && [ "$MODVERPATCH" -lt "$RELVERPATCH" ]; then - echo "Module version ${{ env.MODULE_VERSION }} is less than latest release ${{ env.LATEST_RELEASE }}" - exit 1 - fi - name: Check module version is greater than latest release diff --git a/locals.telemetry.tf b/locals.telemetry.tf deleted file mode 100644 index 2f579c7c..00000000 --- a/locals.telemetry.tf +++ /dev/null @@ -1,66 +0,0 @@ -# Telemetry is collected by creating an empty ARM deployment with a specific name -# If you want to disable telemetry, you can set the disable_telemetry variable to true -locals { - # root_module_puid is the UUID that identifies the root module in the telemetry ARM deployment. - telem_root_puid = "50a8a460-d517-4b11-b86c-6de447806b67" - - # telem_arm_subscription_template is the ARM template content for the telemetry deployment. - telem_arm_subscription_template = { - "$schema" = "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#" - contentVersion = "1.0.0.0" - parameters = {} - variables = {} - resources = [] - outputs = { - telemetry = { - type = "String" - value = "For more information, see https://aka.ms/lz-vending/tf/telemetry" - } - } - } - - # subscription telemetry bit fields - telem_root_subscription_alias_enabled = var.subscription_alias_enabled ? 1 : 0 - telem_root_subscription_management_group_association_enabled = var.subscription_management_group_association_enabled ? 2 : 0 - telem_root_subscription_tags_enabled = length(var.subscription_tags) > 0 ? 4 : 0 - - # virtualnetwork telemetry bit fields - telem_root_virtual_network_enabled = var.virtual_network_enabled ? 256 : 0 - telem_root_virtual_network_peering_enabled = anytrue([for k, v in var.virtual_networks : v.hub_peering_enabled]) ? 512 : 0 - telem_root_virtual_network_vwan_connection_enabled = anytrue([for k, v in var.virtual_networks : v.vwan_connection_enabled]) ? 1024 : 0 - telem_virtual_network_resource_lock_enabled = anytrue([for k, v in var.virtual_networks : v.resource_group_lock_enabled]) ? 2048 : 0 - telem_root_vwan_advanced_routing_enabled = anytrue([for k, v in var.virtual_networks : length(v.vwan_propagated_routetables_labels) > 0 || length(v.vwan_propagated_routetables_resource_ids) > 0 || v.vwan_associated_routetable_resource_id != ""]) ? 4096 : 0 - - # roleassignment telemetry bit fields - telem_root_role_assignment_enabled = var.role_assignment_enabled ? 65536 : 0 - - # Calculate the denary value of the bit fields - telem_root_bitfield_denary = ( - local.telem_root_subscription_alias_enabled + - local.telem_root_subscription_management_group_association_enabled + - local.telem_root_subscription_tags_enabled + - local.telem_root_virtual_network_enabled + - local.telem_root_virtual_network_peering_enabled + - local.telem_root_virtual_network_vwan_connection_enabled + - local.telem_virtual_network_resource_lock_enabled + - local.telem_root_vwan_advanced_routing_enabled + - local.telem_root_role_assignment_enabled - ) - - # Convert the denary value to hexadecimal and pad with zeros to the left to a length of 8 characters. - telem_root_bitfield_hex = format("%08x", local.telem_root_bitfield_denary) - - # This constructs the ARM deployment name that is used for the telemetry. - # We shouldn't ever hit the 64 character limit but use substr just in case - - telem_root_arm_deployment_name = substr( - format( - "pid-%s_%s_%s", - local.telem_root_puid, - local.module_version, - local.telem_root_bitfield_hex, - ), - 0, - 64 - ) -} diff --git a/locals.version.tf.json b/locals.version.tf.json deleted file mode 100644 index 2716b922..00000000 --- a/locals.version.tf.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "locals": { - "module_version": "4.1.3" - } -} diff --git a/main.telemetry.tf b/main.telemetry.tf index 6630dd9e..b8482b25 100644 --- a/main.telemetry.tf +++ b/main.telemetry.tf @@ -1,16 +1,24 @@ -# This is the root module telemetry deployment that is only created if telemetry is enabled. -# It is deployed to the created or supplied subscription -resource "azapi_resource" "telemetry_root" { - count = var.disable_telemetry ? 0 : 1 - parent_id = local.subscription_resource_id - name = local.telem_root_arm_deployment_name - type = "Microsoft.Resources/deployments@2021-04-01" - location = var.location - body = jsonencode({ - properties = { - mode = "Incremental" - template = local.telem_arm_subscription_template - } - }) - ignore_missing_property = true +data "azurerm_client_config" "telemetry" { + count = var.disable_telemetry ? 0 : 1 +} + +data "modtm_module_source" "telemetry" { + count = var.disable_telemetry ? 0 : 1 + module_path = path.module +} + +resource "random_uuid" "telemetry" { + count = var.disable_telemetry ? 0 : 1 +} + +resource "modtm_telemetry" "telemetry" { + count = var.disable_telemetry ? 0 : 1 + + tags = { + subscription_id = local.subscription_id + tenant_id = one(data.azurerm_client_config.telemetry).tenant_id + module_source = one(data.modtm_module_source.telemetry).module_source + module_version = one(data.modtm_module_source.telemetry).module_version + random_id = one(random_uuid.telemetry).result + } } diff --git a/terraform.tf b/terraform.tf index d2bdb883..0f45ac2e 100644 --- a/terraform.tf +++ b/terraform.tf @@ -1,9 +1,13 @@ terraform { - required_version = ">= 1.3.0" + required_version = "~> 1.3" required_providers { azapi = { source = "azure/azapi" - version = ">= 1.4.0" + version = "~> 1.4" + } + modtm = { + source = "azure/modtm" + version = "~> 0.3" } } } diff --git a/variables.tf b/variables.tf index 24eb58d0..da7c6205 100644 --- a/variables.tf +++ b/variables.tf @@ -29,4 +29,5 @@ module "lz_vending" { ``` DESCRIPTION default = false + nullable = false } From 678d436be2a57e9de633291fd022400ef01e3c8b Mon Sep 17 00:00:00 2001 From: Matt White <16320656+matt-FFFFFF@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:27:08 +0100 Subject: [PATCH 2/5] style: use pessimistic version constraint --- modules/budget/terraform.tf | 4 ++-- modules/resourcegroup/terraform.tf | 4 ++-- modules/resourceprovider/terraform.tf | 4 ++-- modules/roleassignment/terraform.tf | 4 ++-- modules/subscription/terraform.tf | 8 ++++---- modules/usermanagedidentity/terraform.tf | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/budget/terraform.tf b/modules/budget/terraform.tf index ff5db750..60d17dc7 100644 --- a/modules/budget/terraform.tf +++ b/modules/budget/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.3.0" + required_version = "~> 1.3" required_providers { azapi = { source = "azure/azapi" - version = ">= 1.11.0" + version = "~> 1.11" } } } diff --git a/modules/resourcegroup/terraform.tf b/modules/resourcegroup/terraform.tf index ff5db750..ca1472a5 100644 --- a/modules/resourcegroup/terraform.tf +++ b/modules/resourcegroup/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.3.0" + required_version = "~> 1.3" required_providers { azapi = { source = "azure/azapi" - version = ">= 1.11.0" + version = "~> 1.13" } } } diff --git a/modules/resourceprovider/terraform.tf b/modules/resourceprovider/terraform.tf index f0b843f5..eed9a02c 100644 --- a/modules/resourceprovider/terraform.tf +++ b/modules/resourceprovider/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.3.0" + required_version = "~> 1.3" required_providers { azapi = { source = "Azure/azapi" - version = ">= 1.11.0" + version = "~> 1.11" } } } diff --git a/modules/roleassignment/terraform.tf b/modules/roleassignment/terraform.tf index aad40749..fe670e86 100644 --- a/modules/roleassignment/terraform.tf +++ b/modules/roleassignment/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.3.0" + required_version = "~> 1.3" required_providers { azurerm = { source = "hashicorp/azurerm" - version = ">= 3.7.0" + version = "~> 3.7" } } } diff --git a/modules/subscription/terraform.tf b/modules/subscription/terraform.tf index ceb0bccb..3eb3935c 100644 --- a/modules/subscription/terraform.tf +++ b/modules/subscription/terraform.tf @@ -1,17 +1,17 @@ terraform { - required_version = ">= 1.4.0" + required_version = "~> 1.4" required_providers { azurerm = { source = "hashicorp/azurerm" - version = ">= 3.7.0" + version = "~> 3.7" } azapi = { source = "Azure/azapi" - version = ">= 1.11.0" + version = "~> 1.11" } time = { source = "hashicorp/time" - version = ">= 0.9.1" + version = "~> 0.9" } } } diff --git a/modules/usermanagedidentity/terraform.tf b/modules/usermanagedidentity/terraform.tf index ff5db750..60d17dc7 100644 --- a/modules/usermanagedidentity/terraform.tf +++ b/modules/usermanagedidentity/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.3.0" + required_version = "~> 1.3" required_providers { azapi = { source = "azure/azapi" - version = ">= 1.11.0" + version = "~> 1.11" } } } From 83a8d73e5e7244aaad1b035721ff2a7a5ca44c5a Mon Sep 17 00:00:00 2001 From: Matt White <16320656+matt-FFFFFF@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:29:43 +0100 Subject: [PATCH 3/5] docs: make docs --- README.md | 13 +++++++++---- modules/budget/README.md | 4 ++-- modules/resourcegroup/README.md | 4 ++-- modules/resourceprovider/README.md | 4 ++-- modules/roleassignment/README.md | 4 ++-- modules/subscription/README.md | 8 ++++---- modules/usermanagedidentity/README.md | 4 ++-- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 10114f7b..6122972c 100644 --- a/README.md +++ b/README.md @@ -136,9 +136,11 @@ module "lz_vending" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.3.0) +- [terraform](#requirement\_terraform) (~> 1.3) -- [azapi](#requirement\_azapi) (>= 1.4.0) +- [azapi](#requirement\_azapi) (~> 1.4) + +- [modtm](#requirement\_modtm) (~> 0.3) ## Modules @@ -1021,7 +1023,10 @@ Default: `{}` The following resources are used by this module: -- [azapi_resource.telemetry_root](https://registry.terraform.io/providers/azure/azapi/latest/docs/resources/resource) (resource) +- [modtm_telemetry.telemetry](https://registry.terraform.io/providers/azure/modtm/latest/docs/resources/telemetry) (resource) +- [random_uuid.telemetry](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/uuid) (resource) +- [azurerm_client_config.telemetry](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) (data source) +- [modtm_module_source.telemetry](https://registry.terraform.io/providers/azure/modtm/latest/docs/data-sources/module_source) (data source) ## Outputs @@ -1108,4 +1113,4 @@ Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. - + \ No newline at end of file diff --git a/modules/budget/README.md b/modules/budget/README.md index c89b99ad..0f35f4ed 100644 --- a/modules/budget/README.md +++ b/modules/budget/README.md @@ -34,9 +34,9 @@ module "budget" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.3.0) +- [terraform](#requirement\_terraform) (~> 1.3) -- [azapi](#requirement\_azapi) (>= 1.11.0) +- [azapi](#requirement\_azapi) (~> 1.11) ## Modules diff --git a/modules/resourcegroup/README.md b/modules/resourcegroup/README.md index a89fd163..b46dad8d 100644 --- a/modules/resourcegroup/README.md +++ b/modules/resourcegroup/README.md @@ -33,9 +33,9 @@ module "resourcegroups" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.3.0) +- [terraform](#requirement\_terraform) (~> 1.3) -- [azapi](#requirement\_azapi) (>= 1.11.0) +- [azapi](#requirement\_azapi) (~> 1.13) ## Modules diff --git a/modules/resourceprovider/README.md b/modules/resourceprovider/README.md index ae89c907..d945951f 100644 --- a/modules/resourceprovider/README.md +++ b/modules/resourceprovider/README.md @@ -28,9 +28,9 @@ module "resourceproviders" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.3.0) +- [terraform](#requirement\_terraform) (~> 1.3) -- [azapi](#requirement\_azapi) (>= 1.11.0) +- [azapi](#requirement\_azapi) (~> 1.11) ## Modules diff --git a/modules/roleassignment/README.md b/modules/roleassignment/README.md index 2ea8a52c..501f0441 100644 --- a/modules/roleassignment/README.md +++ b/modules/roleassignment/README.md @@ -29,9 +29,9 @@ module "roleassignment" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.3.0) +- [terraform](#requirement\_terraform) (~> 1.3) -- [azurerm](#requirement\_azurerm) (>= 3.7.0) +- [azurerm](#requirement\_azurerm) (~> 3.7) ## Modules diff --git a/modules/subscription/README.md b/modules/subscription/README.md index ef2d5c20..08be940d 100644 --- a/modules/subscription/README.md +++ b/modules/subscription/README.md @@ -31,13 +31,13 @@ module "subscription" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.4.0) +- [terraform](#requirement\_terraform) (~> 1.4) -- [azapi](#requirement\_azapi) (>= 1.11.0) +- [azapi](#requirement\_azapi) (~> 1.11) -- [azurerm](#requirement\_azurerm) (>= 3.7.0) +- [azurerm](#requirement\_azurerm) (~> 3.7) -- [time](#requirement\_time) (>= 0.9.1) +- [time](#requirement\_time) (~> 0.9) ## Modules diff --git a/modules/usermanagedidentity/README.md b/modules/usermanagedidentity/README.md index 80781a96..7ef6e4c1 100644 --- a/modules/usermanagedidentity/README.md +++ b/modules/usermanagedidentity/README.md @@ -67,9 +67,9 @@ module "umi" { The following requirements are needed by this module: -- [terraform](#requirement\_terraform) (>= 1.3.0) +- [terraform](#requirement\_terraform) (~> 1.3) -- [azapi](#requirement\_azapi) (>= 1.11.0) +- [azapi](#requirement\_azapi) (~> 1.11) ## Modules From 514f44aa9ae4c760dc8fae7f21a1a4370b26edff Mon Sep 17 00:00:00 2001 From: Matt White <16320656+matt-FFFFFF@users.noreply.github.com> Date: Thu, 11 Jul 2024 09:33:21 +0100 Subject: [PATCH 4/5] fix: add random provider --- README.md | 2 ++ terraform.tf | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 6122972c..591b398f 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ The following requirements are needed by this module: - [modtm](#requirement\_modtm) (~> 0.3) +- [random](#requirement\_random) (~> 3.6) + ## Modules The following Modules are called: diff --git a/terraform.tf b/terraform.tf index 0f45ac2e..cd0b4114 100644 --- a/terraform.tf +++ b/terraform.tf @@ -9,5 +9,9 @@ terraform { source = "azure/modtm" version = "~> 0.3" } + random = { + source = "hashicorp/random" + version = "~> 3.6" + } } } From 117060cab77041507c1689c3985282e27f6adc6e Mon Sep 17 00:00:00 2001 From: Matt White <16320656+matt-FFFFFF@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:47:46 +0100 Subject: [PATCH 5/5] feat: begin migration to terraform test --- .github/workflows/go-test-unit.yml | 21 ++------ Makefile | 3 +- main.telemetry.tf | 2 +- modules/budget/main.tf | 6 +-- modules/budget/terraform.tf | 4 +- modules/resourcegroup/main.tf | 2 +- modules/resourcegroup/terraform.tf | 4 +- modules/resourceprovider/terraform.tf | 4 +- modules/roleassignment/terraform.tf | 4 +- modules/subscription/main.tf | 4 +- modules/subscription/terraform.tf | 6 +-- modules/usermanagedidentity/main.tf | 26 +++++----- modules/usermanagedidentity/terraform.tf | 4 +- modules/virtualnetwork/main.tf | 52 ++++--------------- modules/virtualnetwork/terraform.tf | 2 +- modules/virtualnetwork/tests/valid.tftest.hcl | 44 ++++++++++++++++ terraform.tf | 4 +- tests/go.mod | 2 +- tests/virtualnetwork/virtualnetwork_test.go | 39 -------------- 19 files changed, 96 insertions(+), 137 deletions(-) create mode 100644 modules/virtualnetwork/tests/valid.tftest.hcl diff --git a/.github/workflows/go-test-unit.yml b/.github/workflows/go-test-unit.yml index b735ae6a..3e54a355 100644 --- a/.github/workflows/go-test-unit.yml +++ b/.github/workflows/go-test-unit.yml @@ -56,9 +56,9 @@ jobs: strategy: fail-fast: false matrix: - azapi_version: ['latest', '1.11.0'] - azurerm_version: ['latest', '3.7.0'] - terraform_version: ['latest', '1.4.0'] + azapi_version: ['latest', '1.14.0'] + azurerm_version: ['latest', '3.107.0'] + terraform_version: ['latest', '1.6.0'] steps: - name: Checkout repository uses: actions/checkout@v4 @@ -72,20 +72,7 @@ jobs: terraform_version: ${{ matrix.terraform_version }} terraform_wrapper: false - - name: Setup go - uses: actions/setup-go@v5 - with: - go-version-file: tests/go.mod - cache-dependency-path: tests/go.sum - - - name: Set GOMAXPROCS to 2 * number of cores - run: | - CORES="$(grep -Pc '^processor\t' /proc/cpuinfo)" - ((DOUBLE=CORES*2)) - echo "Setting GOMAXPROCS to $DOUBLE" - echo "GOMAXPROCS=$DOUBLE" >> "$GITHUB_ENV" - - - name: Go test + - name: Terraform test run: make TESTARGS='-v' TESTFILTER='${{ github.event.inputs.test_filter }}' test env: ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} diff --git a/Makefile b/Makefile index 0f906af3..e969a76e 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,8 @@ lint: cd tests && golangci-lint run test: fmtcheck - cd tests && go test $(TEST) $(TESTARGS) -run ^Test$(TESTFILTER) -timeout=$(TESTTIMEOUT) + terraform test + if [ -d "modules" ]; then for dir in modules/*; do if [ -d "$$dir" ]; then echo "==> Testing $$dir"; cd $$dir; terraform test; echo; cd - >/dev/null; fi; done; fi testdeploy: fmtcheck cd tests && TERRATEST_DEPLOY=1 go test $(TEST) $(TESTARGS) -run ^TestDeploy$(TESTFILTER) -timeout $(TESTTIMEOUT) diff --git a/main.telemetry.tf b/main.telemetry.tf index b8482b25..e57bc039 100644 --- a/main.telemetry.tf +++ b/main.telemetry.tf @@ -1,4 +1,4 @@ -data "azurerm_client_config" "telemetry" { +data "azapi_client_config" "telemetry" { count = var.disable_telemetry ? 0 : 1 } diff --git a/modules/budget/main.tf b/modules/budget/main.tf index 864fa335..c75e6717 100644 --- a/modules/budget/main.tf +++ b/modules/budget/main.tf @@ -2,7 +2,7 @@ resource "azapi_resource" "budget" { type = "Microsoft.Consumption/budgets@2021-10-01" name = var.budget_name parent_id = var.budget_scope - body = jsonencode({ + body = { properties = { amount = var.budget_amount category = "Cost" @@ -13,7 +13,5 @@ resource "azapi_resource" "budget" { startDate = var.budget_time_period.start_date } } - }) + } } - - diff --git a/modules/budget/terraform.tf b/modules/budget/terraform.tf index 60d17dc7..39685a89 100644 --- a/modules/budget/terraform.tf +++ b/modules/budget/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = "~> 1.3" + required_version = "~> 1.6" required_providers { azapi = { source = "azure/azapi" - version = "~> 1.11" + version = "~> 1.14" } } } diff --git a/modules/resourcegroup/main.tf b/modules/resourcegroup/main.tf index 4589b9bd..6ed72798 100644 --- a/modules/resourcegroup/main.tf +++ b/modules/resourcegroup/main.tf @@ -3,6 +3,6 @@ resource "azapi_resource" "rg" { type = "Microsoft.Resources/resourceGroups@2021-04-01" name = var.resource_group_name location = var.location - body = jsonencode({}) + body = {} tags = var.tags } diff --git a/modules/resourcegroup/terraform.tf b/modules/resourcegroup/terraform.tf index ca1472a5..39685a89 100644 --- a/modules/resourcegroup/terraform.tf +++ b/modules/resourcegroup/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = "~> 1.3" + required_version = "~> 1.6" required_providers { azapi = { source = "azure/azapi" - version = "~> 1.13" + version = "~> 1.14" } } } diff --git a/modules/resourceprovider/terraform.tf b/modules/resourceprovider/terraform.tf index eed9a02c..f8023117 100644 --- a/modules/resourceprovider/terraform.tf +++ b/modules/resourceprovider/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = "~> 1.3" + required_version = "~> 1.6" required_providers { azapi = { source = "Azure/azapi" - version = "~> 1.11" + version = "~> 1.14" } } } diff --git a/modules/roleassignment/terraform.tf b/modules/roleassignment/terraform.tf index fe670e86..01f91545 100644 --- a/modules/roleassignment/terraform.tf +++ b/modules/roleassignment/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = "~> 1.3" + required_version = "~> 1.6" required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.7" + version = "~> 3.107" } } } diff --git a/modules/subscription/main.tf b/modules/subscription/main.tf index 27dc4d41..14d60d99 100644 --- a/modules/subscription/main.tf +++ b/modules/subscription/main.tf @@ -23,7 +23,7 @@ resource "azapi_resource" "subscription" { name = var.subscription_alias_name parent_id = "/" - body = jsonencode({ + body = { properties = { displayName = var.subscription_display_name workload = var.subscription_workload @@ -33,7 +33,7 @@ resource "azapi_resource" "subscription" { tags = var.subscription_tags } } - }) + } response_export_values = ["properties.subscriptionId"] lifecycle { ignore_changes = [ diff --git a/modules/subscription/terraform.tf b/modules/subscription/terraform.tf index 3eb3935c..dc86a07c 100644 --- a/modules/subscription/terraform.tf +++ b/modules/subscription/terraform.tf @@ -1,13 +1,13 @@ terraform { - required_version = "~> 1.4" + required_version = "~> 1.6" required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.7" + version = "~> 3.107" } azapi = { source = "Azure/azapi" - version = "~> 1.11" + version = "~> 1.14" } time = { source = "hashicorp/time" diff --git a/modules/usermanagedidentity/main.tf b/modules/usermanagedidentity/main.tf index 13aed69b..f4ad91b4 100644 --- a/modules/usermanagedidentity/main.tf +++ b/modules/usermanagedidentity/main.tf @@ -12,11 +12,11 @@ resource "azapi_resource" "rg_lock" { type = "Microsoft.Authorization/locks@2020-05-01" parent_id = one(azapi_resource.rg).id name = coalesce(var.resource_group_lock_name, "lock-${one(azapi_resource.rg).name}") - body = jsonencode({ + body = { properties = { level = "CanNotDelete" } - }) + } depends_on = [ azapi_resource.rg, azapi_resource.umi, @@ -33,7 +33,7 @@ resource "azapi_resource" "umi" { type = "Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31" name = var.name parent_id = var.resource_group_creation_enabled ? one(azapi_resource.rg).id : "/subscriptions/${var.subscription_id}/resourceGroups/${var.resource_group_name}" - body = jsonencode({}) + body = {} location = var.location tags = var.tags response_export_values = [ @@ -49,13 +49,13 @@ resource "azapi_resource" "umi_federated_credential_github_branch" { name = coalesce(each.value.name, "github-${each.value.organization}-${each.value.repository}-branch-${each.value.value}") parent_id = azapi_resource.umi.id locks = [azapi_resource.umi.id] # Concurrent Federated Identity Credentials writes under the same managed identity are not supported - body = jsonencode({ + body = { properties = { audiences = ["api://AzureADTokenExchange"] issuer = "https://token.actions.githubusercontent.com" subject = "repo:${each.value.organization}/${each.value.repository}:ref:refs/heads/${each.value.value}" } - }) + } } resource "azapi_resource" "umi_federated_credential_github_tag" { @@ -79,13 +79,13 @@ resource "azapi_resource" "umi_federated_credential_github_environment" { name = coalesce(each.value.name, "github-${each.value.organization}-${each.value.repository}-environment-${each.value.value}") parent_id = azapi_resource.umi.id locks = [azapi_resource.umi.id] # Concurrent Federated Identity Credentials writes under the same managed identity are not supported - body = jsonencode({ + body = { properties = { audiences = ["api://AzureADTokenExchange"] issuer = "https://token.actions.githubusercontent.com" subject = "repo:${each.value.organization}/${each.value.repository}:environment:${each.value.value}" } - }) + } } resource "azapi_resource" "umi_federated_credential_github_pull_request" { @@ -94,13 +94,13 @@ resource "azapi_resource" "umi_federated_credential_github_pull_request" { name = coalesce(each.value.name, "github-${each.value.organization}-${each.value.repository}-pull-request") parent_id = azapi_resource.umi.id locks = [azapi_resource.umi.id] # Concurrent Federated Identity Credentials writes under the same managed identity are not supported - body = jsonencode({ + body = { properties = { audiences = ["api://AzureADTokenExchange"] issuer = "https://token.actions.githubusercontent.com" subject = "repo:${each.value.organization}/${each.value.repository}:pull_request" } - }) + } } resource "azapi_resource" "umi_federated_credential_terraform_cloud" { @@ -109,13 +109,13 @@ resource "azapi_resource" "umi_federated_credential_terraform_cloud" { name = coalesce(each.value.name, "terraformcloud-${each.value.organization}-${each.value.project}-${each.value.workspace}-${each.value.run_phase}") parent_id = azapi_resource.umi.id locks = [azapi_resource.umi.id] # Concurrent Federated Identity Credentials writes under the same managed identity are not supported - body = jsonencode({ + body = { properties = { audiences = ["api://AzureADTokenExchange"] issuer = "https://app.terraform.io" subject = "organization:${each.value.organization}:project:${each.value.project}:workspace:${each.value.workspace}:run_phase:${each.value.run_phase}" } - }) + } } resource "azapi_resource" "umi_federated_credential_advanced" { @@ -124,11 +124,11 @@ resource "azapi_resource" "umi_federated_credential_advanced" { name = each.value.name parent_id = azapi_resource.umi.id locks = [azapi_resource.umi.id] # Concurrent Federated Identity Credentials writes under the same managed identity are not supported - body = jsonencode({ + body = { properties = { audiences = each.value.audiences issuer = each.value.issuer_url subject = each.value.subject_identifier } - }) + } } diff --git a/modules/usermanagedidentity/terraform.tf b/modules/usermanagedidentity/terraform.tf index 60d17dc7..39685a89 100644 --- a/modules/usermanagedidentity/terraform.tf +++ b/modules/usermanagedidentity/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = "~> 1.3" + required_version = "~> 1.6" required_providers { azapi = { source = "azure/azapi" - version = "~> 1.11" + version = "~> 1.14" } } } diff --git a/modules/virtualnetwork/main.tf b/modules/virtualnetwork/main.tf index 0c07bfc7..72cf87de 100644 --- a/modules/virtualnetwork/main.tf +++ b/modules/virtualnetwork/main.tf @@ -16,14 +16,13 @@ resource "azapi_resource" "rg_lock" { type = "Microsoft.Authorization/locks@2017-04-01" parent_id = azapi_resource.rg[each.key].id name = coalesce(each.value.lock_name, substr("lock-${each.key}", 0, 90)) - body = jsonencode({ + body = { properties = { level = "CanNotDelete" } - }) + } depends_on = [ azapi_resource.vnet, - azapi_update_resource.vnet, azapi_resource.peering_hub_outbound, azapi_resource.peering_hub_inbound, azapi_resource.peering_mesh, @@ -32,15 +31,14 @@ resource "azapi_resource" "rg_lock" { } # azapi_resource.vnet are the virtual networks that will be created -# lifecycle ignore changes to the body to prevent subnets being deleted -# see #45 for more information resource "azapi_resource" "vnet" { for_each = var.virtual_networks parent_id = "${local.subscription_resource_id}/resourceGroups/${each.value.resource_group_name}" - type = "Microsoft.Network/virtualNetworks@2021-08-01" + type = "Microsoft.Network/virtualNetworks@2024-01-01" name = each.value.name location = coalesce(each.value.location, var.location) - body = jsonencode({ + tags = each.value.tags + body = { properties = merge( { addressSpace = { @@ -49,6 +47,7 @@ resource "azapi_resource" "vnet" { dhcpOptions = { dnsServers = each.value.dns_servers } + subnets = null }, each.value.ddos_protection_enabled ? { ddosProtectionPlan = { @@ -57,44 +56,14 @@ resource "azapi_resource" "vnet" { enableDdosProtection = true } : null ) - }) - tags = each.value.tags - lifecycle { - ignore_changes = [body, tags] } + response_export_values = ["*"] + depends_on = [ azapi_resource.rg, ] } -# azapi_update_resource.vnet are the virtual networks that will be created -# This is a workaround for #45 to allow updates to the virtual network -# without deleting the subnets created elsewhere -resource "azapi_update_resource" "vnet" { - for_each = var.virtual_networks - resource_id = azapi_resource.vnet[each.key].id - type = "Microsoft.Network/virtualNetworks@2021-08-01" - body = jsonencode({ - properties = merge( - { - addressSpace = { - addressPrefixes = each.value.address_space - } - dhcpOptions = { - dnsServers = each.value.dns_servers - } - }, - each.value.ddos_protection_enabled ? { - ddosProtectionPlan = { - id = each.value.ddos_protection_plan_id - } - enableDdosProtection = true - } : null - ) - tags = each.value.tags - }) -} - # azapi_resource.peering_hub_outbound creates one-way peering from the spoke to the supplied hub virtual network. # They are not created if the hub virtual network is an empty string. resource "azapi_resource" "peering_hub_outbound" { @@ -161,7 +130,7 @@ resource "azapi_resource" "vhubconnection" { type = "Microsoft.Network/virtualHubs/hubVirtualNetworkConnections@2022-07-01" parent_id = each.value.vwan_hub_resource_id name = coalesce(each.value.vwan_connection_name, "vhc-${uuidv5("url", azapi_resource.vnet[each.key].id)}") - body = jsonencode({ + body = { properties = merge({ enableInternetSecurity = each.value.vwan_security_configuration.secure_internet_traffic remoteVirtualNetwork = { @@ -180,6 +149,5 @@ resource "azapi_resource" "vhubconnection" { } } }) - }) - ignore_body_changes = each.value.vwan_security_configuration.routing_intent_enabled ? ["properties.routingConfiguration"] : [] + } } diff --git a/modules/virtualnetwork/terraform.tf b/modules/virtualnetwork/terraform.tf index ff5db750..2aac0e59 100644 --- a/modules/virtualnetwork/terraform.tf +++ b/modules/virtualnetwork/terraform.tf @@ -3,7 +3,7 @@ terraform { required_providers { azapi = { source = "azure/azapi" - version = ">= 1.11.0" + version = "~> 1.14" } } } diff --git a/modules/virtualnetwork/tests/valid.tftest.hcl b/modules/virtualnetwork/tests/valid.tftest.hcl new file mode 100644 index 00000000..08a6d30c --- /dev/null +++ b/modules/virtualnetwork/tests/valid.tftest.hcl @@ -0,0 +1,44 @@ +mock_provider "azapi" {} + +variables { + subscription_id = "00000000-0000-0000-0000-000000000000" + virtual_networks = { + primary = { + name = "primary-vnet", + address_space = ["192.168.0.0/24"], + location = "westeurope", + resource_group_name = "primary-rg", + } + secondary = { + name = "secondary-vnet", + address_space = ["192.168.1.0/24"], + location = "northeurope", + resource_group_name = "secondary-rg", + } + } +} + +run "valid" { + command = plan + + assert { + condition = alltrue([for k, v in var.virtual_networks : azapi_resource.vnet[k].name == v.name]) + error_message = "Virtual network names do not match input" + } + assert { + condition = alltrue([for k, v in var.virtual_networks : azapi_resource.vnet[k].location == v.location]) + error_message = "Virtual network locations do not match input" + } + assert { + condition = alltrue([for k, v in var.virtual_networks : azapi_resource.vnet[k].body.properties.addressSpace.addressPrefixes == v.address_space]) + error_message = "Virtual network locations do not match input" + } + assert { + condition = alltrue([for k, v in var.virtual_networks : azapi_resource.rg["${k}-rg"].name == v.resource_group_name]) + error_message = "Resource group names do not match input" + } + assert { + condition = alltrue([for k, v in var.virtual_networks : azapi_resource.rg["${k}-rg"].location == v.location]) + error_message = "Resource group locations do not match input" + } +} diff --git a/terraform.tf b/terraform.tf index cd0b4114..bc627772 100644 --- a/terraform.tf +++ b/terraform.tf @@ -1,9 +1,9 @@ terraform { - required_version = "~> 1.3" + required_version = "~> 1.6" required_providers { azapi = { source = "azure/azapi" - version = "~> 1.4" + version = "~> 1.14" } modtm = { source = "azure/modtm" diff --git a/tests/go.mod b/tests/go.mod index f0e7cccd..ca321f5d 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -1,6 +1,6 @@ module github.com/Azure/terraform-azurerm-lz-vending/tests -go 1.21.4 +go 1.22.5 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 diff --git a/tests/virtualnetwork/virtualnetwork_test.go b/tests/virtualnetwork/virtualnetwork_test.go index 1cc89b13..fb844218 100644 --- a/tests/virtualnetwork/virtualnetwork_test.go +++ b/tests/virtualnetwork/virtualnetwork_test.go @@ -16,45 +16,6 @@ const ( moduleDir = "../../modules/virtualnetwork" ) -// TestVirtualNetworkCreateValid tests the creation of a plan that -// creates two virtual networks in the specified resource groups. -func TestVirtualNetworkCreateValid(t *testing.T) { - t.Parallel() - - v := getMockInputVariables() - test, err := setuptest.Dirs(moduleDir, "").WithVars(v).InitPlanShowWithPrepFunc(t, utils.AzureRmAndRequiredProviders) - require.NoError(t, err) - defer test.Cleanup() - - resources := []string{ - "azapi_resource.rg[\"primary-rg\"]", - "azapi_resource.rg[\"secondary-rg\"]", - "azapi_resource.vnet[\"primary\"]", - "azapi_resource.vnet[\"secondary\"]", - "azapi_update_resource.vnet[\"primary\"]", - "azapi_update_resource.vnet[\"secondary\"]", - "azapi_resource.rg_lock[\"primary-rg\"]", - "azapi_resource.rg_lock[\"secondary-rg\"]", - } - check.InPlan(test.PlanStruct).NumberOfResourcesEquals(len(resources)).ErrorIsNilFatal(t) - - for _, r := range resources { - check.InPlan(test.PlanStruct).That(r).Exists().ErrorIsNil(t) - } - - // Loop through each virtual network and check the values - vns := v["virtual_networks"].(map[string]map[string]any) - for k, v := range vns { - rgres := fmt.Sprintf("azapi_resource.rg[\"%s-rg\"]", k) - vnetres := fmt.Sprintf("azapi_resource.vnet[\"%s\"]", k) - check.InPlan(test.PlanStruct).That(rgres).Key("name").HasValue(v["resource_group_name"]).ErrorIsNil(t) - check.InPlan(test.PlanStruct).That(rgres).Key("location").HasValue(v["location"]).ErrorIsNil(t) - check.InPlan(test.PlanStruct).That(vnetres).Key("name").HasValue(v["name"]).ErrorIsNil(t) - check.InPlan(test.PlanStruct).That(vnetres).Key("location").HasValue(v["location"]).ErrorIsNil(t) - check.InPlan(test.PlanStruct).That(vnetres).Key("body").Query("properties.addressSpace.addressPrefixes").HasValue(v["address_space"]).ErrorIsNil(t) - } -} - // TestVirtualNetworkCreateValid tests the creation of a plan that // creates two virtual networks in the specified resource groups with custom DNS servers. func TestVirtualNetworkCreateValidWithCustomDns(t *testing.T) {