Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_iam_user_group_membership: allow non-exclusive group memberships #3365

Merged

Conversation

devonbleak
Copy link
Contributor

Resolves #2881 - allows multiple aws_iam_group_memberships to co-exist peacefully by adding a new computed field group_members to manage adding/removing users.

Adds a remove_unmanaged_members property that can be set to true to restore previous functionality and documentation caveat about flapping with that setting.

@ghost ghost added the size/L Managed by automation to categorize the size of a PR. label Feb 14, 2018
@radeksimko radeksimko added bug Addresses a defect in current functionality. service/iam Issues and PRs that pertain to the iam service. labels Feb 14, 2018
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @devonbleak 👋 thanks for this contribution! This is a nice enhancement, but I think the implementation is more complex than it needs to be. I may be missing the use case that requires non-exclusively managing a list of users in one resource though, please let me know!

To simplify this, I would prefer to introduce a singular user attribute that manages a single user-to-group membership (almost like how it used to be 2 years ago). Everything becomes much simpler afterwards:

  • No CustomizeDiff
  • No additional flag attribute (remove_unmanaged_members)
  • No sidecar storage attribute (group_members)

The implementation would entail something like:

  • Set user to ForceNew: true
  • Set users to Optional: true
  • Set users to ConflictsWith: []string{"user"}
  • Use d.GetOk() to switch between the user list of 1 item or the full user lists for addition/removal in Create/Delete
  • Use d.GetOk() to switch to only checking if the singular user is a member in Read
  • No update logic necessary

What do you think?

"remove_unmanaged_members": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change for the resource as written. It should default to true to keep backwards compatibility.

@@ -35,6 +36,18 @@ func resourceAwsIamGroupMembership() *schema.Resource {
Required: true,
ForceNew: true,
},

"remove_unmanaged_members": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nitpick: As of Go 1.7+, the &schema.Schema after the attribute name declarations are unnecessary

