From 63b464ff8e373c99f1407592d0aa54cbfbcc6e14 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 10 Mar 2026 14:12:35 -0400 Subject: [PATCH 1/5] r/aws_iam_user: add resource identity support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This resource uses a parameterized identity with a single attribute, `name`. ```console % make t K=iam T=TestAccIAMUser_Identity make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... make: Running acceptance tests on branch: 🌿 tmp1 🌿... TF_ACC=1 go1.25.8 test ./internal/service/iam/... -v -count 1 -parallel 20 -run='TestAccIAMUser_Identity' -timeout 360m -vet=off 2026/03/10 14:11:46 Creating Terraform AWS Provider (SDKv2-style)... 2026/03/10 14:11:46 Initializing Terraform AWS Provider (SDKv2-style)... --- PASS: TestAccIAMUser_Identity_basic (29.64s) --- PASS: TestAccIAMUser_Identity_ExistingResource_basic (50.50s) --- PASS: TestAccIAMUser_Identity_ExistingResource_noRefreshNoChange (54.20s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 61.441s ``` --- internal/service/iam/service_package_gen.go | 6 +- .../iam/testdata/User/basic/main_gen.tf | 12 + .../testdata/User/basic_v6.35.1/main_gen.tf | 22 ++ internal/service/iam/user.go | 11 +- .../service/iam/user_identity_gen_test.go | 222 ++++++++++++++++++ website/docs/r/iam_user.html.markdown | 32 ++- 6 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 internal/service/iam/testdata/User/basic/main_gen.tf create mode 100644 internal/service/iam/testdata/User/basic_v6.35.1/main_gen.tf create mode 100644 internal/service/iam/user_identity_gen_test.go diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index 5fadf6fb6ed1..d00c0193e49d 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -406,7 +406,11 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*inttypes.ServicePa IdentifierAttribute: names.AttrID, ResourceType: "User", }), - Region: unique.Make(inttypes.ResourceRegionDisabled()), + Region: unique.Make(inttypes.ResourceRegionDisabled()), + Identity: inttypes.GlobalSingleParameterIdentity(names.AttrName), + Import: inttypes.SDKv2Import{ + WrappedImport: true, + }, }, { Factory: resourceUserGroupMembership, diff --git a/internal/service/iam/testdata/User/basic/main_gen.tf b/internal/service/iam/testdata/User/basic/main_gen.tf new file mode 100644 index 000000000000..8ebab2776d8e --- /dev/null +++ b/internal/service/iam/testdata/User/basic/main_gen.tf @@ -0,0 +1,12 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_user" "test" { + name = var.rName +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} diff --git a/internal/service/iam/testdata/User/basic_v6.35.1/main_gen.tf b/internal/service/iam/testdata/User/basic_v6.35.1/main_gen.tf new file mode 100644 index 000000000000..38e8a0af104f --- /dev/null +++ b/internal/service/iam/testdata/User/basic_v6.35.1/main_gen.tf @@ -0,0 +1,22 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_user" "test" { + name = var.rName +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "6.35.1" + } + } +} + +provider "aws" {} diff --git a/internal/service/iam/user.go b/internal/service/iam/user.go index b9d4b2c0a8c6..32558394e714 100644 --- a/internal/service/iam/user.go +++ b/internal/service/iam/user.go @@ -29,8 +29,13 @@ import ( ) // @SDKResource("aws_iam_user", name="User") +// @IdentityAttribute("name") +// @MutableIdentity // @Tags(identifierAttribute="id", resourceType="User") -// @Testing(existsType="github.com/aws/aws-sdk-go-v2/service/iam/types;types.User", importIgnore="force_destroy") +// @Testing(existsType="github.com/aws/aws-sdk-go-v2/service/iam/types;types.User") +// @Testing(importIgnore="force_destroy") +// @Testing(plannableImportAction="NoOp") +// @Testing(preIdentityVersion="v6.35.1") func resourceUser() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceUserCreate, @@ -38,10 +43,6 @@ func resourceUser() *schema.Resource { UpdateWithoutTimeout: resourceUserUpdate, DeleteWithoutTimeout: resourceUserDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ names.AttrARN: { Type: schema.TypeString, diff --git a/internal/service/iam/user_identity_gen_test.go b/internal/service/iam/user_identity_gen_test.go new file mode 100644 index 000000000000..827cf61d84e9 --- /dev/null +++ b/internal/service/iam/user_identity_gen_test.go @@ -0,0 +1,222 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +// Code generated by internal/generate/identitytests/main.go; DO NOT EDIT. + +package iam_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfknownvalue "github.com/hashicorp/terraform-provider-aws/internal/acctest/knownvalue" + tfstatecheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/statecheck" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccIAMUser_Identity_basic(t *testing.T) { + ctx := acctest.Context(t) + + var v types.User + resourceName := "aws_iam_user.test" + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + + acctest.ParallelTest(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + CheckDestroy: testAccCheckUserDestroy(ctx, t), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + // Step 1: Setup + { + ConfigDirectory: config.StaticDirectory("testdata/User/basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckUserExists(ctx, t, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrName: knownvalue.NotNull(), + }), + statecheck.ExpectIdentityValueMatchesState(resourceName, tfjsonpath.New(names.AttrName)), + }, + }, + + // Step 2: Import command + { + ConfigDirectory: config.StaticDirectory("testdata/User/basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + ImportStateKind: resource.ImportCommandWithID, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + names.AttrForceDestroy, + }, + }, + + // Step 3: Import block with Import ID + { + ConfigDirectory: config.StaticDirectory("testdata/User/basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithID, + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrName), knownvalue.NotNull()), + }, + }, + }, + + // Step 4: Import block with Resource Identity + { + ConfigDirectory: config.StaticDirectory("testdata/User/basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + ResourceName: resourceName, + ImportState: true, + ImportStateKind: resource.ImportBlockWithResourceIdentity, + ImportPlanChecks: resource.ImportPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrName), knownvalue.NotNull()), + }, + }, + }, + }, + }) +} + +// Resource Identity was added after v6.35.1 +func TestAccIAMUser_Identity_ExistingResource_basic(t *testing.T) { + ctx := acctest.Context(t) + + var v types.User + resourceName := "aws_iam_user.test" + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + + acctest.ParallelTest(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + CheckDestroy: testAccCheckUserDestroy(ctx, t), + Steps: []resource.TestStep{ + // Step 1: Create pre-Identity + { + ConfigDirectory: config.StaticDirectory("testdata/User/basic_v6.35.1/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckUserExists(ctx, t, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectNoIdentity(resourceName), + }, + }, + + // Step 2: Current version + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/User/basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectIdentity(resourceName, map[string]knownvalue.Check{ + names.AttrAccountID: tfknownvalue.AccountID(), + names.AttrName: knownvalue.NotNull(), + }), + statecheck.ExpectIdentityValueMatchesState(resourceName, tfjsonpath.New(names.AttrName)), + }, + }, + }, + }) +} + +// Resource Identity was added after v6.35.1 +func TestAccIAMUser_Identity_ExistingResource_noRefreshNoChange(t *testing.T) { + ctx := acctest.Context(t) + + var v types.User + resourceName := "aws_iam_user.test" + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + + acctest.ParallelTest(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_12_0), + }, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + CheckDestroy: testAccCheckUserDestroy(ctx, t), + AdditionalCLIOptions: &resource.AdditionalCLIOptions{ + Plan: resource.PlanOptions{ + NoRefresh: true, + }, + }, + Steps: []resource.TestStep{ + // Step 1: Create pre-Identity + { + ConfigDirectory: config.StaticDirectory("testdata/User/basic_v6.35.1/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckUserExists(ctx, t, resourceName, &v), + ), + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectNoIdentity(resourceName), + }, + }, + + // Step 2: Current version + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + ConfigDirectory: config.StaticDirectory("testdata/User/basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionNoop), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + tfstatecheck.ExpectNoIdentity(resourceName), + }, + }, + }, + }) +} diff --git a/website/docs/r/iam_user.html.markdown b/website/docs/r/iam_user.html.markdown index af4de367decb..43b469fb0c98 100644 --- a/website/docs/r/iam_user.html.markdown +++ b/website/docs/r/iam_user.html.markdown @@ -69,17 +69,43 @@ This resource exports the following attributes in addition to the arguments abov ## Import +In Terraform v1.12.0 and later, the [`import` block](https://developer.hashicorp.com/terraform/language/import) can be used with the `identity` attribute. For example: + +```terraform +import { + to = aws_iam_user.example + identity = { + name = "example-user" + } +} + +resource "aws_iam_user" "example" { + ### Configuration omitted for brevity ### +} +``` + +### Identity Schema + +#### Required + +* `name` (String) User name. + +#### Optional + +* `account_id` (String) AWS Account where this resource is managed. +* `region` (String) Region where this resource is managed. + In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import IAM Users using the `name`. For example: ```terraform import { - to = aws_iam_user.lb - id = "loadbalancer" + to = aws_iam_user.example + id = "example-user" } ``` Using `terraform import`, import IAM Users using the `name`. For example: ```console -% terraform import aws_iam_user.lb loadbalancer +% terraform import aws_iam_user.example example-user ``` From 5dee9fc927de858c4ec4d4862099397e8c64b152 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Tue, 10 Mar 2026 15:42:35 -0400 Subject: [PATCH 2/5] names: add `human_friendly_short` for `iam` service --- names/data/names_data.hcl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/names/data/names_data.hcl b/names/data/names_data.hcl index 0ae4522ee6fb..8752f20e806d 100644 --- a/names/data/names_data.hcl +++ b/names/data/names_data.hcl @@ -4203,8 +4203,9 @@ service "iam" { } names { - provider_name_upper = "IAM" - human_friendly = "IAM (Identity & Access Management)" + provider_name_upper = "IAM" + human_friendly = "IAM (Identity & Access Management)" + human_friendly_short = "IAM" } env_var { From c974c60dcc8f52ae78bc22e130e2f7625e63bcd1 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 11 Mar 2026 10:40:32 -0400 Subject: [PATCH 3/5] r/aws_iam_user: add list support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```console % make t K=iam T=TestAccIAMUser_ make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... make: Running acceptance tests on branch: 🌿 f-iam_user-list 🌿... TF_ACC=1 go1.25.8 test ./internal/service/iam/... -v -count 1 -parallel 20 -run='TestAccIAMUser_' -timeout 360m -vet=off 2026/03/10 16:59:25 Creating Terraform AWS Provider (SDKv2-style)... 2026/03/10 16:59:25 Initializing Terraform AWS Provider (SDKv2-style)... --- PASS: TestAccIAMUser_ForceDestroy_policyInlineAttached (37.44s) === CONT TestAccIAMUser_Tags_DefaultTags_nullOverlappingResourceTag --- PASS: TestAccIAMUser_ForceDestroy_policyInline (38.11s) === CONT TestAccIAMUser_Tags_addOnUpdate --- PASS: TestAccIAMUser_ForceDestroy_policyAttached (40.43s) === CONT TestAccIAMUser_Tags_EmptyTag_onCreate --- PASS: TestAccIAMUser_ForceDestroy_serviceSpecificCred (48.37s) === CONT TestAccIAMUser_List_basic --- PASS: TestAccIAMUser_ForceDestroy_sshKey (49.97s) === CONT TestAccIAMUser_List_pathPrefix --- PASS: TestAccIAMUser_ForceDestroy_signingCertificate (50.13s) === CONT TestAccIAMUser_List_includeResource --- PASS: TestAccIAMUser_Tags_DefaultTags_nullNonOverlappingResourceTag (52.72s) === CONT TestAccIAMUser_Identity_ExistingResource_noRefreshNoChange --- PASS: TestAccIAMUser_Tags_DefaultTags_emptyProviderOnlyTag (52.79s) === CONT TestAccIAMUser_Tags_EmptyTag_OnUpdate_replace --- PASS: TestAccIAMUser_pathChange (70.56s) === CONT TestAccIAMUser_Tags_DefaultTags_overlapping --- PASS: TestAccIAMUser_basic (74.38s) === CONT TestAccIAMUser_ForceDestroy_loginProfile --- PASS: TestAccIAMUser_nameChange (74.60s) === CONT TestAccIAMUser_ForceDestroy_mfaDevice --- PASS: TestAccIAMUser_nameAndTags (78.83s) === CONT TestAccIAMUser_Tags_emptyMap --- PASS: TestAccIAMUser_Identity_basic (80.27s) === CONT TestAccIAMUser_ForceDestroy_accessKey --- PASS: TestAccIAMUser_List_basic (41.29s) === CONT TestAccIAMUser_Identity_ExistingResource_basic --- PASS: TestAccIAMUser_List_pathPrefix (39.78s) === CONT TestAccIAMUser_Tags_DefaultTags_emptyResourceTag --- PASS: TestAccIAMUser_Tags_ComputedTag_OnUpdate_replace (89.84s) === CONT TestAccIAMUser_Tags_IgnoreTags_Overlap_resourceTag --- PASS: TestAccIAMUser_Tags_DefaultTags_updateToProviderOnly (93.33s) === CONT TestAccIAMUser_Tags_IgnoreTags_Overlap_defaultTag --- PASS: TestAccIAMUser_List_includeResource (43.50s) === CONT TestAccIAMUser_Tags_DefaultTags_updateToResourceOnly --- PASS: TestAccIAMUser_Tags_DefaultTags_nullOverlappingResourceTag (56.25s) === CONT TestAccIAMUser_Tags_null --- PASS: TestAccIAMUser_ForceDestroy_loginProfile (48.24s) === CONT TestAccIAMUser_Tags_ComputedTag_OnUpdate_add --- PASS: TestAccIAMUser_ForceDestroy_mfaDevice (48.13s) === CONT TestAccIAMUser_disappears --- PASS: TestAccIAMUser_Tags_addOnUpdate (88.04s) === CONT TestAccIAMUser_Tags_ComputedTag_onCreate --- PASS: TestAccIAMUser_ForceDestroy_accessKey (46.02s) --- PASS: TestAccIAMUser_Tags_EmptyTag_OnUpdate_add (129.04s) --- PASS: TestAccIAMUser_Identity_ExistingResource_noRefreshNoChange (83.11s) --- PASS: TestAccIAMUser_Tags_DefaultTags_nonOverlapping (137.36s) --- PASS: TestAccIAMUser_Tags_EmptyTag_OnUpdate_replace (85.10s) --- PASS: TestAccIAMUser_Tags_DefaultTags_emptyResourceTag (51.48s) --- PASS: TestAccIAMUser_Tags_EmptyTag_onCreate (101.00s) --- PASS: TestAccIAMUser_Tags_emptyMap (72.41s) --- PASS: TestAccIAMUser_disappears (29.67s) --- PASS: TestAccIAMUser_Tags_null (62.64s) --- PASS: TestAccIAMUser_Identity_ExistingResource_basic (68.49s) --- PASS: TestAccIAMUser_Tags_DefaultTags_updateToResourceOnly (67.80s) --- PASS: TestAccIAMUser_Tags_ComputedTag_onCreate (35.76s) --- PASS: TestAccIAMUser_tags (163.88s) --- PASS: TestAccIAMUser_Tags_DefaultTags_providerOnly (166.94s) --- PASS: TestAccIAMUser_permissionsBoundary (169.05s) --- PASS: TestAccIAMUser_Tags_IgnoreTags_Overlap_defaultTag (77.29s) --- PASS: TestAccIAMUser_Tags_ComputedTag_OnUpdate_add (49.49s) --- PASS: TestAccIAMUser_Tags_DefaultTags_overlapping (102.18s) --- PASS: TestAccIAMUser_Tags_IgnoreTags_Overlap_resourceTag (84.36s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 181.107s ``` --- .changelog/46869.txt | 6 + internal/service/iam/service_package_gen.go | 19 +- .../iam/testdata/User/list_basic/main.tf | 20 ++ .../User/list_basic/query.tfquery.hcl | 6 + .../User/list_include_resource/main.tf | 28 +++ .../list_include_resource/query.tfquery.hcl | 8 + .../testdata/User/list_path_prefix/main.tf | 40 ++++ .../User/list_path_prefix/query.tfquery.hcl | 10 + internal/service/iam/user.go | 23 +- internal/service/iam/user_list.go | 129 +++++++++++ internal/service/iam/user_list_test.go | 217 ++++++++++++++++++ .../list-resources/iam_user.html.markdown | 43 ++++ 12 files changed, 537 insertions(+), 12 deletions(-) create mode 100644 .changelog/46869.txt create mode 100644 internal/service/iam/testdata/User/list_basic/main.tf create mode 100644 internal/service/iam/testdata/User/list_basic/query.tfquery.hcl create mode 100644 internal/service/iam/testdata/User/list_include_resource/main.tf create mode 100644 internal/service/iam/testdata/User/list_include_resource/query.tfquery.hcl create mode 100644 internal/service/iam/testdata/User/list_path_prefix/main.tf create mode 100644 internal/service/iam/testdata/User/list_path_prefix/query.tfquery.hcl create mode 100644 internal/service/iam/user_list.go create mode 100644 internal/service/iam/user_list_test.go create mode 100644 website/docs/list-resources/iam_user.html.markdown diff --git a/.changelog/46869.txt b/.changelog/46869.txt new file mode 100644 index 000000000000..97e560cb0c43 --- /dev/null +++ b/.changelog/46869.txt @@ -0,0 +1,6 @@ +```release-note:new-list-resource +aws_iam_user +``` +```release-note:enhancement +resource/aws_iam_user: Add resource identity support +``` diff --git a/internal/service/iam/service_package_gen.go b/internal/service/iam/service_package_gen.go index d00c0193e49d..dfd3cdca003d 100644 --- a/internal/service/iam/service_package_gen.go +++ b/internal/service/iam/service_package_gen.go @@ -406,8 +406,10 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*inttypes.ServicePa IdentifierAttribute: names.AttrID, ResourceType: "User", }), - Region: unique.Make(inttypes.ResourceRegionDisabled()), - Identity: inttypes.GlobalSingleParameterIdentity(names.AttrName), + Region: unique.Make(inttypes.ResourceRegionDisabled()), + Identity: inttypes.GlobalSingleParameterIdentity(names.AttrName, + inttypes.WithMutableIdentity(), + ), Import: inttypes.SDKv2Import{ WrappedImport: true, }, @@ -499,6 +501,19 @@ func (p *servicePackage) SDKListResources(ctx context.Context) iter.Seq[*inttype inttypes.StringIdentityAttribute("policy_arn", true), }), }, + { + Factory: newUserResourceAsListResource, + TypeName: "aws_iam_user", + Name: "User", + Region: unique.Make(inttypes.ResourceRegionDisabled()), + Tags: unique.Make(inttypes.ServicePackageResourceTags{ + IdentifierAttribute: names.AttrID, + ResourceType: "User", + }), + Identity: inttypes.GlobalSingleParameterIdentity(names.AttrName, + inttypes.WithMutableIdentity(), + ), + }, }) } diff --git a/internal/service/iam/testdata/User/list_basic/main.tf b/internal/service/iam/testdata/User/list_basic/main.tf new file mode 100644 index 000000000000..8bdd225b5859 --- /dev/null +++ b/internal/service/iam/testdata/User/list_basic/main.tf @@ -0,0 +1,20 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_user" "test" { + count = var.resource_count + + name = "${var.rName}-${count.index}" +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "resource_count" { + description = "Number of resources to create" + type = number + nullable = false +} diff --git a/internal/service/iam/testdata/User/list_basic/query.tfquery.hcl b/internal/service/iam/testdata/User/list_basic/query.tfquery.hcl new file mode 100644 index 000000000000..300de6344518 --- /dev/null +++ b/internal/service/iam/testdata/User/list_basic/query.tfquery.hcl @@ -0,0 +1,6 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +list "aws_iam_user" "test" { + provider = aws +} diff --git a/internal/service/iam/testdata/User/list_include_resource/main.tf b/internal/service/iam/testdata/User/list_include_resource/main.tf new file mode 100644 index 000000000000..171e1af513c8 --- /dev/null +++ b/internal/service/iam/testdata/User/list_include_resource/main.tf @@ -0,0 +1,28 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_user" "test" { + count = var.resource_count + + name = "${var.rName}-${count.index}" + + tags = var.resource_tags +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "resource_count" { + description = "Number of resources to create" + type = number + nullable = false +} + +variable "resource_tags" { + description = "Tags to set on resource" + type = map(string) + nullable = false +} diff --git a/internal/service/iam/testdata/User/list_include_resource/query.tfquery.hcl b/internal/service/iam/testdata/User/list_include_resource/query.tfquery.hcl new file mode 100644 index 000000000000..bb2907d13d8a --- /dev/null +++ b/internal/service/iam/testdata/User/list_include_resource/query.tfquery.hcl @@ -0,0 +1,8 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +list "aws_iam_user" "test" { + provider = aws + + include_resource = true +} diff --git a/internal/service/iam/testdata/User/list_path_prefix/main.tf b/internal/service/iam/testdata/User/list_path_prefix/main.tf new file mode 100644 index 000000000000..fafd27ed2275 --- /dev/null +++ b/internal/service/iam/testdata/User/list_path_prefix/main.tf @@ -0,0 +1,40 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_user" "expected" { + count = var.resource_count + + name = "${var.rName}-${count.index}" + path = var.expected_path_name +} + +resource "aws_iam_user" "not_expected" { + count = var.resource_count + + name = "${var.rName}-other-${count.index}" + path = var.other_path_name +} + +variable "rName" { + description = "Name for resource" + type = string + nullable = false +} + +variable "resource_count" { + description = "Number of resources to create" + type = number + nullable = false +} + +variable "expected_path_name" { + description = "Path name for expected resources" + type = string + nullable = false +} + +variable "other_path_name" { + description = "Path name for non-expected resources" + type = string + nullable = false +} diff --git a/internal/service/iam/testdata/User/list_path_prefix/query.tfquery.hcl b/internal/service/iam/testdata/User/list_path_prefix/query.tfquery.hcl new file mode 100644 index 000000000000..252f8b568285 --- /dev/null +++ b/internal/service/iam/testdata/User/list_path_prefix/query.tfquery.hcl @@ -0,0 +1,10 @@ +# Copyright IBM Corp. 2014, 2026 +# SPDX-License-Identifier: MPL-2.0 + +list "aws_iam_user" "test" { + provider = aws + + config { + path_prefix = var.expected_path_name + } +} diff --git a/internal/service/iam/user.go b/internal/service/iam/user.go index 32558394e714..6ae144f7ce0a 100644 --- a/internal/service/iam/user.go +++ b/internal/service/iam/user.go @@ -157,16 +157,7 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta any) dia return sdkdiag.AppendErrorf(diags, "reading IAM User (%s): %s", d.Id(), err) } - d.Set(names.AttrARN, user.Arn) - d.Set(names.AttrName, user.UserName) - d.Set(names.AttrPath, user.Path) - if user.PermissionsBoundary != nil { - d.Set("permissions_boundary", user.PermissionsBoundary.PermissionsBoundaryArn) - } else { - d.Set("permissions_boundary", nil) - } - d.Set("unique_id", user.UserId) - + resourceUserFlatten(user, d) setTagsOut(ctx, user.Tags) return diags @@ -667,3 +658,15 @@ func retryCreateUser(ctx context.Context, conn *iam.Client, input *iam.CreateUse return output, err } + +func resourceUserFlatten(user *awstypes.User, d *schema.ResourceData) { + d.Set(names.AttrARN, user.Arn) + d.Set(names.AttrName, user.UserName) + d.Set(names.AttrPath, user.Path) + if user.PermissionsBoundary != nil { + d.Set("permissions_boundary", user.PermissionsBoundary.PermissionsBoundaryArn) + } else { + d.Set("permissions_boundary", nil) + } + d.Set("unique_id", user.UserId) +} diff --git a/internal/service/iam/user_list.go b/internal/service/iam/user_list.go new file mode 100644 index 000000000000..8220226de9f9 --- /dev/null +++ b/internal/service/iam/user_list.go @@ -0,0 +1,129 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package iam + +import ( + "context" + "fmt" + "iter" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/iam" + awstypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/hashicorp/terraform-plugin-framework/list" + listschema "github.com/hashicorp/terraform-plugin-framework/list/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/logging" + inttypes "github.com/hashicorp/terraform-provider-aws/internal/types" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKListResource("aws_iam_user") +func newUserResourceAsListResource() inttypes.ListResourceForSDK { + l := userListResource{} + l.SetResourceSchema(resourceUser()) + return &l +} + +var _ list.ListResource = &userListResource{} + +type userListResource struct { + framework.ListResourceWithSDKv2Resource +} + +type listUserModel struct { + PathPrefix types.String `tfsdk:"path_prefix"` +} + +func (l *userListResource) ListResourceConfigSchema(ctx context.Context, request list.ListResourceSchemaRequest, response *list.ListResourceSchemaResponse) { + response.Schema = listschema.Schema{ + Attributes: map[string]listschema.Attribute{ + "path_prefix": listschema.StringAttribute{ + Optional: true, + Description: "Path prefix on which to filter user names.", + Validators: []validator.String{ + validPolicyPathFramework, + }, + }, + }, + } +} + +func (l *userListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream) { + conn := l.Meta().IAMClient(ctx) + + var query listUserModel + if request.Config.Raw.IsKnown() && !request.Config.Raw.IsNull() { + if diags := request.Config.Get(ctx, &query); diags.HasError() { + stream.Results = list.ListResultsStreamDiagnostics(diags) + return + } + } + + pathPrefix := query.PathPrefix.ValueString() + + tflog.Info(ctx, "Listing Resources", map[string]any{ + logging.ResourceAttributeKey("path_prefix"): pathPrefix, + }) + + stream.Results = func(yield func(list.ListResult) bool) { + input := iam.ListUsersInput{ + PathPrefix: query.PathPrefix.ValueStringPointer(), + } + for item, err := range listUsers(ctx, conn, &input) { + if err != nil { + result := fwdiag.NewListResultErrorDiagnostic(err) + yield(result) + return + } + name := aws.ToString(item.UserName) + ctx := tflog.SetField(ctx, logging.ResourceAttributeKey(names.AttrName), name) + + result := request.NewListResult(ctx) + + rd := l.ResourceData() + rd.SetId(name) + rd.Set(names.AttrName, name) + + if request.IncludeResource { + resourceUserFlatten(&item, rd) + } + + result.DisplayName = aws.ToString(item.UserName) + + l.SetResult(ctx, l.Meta(), request.IncludeResource, &result, rd) + if result.Diagnostics.HasError() { + yield(result) + return + } + + if !yield(result) { + return + } + } + } +} + +func listUsers(ctx context.Context, conn *iam.Client, input *iam.ListUsersInput) iter.Seq2[awstypes.User, error] { + return func(yield func(awstypes.User, error) bool) { + pages := iam.NewListUsersPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + if err != nil { + yield(awstypes.User{}, fmt.Errorf("listing IAM User resources: %w", err)) + return + } + + for _, item := range page.Users { + if !yield(item, nil) { + return + } + } + } + } +} diff --git a/internal/service/iam/user_list_test.go b/internal/service/iam/user_list_test.go new file mode 100644 index 000000000000..f30791c97b1c --- /dev/null +++ b/internal/service/iam/user_list_test.go @@ -0,0 +1,217 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package iam_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/querycheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfquerycheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/querycheck" + tfqueryfilter "github.com/hashicorp/terraform-provider-aws/internal/acctest/queryfilter" + tfstatecheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/statecheck" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccIAMUser_List_basic(t *testing.T) { + ctx := acctest.Context(t) + + resourceName1 := "aws_iam_user.test[0]" + resourceName2 := "aws_iam_user.test[1]" + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + + identity1 := tfstatecheck.Identity() + identity2 := tfstatecheck.Identity() + + acctest.ParallelTest(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + CheckDestroy: testAccCheckUserDestroy(ctx, t), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + // Step 1: Setup + { + ConfigDirectory: config.StaticDirectory("testdata/User/list_basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "resource_count": config.IntegerVariable(2), + }, + ConfigStateChecks: []statecheck.StateCheck{ + identity1.GetIdentity(resourceName1), + statecheck.ExpectKnownValue(resourceName1, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-0")), + + identity2.GetIdentity(resourceName2), + statecheck.ExpectKnownValue(resourceName2, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-1")), + }, + }, + + // Step 2: Query + { + Query: true, + ConfigDirectory: config.StaticDirectory("testdata/User/list_basic/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "resource_count": config.IntegerVariable(2), + }, + QueryResultChecks: []querycheck.QueryResultCheck{ + tfquerycheck.ExpectIdentityFunc("aws_iam_user.test", identity1.Checks()), + querycheck.ExpectResourceDisplayName("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity1.Checks()), knownvalue.StringExact(rName+"-0")), + tfquerycheck.ExpectNoResourceObject("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity1.Checks())), + + tfquerycheck.ExpectIdentityFunc("aws_iam_user.test", identity2.Checks()), + querycheck.ExpectResourceDisplayName("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity2.Checks()), knownvalue.StringExact(rName+"-1")), + tfquerycheck.ExpectNoResourceObject("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity2.Checks())), + }, + }, + }, + }) +} + +func TestAccIAMUser_List_includeResource(t *testing.T) { + ctx := acctest.Context(t) + + resourceName1 := "aws_iam_user.test[0]" + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + + identity1 := tfstatecheck.Identity() + + acctest.ParallelTest(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + CheckDestroy: testAccCheckUserDestroy(ctx, t), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + // Step 1: Setup + { + ConfigDirectory: config.StaticDirectory("testdata/User/list_include_resource/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "resource_count": config.IntegerVariable(1), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + ConfigStateChecks: []statecheck.StateCheck{ + identity1.GetIdentity(resourceName1), + statecheck.ExpectKnownValue(resourceName1, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-0")), + }, + }, + + // Step 2: Query + { + Query: true, + ConfigDirectory: config.StaticDirectory("testdata/User/list_include_resource/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "resource_count": config.IntegerVariable(1), + acctest.CtResourceTags: config.MapVariable(map[string]config.Variable{ + acctest.CtKey1: config.StringVariable(acctest.CtValue1), + }), + }, + QueryResultChecks: []querycheck.QueryResultCheck{ + tfquerycheck.ExpectIdentityFunc("aws_iam_user.test", identity1.Checks()), + querycheck.ExpectResourceDisplayName("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity1.Checks()), knownvalue.StringExact(rName+"-0")), + querycheck.ExpectResourceKnownValues("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity1.Checks()), []querycheck.KnownValueCheck{ + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrID), knownvalue.StringExact(rName+"-0")), + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-0")), + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }), + }, + }, + }, + }) +} + +func TestAccIAMUser_List_pathPrefix(t *testing.T) { + ctx := acctest.Context(t) + + resourceNameExpected1 := "aws_iam_user.expected[0]" + resourceNameExpected2 := "aws_iam_user.expected[1]" + resourceNameNotExpected1 := "aws_iam_user.not_expected[0]" + resourceNameNotExpected2 := "aws_iam_user.not_expected[1]" + + rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + rPathName := "/" + acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + "/" + rOtherPathName := "/" + acctest.RandomWithPrefix(t, acctest.ResourcePrefix) + "/" + + identityExpected1 := tfstatecheck.Identity() + identityExpected2 := tfstatecheck.Identity() + identityNotExpected1 := tfstatecheck.Identity() + identityNotExpected2 := tfstatecheck.Identity() + + acctest.ParallelTest(ctx, t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.IAMServiceID), + CheckDestroy: testAccCheckUserDestroy(ctx, t), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + // Step 1: Setup + { + ConfigDirectory: config.StaticDirectory("testdata/User/list_path_prefix/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "resource_count": config.IntegerVariable(2), + "expected_path_name": config.StringVariable(rPathName), + "other_path_name": config.StringVariable(rOtherPathName), + }, + ConfigStateChecks: []statecheck.StateCheck{ + identityExpected1.GetIdentity(resourceNameExpected1), + statecheck.ExpectKnownValue(resourceNameExpected1, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-0")), + + identityExpected2.GetIdentity(resourceNameExpected2), + statecheck.ExpectKnownValue(resourceNameExpected2, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-1")), + + identityNotExpected1.GetIdentity(resourceNameNotExpected1), + statecheck.ExpectKnownValue(resourceNameNotExpected1, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-other-0")), + + identityNotExpected2.GetIdentity(resourceNameNotExpected2), + statecheck.ExpectKnownValue(resourceNameNotExpected2, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-other-1")), + }, + }, + + // Step 2: Query + { + Query: true, + ConfigDirectory: config.StaticDirectory("testdata/User/list_path_prefix/"), + ConfigVariables: config.Variables{ + acctest.CtRName: config.StringVariable(rName), + "resource_count": config.IntegerVariable(2), + "expected_path_name": config.StringVariable(rPathName), + "other_path_name": config.StringVariable(rOtherPathName), + }, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("aws_iam_user.test", 2), + tfquerycheck.ExpectIdentityFunc("aws_iam_user.test", identityExpected1.Checks()), + tfquerycheck.ExpectIdentityFunc("aws_iam_user.test", identityExpected2.Checks()), + }, + }, + }, + }) +} diff --git a/website/docs/list-resources/iam_user.html.markdown b/website/docs/list-resources/iam_user.html.markdown new file mode 100644 index 000000000000..c945edd46b6d --- /dev/null +++ b/website/docs/list-resources/iam_user.html.markdown @@ -0,0 +1,43 @@ +--- +subcategory: "IAM (Identity & Access Management)" +layout: "aws" +page_title: "AWS: aws_iam_user" +description: |- + Lists IAM (Identity & Access Management) User resources. +--- + +# List Resource: aws_iam_user + +Lists IAM (Identity & Access Management) User resources. + +## Example Usage + +### Basic Usage + +```terraform +list "aws_iam_user" "example" { + provider = aws +} +``` + +### Filter by Path Prefix + +This example will return IAM Users with a `path` equal to or beginning with `/example/`. + +```terraform +list "aws_iam_user" "example" { + provider = aws + + config { + path_prefix = "/example/" + } +} +``` + +## Argument Reference + +This list resource supports the following arguments: + +* `path_prefix` - (Optional) Limits the returned IAM Users to those within this path. + If `path_prefix` is not specified, or is `"/"`, returns all IAM Users. + Must begin and end with a slash (`/`) and contain uppercase or lowercase alphanumeric characters or any of the following: `/`, `,`, `.`, `+`, `@`, `=`, `_`, or `-`. From a8778d4cb9662c2f63ac5078016e8f1f11400e52 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 12 Mar 2026 16:02:32 -0400 Subject: [PATCH 4/5] r/aws_iam_user(test): additional `_List_includeResource` checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```console % make t K=iam T=TestAccIAMUser_List make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... make: Running acceptance tests on branch: 🌿 f-iam_user-list 🌿... TF_ACC=1 go1.25.8 test ./internal/service/iam/... -v -count 1 -parallel 20 -run='TestAccIAMUser_List' -timeout 360m -vet=off 2026/03/12 15:54:44 Creating Terraform AWS Provider (SDKv2-style)... 2026/03/12 15:54:44 Initializing Terraform AWS Provider (SDKv2-style)... --- PASS: TestAccIAMUser_List_basic (12.64s) --- PASS: TestAccIAMUser_List_pathPrefix (12.70s) --- PASS: TestAccIAMUser_List_includeResource (12.74s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 19.583s ``` --- internal/service/iam/user_list_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/service/iam/user_list_test.go b/internal/service/iam/user_list_test.go index f30791c97b1c..c722a5d5f38f 100644 --- a/internal/service/iam/user_list_test.go +++ b/internal/service/iam/user_list_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" "github.com/hashicorp/terraform-plugin-testing/tfversion" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfknownvalue "github.com/hashicorp/terraform-provider-aws/internal/acctest/knownvalue" tfquerycheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/querycheck" tfqueryfilter "github.com/hashicorp/terraform-provider-aws/internal/acctest/queryfilter" tfstatecheck "github.com/hashicorp/terraform-provider-aws/internal/acctest/statecheck" @@ -129,8 +130,13 @@ func TestAccIAMUser_List_includeResource(t *testing.T) { tfquerycheck.ExpectIdentityFunc("aws_iam_user.test", identity1.Checks()), querycheck.ExpectResourceDisplayName("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity1.Checks()), knownvalue.StringExact(rName+"-0")), querycheck.ExpectResourceKnownValues("aws_iam_user.test", tfqueryfilter.ByResourceIdentityFunc(identity1.Checks()), []querycheck.KnownValueCheck{ + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrARN), tfknownvalue.GlobalARNExact("iam", "user/"+rName+"-0")), + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrForceDestroy), knownvalue.Null()), tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrID), knownvalue.StringExact(rName+"-0")), tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName+"-0")), + tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrPath), knownvalue.StringExact("/")), + tfquerycheck.KnownValueCheck(tfjsonpath.New("permissions_boundary"), knownvalue.NotNull()), + tfquerycheck.KnownValueCheck(tfjsonpath.New("unique_id"), knownvalue.NotNull()), tfquerycheck.KnownValueCheck(tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), })), From 3a3da1a73530cdff0f18336d2b3dc03b3b65c6ef Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 13 Mar 2026 14:55:43 -0400 Subject: [PATCH 5/5] r/aws_iam_user: post-rebase build fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `SetResult` signature swapped the order of the final two arguments. ```console % make t K=iam T=TestAccIAMUser_List make: Verifying source code with gofmt... ==> Checking that code complies with gofmt requirements... make: Running acceptance tests on branch: 🌿 f-iam_user-list 🌿... TF_ACC=1 go1.25.8 test ./internal/service/iam/... -v -count 1 -parallel 20 -run='TestAccIAMUser_List' -timeout 360m -vet=off 2026/03/13 14:37:22 Creating Terraform AWS Provider (SDKv2-style)... 2026/03/13 14:37:22 Initializing Terraform AWS Provider (SDKv2-style)... --- PASS: TestAccIAMUser_List_basic (23.29s) --- PASS: TestAccIAMUser_List_pathPrefix (23.40s) --- PASS: TestAccIAMUser_List_includeResource (23.64s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 33.605s ``` --- internal/service/iam/user_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iam/user_list.go b/internal/service/iam/user_list.go index 8220226de9f9..e61e5f6273f2 100644 --- a/internal/service/iam/user_list.go +++ b/internal/service/iam/user_list.go @@ -96,7 +96,7 @@ func (l *userListResource) List(ctx context.Context, request list.ListRequest, s result.DisplayName = aws.ToString(item.UserName) - l.SetResult(ctx, l.Meta(), request.IncludeResource, &result, rd) + l.SetResult(ctx, l.Meta(), request.IncludeResource, rd, &result) if result.Diagnostics.HasError() { yield(result) return