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

Add support for mail lookup #996

Merged
merged 5 commits into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/data-sources/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
39 changes: 30 additions & 9 deletions internal/services/users/user_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -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,
},

Expand All @@ -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,
},

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)),
Expand Down
41 changes: 41 additions & 0 deletions internal/services/users/user_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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)
}