diff --git a/azuread/data_user.go b/azuread/data_user.go index f202a1778b..e85cb9d859 100644 --- a/azuread/data_user.go +++ b/azuread/data_user.go @@ -34,6 +34,14 @@ func dataUser() *schema.Resource { ConflictsWith: []string{"object_id"}, }, + "mail_nickname": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.NoEmptyStrings, + ConflictsWith: []string{"object_id", "user_principal_name"}, + }, + "account_enabled": { Type: schema.TypeBool, Computed: true, @@ -49,11 +57,6 @@ func dataUser() *schema.Resource { Computed: true, }, - "mail_nickname": { - Type: schema.TypeString, - Computed: true, - }, - "usage_location": { Type: schema.TypeString, Computed: true, @@ -80,8 +83,14 @@ func dataSourceUserRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error finding Azure AD User with object ID %q: %+v", oId, err) } user = *u + } else if mailNickname, ok := d.Get("mail_nickname").(string); ok && mailNickname != "" { + u, err := graph.UserGetByMailNickname(&client, ctx, mailNickname) + if err != nil { + return fmt.Errorf("Error finding Azure AD User with email alias %q: %+v", mailNickname, err) + } + user = *u } else { - return fmt.Errorf("one of `object_id` or `user_principal_name` must be supplied") + return fmt.Errorf("one of `object_id`, `user_principal_name` and `mail_nickname` must be supplied") } if user.ObjectID == nil { diff --git a/azuread/data_user_test.go b/azuread/data_user_test.go index 3acc001b99..5d665f2677 100644 --- a/azuread/data_user_test.go +++ b/azuread/data_user_test.go @@ -54,6 +54,28 @@ func TestAccAzureADUserDataSource_byObjectId(t *testing.T) { }) } +func TestAccAzureADUserDataSource_byMailNickname(t *testing.T) { + dsn := "data.azuread_user.test" + id := tf.AccRandTimeInt() + password := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADUserDataSource_byMailNickname(id, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dsn, "user_principal_name"), + resource.TestCheckResourceAttrSet(dsn, "account_enabled"), + resource.TestCheckResourceAttrSet(dsn, "display_name"), + resource.TestCheckResourceAttrSet(dsn, "mail_nickname"), + ), + }, + }, + }) +} + func testAccAzureADUserDataSource_byUserPrincipalName(id int, password string) string { return fmt.Sprintf(` %s @@ -73,3 +95,13 @@ data "azuread_user" "test" { } `, testAccADUser_basic(id, password)) } + +func testAccAzureADUserDataSource_byMailNickname(id int, password string) string { + return fmt.Sprintf(` +%s + +data "azuread_user" "test" { + mail_nickname = "${azuread_user.test.mail_nickname}" +} +`, testAccADUser_basic(id, password)) +} diff --git a/azuread/data_users.go b/azuread/data_users.go index 5fc8cf9aef..78a62427bd 100644 --- a/azuread/data_users.go +++ b/azuread/data_users.go @@ -45,6 +45,18 @@ func dataUsers() *schema.Resource { ValidateFunc: validate.NoEmptyStrings, }, }, + + "mail_nicknames": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MinItems: 1, + ConflictsWith: []string{"object_ids", "user_principal_names"}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, + }, + }, }, } } @@ -75,8 +87,17 @@ func dataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { } users = append(users, *u) } + } else if mailNicknames, ok := d.Get("mail_nicknames").([]interface{}); ok && len(mailNicknames) > 0 { + expectedCount = len(mailNicknames) + for _, v := range mailNicknames { + u, err := graph.UserGetByMailNickname(&client, ctx, v.(string)) + if err != nil { + return fmt.Errorf("Error finding Azure AD User with email alias %q: %+v", v.(string), err) + } + users = append(users, *u) + } } else { - return fmt.Errorf("one of `object_ids` or `user_principal_names` must be supplied") + return fmt.Errorf("one of `object_ids`, `user_principal_names` or `mail_nicknames` must be supplied") } if len(users) != expectedCount { @@ -85,6 +106,7 @@ func dataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { upns := make([]string, 0, len(users)) oids := make([]string, 0, len(users)) + mailNicknames := make([]string, 0, len(users)) for _, u := range users { if u.ObjectID == nil || u.UserPrincipalName == nil { return fmt.Errorf("User with nil ObjectId or UPN was found: %v", u) @@ -92,6 +114,7 @@ func dataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { oids = append(oids, *u.ObjectID) upns = append(upns, *u.UserPrincipalName) + mailNicknames = append(mailNicknames, *u.MailNickname) } h := sha1.New() @@ -102,5 +125,6 @@ func dataSourceUsersRead(d *schema.ResourceData, meta interface{}) error { d.SetId("users#" + base64.URLEncoding.EncodeToString(h.Sum(nil))) d.Set("object_ids", oids) d.Set("user_principal_names", upns) + d.Set("mail_nicknames", mailNicknames) return nil } diff --git a/azuread/data_users_test.go b/azuread/data_users_test.go index e8ed80833d..f4d61cfc37 100644 --- a/azuread/data_users_test.go +++ b/azuread/data_users_test.go @@ -50,6 +50,27 @@ func TestAccAzureADUsersDataSource_byObjectIds(t *testing.T) { }) } +func TestAccAzureADUsersDataSource_byMailNicknames(t *testing.T) { + dsn := "data.azuread_users.test" + id := tf.AccRandTimeInt() + password := "p@$$wR2" + acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureADUsersDataSource_byMailNicknames(id, password), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dsn, "user_principal_names.#", "2"), + resource.TestCheckResourceAttr(dsn, "object_ids.#", "2"), + resource.TestCheckResourceAttr(dsn, "mail_nicknames.#", "2"), + ), + }, + }, + }) +} + func testAccAzureADUsersDataSource_byUserPrincipalNames(id int, password string) string { return fmt.Sprintf(` %s @@ -69,3 +90,13 @@ data "azuread_users" "test" { } `, testAccADUser_threeUsersABC(id, password)) } + +func testAccAzureADUsersDataSource_byMailNicknames(id int, password string) string { + return fmt.Sprintf(` +%s + +data "azuread_users" "test" { + mail_nicknames = ["${azuread_user.testA.mail_nickname}", "${azuread_user.testB.mail_nickname}"] +} +`, testAccADUser_threeUsersABC(id, password)) +} diff --git a/azuread/helpers/graph/user.go b/azuread/helpers/graph/user.go index 559df36bf1..7b2d38516f 100644 --- a/azuread/helpers/graph/user.go +++ b/azuread/helpers/graph/user.go @@ -35,3 +35,29 @@ func UserGetByObjectId(client *graphrbac.UsersClient, ctx context.Context, objec return &user, nil } + +func UserGetByMailNickname(client *graphrbac.UsersClient, ctx context.Context, mailNickname string) (*graphrbac.User, error) { + filter := fmt.Sprintf("startswith(mailNickname,'%s')", mailNickname) + resp, err := client.ListComplete(ctx, filter) + if err != nil { + return nil, fmt.Errorf("Error listing Azure AD Users for filter %q: %+v", filter, err) + } + + values := resp.Response().Value + if values == nil { + return nil, fmt.Errorf("nil values for AD Users matching %q", filter) + } + if len(*values) == 0 { + return nil, fmt.Errorf("Found no AD Users matching %q", filter) + } + if len(*values) > 2 { + return nil, fmt.Errorf("Found multiple AD Users matching %q", filter) + } + + user := (*values)[0] + if user.DisplayName == nil { + return nil, fmt.Errorf("nil DisplayName for AD Users matching %q", filter) + } + + return &user, nil +} diff --git a/website/docs/d/user.html.markdown b/website/docs/d/user.html.markdown index 8c6243ca74..d5b40833aa 100644 --- a/website/docs/d/user.html.markdown +++ b/website/docs/d/user.html.markdown @@ -29,7 +29,9 @@ The following arguments are supported: * `object_id` - (Optional) Specifies the Object ID of the Application within Azure Active Directory. --> **NOTE:** Either a `user_principal_name` or an `object_id` must be specified. +* `mail_nickname` - (Optional) The email alias of the Azure AD User. + +-> **NOTE:** Either `user_principal_name`, `object_id` or `mail_nickname` must be specified. ## Attributes Reference diff --git a/website/docs/d/users.html.markdown b/website/docs/d/users.html.markdown index 6d716b6515..e7cd3da702 100644 --- a/website/docs/d/users.html.markdown +++ b/website/docs/d/users.html.markdown @@ -29,7 +29,9 @@ The following arguments are supported: * `object_ids` - (Optional) The Object IDs of the Azure AD Users. --> **NOTE:** Either `user_principal_names` or `object_ids` must be specified. +* `mail_nicknames` - (Optional) The email aliases of the Azure AD Users. + +-> **NOTE:** Either `user_principal_names`, `object_ids` or `mail_nicknames` must be specified. ## Attributes Reference @@ -37,3 +39,4 @@ The following attributes are exported: * `object_ids` - The Object IDs of the Azure AD Users. * `user_principal_names` - The User Principal Names of the Azure AD Users. +* `mail_nicknames` - The email aliases of the Azure AD Users.