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

data.azuread_users: support for the mails property #1400

Merged
merged 1 commit into from
Jun 6, 2024
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
8 changes: 5 additions & 3 deletions docs/data-sources/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,20 @@ The following arguments are supported:
* `employee_ids` - (Optional) The employee identifiers assigned to the users by the organisation.
* `ignore_missing` - (Optional) Ignore missing users and return users that were found. The data source will still fail if no users are found. Cannot be specified with `return_all`. Defaults to `false`.
* `mail_nicknames` - (Optional) The email aliases of the users.
* `mails` - (Optional) The SMTP email addresses of the users.
* `object_ids` - (Optional) The object IDs of the users.
* `return_all` - (Optional) When `true`, the data source will return all users. Cannot be used with `ignore_missing`. Defaults to `false`.
* `user_principal_names` - (Optional) The user principal names (UPNs) of the users.

~> Either `return_all`, or one of `user_principal_names`, `object_ids`, `mail_nicknames` or `employee_ids` must be specified. These _may_ be specified as an empty list, in which case no results will be returned.
~> Either `return_all`, or one of `user_principal_names`, `object_ids`, `mail_nicknames`, `mails`, or `employee_ids` must be specified. These _may_ be specified as an empty list, in which case no results will be returned.

## Attributes Reference

The following attributes are exported:

* `employee_ids` - The employee identifiers assigned to the users by the organisation.
* `mail_nicknames` - The email aliases of the users.
* `mails` - The SMTP email addresses of the users.
* `object_ids` - The object IDs of the users.
* `user_principal_names` - The user principal names (UPNs) of the users.
* `users` - A list of users. Each `user` object provides the attributes documented below.
Expand All @@ -49,11 +51,11 @@ The following attributes are exported:

`user` object exports the following:

* `account_enabled` - Whether or not the account is enabled.
* `account_enabled` - Whether the account is enabled.
* `display_name` - The display name of the user.
* `employee_id` - The employee identifier assigned to the user by the organisation.
* `mail_nickname` - The email alias of the user.
* `mail` - The primary email address of the user.
* `mail` - The SMTP email address of the user.
* `object_id` - The object ID of the user.
* `onpremises_immutable_id` - The value used to associate an on-premises Active Directory user account with their Azure AD user object.
* `onpremises_sam_account_name` - The on-premise SAM account name of the user.
Expand Down
2 changes: 2 additions & 0 deletions internal/services/users/user_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,15 @@ resource "azuread_user" "testA" {
user_principal_name = "acctestUser'%[1]d.A@${data.azuread_domains.test.domains.0.domain_name}"
display_name = "acctestUser-%[1]d-A"
employee_id = "A%[3]s%[3]s"
mail = "acctestUser-%[1]d-A@${data.azuread_domains.test.domains.0.domain_name}"
password = "%[2]s"
}

resource "azuread_user" "testB" {
user_principal_name = "acctestUser.%[1]d.B@${data.azuread_domains.test.domains.0.domain_name}"
display_name = "acctestUser-%[1]d-B"
mail_nickname = "acctestUser-%[1]d-B"
mail = "acctestUser-%[1]d-B@${data.azuread_domains.test.domains.0.domain_name}"
employee_id = "B%[3]s%[3]s"
password = "%[2]s"
}
Expand Down
52 changes: 47 additions & 5 deletions internal/services/users/users_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func usersData() *pluginsdk.Resource {
Type: pluginsdk.TypeList,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "employee_ids", "return_all"},
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "mails", "employee_ids", "return_all"},
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty),
Expand All @@ -47,7 +47,19 @@ func usersData() *pluginsdk.Resource {
Type: pluginsdk.TypeList,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "employee_ids", "return_all"},
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "mails", "employee_ids", "return_all"},
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty),
},
},

"mails": {
Description: "The SMTP address of the users",
Type: pluginsdk.TypeList,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "mails", "employee_ids", "return_all"},
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty),
Expand All @@ -59,7 +71,7 @@ func usersData() *pluginsdk.Resource {
Type: pluginsdk.TypeList,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "employee_ids", "return_all"},
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "mails", "employee_ids", "return_all"},
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID),
Expand All @@ -71,7 +83,7 @@ func usersData() *pluginsdk.Resource {
Type: pluginsdk.TypeList,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "employee_ids", "return_all"},
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "mails", "employee_ids", "return_all"},
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty),
Expand All @@ -92,7 +104,7 @@ func usersData() *pluginsdk.Resource {
Optional: true,
Default: false,
ConflictsWith: []string{"ignore_missing"},
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "employee_ids", "return_all"},
ExactlyOneOf: []string{"object_ids", "user_principal_names", "mail_nicknames", "mails", "employee_ids", "return_all"},
},

