-
Notifications
You must be signed in to change notification settings - Fork 300
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
efbb8f3
commit 5bd6cfd
Showing
15 changed files
with
487 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
174 changes: 174 additions & 0 deletions
174
internal/services/conditionalaccess/authentication_strength_policy_resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
137 changes: 137 additions & 0 deletions
137
internal/services/conditionalaccess/authentication_strength_policy_resource_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Oops, something went wrong.