diff --git a/azuread/data_user.go b/azuread/data_user.go index b92635c46e..bb29c9d621 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -52,6 +52,11 @@ func dataUser() *schema.Resource { Computed: true, }, + "immutable_id": { + Type: schema.TypeString, + Computed: true, + }, + "mail": { Type: schema.TypeString, Computed: true, @@ -112,6 +117,7 @@ func dataSourceUserRead(d *schema.ResourceData, meta interface{}) error { d.Set("user_principal_name", user.UserPrincipalName) d.Set("account_enabled", user.AccountEnabled) d.Set("display_name", user.DisplayName) + d.Set("immutable_id", user.ImmutableID) d.Set("mail", user.Mail) d.Set("mail_nickname", user.MailNickname) d.Set("usage_location", user.UsageLocation) diff --git a/azuread/helpers/p/p.go b/azuread/helpers/p/p.go index a1d6a05b34..0a04e9b8a9 100644 --- a/azuread/helpers/p/p.go +++ b/azuread/helpers/p/p.go @@ -5,10 +5,25 @@ func Bool(input bool) *bool { return &input } +func BoolI(i interface{}) *bool { + b := i.(bool) + return &b +} + func Int32(input int32) *int32 { return &input } +func Int32I(i interface{}) *int32 { + i32 := i.(int32) + return &i32 +} + func String(input string) *string { return &input } + +func StringI(i interface{}) *string { + s := i.(string) + return &s +} diff --git a/azuread/resource_user.go b/azuread/resource_user.go index 5b133da3ce..8f3923a679 100644 --- a/azuread/resource_user.go +++ b/azuread/resource_user.go @@ -80,6 +80,14 @@ func resourceUser() *schema.Resource { Computed: true, }, + "immutable_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "This must be specified if you are using a federated domain for the user's userPrincipalName (UPN) property when creating a new user account. " + + "It is used to associate an on-premises Active Directory user account with their Azure AD user object.", + }, + "object_id": { Type: schema.TypeString, Computed: true, @@ -102,11 +110,7 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { ctx := meta.(*ArmClient).StopContext upn := d.Get("user_principal_name").(string) - displayName := d.Get("display_name").(string) mailNickName := d.Get("mail_nickname").(string) - accountEnabled := d.Get("account_enabled").(bool) - password := d.Get("password").(string) - forcePasswordChange := d.Get("force_password_change").(bool) //default mail nickname to the first part of the UPN (matches the portal) if mailNickName == "" { @@ -114,18 +118,22 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { } userCreateParameters := graphrbac.UserCreateParameters{ - AccountEnabled: &accountEnabled, - DisplayName: &displayName, + AccountEnabled: p.BoolI(d.Get("account_enabled")), + DisplayName: p.StringI(d.Get("display_name")), MailNickname: &mailNickName, PasswordProfile: &graphrbac.PasswordProfile{ - ForceChangePasswordNextLogin: &forcePasswordChange, - Password: &password, + ForceChangePasswordNextLogin: p.BoolI(d.Get("force_password_change")), + Password: p.StringI(d.Get("password")), }, UserPrincipalName: &upn, } if v, ok := d.GetOk("usage_location"); ok { - userCreateParameters.UsageLocation = p.String(v.(string)) + userCreateParameters.UsageLocation = p.StringI(v) + } + + if v, ok := d.GetOk("immutable_id"); ok { + userCreateParameters.ImmutableID = p.StringI(v) } user, err := client.Create(ctx, userCreateParameters) @@ -147,6 +155,46 @@ func resourceUserCreate(d *schema.ResourceData, meta interface{}) error { return resourceUserRead(d, meta) } +func resourceUserUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).usersClient + ctx := meta.(*ArmClient).StopContext + + var userUpdateParameters graphrbac.UserUpdateParameters + + if d.HasChange("display_name") { + userUpdateParameters.DisplayName = p.StringI(d.Get("display_name")) + } + + if d.HasChange("mail_nickname") { + userUpdateParameters.MailNickname = p.StringI(d.Get("mail_nickname")) + } + + if d.HasChange("account_enabled") { + userUpdateParameters.AccountEnabled = p.BoolI(d.Get("account_enabled")) + } + + if d.HasChange("password") { + userUpdateParameters.PasswordProfile = &graphrbac.PasswordProfile{ + ForceChangePasswordNextLogin: p.BoolI(d.Get("force_password_change")), + Password: p.StringI(d.Get("password")), + } + } + + if d.HasChange("usage_location") { + userUpdateParameters.UsageLocation = p.StringI(d.Get("usage_location")) + } + + if d.HasChange("immutable_id") { + userUpdateParameters.ImmutableID = p.StringI(d.Get("immutable_id")) + } + + if _, err := client.Update(ctx, d.Id(), userUpdateParameters); err != nil { + return fmt.Errorf("Error updating User with ID %q: %+v", d.Id(), err) + } + + return resourceUserRead(d, meta) +} + func resourceUserRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).usersClient ctx := meta.(*ArmClient).StopContext @@ -170,6 +218,7 @@ func resourceUserRead(d *schema.ResourceData, meta interface{}) error { d.Set("account_enabled", user.AccountEnabled) d.Set("object_id", user.ObjectID) d.Set("usage_location", user.UsageLocation) + d.Set("immutable_id", user.ImmutableID) d.Set("onpremises_sam_account_name", user.AdditionalProperties["onPremisesSamAccountName"]) d.Set("onpremises_user_principal_name", user.AdditionalProperties["onPremisesUserPrincipalName"]) @@ -177,51 +226,6 @@ func resourceUserRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceUserUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).usersClient - ctx := meta.(*ArmClient).StopContext - - var userUpdateParameters graphrbac.UserUpdateParameters - - if d.HasChange("display_name") { - displayName := d.Get("display_name").(string) - userUpdateParameters.DisplayName = p.String(displayName) - } - - if d.HasChange("mail_nickname") { - mailNickName := d.Get("mail_nickname").(string) - userUpdateParameters.MailNickname = p.String(mailNickName) - } - - if d.HasChange("account_enabled") { - accountEnabled := d.Get("account_enabled").(bool) - userUpdateParameters.AccountEnabled = p.Bool(accountEnabled) - } - - if d.HasChange("password") { - password := d.Get("password").(string) - forcePasswordChange := d.Get("force_password_change").(bool) - - passwordProfile := &graphrbac.PasswordProfile{ - ForceChangePasswordNextLogin: &forcePasswordChange, - Password: &password, - } - - userUpdateParameters.PasswordProfile = passwordProfile - } - - if d.HasChange("usage_location") { - usageLocation := d.Get("usage_location").(string) - userUpdateParameters.UsageLocation = p.String(usageLocation) - } - - if _, err := client.Update(ctx, d.Id(), userUpdateParameters); err != nil { - return fmt.Errorf("Error updating User with ID %q: %+v", d.Id(), err) - } - - return resourceUserRead(d, meta) -} - func resourceUserDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).usersClient ctx := meta.(*ArmClient).StopContext diff --git a/azuread/resource_user_test.go b/azuread/resource_user_test.go index 1509e1938e..754597d853 100644 --- a/azuread/resource_user_test.go +++ b/azuread/resource_user_test.go @@ -2,6 +2,7 @@ package azuread import ( "fmt" + "strconv" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" @@ -65,6 +66,7 @@ func TestAccAzureADUser_complete(t *testing.T) { resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestUser-%d-Updated", id)), resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser-%d-Updated", id)), resource.TestCheckResourceAttr(rn, "account_enabled", "false"), + resource.TestCheckResourceAttr(rn, "immutable_id", strconv.Itoa(id)), ), }, { @@ -111,6 +113,7 @@ func TestAccAzureADUser_update(t *testing.T) { resource.TestCheckResourceAttr(rn, "display_name", fmt.Sprintf("acctestUser-%d-Updated", id)), resource.TestCheckResourceAttr(rn, "mail_nickname", fmt.Sprintf("acctestUser-%d-Updated", id)), resource.TestCheckResourceAttr(rn, "account_enabled", "false"), + resource.TestCheckResourceAttr(rn, "immutable_id", strconv.Itoa(id)), ), }, { @@ -204,6 +207,7 @@ resource "azuread_user" "test" { password = "%[2]s" force_password_change = true usage_location = "NO" + immutable_id = "%[1]d" } `, id, password) } diff --git a/website/docs/d/user.html.markdown b/website/docs/d/user.html.markdown index e837582e29..47a1170e7b 100644 --- a/website/docs/d/user.html.markdown +++ b/website/docs/d/user.html.markdown @@ -31,7 +31,7 @@ The following arguments are supported: * `mail_nickname` - (Optional) The email alias of the Azure AD User. --> **NOTE:** Either `user_principal_name`, `object_id` or `mail_nickname` must be specified. +-> **NOTE:** One of `user_principal_name`, `object_id` or `mail_nickname` must be specified. ## Attributes Reference @@ -47,3 +47,4 @@ The following attributes are exported: * `onpremises_sam_account_name` - The on premise sam account name of the Azure AD User. * `onpremises_user_principal_name` - The on premise user principal name of the Azure AD User. * `usage_location` - The usage location of the Azure AD User. +* `immutable_id` - The value used to associate an on-premises Active Directory user account with their Azure AD user object. diff --git a/website/docs/r/user.html.markdown b/website/docs/r/user.html.markdown index c7e378de5d..3bd727891d 100644 --- a/website/docs/r/user.html.markdown +++ b/website/docs/r/user.html.markdown @@ -34,6 +34,7 @@ The following arguments are supported: * `mail_nickname`- (Optional) The mail alias for the user. Defaults to the user name part of the User Principal Name. * `password` - (Required) The password for the User. The password must satisfy minimum requirements as specified by the password policy. The maximum length is 256 characters. * `force_password_change` - (Optional) `true` if the User is forced to change the password during the next sign-in. Defaults to `false`. +* `immutable_id` - (Optional) The value used to associate an on-premises Active Directory user account with their Azure AD user object. This must be specified if you are using a federated domain for the user's userPrincipalName (UPN) property when creating a new user account. * `usage_location` - (Optional) The usage location of the User. Required for users that will be assigned licenses due to legal requirement to check for availability of services in countries. The usage location is a two letter country code (ISO standard 3166). Examples include: `NO`, `JP`, and `GB`. Cannot be reset to null once set. ## Attributes Reference