Skip to content

Commit

Permalink
Find user and users by using employee ID
Browse files Browse the repository at this point in the history
  • Loading branch information
AL1034257 authored and pchanvallon committed Mar 7, 2023
1 parent b748de5 commit e9b22a7
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 53 deletions.
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:

* `employee_id` - (Optional) The employee identifier assigned to the user by the organisation.
* `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_nickname` or `employee_id` must be specified.

## Attributes Reference

Expand Down
5 changes: 4 additions & 1 deletion docs/data-sources/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ data "azuread_users" "users" {

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.
* `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` or `mail_nicknames` 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` or `employee_id` 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 identifier assigned to the users by the organisation.
* `mail_nicknames` - The email aliases of the users.
* `object_ids` - The object IDs of the users.
* `user_principal_names` - The user principal names (UPNs) of the users.
Expand All @@ -49,6 +51,7 @@ The following attributes are exported:

* `account_enabled` - Whether or not 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.
* `object_id` - The object ID of the user.
Expand Down
42 changes: 32 additions & 10 deletions internal/services/users/user_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
)

func userDataSource() *schema.Resource {
exactlyOneOfList := []string{"object_id", "user_principal_name", "mail_nickname", "employee_id"}
return &schema.Resource{
ReadContext: userDataSourceRead,

Expand All @@ -26,12 +27,21 @@ func userDataSource() *schema.Resource {
},

Schema: map[string]*schema.Schema{
"employee_id": {
Description: "The employee identifier assigned to the user by the organisation",
Type: schema.TypeString,
Optional: true,
Computed: true,
ExactlyOneOf: exactlyOneOfList,
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"},
Computed: true,
ExactlyOneOf: exactlyOneOfList,
ValidateDiagFunc: validate.NoEmptyStrings,
},

Expand All @@ -40,7 +50,7 @@ func userDataSource() *schema.Resource {
Type: schema.TypeString,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"mail_nickname", "object_id", "user_principal_name"},
ExactlyOneOf: exactlyOneOfList,
ValidateDiagFunc: validate.UUID,
},

Expand All @@ -49,7 +59,7 @@ func userDataSource() *schema.Resource {
Type: schema.TypeString,
Optional: true,
Computed: true,
ExactlyOneOf: []string{"mail_nickname", "object_id", "user_principal_name"},
ExactlyOneOf: exactlyOneOfList,
ValidateDiagFunc: validate.NoEmptyStrings,
},

Expand Down Expand Up @@ -128,12 +138,6 @@ func userDataSource() *schema.Resource {
Computed: true,
},

"employee_id": {
Description: "The employee identifier assigned to the user by the organisation",
Type: schema.TypeString,
Computed: true,
},

"employee_type": {
Description: "Captures enterprise worker type. For example, Employee, Contractor, Consultant, or Vendor.",
Type: schema.TypeString,
Expand Down Expand Up @@ -362,8 +366,26 @@ func userDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interf
return tf.ErrorDiagPathF(err, "mail_nickname", "User not found with email alias: %q", mailNickname)
}
user = (*users)[0]
} else if employeeId, ok := d.Get("employee_id").(string); ok && employeeId != "" {
query := odata.Query{
Filter: fmt.Sprintf("employeeId eq '%s'", utils.EscapeSingleQuote(employeeId)),
}
users, _, err := client.List(ctx, query)
if err != nil {
return tf.ErrorDiagF(err, "Finding user with employee ID: %q", employeeId)
}
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, "employee_id", "More than one user found with employee ID: %q", employeeId)
} else if count == 0 {
return tf.ErrorDiagPathF(err, "employee_id", "User not found with employee ID: %q", employeeId)
}
user = (*users)[0]
} else {
return tf.ErrorDiagF(nil, "One of `object_id`, `user_principal_name` or `mail_nickname` must be supplied")
return tf.ErrorDiagF(nil, "One of `object_id`, `user_principal_name`, `mail_nickname` or `employee_id` must be supplied")
}

if user.ID() == nil {
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_byEmployeeId(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azuread_user", "test")
r := UserDataSource{}

data.DataSourceTest(t, []resource.TestStep{{
Config: r.byEmployeeId(data),
Check: r.testCheckFunc(data),
}})
}

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

data.DataSourceTest(t, []resource.TestStep{{
Config: UserDataSource{}.byEmployeeIdNonexistent(data),
ExpectError: regexp.MustCompile("User not found with employee ID:"),
}})
}

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) byEmployeeId(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s
data "azuread_user" "test" {
employee_id = azuread_user.test.employee_id
}
`, UserResource{}.complete(data))
}

func (UserDataSource) byEmployeeIdNonexistent(data acceptance.TestData) string {
return `
data "azuread_domains" "test" {
only_initial = true
}
data "azuread_user" "test" {
employee_id = "not-a-real-employeeid"
}
`
}
4 changes: 3 additions & 1 deletion internal/services/users/user_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,15 @@ data "azuread_domains" "test" {
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"
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"
employee_id = "B%[3]s%[3]s"
password = "%[2]s"
}
Expand All @@ -239,7 +241,7 @@ resource "azuread_user" "testC" {
display_name = "acctestUser-%[1]d-C"
password = "%[2]s"
}
`, data.RandomInteger, data.RandomPassword)
`, data.RandomInteger, data.RandomPassword, data.RandomString)
}

func (UserResource) withRandomProvider(data acceptance.TestData) string {
Expand Down
Loading

0 comments on commit e9b22a7

Please sign in to comment.