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

azuread_invitation: support same properties as in azuread_user #650

Open
ppanyukov opened this issue Oct 28, 2021 · 13 comments
Open

azuread_invitation: support same properties as in azuread_user #650

ppanyukov opened this issue Oct 28, 2021 · 13 comments

Comments

@ppanyukov
Copy link

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritise this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritise the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

The azuread_user resource supports many properties like department, company etc, and most notably account_enabled -- pretty much all fields I can set manually in AAD.

The azuread_invitation doesn't have any of these at all. And again most notably it doesn't have account_enabled. At the same time, we can edit and populate all these fields manually in AAD.

We use many external users in our AAD and we use the metadata extensively to document their roles and why they are in our AAD at all.

The proposal is to "beef up" azuread_invitation to support all the same properties as azuread_user (except there is no need for password of course).

Looking at the graph SDK, the Invitation struct does have the *User member, and it is the same type being used by azuread_user, so in theory this should be just a matter of exposing the properties and fill out this object in azuread_invitation?

type Invitation struct {
	ID                      *string          `json:"id,omitempty"`
	InvitedUserDisplayName  *string          `json:"invitedUserDisplayName,omitempty"`
	InvitedUserEmailAddress *string          `json:"invitedUserEmailAddress,omitempty"`
	SendInvitationMessage   *bool            `json:"sendInvitationMessage,omitempty"`
	InviteRedirectURL       *string          `json:"inviteRedirectUrl,omitempty"`
	InviteRedeemURL         *string          `json:"inviteRedeemUrl,omitempty"`
	Status                  *string          `json:"status,omitempty"`
	InvitedUserType         *InvitedUserType `json:"invitedUserType,omitempty"`

	InvitedUserMessageInfo *InvitedUserMessageInfo `json:"invitedUserMessageInfo,omitempty"`
	InvitedUser            *User                   `json:"invitedUser,omitempty"`  // <== THIS
}

Of course if this is possible and is implemented, then we'd want azuread_invitation to return extra attributes too.

@manicminer
Copy link
Member

Hi @ppanyukov, thanks for requesting this. Whilst the Invitation object contains a User container, this is read-only and only the user's ID is returned by the API.

We could potentially implement this by updating the guest user separately after the invitation is created, but I'd first like to experiment and see if this can be done with the existing user resource, and also investigate to see which properties are updateable for guest users yet to accept their invitation.

@ppanyukov
Copy link
Author

Hmm, I really hoped this wouldn't be the case but oh well...

Would it be breaking "tf resource best practice" if we do two operations (create and then update) on a single resource? Say create succeeds but update fails for some reason (network issue), what happens with the tf state? Tainted? Although azurerm provider does lots of these "combined operations", e.g. azurerm_app_service is full of them.

Another alternative is to have separate resource say azuread_invitation_metadata which will just update these meta properties and will take an object_id as required parameter?

Of course having all these just in azuread_invitation resource is much more convenient from the usage perspective.

Judging from the Azure Portal, we can edit all the same properties for invited users as for the regular ones. I don't know if this helps?

@manicminer
Copy link
Member

Performing multiple operations for a single logical operation in Terraform is fine, as you say we do this in lots of places. If the create operation gets far enough to assign a resource ID, then it is in fact tainted on failure.

@ppanyukov
Copy link
Author

Oh, while we are at it, this makes me very sad :(

This resource does not support importing.

@manicminer
Copy link
Member

There's no way to read back invitations as it's a POST only endpoint - however you can import guest users into an azuread_user resource! :)

@TheMacStack
Copy link

What we struggle with not having any way to set the First and Last names for the created object, this is an issue with things like AAD SCIM sync to sync the guests to other 3rd party apps, (AWS SSO for example) that require First and Last name attributes.

I understand this is likely limitation on the azure api but still my 2 cents

@StickNitro
Copy link

Seems like it should be possible to support the attributes in the Azure Portal when inviting a New User. Looking at the code there is an update of the newly created user that takes place following the actual invitation so this could support updating attributes like the First Name and Last Name, etc. (I guess at least should be able to set the First Name, Last Name, Job Title, Department, Company name and possibly Usage location).