@@ -242,3 +324,14 @@ resource "aws_iam_user" "user" {
}
`, groupName, membershipName, userNamePrefix)
}

func testAccAWSGroupMemberAddUnmanagedUser(groupName string, userName string) func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably create a complimentary testAccAWSGroupMemberRemoveManagedUser

@bflad bflad added the waiting-response Maintainers are waiting on response from community or contributor. label Feb 27, 2018
@devonbleak
Copy link
Contributor Author

devonbleak commented Feb 27, 2018 via email

@devonbleak
Copy link
Contributor Author

@bflad thinking about this more, would it make sense to just create a new aws_iam_user_group_membership resource rather than having a single resource that may conflict with itself even with the changes to user vs users?

@devonbleak
Copy link
Contributor Author

@bflad the more I think about having a 1:1 user-group membership resource the more it breaks down. Consider the case where we have a list of users and a list of groups - there wouldn't be a way to add all the users to all the groups if the lengths of those lists has a common factor. I'm going to go ahead with creating a new resource that will take a list of users and a single group and will be non-exclusive with group memberships.

@bflad
Copy link
Contributor

bflad commented Mar 14, 2018

@devonbleak sorry for my lack of reply earlier. Do you need any help?

Just to confirm the use case you're thinking about, it is something like this?

variable "users" {
  default = [
    "a",
    "b",
  ]
}

variable "groups" {
  default = [
    "z",
    "y",
    "x",
  ]
}

resource "aws_iam_user" "example" {
  count = "${length(var.users)}"
  # ... other configuration ...
}

resource "aws_iam_group" "example" {
  count = "${length(var.groups)}"
  # ... other configuration ...
}

resource "aws_iam_user_group_membership" "example" {
  # yikes what would we put here? :)
}

What do you think about a groups attribute on aws_iam_user instead? The above becomes something like:

variable "users" {
  default = [
    "a",
    "b",
  ]
}

variable "groups" {
  default = [
    "z",
    "y",
    "x",
  ]
}

resource "aws_iam_user" "example" {
  count = "${length(var.users)}"
  # ... other configuration ...
  # add user to all groups
  groups = ["${aws_iam_group.example.*.id}"]
}

resource "aws_iam_group" "example" {
  count = "${length(var.groups)}"
  # ... other configuration ...
}

@devonbleak
Copy link
Contributor Author

@bflad The aws_iam_user_group_membership would basically work the same as aws_iam_group_membership except it wouldn't touch user memberships that were added outside of it (similar to how the original PR works here with remove_unmanaged_users = false but without requiring the extra logic in CustomizeDiff or the sidecar). It would probably need a better name.

Adding a groups argument to aws_iam_user is an interesting solution but I think we'll still be missing a resource that just adds a list of users to a group and keeps them there without doing anything else (or vice versa adding a list of groups to a user).

@bflad
Copy link
Contributor

bflad commented Apr 6, 2018

@devonbleak I guess we have come full circle. 😄 Do you think you could implement this PR without the sidecar and defaulting the flag attribute to keep existing behavior (exclusive management)?

@devonbleak
Copy link
Contributor Author

@bflad been thinking about this again the past couple days and thinking about implementing something like an aws_iam_user_group_memberships that would do the non-exclusive thing without the sidecar or the CustomizeDiff. The analogy I have in my head is like aws_iam_policy_attachment vs aws_iam_{group,role,user}_policy_attachment - the former behaves like aws_iam_group_membership while the latter ignores things it doesn't know about.

My original implementation of this basically just ignored things it didn't know about in the Read function and worked great except that it took two applies for a change to remove_unmanaged_users to actually complete which is why I ended up going the sidecar/CustomizeDiff route. It looks like we have what we need from the Go SDK in ListGroupsForUser.

I'll start working on this approach, but let me know if you'd like to see something different.

resource "aws_iam_user_group_memberships" "test" {
  user   = "${aws_iam_user.test.name}"
  groups = [
    "${aws_iam_group.test1.name}", 
    "${aws_iam_group.test2.name}"
  ]
}

@bflad
Copy link
Contributor

bflad commented Apr 7, 2018

I'd drop the s pluralization on the resource name, but sounds good.

@devonbleak devonbleak force-pushed the f-iam-group-membership-managed-users branch from e87df1d to 7805065 Compare April 10, 2018 01:40
@ghost ghost added the size/L Managed by automation to categorize the size of a PR. label Apr 10, 2018
@devonbleak devonbleak changed the title resource/aws_iam_group_membership: allow non-exclusive group memberships [WIP] resource/aws_iam_user_group_membership: allow non-exclusive group memberships Apr 10, 2018
@devonbleak devonbleak force-pushed the f-iam-group-membership-managed-users branch from 7805065 to d898afa Compare April 11, 2018 01:03
@ghost ghost added size/XL Managed by automation to categorize the size of a PR. and removed size/L Managed by automation to categorize the size of a PR. labels Apr 11, 2018
@devonbleak devonbleak force-pushed the f-iam-group-membership-managed-users branch from d898afa to 79fe763 Compare April 13, 2018 23:02
@ghost ghost added the size/XL Managed by automation to categorize the size of a PR. label Apr 13, 2018
@devonbleak devonbleak changed the title [WIP] resource/aws_iam_user_group_membership: allow non-exclusive group memberships resource/aws_iam_user_group_membership: allow non-exclusive group memberships Apr 13, 2018
@devonbleak
Copy link
Contributor Author

@bflad implementation is done on resource/aws_iam_user_group_membership. I also added a note to aws_iam_group_membership about the conflicts behavior so new users aren't caught by surprise with it. Let me know if you'd like any additional changes!

Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @devonbleak 👋 This is looking pretty good -- can you see my comments below and let me know if you have any questions or do not have time to implement the feedback? Thanks!

Delete: resourceAwsIamUserGroupMembershipDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this attribute? It seems to only be necessary for the resource ID. We can instead have Terraform generate a unique ID as it serves no useful value:

d.SetId(resource.UniqueId())

Other nitpick: The &schema.Schema declarations for the attributes are extraneous as of Go 1.7 as its already declared in map[string]*schema.Schema

Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can omit Set: schema.HashString

})
if err != nil {
// unwrap aws-specific error
if awsErr, ok := err.(awserr.Error); ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a helper for these:

if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
  // ...
}

if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NoSuchEntity" {
// no such user
d.SetId("")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should log a WARN message when removing something from the Terraform state, e.g.

log.Printf("[WARN] Groups not found for user (%s), removing from state", user)

GroupName: group,
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchEntity" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here about using isAWSErr() helper

func testAccAWSUserGroupMembershipDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).iamconn

// check that all users and groups have been destroyed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The below code seems to be checking that the physical IAM users and groups are destroyed. We should only be checking for the removal of group attachments to users defined by aws_iam_user_group_membership resources. Checking the opposite would mask coding errors in this resource fully deleting users/groups.

for _, group := range groupsNeg {
for _, groupFound := range userGroupList.Groups {
if group == *groupFound.GroupName {
return fmt.Errorf("Required negative group found for %s: %s", userName, group)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be easier to understand with Unexpected group found for


Provides a resource for adding an [IAM User][2] to [IAM Groups][1]. This
resource will not conflict with itself when used multiple times for the same
user.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user unless there are overlapping groups.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resource description here and vice-versa with aws_iam_group_membership should link to each other and mention why to use one over the other (exclusive management vs non-exclusive).

Config: usersAndGroupsConfig + testAccAWSUserGroupMembershipConfigInit(membershipName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("aws_iam_user_group_membership.user1_test1", "user", userName1),
testAccAWSUserGroupMembershipCheckGroupListForUser(userName1, []string{groupName1}, []string{groupName2, groupName3}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd probably be good to also test that userName2 is not attached to anything 👍

@devonbleak devonbleak force-pushed the f-iam-group-membership-managed-users branch from 79fe763 to 01a3425 Compare May 1, 2018 05:07
@ghost ghost added the size/XL Managed by automation to categorize the size of a PR. label May 1, 2018
@bflad bflad removed the waiting-response Maintainers are waiting on response from community or contributor. label May 1, 2018
@bflad bflad added this to the v1.17.0 milestone May 1, 2018
Copy link
Contributor

@bflad bflad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work here @devonbleak! Let's get this in. 🚀

I'll make the minor testing/documentation updates on merge.

1 test passed (all tests)
=== RUN   TestAccAWSUserGroupMembership_basic
--- PASS: TestAccAWSUserGroupMembership_basic (46.68s)

@bflad bflad merged commit a468666 into hashicorp:master May 1, 2018
bflad added a commit that referenced this pull request May 1, 2018
@devonbleak devonbleak deleted the f-iam-group-membership-managed-users branch May 1, 2018 22:01
@bflad
Copy link
Contributor

bflad commented May 2, 2018

This has been released in version 1.17.0 of the AWS provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading.

alext added a commit to alphagov/paas-cf that referenced this pull request Nov 27, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
alext added a commit to alphagov/paas-bootstrap that referenced this pull request Nov 27, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
alext added a commit to alphagov/paas-bootstrap that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
alext added a commit to alphagov/paas-bootstrap that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
alext added a commit to alphagov/paas-cf that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
alext added a commit to alphagov/paas-bootstrap that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
alext added a commit to alphagov/paas-bootstrap that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
bandesz pushed a commit to alphagov/paas-bootstrap that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
bandesz pushed a commit to alphagov/paas-cf that referenced this pull request Nov 28, 2018
Now that the aws_iam_user_group_membership resource exists[1], we can
add these users to the relevant groups without clobbering existing
members of the group, meaning that our workaround is no longer
necessary.

[1]hashicorp/terraform-provider-aws#3365
@ghost
Copy link

ghost commented Apr 6, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

@ghost ghost locked and limited conversation to collaborators Apr 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Addresses a defect in current functionality. service/iam Issues and PRs that pertain to the iam service. size/XL Managed by automation to categorize the size of a PR.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

State being written incorrectly for IAM group membership?
3 participants