diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md index 89f105b799..a3eddf0a6b 100644 --- a/docs/data-sources/user.md +++ b/docs/data-sources/user.md @@ -26,11 +26,12 @@ data "azuread_user" "example" { The following arguments are supported: +* `mail` - (Optional) The SMTP address for the user. * `mail_nickname` - (Optional) The email alias of the user. * `object_id` - (Optional) The object ID of the user. * `user_principal_name` - (Optional) The user principal name (UPN) of the user. -~> One of `user_principal_name`, `object_id` or `mail_nickname` must be specified. +~> One of `user_principal_name`, `object_id`, `mail` or `mail_nickname` must be specified. ## Attributes Reference diff --git a/internal/services/users/user_data_source.go b/internal/services/users/user_data_source.go index 544398c743..3045e80606 100644 --- a/internal/services/users/user_data_source.go +++ b/internal/services/users/user_data_source.go @@ -26,11 +26,20 @@ func userDataSource() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "mail": { + Description: "The SMTP address for the user", + Type: schema.TypeString, + Optional: true, + ExactlyOneOf: []string{"mail", "mail_nickname", "object_id", "user_principal_name"}, + Computed: true, + ValidateDiagFunc: validate.NoEmptyStrings, + }, + "mail_nickname": { Description: "The email alias of the user", Type: schema.TypeString, Optional: true, - ExactlyOneOf: []string{"mail_nickname", "object_id", "user_principal_name"}, + ExactlyOneOf: []string{"mail", "mail_nickname", "object_id", "user_principal_name"}, Computed: true, ValidateDiagFunc: validate.NoEmptyStrings, }, @@ -40,7 +49,7 @@ func userDataSource() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ExactlyOneOf: []string{"mail_nickname", "object_id", "user_principal_name"}, + ExactlyOneOf: []string{"mail", "mail_nickname", "object_id", "user_principal_name"}, ValidateDiagFunc: validate.UUID, }, @@ -49,7 +58,7 @@ func userDataSource() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ExactlyOneOf: []string{"mail_nickname", "object_id", "user_principal_name"}, + ExactlyOneOf: []string{"mail", "mail_nickname", "object_id", "user_principal_name"}, ValidateDiagFunc: validate.NoEmptyStrings, }, @@ -173,12 +182,6 @@ func userDataSource() *schema.Resource { Computed: true, }, - "mail": { - Description: "The SMTP address for the user", - Type: schema.TypeString, - Computed: true, - }, - "manager_id": { Description: "The object ID of the user's manager", Type: schema.TypeString, @@ -344,6 +347,24 @@ func userDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interf return tf.ErrorDiagPathF(nil, "object_id", "User not found with object ID: %q", objectId) } user = *u + } else if mail, ok := d.Get("mail").(string); ok && mail != "" { + query := odata.Query{ + Filter: fmt.Sprintf("mail eq '%s'", utils.EscapeSingleQuote(mail)), + } + users, _, err := client.List(ctx, query) + if err != nil { + return tf.ErrorDiagF(err, "Finding user with mail: %q", mail) + } + if users == nil { + return tf.ErrorDiagF(errors.New("API returned nil result"), "Bad API Response") + } + count := len(*users) + if count > 1 { + return tf.ErrorDiagPathF(nil, "mail", "More than one user found with mail: %q", upn) + } else if count == 0 { + return tf.ErrorDiagPathF(err, "mail", "User not found with mail: %q", upn) + } + user = (*users)[0] } else if mailNickname, ok := d.Get("mail_nickname").(string); ok && mailNickname != "" { query := odata.Query{ Filter: fmt.Sprintf("mailNickname eq '%s'", utils.EscapeSingleQuote(mailNickname)), diff --git a/internal/services/users/user_data_source_test.go b/internal/services/users/user_data_source_test.go index 92d70b94b7..7b0cadb74b 100644 --- a/internal/services/users/user_data_source_test.go +++ b/internal/services/users/user_data_source_test.go @@ -69,6 +69,25 @@ func TestAccUserDataSource_byMailNicknameNonexistent(t *testing.T) { }}) } +func TestAccUserDataSource_byMail(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azuread_user", "test") + r := UserDataSource{} + + data.DataSourceTest(t, []resource.TestStep{{ + Config: r.byMail(data), + Check: r.testCheckFunc(data), + }}) +} + +func TestAccUserDataSource_byMailNonexistent(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azuread_user", "test") + + data.DataSourceTest(t, []resource.TestStep{{ + Config: UserDataSource{}.byMailNonexistent(data), + ExpectError: regexp.MustCompile("User not found with mail:"), + }}) +} + func (UserDataSource) testCheckFunc(data acceptance.TestData) resource.TestCheckFunc { return resource.ComposeTestCheckFunc( check.That(data.ResourceName).Key("account_enabled").Exists(), @@ -157,3 +176,25 @@ data "azuread_user" "test" { } `, data.RandomInteger) } + +func (UserDataSource) byMail(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +data "azuread_user" "test" { + mail = azuread_user.test.mail +} +`, UserResource{}.complete(data)) +} + +func (UserDataSource) byMailNonexistent(data acceptance.TestData) string { + return fmt.Sprintf(` +data "azuread_domains" "test" { + only_initial = true +} + +data "azuread_user" "test" { + mail = "not-a-real-user-%[1]d${data.azuread_domains.test.domains.0.domain_name}" +} +`, data.RandomInteger) +}