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

Bugfix: password validation at apply-time … #543

Merged
merged 2 commits into from
Sep 1, 2021
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: 3 additions & 0 deletions docs/resources/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ The following arguments are supported:
* `onpremises_immutable_id` - (Optional) The value used to associate an on-premise Active Directory user account with their Azure AD user object. This must be specified if you are using a federated domain for the user's `user_principal_name` property when creating a new user account.
* `other_mails` - (Optional) A list of additional email addresses for the user.
* `password` - (Optional) The password for the user. The password must satisfy minimum requirements as specified by the password policy. The maximum length is 256 characters. This property is required when creating a new user.

-> **Passwords and importing users** Passwords can be changed but not cleared. Removing the `password` property for an existing user resource, or setting the password value to a blank string, will not remove the password. When importing a user, Terraform will not reset the password unless the value is subsequently changed in your configuration.

* `postal_code` - (Optional) The postal code for the user's postal address. The postal code is specific to the user's country/region. In the United States of America, this attribute contains the ZIP code.
* `preferred_language` - (Optional) The user's preferred language, in ISO 639-1 notation.
* `show_in_address_list` - (Optional) Whether or not the Outlook global address list should include this user. Defaults to `true`.
Expand Down
15 changes: 8 additions & 7 deletions internal/services/users/user_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,6 @@ func userResource() *schema.Resource {
}

func userResourceCustomizeDiff(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error {
if diff.Id() == "" && diff.Get("password").(string) == "" {
return fmt.Errorf("`password` is required when creating a new user")
}

ageGroup := diff.Get("age_group").(string)
consentRequired := diff.Get("consent_provided_for_minor").(string)

Expand All @@ -358,6 +354,11 @@ func userResourceCustomizeDiff(ctx context.Context, diff *schema.ResourceDiff, m
func userResourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*clients.Client).Users.UsersClient

password := d.Get("password").(string)
if password == "" {
return tf.ErrorDiagPathF(errors.New("`password` is required when creating a new user"), "password", "Could not create user")
}

upn := d.Get("user_principal_name").(string)
mailNickName := d.Get("mail_nickname").(string)

Expand Down Expand Up @@ -395,7 +396,7 @@ func userResourceCreate(ctx context.Context, d *schema.ResourceData, meta interf

PasswordProfile: &msgraph.UserPasswordProfile{
ForceChangePasswordNextSignIn: utils.Bool(d.Get("force_password_change").(bool)),
Password: utils.String(d.Get("password").(string)),
Password: utils.String(password),
},
}

Expand Down Expand Up @@ -453,10 +454,10 @@ func userResourceUpdate(ctx context.Context, d *schema.ResourceData, meta interf
UsageLocation: utils.NullableString(d.Get("usage_location").(string)),
}

if d.HasChange("password") {
if password := d.Get("password").(string); d.HasChange("password") && password != "" {
properties.PasswordProfile = &msgraph.UserPasswordProfile{
ForceChangePasswordNextSignIn: utils.Bool(d.Get("force_password_change").(bool)),
Password: utils.String(d.Get("password").(string)),
Password: utils.String(password),
}
}

Expand Down
64 changes: 64 additions & 0 deletions internal/services/users/user_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
Expand Down Expand Up @@ -98,6 +99,33 @@ func TestAccUser_threeUsersABC(t *testing.T) {
})
}

func TestAccUser_withRandomProvider(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_user", "test")
r := UserResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.withRandomProvider(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("force_password_change", "password"),
})
}

func TestAccUser_passwordOmitted(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_user", "test")
r := UserResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.passwordOmitted(data),
ExpectError: regexp.MustCompile("`password` is required when creating a new user"),
},
})
}

func (r UserResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) {
client := clients.Users.UsersClient
client.BaseClient.DisableRetries = true
Expand Down Expand Up @@ -202,3 +230,39 @@ resource "azuread_user" "testC" {
}
`, data.RandomInteger, data.RandomPassword)
}

func (UserResource) withRandomProvider(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azuread" {}
provider "random" {}

data "azuread_domains" "test" {
only_initial = true
}

resource "random_password" "test" {
length = 32
}

resource "azuread_user" "test" {
user_principal_name = "acctestUser.%[1]d@${data.azuread_domains.test.domains.0.domain_name}"
display_name = "acctestUser-%[1]d"
password = random_password.test.result
}
`, data.RandomInteger)
}

func (UserResource) passwordOmitted(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azuread" {}

data "azuread_domains" "test" {
only_initial = true
}

resource "azuread_user" "test" {
user_principal_name = "acctestUser.%[1]d@${data.azuread_domains.test.domains.0.domain_name}"
display_name = "acctestUser-%[1]d"
}
`, data.RandomInteger)
}