From 907accf2be4a5d1b2b1f2f7a590bbe4502394650 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 15 Apr 2026 16:00:47 -0700 Subject: [PATCH 1/3] workflows: enforce list resource implementation for new resources --- .github/workflows/enforce-list-resources.yaml | 46 +++++++++++++ scripts/enforce-list-for-new-resources.sh | 67 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 .github/workflows/enforce-list-resources.yaml create mode 100755 scripts/enforce-list-for-new-resources.sh diff --git a/.github/workflows/enforce-list-resources.yaml b/.github/workflows/enforce-list-resources.yaml new file mode 100644 index 000000000000..804b3054d1e2 --- /dev/null +++ b/.github/workflows/enforce-list-resources.yaml @@ -0,0 +1,46 @@ +--- +name: Enforce List Resource for New Resources + +permissions: + contents: read + pull-requests: read + +on: + pull_request: + types: ['opened', 'synchronize', 'labeled', 'unlabeled'] + paths: + - '.github/workflows/enforce-list-resources.yaml' + - 'scripts/enforce-list-for-new-resources.sh' + - 'internal/services/**/*_resource.go' + +concurrency: + group: 'enforce-list-${{ github.head_ref }}' + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.labels.*.name, 'allow-without-list') }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - run: bash scripts/enforce-list-for-new-resources.sh + - name: Guidance on failure + if: failure() + run: | + echo "::error::New resource(s) detected without a list implementation." + echo "" + echo "Every new resource must include a *_resource_list.go file and be" + echo "registered in the service's ListResources() method." + echo "" + echo "If this resource cannot support listing, add the 'allow-without-list'" + echo "label to this pull request." + save-artifacts-on-fail: + needs: check + if: ${{ failure() }} + uses: ./.github/workflows/save-artifacts.yaml + comment-on-fail: + needs: check + if: ${{ failure() }} + uses: ./.github/workflows/comment-failure.yaml diff --git a/scripts/enforce-list-for-new-resources.sh b/scripts/enforce-list-for-new-resources.sh new file mode 100755 index 000000000000..38743e4e9198 --- /dev/null +++ b/scripts/enforce-list-for-new-resources.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2014, 2025 +# SPDX-License-Identifier: MPL-2.0 + +# This script enforces that all NEW resources added to the provider include a +# corresponding list resource implementation (*_resource_list.go). +# +# A list resource can be skipped by applying the "allow-without-list" label to +# the pull request. + +echo "==> Checking that new resources include a list implementation..." + +# Only look at files newly added in this PR (not modified/renamed) +new_files=$(git diff --diff-filter=A origin/main --name-only --merge-base 2>/dev/null || true) + +if [ -z "$new_files" ]; then + echo " No new files detected. ✓" + exit 0 +fi + +missing=() + +while IFS= read -r f; do + # Only consider resource implementation files under internal/services/ + case "$f" in + internal/services/*_resource.go) ;; + *) continue ;; + esac + + # Skip test files, list files + case "$f" in + *_test.go) continue ;; + *_resource_list.go) continue ;; + esac + + # Derive the expected list file: foo_resource.go -> foo_resource_list.go + expected_list="${f%_resource.go}_resource_list.go" + + # Check if the list file exists in the repo (already present or added in this PR) + if [ ! -f "$expected_list" ]; then + missing+=("$f") + fi +done <<< "$new_files" + +if [ ${#missing[@]} -eq 0 ]; then + echo " All new resources have a corresponding list implementation. ✓" + exit 0 +fi + +echo "" +echo "ERROR: The following new resource(s) are missing a list implementation:" +echo "" +for f in "${missing[@]}"; do + expected_list="${f%_resource.go}_resource_list.go" + echo " • $f" + echo " expected: $expected_list" +done +echo "" +echo "Every new resource must include a list resource so that it is compatible" +echo "with Terraform's 'list' block (Terraform >= 1.14)." +echo "" +echo "To add a list implementation, create the *_resource_list.go file and" +echo "register it in the service's ListResources() method in registration.go." +echo "" +echo "If this resource genuinely cannot support listing (e.g. it has no List API)," +echo "apply the 'allow-without-list' label to this pull request to skip this check." +exit 1 From d66d51387392adc3ad42c88fd6e388a361218096 Mon Sep 17 00:00:00 2001 From: kt Date: Wed, 15 Apr 2026 16:02:37 -0700 Subject: [PATCH 2/3] add link to contrib docs --- .github/workflows/enforce-list-resources.yaml | 3 +++ scripts/enforce-list-for-new-resources.sh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/enforce-list-resources.yaml b/.github/workflows/enforce-list-resources.yaml index 804b3054d1e2..0493f0b9504a 100644 --- a/.github/workflows/enforce-list-resources.yaml +++ b/.github/workflows/enforce-list-resources.yaml @@ -34,6 +34,9 @@ jobs: echo "Every new resource must include a *_resource_list.go file and be" echo "registered in the service's ListResources() method." echo "" + echo "For detailed instructions, see the contributor guide:" + echo " https://github.com/hashicorp/terraform-provider-azurerm/blob/main/contributing/topics/guide-list-resource.md" + echo "" echo "If this resource cannot support listing, add the 'allow-without-list'" echo "label to this pull request." save-artifacts-on-fail: diff --git a/scripts/enforce-list-for-new-resources.sh b/scripts/enforce-list-for-new-resources.sh index 38743e4e9198..c4d6d6cdcc2f 100755 --- a/scripts/enforce-list-for-new-resources.sh +++ b/scripts/enforce-list-for-new-resources.sh @@ -62,6 +62,9 @@ echo "" echo "To add a list implementation, create the *_resource_list.go file and" echo "register it in the service's ListResources() method in registration.go." echo "" +echo "For detailed instructions, see the contributor guide:" +echo " https://github.com/hashicorp/terraform-provider-azurerm/blob/main/contributing/topics/guide-list-resource.md" +echo "" echo "If this resource genuinely cannot support listing (e.g. it has no List API)," echo "apply the 'allow-without-list' label to this pull request to skip this check." exit 1 From e07f339dcdf813e3f48019d014f32844188e118c Mon Sep 17 00:00:00 2001 From: kt Date: Fri, 24 Apr 2026 12:01:06 -0700 Subject: [PATCH 3/3] update docs --- .github/workflows/enforce-list-resources.yaml | 7 ++-- contributing/topics/guide-list-resource.md | 3 ++ contributing/topics/guide-new-resource.md | 41 +++++++++++++------ .../topics/guide-resource-identity.md | 3 ++ scripts/enforce-list-for-new-resources.sh | 7 ++-- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/.github/workflows/enforce-list-resources.yaml b/.github/workflows/enforce-list-resources.yaml index 0493f0b9504a..14d92f46b0ce 100644 --- a/.github/workflows/enforce-list-resources.yaml +++ b/.github/workflows/enforce-list-resources.yaml @@ -20,7 +20,7 @@ concurrency: jobs: check: runs-on: ubuntu-latest - if: ${{ !contains(github.event.pull_request.labels.*.name, 'allow-without-list') }} + if: ${{ !contains(github.event.pull_request.labels.*.name, 'allow-without-list') && !contains(github.event.pull_request.labels.*.name, 'list-not-supported') }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -37,8 +37,9 @@ jobs: echo "For detailed instructions, see the contributor guide:" echo " https://github.com/hashicorp/terraform-provider-azurerm/blob/main/contributing/topics/guide-list-resource.md" echo "" - echo "If this resource cannot support listing, add the 'allow-without-list'" - echo "label to this pull request." + echo "If this resource cannot support listing, please explain why in the PR" + echo "description and a maintainer will apply the 'allow-without-list' or" + echo "'list-not-supported' label to skip this check." save-artifacts-on-fail: needs: check if: ${{ failure() }} diff --git a/contributing/topics/guide-list-resource.md b/contributing/topics/guide-list-resource.md index 61330d7e91c2..bd280aa1fea8 100644 --- a/contributing/topics/guide-list-resource.md +++ b/contributing/topics/guide-list-resource.md @@ -2,6 +2,9 @@ This guide covers how to add a List Resource for an existing resource, using `azurerm_network_profile` as an example. For more information on Lists, see [Resources - List](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/list). +> [!IMPORTANT] +> **List Resource implementations are mandatory for all new resources.** A CI check (`enforce-list-resources`) will verify that every new resource file (`*_resource.go`) has a corresponding `*_resource_list.go` file. If your resource genuinely cannot support listing (e.g. no List API exists), please explain why in the PR description and a maintainer will apply the `allow-without-list` or `list-not-supported` label to skip the check. + ## Prerequisites Before adding a List Resource, the resource must have Resource Identity implemented. For more information on implementing Resource Identity see [Guide: Resource Identity](guide-resource-identity.md). diff --git a/contributing/topics/guide-new-resource.md b/contributing/topics/guide-new-resource.md index b333d318924c..bab9b4191704 100755 --- a/contributing/topics/guide-new-resource.md +++ b/contributing/topics/guide-new-resource.md @@ -17,11 +17,16 @@ This guide covers adding a new Typed Resource, which makes uses the Typed SDK wi 2. Add an SDK Client (if required). 3. Define the Resource ID. 4. Scaffold an empty/new Resource. -5. Register the new Resource. -6. Add Acceptance Test(s) for this Resource. -7. Run the Acceptance Test(s). -8. Add Documentation for this Resource. -9. Send the Pull Request. +5. Add Resource Identity (**required**). +6. Add a List Resource (**required**). +7. Register the new Resource. +8. Add Acceptance Test(s) for this Resource. +9. Run the Acceptance Test(s). +10. Add Documentation for this Resource. +11. Send the Pull Request. + +> [!IMPORTANT] +> **Resource Identity** and **List Resource** implementations are mandatory for all new resources. Pull requests adding new resources without these will not pass CI checks. If your resource genuinely cannot support one of these (e.g. no List API exists), please explain why in the PR description and a maintainer will apply the `allow-without-list` or `list-not-supported` label. We'll go through each of those steps in turn, presuming that we're creating a Resource for a Resource Group. @@ -686,11 +691,23 @@ func (r ExampleResource) CustomizeDiff() sdk.ResourceFunc { } ``` -### Step 4: Adding Resource Identity +### Step 5: Adding Resource Identity (Required) + +All new resources **must** add support for Resource Identity. Please reference the [Resource Identity](guide-resource-identity.md) guide for detailed instructions. + +> [!IMPORTANT] +> Resource Identity is a prerequisite for List Resources (Step 6). Ensure this is implemented before proceeding. + +### Step 6: Adding a List Resource (Required) + +All new resources **must** include a List Resource implementation. This enables support for Terraform's `list` block (Terraform >= 1.14), allowing users to query and enumerate existing instances of the resource. + +Please reference the [List Resource](guide-list-resource.md) guide for detailed instructions. -New resources should add support for Resource Identity, please reference the [Resource Identity](guide-resource-identity.md) guide. +> [!NOTE] +> A CI check (`enforce-list-resources`) will automatically verify that new resources include a `*_resource_list.go` file. If your resource cannot support listing, please explain why in the PR description and a maintainer will apply the `allow-without-list` or `list-not-supported` label to skip the check. -### Step 5: Register the new Resource +### Step 7: Register the new Resource Resources are registered within the `registration.go` within each Service Package - and should look something like this: @@ -774,7 +791,7 @@ output "id" { } ``` -### Step 6: Add Acceptance Test(s) for this Resource +### Step 8: Add Acceptance Test(s) for this Resource We're going to test the Resource that we've just built by dynamically provisioning a Resource Group using the new `azurerm_resource_group_example` Resource. @@ -931,7 +948,7 @@ There's a more detailed breakdown of how this works [in the Acceptance Testing r At this point we should be able to run this test. -### Step 7: Run the Acceptance Test(s) +### Step 9: Run the Acceptance Test(s) Detailed [instructions on Running the Tests can be found in this guide](running-the-tests.md) - when a Service Principal is configured you can run the test above using: @@ -964,7 +981,7 @@ PASS ok github.com/hashicorp/terraform-provider-azurerm/internal/services/resource 324.753s ``` -### Step 8: Add Documentation for this Resource +### Step 10: Add Documentation for this Resource At this point in time documentation for each Resource (and Data Source) is written manually, located within the `./website` folder - in this case this will be located at `./website/docs/d/resource_group_example.html.markdown`. @@ -1034,6 +1051,6 @@ terraform import azurerm_resource_group_example.example /subscriptions/00000000- ``` ```` -### Step 9: Send the Pull Request +### Step 11: Send the Pull Request See [our recommendations for opening a Pull Request](guide-opening-a-pr.md). diff --git a/contributing/topics/guide-resource-identity.md b/contributing/topics/guide-resource-identity.md index 5d6c571ae711..366ed1844ad0 100644 --- a/contributing/topics/guide-resource-identity.md +++ b/contributing/topics/guide-resource-identity.md @@ -2,6 +2,9 @@ This guide covers adding Resource Identity to a new or existing resource. For more information on Resource Identity, see [Resources - Identity](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/identity). +> [!IMPORTANT] +> **Resource Identity is mandatory for all new resources.** It is also a prerequisite for [List Resources](guide-list-resource.md), which are equally required. If your resource cannot support Resource Identity (see caveats below), please explain why in the PR description. + > The provider's Resource Identity generator does not yet support all identity types. `commonids.CompositeResourceID` and any custom resource IDs (i.e. not one provided by `commonids` or `go-azure-sdk/resource-manager`) are not supported. > **Caution:** Do not implement Resource Identity for resources with numbers in their ID segment names (e.g., `ServerGroupsv2Name`) until the `strcase.ToSnake()` issue is resolved. The current implementation splits on number boundaries, converting `ServerGroupsv2Name` to `server_groupsv_2_name` instead of the expected `server_groupsv2_name`. This causes test failures and incorrect identity schema field names. diff --git a/scripts/enforce-list-for-new-resources.sh b/scripts/enforce-list-for-new-resources.sh index c4d6d6cdcc2f..97350271faaf 100755 --- a/scripts/enforce-list-for-new-resources.sh +++ b/scripts/enforce-list-for-new-resources.sh @@ -5,8 +5,8 @@ # This script enforces that all NEW resources added to the provider include a # corresponding list resource implementation (*_resource_list.go). # -# A list resource can be skipped by applying the "allow-without-list" label to -# the pull request. +# This check can be skipped by applying the "allow-without-list" or +# "list-not-supported" label to the pull request. echo "==> Checking that new resources include a list implementation..." @@ -66,5 +66,6 @@ echo "For detailed instructions, see the contributor guide:" echo " https://github.com/hashicorp/terraform-provider-azurerm/blob/main/contributing/topics/guide-list-resource.md" echo "" echo "If this resource genuinely cannot support listing (e.g. it has no List API)," -echo "apply the 'allow-without-list' label to this pull request to skip this check." +echo "please explain why in the PR description or with a comment and a maintainer will apply the" +echo "'allow-without-list' or 'list-not-supported' label to skip this check." exit 1