From 5bd6cfde45fb5b498cbcc5a081e0ebfcd37ac250 Mon Sep 17 00:00:00 2001 From: Alex Wilcox Date: Tue, 26 Sep 2023 10:43:34 +0000 Subject: [PATCH 1/3] Add authentication_strength_policy --- .../authentication_strength_policy.md | 50 +++++ docs/resources/conditional_access_policy.md | 3 +- go.mod | 2 +- go.sum | 4 +- ...authentication_strength_policy_resource.go | 174 ++++++++++++++++++ ...ntication_strength_policy_resource_test.go | 137 ++++++++++++++ .../conditionalaccess/client/client.go | 13 +- .../conditional_access_policy_resource.go | 11 +- ...conditional_access_policy_resource_test.go | 47 +++++ .../conditionalaccess/conditionalaccess.go | 5 + .../conditionalaccess/registration.go | 5 +- .../msgraph/authentication_strength_policy.go | 36 ++++ .../manicminer/hamilton/msgraph/models.go | 6 + .../manicminer/hamilton/msgraph/valuetypes.go | 5 + vendor/modules.txt | 2 +- 15 files changed, 487 insertions(+), 13 deletions(-) create mode 100644 docs/resources/authentication_strength_policy.md create mode 100644 internal/services/conditionalaccess/authentication_strength_policy_resource.go create mode 100644 internal/services/conditionalaccess/authentication_strength_policy_resource_test.go diff --git a/docs/resources/authentication_strength_policy.md b/docs/resources/authentication_strength_policy.md new file mode 100644 index 0000000000..b297cdc45f --- /dev/null +++ b/docs/resources/authentication_strength_policy.md @@ -0,0 +1,50 @@ +--- +subcategory: "Conditional Access" +--- + +# Resource: azuread_authentication_strength_policy + +Manages a Authentication Strength Policy within Azure Active Directory. + +## API Permissions + +The following API permissions are required in order to use this resource. + +When authenticated with a service principal, this resource requires the following application roles: `Policy.ReadWrite.ConditionalAccess` and `Policy.Read.All` + +When authenticated with a user principal, this resource requires one of the following directory roles: `Conditional Access Administrator` or `Global Administrator` + +## Example Usage + +```terraform +resource "azuread_authentication_strength_policy" "example" { + display_name = "Example Authentication Strength Policy" + description = "Policy for demo purposes" + allowed_combinations = [ + "fido2", + "password", + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +- `allowed_combinations` - (Required) List of allowed authentication methods for this authentication strength policy. +- `description` - (Optional) The description for this authentication strength policy. +- `display_name` - (Required) The friendly name for this authentication strength policy. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the authentication strength policy. + +## Import + +Authentication Strength Policies can be imported using the `id`, e.g. + +```shell +terraform import azuread_authentication_strength_policy.my_policy 00000000-0000-0000-0000-000000000000 +``` diff --git a/docs/resources/conditional_access_policy.md b/docs/resources/conditional_access_policy.md index 41a81c5da7..66febde321 100644 --- a/docs/resources/conditional_access_policy.md +++ b/docs/resources/conditional_access_policy.md @@ -227,12 +227,13 @@ The following arguments are supported: `grant_controls` block supports the following: +* `authentication_strength_id` - (Optional) ID of an Authentication Strength Policy to use in this policy. * `built_in_controls` - (Optional) List of built-in controls required by the policy. Possible values are: `block`, `mfa`, `approvedApplication`, `compliantApplication`, `compliantDevice`, `domainJoinedDevice`, `passwordChange` or `unknownFutureValue`. * `custom_authentication_factors` - (Optional) List of custom controls IDs required by the policy. * `operator` - (Required) Defines the relationship of the grant controls. Possible values are: `AND`, `OR`. * `terms_of_use` - (Optional) List of terms of use IDs required by the policy. --> At least one of `built_in_controls` or `terms_of_use` must be specified. +-> At least one of `built_in_controls`, `authentication_strength_id` or `terms_of_use` must be specified. --- diff --git a/go.mod b/go.mod index bcda8f294b..282549ac3b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 - github.com/manicminer/hamilton v0.63.0 + github.com/manicminer/hamilton v0.63.1-0.20230926094900-e40d39cee08b golang.org/x/text v0.9.0 ) diff --git a/go.sum b/go.sum index c05286e65a..7f4ca04a7e 100644 --- a/go.sum +++ b/go.sum @@ -204,8 +204,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/manicminer/hamilton v0.63.0 h1:Pxh+TvuRhGsKl29v3dnzAoNJYUwqn6SNp/TGddg3g7E= -github.com/manicminer/hamilton v0.63.0/go.mod h1:va/X2sztcgQ5+BSxc2eU3FTHYIyxLnHvB4LudlPUZdE= +github.com/manicminer/hamilton v0.63.1-0.20230926094900-e40d39cee08b h1:RSanAVsFITByNGvxLYJA4gWNT/9T1rBWY1TbLMEQaTc= +github.com/manicminer/hamilton v0.63.1-0.20230926094900-e40d39cee08b/go.mod h1:va/X2sztcgQ5+BSxc2eU3FTHYIyxLnHvB4LudlPUZdE= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= diff --git a/internal/services/conditionalaccess/authentication_strength_policy_resource.go b/internal/services/conditionalaccess/authentication_strength_policy_resource.go new file mode 100644 index 0000000000..96cc86d2ae --- /dev/null +++ b/internal/services/conditionalaccess/authentication_strength_policy_resource.go @@ -0,0 +1,174 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package conditionalaccess + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "time" + + "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-azuread/internal/clients" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers" + "github.com/hashicorp/terraform-provider-azuread/internal/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/utils" + "github.com/hashicorp/terraform-provider-azuread/internal/validate" + "github.com/manicminer/hamilton/msgraph" +) + +func authenticationStrengthPolicyResource() *schema.Resource { + return &schema.Resource{ + CreateContext: authenticationStrengthPolicyCreate, + ReadContext: authenticationStrengthPolicyRead, + UpdateContext: authenticationStrengthPolicyUpdate, + DeleteContext: authenticationStrengthPolicyDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(5 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(5 * time.Minute), + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + + Importer: tf.ValidateResourceIDPriorToImport(func(id string) error { + if _, err := uuid.ParseUUID(id); err != nil { + return fmt.Errorf("specified ID (%q) is not valid: %s", id, err) + } + return nil + }), + + Schema: map[string]*schema.Schema{ + "display_name": { + Description: "The display name for the authentication strength policy", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validate.NoEmptyStrings, + }, + + "description": { + Description: "The description for the authentication strength policy", + Type: schema.TypeString, + Optional: true, + }, + + "allowed_combinations": { + Description: "The allowed MFA methods for this policy", + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func authenticationStrengthPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient + + properties := msgraph.AuthenticationStrengthPolicy{ + DisplayName: utils.String(d.Get("display_name").(string)), + Description: utils.String(d.Get("description").(string)), + AllowedCombinations: tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*schema.Set).List()), + } + + authenticationStrengthPolicy, _, err := client.Create(ctx, properties) + if err != nil { + return tf.ErrorDiagF(err, "Could not create authentication strength policy") + } + + d.SetId(*authenticationStrengthPolicy.ID) + + return authenticationStrengthPolicyRead(ctx, d, meta) +} + +func authenticationStrengthPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient + + properties := msgraph.AuthenticationStrengthPolicy{ + ID: utils.String(d.Id()), + DisplayName: utils.String(d.Get("display_name").(string)), + Description: utils.String(d.Get("description").(string)), + // AllowedCombinations: tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*schema.Set).List()), + } + + _, err := client.Update(ctx, properties) + if err != nil { + return tf.ErrorDiagF(err, "Could not update authentication strength policy") + } + + if d.HasChange("allowed_combinations") { + properties.AllowedCombinations = tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*schema.Set).List()) + _, err := client.UpdateAllowedCombinations(ctx, properties) + if err != nil { + return tf.ErrorDiagF(err, "Could not update authentication strength policy allowed combinations") + } + } + + return authenticationStrengthPolicyRead(ctx, d, meta) +} + +func authenticationStrengthPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient + + authenticationStrengthPolicy, status, err := client.Get(ctx, d.Id(), odata.Query{}) + if err != nil { + if status == http.StatusNotFound { + log.Printf("[DEBUG] Authentication Strength Policy with Object ID %q was not found - removing from state", d.Id()) + d.SetId("") + return nil + } + } + if authenticationStrengthPolicy == nil { + return tf.ErrorDiagF(errors.New("Bad API response"), "Result is nil") + } + + d.SetId(*authenticationStrengthPolicy.ID) + tf.Set(d, "display_name", authenticationStrengthPolicy.DisplayName) + tf.Set(d, "description", authenticationStrengthPolicy.Description) + tf.Set(d, "allowed_combinations", tf.FlattenStringSlicePtr(authenticationStrengthPolicy.AllowedCombinations)) + + return nil +} + +func authenticationStrengthPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient + authenticationStrengthPolicyId := d.Id() + + if _, status, err := client.Get(ctx, authenticationStrengthPolicyId, odata.Query{}); err != nil { + if status == http.StatusNotFound { + log.Printf("[DEBUG] Authentication Strength Policy with ID %q already deleted", authenticationStrengthPolicyId) + return nil + } + + return tf.ErrorDiagPathF(err, "id", "Retrieving Authentication Strength Policy with ID %q", authenticationStrengthPolicyId) + } + + status, err := client.Delete(ctx, authenticationStrengthPolicyId) + if err != nil { + return tf.ErrorDiagPathF(err, "id", "Deleting Authentication Strength Policy with ID %q, got status %d", authenticationStrengthPolicyId, status) + } + + if err := helpers.WaitForDeletion(ctx, func(ctx context.Context) (*bool, error) { + defer func() { client.BaseClient.DisableRetries = false }() + client.BaseClient.DisableRetries = true + if _, status, err := client.Get(ctx, authenticationStrengthPolicyId, odata.Query{}); err != nil { + if status == http.StatusNotFound { + return utils.Bool(false), nil + } + return nil, err + } + return utils.Bool(true), nil + }); err != nil { + return tf.ErrorDiagF(err, "waiting for deletion of Authentication Strength Policy with ID %q", authenticationStrengthPolicyId) + } + + return nil +} diff --git a/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go b/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go new file mode 100644 index 0000000000..7964eb9da7 --- /dev/null +++ b/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go @@ -0,0 +1,137 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package conditionalaccess_test + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" + "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azuread/internal/clients" + "github.com/hashicorp/terraform-provider-azuread/internal/utils" +) + +type AuthenticationStrengthPolicyResource struct{} + +func TestAccAuthenticationStrengthPolicy_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azuread_authentication_strength_policy", "test") + r := AuthenticationStrengthPolicyResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccAuthenticationStrengthPolicy_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azuread_authentication_strength_policy", "test") + r := AuthenticationStrengthPolicyResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.complete(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccAuthenticationStrengthPolicy_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azuread_authentication_strength_policy", "test") + r := AuthenticationStrengthPolicyResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r AuthenticationStrengthPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { + var id *string + + authstrengthpolicy, status, err := clients.ConditionalAccess.AuthenticationStrengthPoliciesClient.Get(ctx, state.ID, odata.Query{}) + if err != nil { + if status == http.StatusNotFound { + return nil, fmt.Errorf("Authentication Strength Policy with ID %q does not exist", state.ID) + } + return nil, fmt.Errorf("failed to retrieve Authentication Strength Policy with ID %q: %+v", state.ID, err) + } + id = authstrengthpolicy.ID + + return utils.Bool(id != nil && *id == state.ID), nil +} + +func (AuthenticationStrengthPolicyResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azuread_authentication_strength_policy" "test" { + display_name = "acctestASP-%[1]d" + description = "test" + allowed_combinations = ["password"] +} +`, data.RandomInteger) +} + +func (AuthenticationStrengthPolicyResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azuread_authentication_strength_policy" "test" { + display_name = "acctestASP-%[1]d" + description = "test" + allowed_combinations = [ + "fido2", + "password", + "deviceBasedPush", + "temporaryAccessPassOneTime", + "federatedMultiFactor", + "federatedSingleFactor", + "hardwareOath,federatedSingleFactor", + "microsoftAuthenticatorPush,federatedSingleFactor", + "password,hardwareOath", + "password,microsoftAuthenticatorPush", + "password,sms", + "password,softwareOath", + "password,voice", + "sms", + "sms,federatedSingleFactor", + "softwareOath,federatedSingleFactor", + "temporaryAccessPassMultiUse", + "voice,federatedSingleFactor", + "windowsHelloForBusiness", + "x509CertificateMultiFactor", + "x509CertificateSingleFactor", + ] +} +`, data.RandomInteger) +} diff --git a/internal/services/conditionalaccess/client/client.go b/internal/services/conditionalaccess/client/client.go index fa29ce21ea..c87004f262 100644 --- a/internal/services/conditionalaccess/client/client.go +++ b/internal/services/conditionalaccess/client/client.go @@ -9,8 +9,9 @@ import ( ) type Client struct { - NamedLocationsClient *msgraph.NamedLocationsClient - PoliciesClient *msgraph.ConditionalAccessPoliciesClient + NamedLocationsClient *msgraph.NamedLocationsClient + PoliciesClient *msgraph.ConditionalAccessPoliciesClient + AuthenticationStrengthPoliciesClient *msgraph.AuthenticationStrengthPoliciesClient } func NewClient(o *common.ClientOptions) *Client { @@ -20,8 +21,12 @@ func NewClient(o *common.ClientOptions) *Client { policiesClient := msgraph.NewConditionalAccessPoliciesClient() o.ConfigureClient(&policiesClient.BaseClient) + authenticationStrengthpoliciesClient := msgraph.NewAuthenticationStrengthPoliciesClient() + o.ConfigureClient(&authenticationStrengthpoliciesClient.BaseClient) + return &Client{ - NamedLocationsClient: namedLocationsClient, - PoliciesClient: policiesClient, + NamedLocationsClient: namedLocationsClient, + PoliciesClient: policiesClient, + AuthenticationStrengthPoliciesClient: authenticationStrengthpoliciesClient, } } diff --git a/internal/services/conditionalaccess/conditional_access_policy_resource.go b/internal/services/conditionalaccess/conditional_access_policy_resource.go index a299f826cf..2b12744e08 100644 --- a/internal/services/conditionalaccess/conditional_access_policy_resource.go +++ b/internal/services/conditionalaccess/conditional_access_policy_resource.go @@ -389,7 +389,7 @@ func conditionalAccessPolicyResource() *schema.Resource { "built_in_controls": { Type: schema.TypeList, Optional: true, - AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.terms_of_use"}, + AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_id", "grant_controls.0.terms_of_use"}, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{ @@ -405,6 +405,13 @@ func conditionalAccessPolicyResource() *schema.Resource { }, }, + "authentication_strength_id": { + AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_id", "grant_controls.0.terms_of_use"}, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsUUID, + }, + "custom_authentication_factors": { Type: schema.TypeList, Optional: true, @@ -417,7 +424,7 @@ func conditionalAccessPolicyResource() *schema.Resource { "terms_of_use": { Type: schema.TypeList, Optional: true, - AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.terms_of_use"}, + AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_id", "grant_controls.0.terms_of_use"}, Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: validate.NoEmptyStrings, diff --git a/internal/services/conditionalaccess/conditional_access_policy_resource_test.go b/internal/services/conditionalaccess/conditional_access_policy_resource_test.go index 28f48a44fe..ae4acf8e1b 100644 --- a/internal/services/conditionalaccess/conditional_access_policy_resource_test.go +++ b/internal/services/conditionalaccess/conditional_access_policy_resource_test.go @@ -298,6 +298,24 @@ func TestAccConditionalAccessPolicy_clientApplications(t *testing.T) { }) } +func TestAccConditionalAccessPolicy_authenticationStrength(t *testing.T) { + data := acceptance.BuildTestData(t, "azuread_conditional_access_policy", "test") + r := ConditionalAccessPolicyResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.authenticationStrengthPolicy(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("id").Exists(), + check.That(data.ResourceName).Key("display_name").HasValue(fmt.Sprintf("acctest-CONPOLICY-%d", data.RandomInteger)), + check.That(data.ResourceName).Key("grant_controls.0.authentication_strength_id").Exists(), + ), + }, + data.ImportStep(), + }) +} + func (r ConditionalAccessPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { var id *string @@ -658,3 +676,32 @@ resource "azuread_conditional_access_policy" "test" { } `, data.RandomInteger) } + +func (ConditionalAccessPolicyResource) authenticationStrengthPolicy(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azuread_conditional_access_policy" "test" { + display_name = "acctest-CONPOLICY-%[2]d" + state = "disabled" + + conditions { + client_app_types = ["browser"] + + applications { + included_applications = ["None"] + } + + users { + included_users = ["All"] + excluded_users = ["GuestsOrExternalUsers"] + } + } + + grant_controls { + operator = "OR" + authentication_strength_id = azuread_authentication_strength_policy.test.id + } +} +`, AuthenticationStrengthPolicyResource{}.basic(data), data.RandomInteger) +} diff --git a/internal/services/conditionalaccess/conditionalaccess.go b/internal/services/conditionalaccess/conditionalaccess.go index 0c08557abe..b6d60ee0b8 100644 --- a/internal/services/conditionalaccess/conditionalaccess.go +++ b/internal/services/conditionalaccess/conditionalaccess.go @@ -121,6 +121,7 @@ func flattenConditionalAccessGrantControls(in *msgraph.ConditionalAccessGrantCon map[string]interface{}{ "operator": in.Operator, "built_in_controls": tf.FlattenStringSlicePtr(in.BuiltInControls), + "authentication_strength_id": in.AuthenticationStrength.ID, "custom_authentication_factors": tf.FlattenStringSlicePtr(in.CustomAuthenticationFactors), "terms_of_use": tf.FlattenStringSlicePtr(in.TermsOfUse), }, @@ -392,11 +393,15 @@ func expandConditionalAccessGrantControls(in []interface{}) *msgraph.Conditional config := in[0].(map[string]interface{}) operator := config["operator"].(string) + authenticationStrengthId := config["authentication_strength_id"].(string) builtInControls := config["built_in_controls"].([]interface{}) customAuthenticationFactors := config["custom_authentication_factors"].([]interface{}) termsOfUse := config["terms_of_use"].([]interface{}) result.Operator = &operator + result.AuthenticationStrength = &msgraph.AuthenticationStrengthPolicy{ + ID: &authenticationStrengthId, + } result.BuiltInControls = tf.ExpandStringSlicePtr(builtInControls) result.CustomAuthenticationFactors = tf.ExpandStringSlicePtr(customAuthenticationFactors) result.TermsOfUse = tf.ExpandStringSlicePtr(termsOfUse) diff --git a/internal/services/conditionalaccess/registration.go b/internal/services/conditionalaccess/registration.go index 6b35e66e6e..beeb403263 100644 --- a/internal/services/conditionalaccess/registration.go +++ b/internal/services/conditionalaccess/registration.go @@ -31,7 +31,8 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azuread_named_location": namedLocationResource(), - "azuread_conditional_access_policy": conditionalAccessPolicyResource(), + "azuread_named_location": namedLocationResource(), + "azuread_conditional_access_policy": conditionalAccessPolicyResource(), + "azuread_authentication_strength_policy": authenticationStrengthPolicyResource(), } } diff --git a/vendor/github.com/manicminer/hamilton/msgraph/authentication_strength_policy.go b/vendor/github.com/manicminer/hamilton/msgraph/authentication_strength_policy.go index 25efbe5e89..1cf18468e7 100644 --- a/vendor/github.com/manicminer/hamilton/msgraph/authentication_strength_policy.go +++ b/vendor/github.com/manicminer/hamilton/msgraph/authentication_strength_policy.go @@ -141,6 +141,42 @@ func (c *AuthenticationStrengthPoliciesClient) Update(ctx context.Context, Authe return status, nil } +// Update amends an existing AuthenticationStrengthPolicy's allowed combinations +func (c *AuthenticationStrengthPoliciesClient) UpdateAllowedCombinations(ctx context.Context, policy AuthenticationStrengthPolicy) (int, error) { + var status int + + if policy.ID == nil { + return status, errors.New("cannot update AuthenticationStrengthPolicy with nil ID") + } + + if policy.AllowedCombinations == nil { + return status, errors.New("cannot update AuthenticationStrengthPolicy with nil AllowedCombinations") + } + + allowedCombinations := AuthenticationStrengthPolicy{ + AllowedCombinations: policy.AllowedCombinations, + } + + body, err := json.Marshal(allowedCombinations) + if err != nil { + return status, fmt.Errorf("json.Marshal(): %v", err) + } + + _, status, _, err = c.BaseClient.Post(ctx, PostHttpRequestInput{ + Body: body, + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: fmt.Sprintf("/policies/authenticationStrengthPolicies/%s/updateAllowedCombinations", *policy.ID), + }, + }) + if err != nil { + return status, fmt.Errorf("AuthenticationStrengthPoliciesClient.BaseClient.Post(): %v", err) + } + + return status, nil +} + // Delete removes a AuthenticationStrengthPolicy. func (c *AuthenticationStrengthPoliciesClient) Delete(ctx context.Context, id string) (int, error) { _, status, _, err := c.BaseClient.Delete(ctx, DeleteHttpRequestInput{ diff --git a/vendor/github.com/manicminer/hamilton/msgraph/models.go b/vendor/github.com/manicminer/hamilton/msgraph/models.go index 21253ff878..76b54f5148 100644 --- a/vendor/github.com/manicminer/hamilton/msgraph/models.go +++ b/vendor/github.com/manicminer/hamilton/msgraph/models.go @@ -1439,6 +1439,7 @@ type ServicePrincipal struct { AppDisplayName *string `json:"appDisplayName,omitempty"` AppId *string `json:"appId,omitempty"` ApplicationTemplateId *string `json:"applicationTemplateId,omitempty"` + AppMetadata *ServicePrincipalAppMetadata `json:"appMetadata,omitempty"` AppOwnerOrganizationId *string `json:"appOwnerOrganizationId,omitempty"` AppRoleAssignmentRequired *bool `json:"appRoleAssignmentRequired,omitempty"` AppRoles *[]AppRole `json:"appRoles,omitempty"` @@ -1481,6 +1482,11 @@ func (s *ServicePrincipal) UnmarshalJSON(data []byte) error { return nil } +type ServicePrincipalAppMetadata struct { + Version *int `json:"version,omitempty"` + Data *[]KeyValueObject `json:"data,omitempty"` +} + type SynchronizationSchedule struct { Expiration *time.Time `json:"expiration,omitempty"` Interval *string `json:"interval,omitempty"` diff --git a/vendor/github.com/manicminer/hamilton/msgraph/valuetypes.go b/vendor/github.com/manicminer/hamilton/msgraph/valuetypes.go index cfd8ce229d..eca99667c1 100644 --- a/vendor/github.com/manicminer/hamilton/msgraph/valuetypes.go +++ b/vendor/github.com/manicminer/hamilton/msgraph/valuetypes.go @@ -22,6 +22,11 @@ func (s StringNullWhenEmpty) MarshalJSON() ([]byte, error) { return json.Marshal(string(s)) } +type KeyValueObject struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + type AccessPackageCatalogState = string const ( diff --git a/vendor/modules.txt b/vendor/modules.txt index a88ac6e6eb..7262f1612e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -158,7 +158,7 @@ github.com/hashicorp/terraform-svchost # github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 ## explicit; go 1.15 github.com/hashicorp/yamux -# github.com/manicminer/hamilton v0.63.0 +# github.com/manicminer/hamilton v0.63.1-0.20230926094900-e40d39cee08b ## explicit; go 1.16 github.com/manicminer/hamilton/errors github.com/manicminer/hamilton/internal/utils From 9b1cc250a33aa5a4f7e6cfe8783de3cc2d39405e Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 20 Oct 2023 03:05:14 +0100 Subject: [PATCH 2/3] conditionalaccess: update pluginsdk wrapper usage, text fixes, rename `authentication_strength_id` to `authentication_strength_policy_id` --- docs/resources/conditional_access_policy.md | 8 +-- ...authentication_strength_policy_resource.go | 68 +++++++++---------- ...ntication_strength_policy_resource_test.go | 27 ++++---- .../conditional_access_policy_resource.go | 8 +-- ...conditional_access_policy_resource_test.go | 14 ++-- .../conditionalaccess/conditionalaccess.go | 12 ++-- 6 files changed, 68 insertions(+), 69 deletions(-) diff --git a/docs/resources/conditional_access_policy.md b/docs/resources/conditional_access_policy.md index 66febde321..a69e063433 100644 --- a/docs/resources/conditional_access_policy.md +++ b/docs/resources/conditional_access_policy.md @@ -227,25 +227,25 @@ The following arguments are supported: `grant_controls` block supports the following: -* `authentication_strength_id` - (Optional) ID of an Authentication Strength Policy to use in this policy. +* `authentication_strength_policy_id` - (Optional) ID of an Authentication Strength Policy to use in this policy. * `built_in_controls` - (Optional) List of built-in controls required by the policy. Possible values are: `block`, `mfa`, `approvedApplication`, `compliantApplication`, `compliantDevice`, `domainJoinedDevice`, `passwordChange` or `unknownFutureValue`. * `custom_authentication_factors` - (Optional) List of custom controls IDs required by the policy. * `operator` - (Required) Defines the relationship of the grant controls. Possible values are: `AND`, `OR`. * `terms_of_use` - (Optional) List of terms of use IDs required by the policy. --> At least one of `built_in_controls`, `authentication_strength_id` or `terms_of_use` must be specified. +-> At least one of `authentication_strength_policy_id`, `built_in_controls` or `terms_of_use` must be specified. --- `session_controls` block supports the following: -* `application_enforced_restrictions_enabled` - (Optional) Whether or not application enforced restrictions are enabled. Defaults to `false`. +* `application_enforced_restrictions_enabled` - (Optional) Whether application enforced restrictions are enabled. Defaults to `false`. -> Only Office 365, Exchange Online and Sharepoint Online support application enforced restrictions. * `cloud_app_security_policy` - (Optional) Enables cloud app security and specifies the cloud app security policy to use. Possible values are: `blockDownloads`, `mcasConfigured`, `monitorOnly` or `unknownFutureValue`. * `disable_resilience_defaults` - (Optional) Disables [resilience defaults](https://learn.microsoft.com/en-us/azure/active-directory/conditional-access/resilience-defaults). Defaults to `false`. -* `persistent_browser_mode` - (Optional) Session control to define whether to persist cookies or not. Possible values are: `always` or `never`. +* `persistent_browser_mode` - (Optional) Session control to define whether to persist cookies. Possible values are: `always` or `never`. * `sign_in_frequency` - (Optional) Number of days or hours to enforce sign-in frequency. Required when `sign_in_frequency_period` is specified. Due to an API issue, removing this property forces a new resource to be created. * `sign_in_frequency_period` - (Optional) The time period to enforce sign-in frequency. Possible values are: `hours` or `days`. Required when `sign_in_frequency_period` is specified. Due to an API issue, removing this property forces a new resource to be created. diff --git a/internal/services/conditionalaccess/authentication_strength_policy_resource.go b/internal/services/conditionalaccess/authentication_strength_policy_resource.go index 96cc86d2ae..210aae5a38 100644 --- a/internal/services/conditionalaccess/authentication_strength_policy_resource.go +++ b/internal/services/conditionalaccess/authentication_strength_policy_resource.go @@ -11,72 +11,72 @@ import ( "net/http" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-sdk/sdk/odata" "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azuread/internal/clients" "github.com/hashicorp/terraform-provider-azuread/internal/helpers" "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/utils" - "github.com/hashicorp/terraform-provider-azuread/internal/validate" + "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" "github.com/manicminer/hamilton/msgraph" ) -func authenticationStrengthPolicyResource() *schema.Resource { - return &schema.Resource{ +func authenticationStrengthPolicyResource() *pluginsdk.Resource { + return &pluginsdk.Resource{ CreateContext: authenticationStrengthPolicyCreate, ReadContext: authenticationStrengthPolicyRead, UpdateContext: authenticationStrengthPolicyUpdate, DeleteContext: authenticationStrengthPolicyDelete, - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(5 * time.Minute), - Read: schema.DefaultTimeout(5 * time.Minute), - Update: schema.DefaultTimeout(5 * time.Minute), - Delete: schema.DefaultTimeout(5 * time.Minute), + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(5 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(5 * time.Minute), + Delete: pluginsdk.DefaultTimeout(5 * time.Minute), }, - Importer: tf.ValidateResourceIDPriorToImport(func(id string) error { + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { if _, err := uuid.ParseUUID(id); err != nil { return fmt.Errorf("specified ID (%q) is not valid: %s", id, err) } return nil }), - Schema: map[string]*schema.Schema{ + Schema: map[string]*pluginsdk.Schema{ "display_name": { - Description: "The display name for the authentication strength policy", - Type: schema.TypeString, - Required: true, - ValidateDiagFunc: validate.NoEmptyStrings, + Description: "The display name for the authentication strength policy", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "description": { Description: "The description for the authentication strength policy", - Type: schema.TypeString, + Type: pluginsdk.TypeString, Optional: true, }, "allowed_combinations": { Description: "The allowed MFA methods for this policy", - Type: schema.TypeSet, + Type: pluginsdk.TypeSet, Required: true, - Elem: &schema.Schema{ - Type: schema.TypeString, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, }, }, }, } } -func authenticationStrengthPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func authenticationStrengthPolicyCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient properties := msgraph.AuthenticationStrengthPolicy{ - DisplayName: utils.String(d.Get("display_name").(string)), - Description: utils.String(d.Get("description").(string)), - AllowedCombinations: tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*schema.Set).List()), + DisplayName: pointer.To(d.Get("display_name").(string)), + Description: pointer.To(d.Get("description").(string)), + AllowedCombinations: tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*pluginsdk.Set).List()), } authenticationStrengthPolicy, _, err := client.Create(ctx, properties) @@ -89,14 +89,14 @@ func authenticationStrengthPolicyCreate(ctx context.Context, d *schema.ResourceD return authenticationStrengthPolicyRead(ctx, d, meta) } -func authenticationStrengthPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func authenticationStrengthPolicyUpdate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient properties := msgraph.AuthenticationStrengthPolicy{ - ID: utils.String(d.Id()), - DisplayName: utils.String(d.Get("display_name").(string)), - Description: utils.String(d.Get("description").(string)), - // AllowedCombinations: tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*schema.Set).List()), + ID: pointer.To(d.Id()), + DisplayName: pointer.To(d.Get("display_name").(string)), + Description: pointer.To(d.Get("description").(string)), + // AllowedCombinations: tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*pluginsdk.Set).List()), } _, err := client.Update(ctx, properties) @@ -105,7 +105,7 @@ func authenticationStrengthPolicyUpdate(ctx context.Context, d *schema.ResourceD } if d.HasChange("allowed_combinations") { - properties.AllowedCombinations = tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*schema.Set).List()) + properties.AllowedCombinations = tf.ExpandStringSlicePtr(d.Get("allowed_combinations").(*pluginsdk.Set).List()) _, err := client.UpdateAllowedCombinations(ctx, properties) if err != nil { return tf.ErrorDiagF(err, "Could not update authentication strength policy allowed combinations") @@ -115,7 +115,7 @@ func authenticationStrengthPolicyUpdate(ctx context.Context, d *schema.ResourceD return authenticationStrengthPolicyRead(ctx, d, meta) } -func authenticationStrengthPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func authenticationStrengthPolicyRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient authenticationStrengthPolicy, status, err := client.Get(ctx, d.Id(), odata.Query{}) @@ -138,7 +138,7 @@ func authenticationStrengthPolicyRead(ctx context.Context, d *schema.ResourceDat return nil } -func authenticationStrengthPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func authenticationStrengthPolicyDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*clients.Client).ConditionalAccess.AuthenticationStrengthPoliciesClient authenticationStrengthPolicyId := d.Id() @@ -161,11 +161,11 @@ func authenticationStrengthPolicyDelete(ctx context.Context, d *schema.ResourceD client.BaseClient.DisableRetries = true if _, status, err := client.Get(ctx, authenticationStrengthPolicyId, odata.Query{}); err != nil { if status == http.StatusNotFound { - return utils.Bool(false), nil + return pointer.To(false), nil } return nil, err } - return utils.Bool(true), nil + return pointer.To(true), nil }); err != nil { return tf.ErrorDiagF(err, "waiting for deletion of Authentication Strength Policy with ID %q", authenticationStrengthPolicyId) } diff --git a/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go b/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go index 7964eb9da7..c8a13e53eb 100644 --- a/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go +++ b/internal/services/conditionalaccess/authentication_strength_policy_resource_test.go @@ -9,13 +9,12 @@ import ( "net/http" "testing" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-sdk/sdk/odata" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azuread/internal/clients" - "github.com/hashicorp/terraform-provider-azuread/internal/utils" + "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" ) type AuthenticationStrengthPolicyResource struct{} @@ -24,10 +23,10 @@ func TestAccAuthenticationStrengthPolicy_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azuread_authentication_strength_policy", "test") r := AuthenticationStrengthPolicyResource{} - data.ResourceTest(t, r, []resource.TestStep{ + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, @@ -39,10 +38,10 @@ func TestAccAuthenticationStrengthPolicy_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azuread_authentication_strength_policy", "test") r := AuthenticationStrengthPolicyResource{} - data.ResourceTest(t, r, []resource.TestStep{ + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, @@ -54,24 +53,24 @@ func TestAccAuthenticationStrengthPolicy_update(t *testing.T) { data := acceptance.BuildTestData(t, "azuread_authentication_strength_policy", "test") r := AuthenticationStrengthPolicyResource{} - data.ResourceTest(t, r, []resource.TestStep{ + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { Config: r.complete(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { Config: r.basic(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, @@ -79,10 +78,10 @@ func TestAccAuthenticationStrengthPolicy_update(t *testing.T) { }) } -func (r AuthenticationStrengthPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { +func (r AuthenticationStrengthPolicyResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { var id *string - authstrengthpolicy, status, err := clients.ConditionalAccess.AuthenticationStrengthPoliciesClient.Get(ctx, state.ID, odata.Query{}) + authstrengthpolicy, status, err := client.ConditionalAccess.AuthenticationStrengthPoliciesClient.Get(ctx, state.ID, odata.Query{}) if err != nil { if status == http.StatusNotFound { return nil, fmt.Errorf("Authentication Strength Policy with ID %q does not exist", state.ID) @@ -91,7 +90,7 @@ func (r AuthenticationStrengthPolicyResource) Exists(ctx context.Context, client } id = authstrengthpolicy.ID - return utils.Bool(id != nil && *id == state.ID), nil + return pointer.To(id != nil && *id == state.ID), nil } func (AuthenticationStrengthPolicyResource) basic(data acceptance.TestData) string { diff --git a/internal/services/conditionalaccess/conditional_access_policy_resource.go b/internal/services/conditionalaccess/conditional_access_policy_resource.go index 5a00d77c1a..d3e631a651 100644 --- a/internal/services/conditionalaccess/conditional_access_policy_resource.go +++ b/internal/services/conditionalaccess/conditional_access_policy_resource.go @@ -386,7 +386,7 @@ func conditionalAccessPolicyResource() *pluginsdk.Resource { "built_in_controls": { Type: pluginsdk.TypeList, Optional: true, - AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_id", "grant_controls.0.terms_of_use"}, + AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_policy_id", "grant_controls.0.terms_of_use"}, Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, ValidateFunc: validation.StringInSlice([]string{ @@ -402,8 +402,8 @@ func conditionalAccessPolicyResource() *pluginsdk.Resource { }, }, - "authentication_strength_id": { - AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_id", "grant_controls.0.terms_of_use"}, + "authentication_strength_policy_id": { + AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_policy_id", "grant_controls.0.terms_of_use"}, Type: pluginsdk.TypeString, Optional: true, ValidateFunc: validation.IsUUID, @@ -421,7 +421,7 @@ func conditionalAccessPolicyResource() *pluginsdk.Resource { "terms_of_use": { Type: pluginsdk.TypeList, Optional: true, - AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_id", "grant_controls.0.terms_of_use"}, + AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.authentication_strength_policy_id", "grant_controls.0.terms_of_use"}, Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), diff --git a/internal/services/conditionalaccess/conditional_access_policy_resource_test.go b/internal/services/conditionalaccess/conditional_access_policy_resource_test.go index 427e36459e..7aa573eb5e 100644 --- a/internal/services/conditionalaccess/conditional_access_policy_resource_test.go +++ b/internal/services/conditionalaccess/conditional_access_policy_resource_test.go @@ -11,10 +11,10 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-sdk/sdk/odata" - "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" "github.com/hashicorp/terraform-provider-azuread/internal/clients" + "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" ) type ConditionalAccessPolicyResource struct{} @@ -301,21 +301,21 @@ func TestAccConditionalAccessPolicy_authenticationStrength(t *testing.T) { data := acceptance.BuildTestData(t, "azuread_conditional_access_policy", "test") r := ConditionalAccessPolicyResource{} - data.ResourceTest(t, r, []resource.TestStep{ + data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.authenticationStrengthPolicy(data), - Check: resource.ComposeTestCheckFunc( + Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("id").Exists(), check.That(data.ResourceName).Key("display_name").HasValue(fmt.Sprintf("acctest-CONPOLICY-%d", data.RandomInteger)), - check.That(data.ResourceName).Key("grant_controls.0.authentication_strength_id").Exists(), + check.That(data.ResourceName).Key("grant_controls.0.authentication_strength_policy_id").Exists(), ), }, data.ImportStep(), }) } -func (r ConditionalAccessPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { +func (r ConditionalAccessPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { var id *string app, status, err := clients.ConditionalAccess.PoliciesClient.Get(ctx, state.ID, odata.Query{}) @@ -698,8 +698,8 @@ resource "azuread_conditional_access_policy" "test" { } grant_controls { - operator = "OR" - authentication_strength_id = azuread_authentication_strength_policy.test.id + operator = "OR" + authentication_strength_policy_id = azuread_authentication_strength_policy.test.id } } `, AuthenticationStrengthPolicyResource{}.basic(data), data.RandomInteger) diff --git a/internal/services/conditionalaccess/conditionalaccess.go b/internal/services/conditionalaccess/conditionalaccess.go index 14f4f300b7..97ed2c12bb 100644 --- a/internal/services/conditionalaccess/conditionalaccess.go +++ b/internal/services/conditionalaccess/conditionalaccess.go @@ -119,11 +119,11 @@ func flattenConditionalAccessGrantControls(in *msgraph.ConditionalAccessGrantCon return []interface{}{ map[string]interface{}{ - "operator": in.Operator, - "built_in_controls": tf.FlattenStringSlicePtr(in.BuiltInControls), - "authentication_strength_id": in.AuthenticationStrength.ID, - "custom_authentication_factors": tf.FlattenStringSlicePtr(in.CustomAuthenticationFactors), - "terms_of_use": tf.FlattenStringSlicePtr(in.TermsOfUse), + "operator": in.Operator, + "built_in_controls": tf.FlattenStringSlicePtr(in.BuiltInControls), + "authentication_strength_policy_id": in.AuthenticationStrength.ID, + "custom_authentication_factors": tf.FlattenStringSlicePtr(in.CustomAuthenticationFactors), + "terms_of_use": tf.FlattenStringSlicePtr(in.TermsOfUse), }, } } @@ -393,7 +393,7 @@ func expandConditionalAccessGrantControls(in []interface{}) *msgraph.Conditional config := in[0].(map[string]interface{}) operator := config["operator"].(string) - authenticationStrengthId := config["authentication_strength_id"].(string) + authenticationStrengthId := config["authentication_strength_policy_id"].(string) builtInControls := config["built_in_controls"].([]interface{}) customAuthenticationFactors := config["custom_authentication_factors"].([]interface{}) termsOfUse := config["terms_of_use"].([]interface{}) From 26591af33e3edde6081349a8c0ef677a4d524aeb Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Fri, 20 Oct 2023 03:23:42 +0100 Subject: [PATCH 3/3] nil checks, test fixes --- .../conditionalaccess/conditionalaccess.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/internal/services/conditionalaccess/conditionalaccess.go b/internal/services/conditionalaccess/conditionalaccess.go index 97ed2c12bb..375237665c 100644 --- a/internal/services/conditionalaccess/conditionalaccess.go +++ b/internal/services/conditionalaccess/conditionalaccess.go @@ -117,11 +117,16 @@ func flattenConditionalAccessGrantControls(in *msgraph.ConditionalAccessGrantCon return []interface{}{} } + var authenticationStrengthPolicyId string + if in.AuthenticationStrength != nil { + authenticationStrengthPolicyId = pointer.From(in.AuthenticationStrength.ID) + } + return []interface{}{ map[string]interface{}{ "operator": in.Operator, "built_in_controls": tf.FlattenStringSlicePtr(in.BuiltInControls), - "authentication_strength_policy_id": in.AuthenticationStrength.ID, + "authentication_strength_policy_id": authenticationStrengthPolicyId, "custom_authentication_factors": tf.FlattenStringSlicePtr(in.CustomAuthenticationFactors), "terms_of_use": tf.FlattenStringSlicePtr(in.TermsOfUse), }, @@ -399,9 +404,13 @@ func expandConditionalAccessGrantControls(in []interface{}) *msgraph.Conditional termsOfUse := config["terms_of_use"].([]interface{}) result.Operator = &operator - result.AuthenticationStrength = &msgraph.AuthenticationStrengthPolicy{ - ID: &authenticationStrengthId, + + if authenticationStrengthId != "" { + result.AuthenticationStrength = &msgraph.AuthenticationStrengthPolicy{ + ID: &authenticationStrengthId, + } } + result.BuiltInControls = tf.ExpandStringSlicePtr(builtInControls) result.CustomAuthenticationFactors = tf.ExpandStringSlicePtr(customAuthenticationFactors) result.TermsOfUse = tf.ExpandStringSlicePtr(termsOfUse)