"users": {
Expand Down Expand Up @@ -263,6 +275,31 @@ func usersDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta in
}
users = append(users, (*result)[0])
}
} else if mails, ok := d.Get("mails").([]interface{}); ok && len(mails) > 0 {
expectedCount = len(mails)
for _, v := range mails {
query := odata.Query{
Filter: fmt.Sprintf("mail eq '%s'", odata.EscapeSingleQuote(v.(string))),
}
result, _, err := client.List(ctx, query)
if err != nil {
return tf.ErrorDiagF(err, "Finding user with mail address: %q", v)
}
if result == nil {
return tf.ErrorDiagF(errors.New("API returned nil result"), "Bad API Response")
}

count := len(*result)
if count > 1 {
return tf.ErrorDiagPathF(nil, "mails", "More than one user found with mail address: %q", v)
} else if count == 0 {
if ignoreMissing {
continue
}
return tf.ErrorDiagPathF(err, "mails", "User not found with mail address: %q", v)
}
users = append(users, (*result)[0])
}
} else if employeeIds, ok := d.Get("employee_ids").([]interface{}); ok && len(employeeIds) > 0 {
expectedCount = len(employeeIds)
for _, v := range employeeIds {
Expand Down Expand Up @@ -299,6 +336,7 @@ func usersDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta in
upns := make([]string, 0)
objectIds := make([]string, 0)
mailNicknames := make([]string, 0)
mails := make([]msgraph.StringNullWhenEmpty, 0)
employeeIds := make([]msgraph.StringNullWhenEmpty, 0)
userList := make([]map[string]interface{}, 0)
for _, u := range users {
Expand All @@ -311,6 +349,9 @@ func usersDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta in
if u.MailNickname != nil {
mailNicknames = append(mailNicknames, *u.MailNickname)
}
if u.Mail != nil {
mails = append(mails, *u.Mail)
}
if u.EmployeeId != nil {
employeeIds = append(employeeIds, *u.EmployeeId)
}
Expand Down Expand Up @@ -339,6 +380,7 @@ func usersDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta in
d.SetId("users#" + base64.URLEncoding.EncodeToString(h.Sum(nil)))
tf.Set(d, "employee_ids", employeeIds)
tf.Set(d, "mail_nicknames", mailNicknames)
tf.Set(d, "mails", mails)
tf.Set(d, "object_ids", objectIds)
tf.Set(d, "user_principal_names", upns)
tf.Set(d, "users", userList)
Expand Down
58 changes: 58 additions & 0 deletions internal/services/users/users_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,38 @@ func TestAccUsersDataSource_byMailNicknamesIgnoreMissing(t *testing.T) {
}})
}

func TestAccUsersDataSource_byMails(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azuread_users", "test")

data.DataSourceTest(t, []acceptance.TestStep{{
Config: UsersDataSource{}.byMails(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("user_principal_names.#").HasValue("2"),
check.That(data.ResourceName).Key("object_ids.#").HasValue("2"),
check.That(data.ResourceName).Key("mail_nicknames.#").HasValue("2"),
check.That(data.ResourceName).Key("mails.#").HasValue("2"),
check.That(data.ResourceName).Key("employee_ids.#").HasValue("2"),
check.That(data.ResourceName).Key("users.#").HasValue("2"),
),
}})
}

func TestAccUsersDataSource_byMailsIgnoreMissing(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azuread_users", "test")

data.DataSourceTest(t, []acceptance.TestStep{{
Config: UsersDataSource{}.byMailsIgnoreMissing(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("user_principal_names.#").HasValue("2"),
check.That(data.ResourceName).Key("object_ids.#").HasValue("2"),
check.That(data.ResourceName).Key("mail_nicknames.#").HasValue("2"),
check.That(data.ResourceName).Key("mails.#").HasValue("2"),
check.That(data.ResourceName).Key("employee_ids.#").HasValue("2"),
check.That(data.ResourceName).Key("users.#").HasValue("2"),
),
}})
}

func TestAccUsersDataSource_byEmployeeIds(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azuread_users", "test")

Expand Down Expand Up @@ -242,6 +274,32 @@ data "azuread_users" "test" {
`, UserResource{}.threeUsersABC(data), data.RandomInteger)
}

func (UsersDataSource) byMails(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s

data "azuread_users" "test" {
mails = [azuread_user.testA.mail, azuread_user.testB.mail]
}
`, UserResource{}.threeUsersABC(data))
}

func (UsersDataSource) byMailsIgnoreMissing(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s

data "azuread_users" "test" {
ignore_missing = true

mails = [
azuread_user.testA.mail,
"not-a-real-user-%[2]d${data.azuread_domains.test.domains.0.domain_name}",
azuread_user.testB.mail,
]
}
`, UserResource{}.threeUsersABC(data), data.RandomInteger)
}

func (UsersDataSource) byEmployeeIds(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s
Expand Down
Loading