Skip to content

Commit

Permalink
data.azuread_users: support for the mails property
Browse files Browse the repository at this point in the history
  • Loading branch information
manicminer committed Jun 6, 2024
1 parent 8b7b123 commit 96e14fe
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 8 deletions.
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

0 comments on commit 96e14fe

Please sign in to comment.