The resource is already reading the Email from the existing user in the read hook which would be able to update these attributes, this would trigger a change in the resource if there are differences. I guess the question is whether these additional attributes trigger an update (not currently implemented) or whether they force a new resource.

Proposed changes:

  • add the attributes First Name, Last Name, Job Title, Department, Company name to the resource
    • should these attributes be defined with forceNew: true??
  • change the second update in the create hook to pass these values (if present)
  • update read hook to update these attributes from the User object retrieved from AD
  • add an update dependent on the answer of the forceNew question above

I don't mind looking at making the changes and creating pull request, that is if the suggested changes are appropriate!

This would at least provide the same level of functionality as the Invite user form in Azure AD (excluding Groups, Roles and Manager)

@bufferoverflow
Copy link

bufferoverflow commented Apr 22, 2023

I'm unsure why you need this as the user_id is an output variable of azuread_invitation, we do e.g.

locals {
invited_folks = yamldecode(file("${path.module}/../invited_folks.yml"))  
}

resource "azuread_invitation" "invite" {
  for_each           = local.invited_folks
  user_email_address = each.key
  redirect_url       = "https://example.com"
  message {
    additional_recipients = [each.value.inviter]
    body                  = <<-EOT
    Hello ${each.key}
    You have been invited to example.com
    Please accept this invite to get access.
    Have a nice day!
    EOT
  }
}

resource "azuread_group_member" "example" {
  group_object_id  = "<an object id>"
  for_each         = local.invited_folks
  member_object_id = azuread_invitation.invite[each.key].user_id
}

with a invited_folks.yml looking like this:

---
jane@example.com:
  inviter: richard@example.com
joe@example.com:
  inviter: rivchard@example.com

@manicminer
Copy link
Member

We intend to approach this by incorporating invite functionality into the azuread_user resource rather than expand the azuread_invitation resource.

@old-guy-coder
Copy link

Any updates on this? This is quite and urgent requirement for out needs as we manage ALL our users through terraform using the azuread_invitation resource and we are currently unable to set any of the additional attributes

@Gonkers
Copy link

Gonkers commented Jun 13, 2024

This pull request was completed in record time. Thank you so much. However, it turned out to be a foot gun when I got it in my hands.

You see, just like the user in #851, I too was inviting users that didn't exist in my Azure Tenant. When I used azuread_users to look up a large group of users by mails it found almost all of the users, but about 5% of users it did not find by email address (for reasons that were no fault of the azuread provider from what I can tell).

For users that were not found I was using azuread_invitation to create invitations. Come to find out, those accounts did in fact exist. Azure in the background somehow mapped those invitations to real accounts unbeknownst to me. When I thought those invitations weren't actually needed, I removed them from my code and the azuread provider deleted those invitations, and the user accounts they got associated with. It was a long afternoon working on a post-mortem and getting the existing accounts restored.

I wanted to provide my feedback as a warning to others who also run into issues like #851. I hope they can be addressed with whatever is happening in this request. In the meantime, we have determined that user invitations into the tenant will be a manual process until the sharp edges can be removed and confidence can be regained in the azuread provider.

@manicminer
Copy link
Member

manicminer commented Jun 13, 2024

@Gonkers Sorry to hear about your outage, but thanks for reporting back - context like this is very useful in shaping design choices around rough primitives like B2B invitations. We can try to add some guardrails here. In your case, did you have multiple invitations corresponding to one external user, and/or an invitation(s) that went unanswered but the resulting user was already present and active in your tenant?

@Gonkers
Copy link

Gonkers commented Jun 13, 2024

There are 2 tenants, one for the entire company and a second for Azure resources and those who work on those Azure resources. All users in the Azure tenant are guests, but their email addresses are not necessarily consistent with the primary tenant. This meant some of the emails that I was using to look them up were not associated with their guest account. They still got the invitations and signed in with their guest account. That last part is what associated the invitation to their guest account. So when I thought we wouldn't need the invitations and removed them, it deleted the user accounts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants