diff --git a/internal/services/identitygovernance/access_package_assignment_policy_resource.go b/internal/services/identitygovernance/access_package_assignment_policy_resource.go index 1aedc0fc0..1c208d81c 100644 --- a/internal/services/identitygovernance/access_package_assignment_policy_resource.go +++ b/internal/services/identitygovernance/access_package_assignment_policy_resource.go @@ -5,20 +5,23 @@ package identitygovernance import ( "context" + "errors" "fmt" "log" - "net/http" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageassignmentpolicy" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-uuid" "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/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/consistency" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) const accessPackageAssignmentPolicyResourceName = "azuread_access_package_assignment_policy" @@ -30,7 +33,7 @@ func accessPackageAssignmentPolicyResource() *pluginsdk.Resource { UpdateContext: accessPackageAssignmentPolicyResourceUpdate, DeleteContext: accessPackageAssignmentPolicyResourceDelete, - CustomizeDiff: assignmentPolicyCustomDiff, + CustomizeDiff: assignmentPolicyCustomizeDiff, Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(5 * time.Minute), @@ -48,24 +51,24 @@ func accessPackageAssignmentPolicyResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "access_package_id": { - Description: "The ID of the access package that will contain the policy", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The ID of the access package that will contain the policy", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.IsUUID, }, "display_name": { - Description: "The display name of the policy", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The display name of the policy", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "description": { - Description: "The description of the policy", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The description of the policy", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "duration_in_days": { @@ -106,19 +109,10 @@ func accessPackageAssignmentPolicyResource() *pluginsdk.Resource { }, "scope_type": { - Description: "Specify the scopes of the requestors", - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - msgraph.RequestorSettingsScopeTypeAllConfiguredConnectedOrganizationSubjects, - msgraph.RequestorSettingsScopeTypeAllExistingConnectedOrganizationSubjects, - msgraph.RequestorSettingsScopeTypeAllExistingDirectoryMemberUsers, - msgraph.RequestorSettingsScopeTypeAllExistingDirectorySubjects, - msgraph.RequestorSettingsScopeTypeAllExternalSubjects, - msgraph.RequestorSettingsScopeTypeNoSubjects, - msgraph.RequestorSettingsScopeTypeSpecificConnectedOrganizationSubjects, - msgraph.RequestorSettingsScopeTypeSpecificDirectorySubjects, - }, false), + Description: "Specify the scopes of the requestors", + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(possibleValuesForRequestorScopeType, false), }, "requestor": { @@ -222,27 +216,17 @@ func accessPackageAssignmentPolicyResource() *pluginsdk.Resource { }, "review_frequency": { - Description: "This will determine how often the access review campaign runs", - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - msgraph.AccessReviewRecurrenceTypeAnnual, - msgraph.AccessReviewRecurrenceTypeHalfYearly, - msgraph.AccessReviewRecurrenceTypeQuarterly, - msgraph.AccessReviewRecurrenceTypeMonthly, - msgraph.AccessReviewRecurrenceTypeWeekly, - }, false), + Description: "This will determine how often the access review campaign runs", + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(possibleValuesForAccessReviewRecurrenceType, false), }, "review_type": { - Description: "Self review or specific reviewers", - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - msgraph.AccessReviewReviewerTypeManager, - msgraph.AccessReviewReviewerTypeReviewers, - msgraph.AccessReviewReviewerTypeSelf, - }, false), + Description: "Self review or specific reviewers", + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(possibleValuesForAccessReviewReviewerType, false), }, "starting_on": { @@ -278,14 +262,10 @@ func accessPackageAssignmentPolicyResource() *pluginsdk.Resource { }, "access_review_timeout_behavior": { - Description: "What actions the system takes if reviewers don't respond in time", - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - msgraph.AccessReviewTimeoutBehaviorTypeAcceptAccessRecommendation, - msgraph.AccessReviewTimeoutBehaviorTypeKeepAccess, - msgraph.AccessReviewTimeoutBehaviorTypeRemoveAccess, - }, false), + Description: "What actions the system takes if reviewers don't respond in time", + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(beta.PossibleValuesForAccessReviewTimeoutBehavior(), false), }, }, }, @@ -347,21 +327,67 @@ func accessPackageAssignmentPolicyResource() *pluginsdk.Resource { } } +func assignmentPolicyDiffSuppress(k, old, new string, d *pluginsdk.ResourceData) bool { + if k == "approval_settings.#" && old == "1" && new == "0" { + return true + } + + if k == "requestor_settings.#" && old == "1" && new == "0" { + return true + } + + if k == "requestor_settings.0.scope_type" && old == RequestorScopeTypeNoSubjects && len(new) == 0 { + return true + } + + if k == "assignment_review_settings.0.starting_on" && len(new) == 0 { + return true + } + + if k == "assignment_review_settings.#" && old == "1" && new == "0" { + return true + } + + if k == "question.#" && old == "1" && new == "0" { + return true + } + + return false +} + +func assignmentPolicyCustomizeDiff(ctx context.Context, diff *pluginsdk.ResourceDiff, meta interface{}) error { + if reviewSettings := diff.Get("assignment_review_settings").([]interface{}); len(reviewSettings) > 0 { + reviewSetting := reviewSettings[0].(map[string]interface{}) + if reviewSetting["enabled"].(bool) && + (reviewSetting["duration_in_days"] == 0 || + len(reviewSetting["review_frequency"].(string)) == 0 || + len(reviewSetting["access_review_timeout_behavior"].(string)) == 0) { + return fmt.Errorf("`duration_in_days`, `review_frequency`, `access_review_timeout_behavior` must be set when review is enabled") + } + } + + return nil +} + func accessPackageAssignmentPolicyResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageAssignmentPolicyClient - var properties msgraph.AccessPackageAssignmentPolicy - var err error - if properties, err = buildAssignmentPolicyResourceData(ctx, d, meta); err != nil { + properties, err := buildAssignmentPolicyResourceData(ctx, d, meta) + if err != nil { return tf.ErrorDiagF(err, "Building resource data from supplied parameters") } - accessPackageAssignmentPolicy, _, err := client.Create(ctx, properties) + resp, err := client.CreateEntitlementManagementAccessPackageAssignmentPolicy(ctx, *properties, entitlementmanagementaccesspackageassignmentpolicy.DefaultCreateEntitlementManagementAccessPackageAssignmentPolicyOperationOptions()) if err != nil { return tf.ErrorDiagF(err, "Creating access package assignment policy %q", d.Get("display_name").(string)) } - d.SetId(*accessPackageAssignmentPolicy.ID) + if resp.Model == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Creating access package assignment policy") + } + + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageAssignmentPolicyID(*resp.Model.Id) + d.SetId(id.AccessPackageAssignmentPolicyId) return accessPackageAssignmentPolicyResourceRead(ctx, d, meta) } @@ -369,17 +395,18 @@ func accessPackageAssignmentPolicyResourceCreate(ctx context.Context, d *plugins func accessPackageAssignmentPolicyResourceUpdate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageAssignmentPolicyClient - var properties msgraph.AccessPackageAssignmentPolicy - var err error - if properties, err = buildAssignmentPolicyResourceData(ctx, d, meta); err != nil { + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageAssignmentPolicyID(d.Id()) + + properties, err := buildAssignmentPolicyResourceData(ctx, d, meta) + if err != nil { return tf.ErrorDiagF(err, "Building resource data from supplied parameters") } - objectId := d.Id() - tf.LockByName(accessPackageAssignmentPolicyResourceName, objectId) - defer tf.UnlockByName(accessPackageAssignmentPolicyResourceName, objectId) - if _, err := client.Update(ctx, properties); err != nil { - return tf.ErrorDiagF(err, "Could not update access package assignment policy with ID: %q", objectId) + tf.LockByName(accessPackageAssignmentPolicyResourceName, id.AccessPackageAssignmentPolicyId) + defer tf.UnlockByName(accessPackageAssignmentPolicyResourceName, id.AccessPackageAssignmentPolicyId) + + if _, err = client.SetEntitlementManagementAccessPackageAssignmentPolicy(ctx, id, *properties, entitlementmanagementaccesspackageassignmentpolicy.DefaultSetEntitlementManagementAccessPackageAssignmentPolicyOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Updating %s", id) } return accessPackageAssignmentPolicyResourceRead(ctx, d, meta) @@ -388,157 +415,103 @@ func accessPackageAssignmentPolicyResourceUpdate(ctx context.Context, d *plugins func accessPackageAssignmentPolicyResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageAssignmentPolicyClient - objectId := d.Id() - accessPackageAssignmentPolicy, status, err := client.Get(ctx, objectId, odata.Query{}) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageAssignmentPolicyID(d.Id()) + + resp, err := client.GetEntitlementManagementAccessPackageAssignmentPolicy(ctx, id, entitlementmanagementaccesspackageassignmentpolicy.DefaultGetEntitlementManagementAccessPackageAssignmentPolicyOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package assignment policy with Object ID %q was not found - removing from state!", objectId) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] %s was not found - removing from state!", id) d.SetId("") return nil } - return tf.ErrorDiagF(err, "Retrieving access package assignment policy with object ID: %q", objectId) + return tf.ErrorDiagF(err, "Retrieving %s", id) } - tf.Set(d, "display_name", accessPackageAssignmentPolicy.DisplayName) - tf.Set(d, "access_package_id", accessPackageAssignmentPolicy.AccessPackageId) - tf.Set(d, "description", accessPackageAssignmentPolicy.Description) - tf.Set(d, "extension_enabled", accessPackageAssignmentPolicy.CanExtend) - tf.Set(d, "duration_in_days", accessPackageAssignmentPolicy.DurationInDays) - if expirationDate := accessPackageAssignmentPolicy.ExpirationDateTime; expirationDate != nil && !expirationDate.IsZero() { - tf.Set(d, "expiration_date", expirationDate.UTC().Format(time.RFC3339)) - } else { - tf.Set(d, "expiration_date", "") + accessPackageAssignmentPolicy := resp.Model + if accessPackageAssignmentPolicy == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", id) } - tf.Set(d, "requestor_settings", flattenRequestorSettings(accessPackageAssignmentPolicy.RequestorSettings)) + tf.Set(d, "access_package_id", accessPackageAssignmentPolicy.AccessPackageId.GetOrZero()) tf.Set(d, "approval_settings", flattenApprovalSettings(accessPackageAssignmentPolicy.RequestApprovalSettings)) tf.Set(d, "assignment_review_settings", flattenAssignmentReviewSettings(accessPackageAssignmentPolicy.AccessReviewSettings)) + tf.Set(d, "description", accessPackageAssignmentPolicy.Description.GetOrZero()) + tf.Set(d, "display_name", accessPackageAssignmentPolicy.DisplayName.GetOrZero()) + tf.Set(d, "duration_in_days", int(accessPackageAssignmentPolicy.DurationInDays.GetOrZero())) + tf.Set(d, "expiration_date", accessPackageAssignmentPolicy.ExpirationDateTime.GetOrZero()) + tf.Set(d, "extension_enabled", accessPackageAssignmentPolicy.CanExtend.GetOrZero()) tf.Set(d, "question", flattenAccessPackageQuestions(accessPackageAssignmentPolicy.Questions)) + tf.Set(d, "requestor_settings", flattenRequestorSettings(accessPackageAssignmentPolicy.RequestorSettings)) return nil } func accessPackageAssignmentPolicyResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageAssignmentPolicyClient - accessPackageAssignmentPolicyId := d.Id() - - _, status, err := client.Get(ctx, accessPackageAssignmentPolicyId, odata.Query{}) - if err != nil { - if status == http.StatusNotFound { - return tf.ErrorDiagPathF(fmt.Errorf("Access package assignment policy was not found"), "id", "Retrieving user with object ID %q", accessPackageAssignmentPolicyId) - } - return tf.ErrorDiagPathF(err, "id", "Retrieving access package assignment policy with object ID %q", accessPackageAssignmentPolicyId) - } + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageAssignmentPolicyID(d.Id()) - status, err = client.Delete(ctx, accessPackageAssignmentPolicyId) - if err != nil { - return tf.ErrorDiagPathF(err, "id", "Deleting access package assignment policy with object ID %q, got status %d", accessPackageAssignmentPolicyId, status) + if _, err := client.DeleteEntitlementManagementAccessPackageAssignmentPolicy(ctx, id, entitlementmanagementaccesspackageassignmentpolicy.DefaultDeleteEntitlementManagementAccessPackageAssignmentPolicyOperationOptions()); err != nil { + return tf.ErrorDiagPathF(err, "id", "Deleting %s", id) } // Wait for user object to be deleted - 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, accessPackageAssignmentPolicyId, odata.Query{}); err != nil { - if status == http.StatusNotFound { + if err := consistency.WaitForDeletion(ctx, func(ctx context.Context) (*bool, error) { + if resp, err := client.GetEntitlementManagementAccessPackageAssignmentPolicy(ctx, id, entitlementmanagementaccesspackageassignmentpolicy.DefaultGetEntitlementManagementAccessPackageAssignmentPolicyOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } return nil, err } return pointer.To(true), nil }); err != nil { - return tf.ErrorDiagF(err, "Waiting for deletion of access package assignment policy with object ID %q", accessPackageAssignmentPolicyId) + return tf.ErrorDiagF(err, "Waiting for deletion of %s", id) } return nil } -func buildAssignmentPolicyResourceData(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) (msgraph.AccessPackageAssignmentPolicy, error) { +func buildAssignmentPolicyResourceData(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) (*beta.AccessPackageAssignmentPolicy, error) { accessPackageClient := meta.(*clients.Client).IdentityGovernance.AccessPackageClient + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(d.Get("access_package_id").(string)) - accessPackageId := d.Get("access_package_id").(string) - _, status, err := accessPackageClient.Get(ctx, accessPackageId, odata.Query{}) + resp, err := accessPackageClient.GetEntitlementManagementAccessPackage(ctx, id, entitlementmanagementaccesspackage.DefaultGetEntitlementManagementAccessPackageOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package with Object ID %q was not found - removing from state!", accessPackageId) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] %s was not found - removing from state!", id) } - return msgraph.AccessPackageAssignmentPolicy{}, fmt.Errorf("retrieving access package with ID %v: %v", accessPackageId, err) + return nil, fmt.Errorf("retrieving %s: %v", id, err) } - properties := msgraph.AccessPackageAssignmentPolicy{ - ID: pointer.To(d.Id()), - DisplayName: pointer.To(d.Get("display_name").(string)), - Description: pointer.To(d.Get("description").(string)), - CanExtend: pointer.To(d.Get("extension_enabled").(bool)), - DurationInDays: pointer.To(int32(d.Get("duration_in_days").(int))), - Questions: expandAccessPackageQuestions(d.Get("question").([]interface{})), - AccessPackageId: pointer.To(d.Get("access_package_id").(string)), - } - - expirationDateValue := d.Get("expiration_date").(string) - if expirationDateValue != "" { - expirationDate, err := time.Parse(time.RFC3339, expirationDateValue) - if err != nil { - return properties, fmt.Errorf("converting expiration date %v to a valid date", expirationDate) - } - - properties.ExpirationDateTime = &expirationDate + properties := beta.AccessPackageAssignmentPolicy{ + AccessPackageId: nullable.NoZero(d.Get("access_package_id").(string)), + CanExtend: nullable.Value(d.Get("extension_enabled").(bool)), + Description: nullable.NoZero(d.Get("description").(string)), + DisplayName: nullable.NoZero(d.Get("display_name").(string)), + DurationInDays: nullable.Value(int64(d.Get("duration_in_days").(int))), + ExpirationDateTime: nullable.NoZero(d.Get("expiration_date").(string)), + Questions: expandAccessPackageQuestions(d.Get("question").([]interface{})), } - properties.RequestorSettings = expandRequestorSettings(d.Get("requestor_settings").([]interface{})) - properties.RequestApprovalSettings = expandApprovalSettings(d.Get("approval_settings").([]interface{})) - - reviewSettingsStruct, err := expandAssignmentReviewSettings(d.Get("assignment_review_settings").([]interface{})) + requestApprovalSettings, err := expandApprovalSettings(d.Get("approval_settings").([]interface{})) if err != nil { - return properties, fmt.Errorf("building assignment_review_settings configuration: %v", err) - } - - properties.AccessReviewSettings = reviewSettingsStruct - - return properties, nil -} - -func assignmentPolicyDiffSuppress(k, old, new string, d *pluginsdk.ResourceData) bool { - if k == "approval_settings.#" && old == "1" && new == "0" { - return true - } - - if k == "requestor_settings.#" && old == "1" && new == "0" { - return true + return nil, fmt.Errorf("expanding `approval_settings`: %v", err) } + properties.RequestApprovalSettings = requestApprovalSettings - if k == "requestor_settings.0.scope_type" && old == msgraph.RequestorSettingsScopeTypeNoSubjects && len(new) == 0 { - return true - } - - if k == "assignment_review_settings.0.starting_on" && len(new) == 0 { - return true - } - - if k == "assignment_review_settings.#" && old == "1" && new == "0" { - return true - } - - if k == "question.#" && old == "1" && new == "0" { - return true + requestorSettings, err := expandRequestorSettings(d.Get("requestor_settings").([]interface{})) + if err != nil { + return nil, fmt.Errorf("building `requestor_settings`: %v", err) } + properties.RequestorSettings = requestorSettings - return false -} - -func assignmentPolicyCustomDiff(ctx context.Context, diff *pluginsdk.ResourceDiff, meta interface{}) error { - if reviewSettings := diff.Get("assignment_review_settings").([]interface{}); len(reviewSettings) > 0 { - reviewSetting := reviewSettings[0].(map[string]interface{}) - if reviewSetting["enabled"].(bool) && - (reviewSetting["duration_in_days"] == 0 || - len(reviewSetting["review_frequency"].(string)) == 0 || - len(reviewSetting["access_review_timeout_behavior"].(string)) == 0) { - return fmt.Errorf("`duration_in_days`, `review_frequency`, `access_review_timeout_behavior` must be set when review is enabled") - } + reviewSettings, err := expandAssignmentReviewSettings(d.Get("assignment_review_settings").([]interface{})) + if err != nil { + return nil, fmt.Errorf("building `assignment_review_settings`: %v", err) } + properties.AccessReviewSettings = reviewSettings - return nil + return &properties, nil } diff --git a/internal/services/identitygovernance/access_package_assignment_policy_resource_test.go b/internal/services/identitygovernance/access_package_assignment_policy_resource_test.go index 021515003..1b613955c 100644 --- a/internal/services/identitygovernance/access_package_assignment_policy_resource_test.go +++ b/internal/services/identitygovernance/access_package_assignment_policy_resource_test.go @@ -6,11 +6,12 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageassignmentpolicy" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -124,16 +125,16 @@ func TestAccAccessPackageAssignmentPolicy_removeQuestion(t *testing.T) { func (AccessPackageAssignmentPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.IdentityGovernance.AccessPackageAssignmentPolicyClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageAssignmentPolicyID(state.ID) - _, status, err := client.Get(ctx, state.ID, odata.Query{}) + resp, err := client.GetEntitlementManagementAccessPackageAssignmentPolicy(ctx, id, entitlementmanagementaccesspackageassignmentpolicy.DefaultGetEntitlementManagementAccessPackageAssignmentPolicyOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve Access package assignment policy with ID %q: %+v", state.ID, err) + return nil, fmt.Errorf("failed to retrieve %s: %+v", id, err) } + return pointer.To(true), nil } @@ -170,18 +171,18 @@ resource "azuread_group" "test" { } resource "azuread_access_package_catalog" "test_catalog" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Catalog %[1]d for access assignment policy" } resource "azuread_access_package" "test" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Access Package %[1]d for access assignment policy" catalog_id = azuread_access_package_catalog.test_catalog.id } resource "azuread_access_package_assignment_policy" "test" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Access Package Assignnment Policy %[1]d" duration_in_days = 90 access_package_id = azuread_access_package.test.id @@ -197,7 +198,7 @@ resource "azuread_access_package_assignment_policy" "test" { primary_approver { object_id = azuread_group.test.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" } } } @@ -229,18 +230,18 @@ resource "azuread_group" "test" { } resource "azuread_access_package_catalog" "test_catalog" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Catalog %[1]d for access assignment policy" } resource "azuread_access_package" "test" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Access Package %[1]d for access assignment policy" catalog_id = azuread_access_package_catalog.test_catalog.id } resource "azuread_access_package_assignment_policy" "test" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Access Package Assignnment Policy %[1]d" duration_in_days = 90 access_package_id = azuread_access_package.test.id @@ -256,7 +257,7 @@ resource "azuread_access_package_assignment_policy" "test" { primary_approver { object_id = azuread_group.test.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" } } } @@ -292,15 +293,16 @@ resource "azuread_group" "second_approver" { } resource "azuread_access_package_catalog" "test_catalog" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "TestAcc Catalog %[1]d for access assignment policy" } resource "azuread_access_package" "test" { - display_name = "testacc-asscess-assignment-%[1]d" + display_name = "testacc-access-assignment-%[1]d" description = "Test Access Package %[1]d for assignment policy" catalog_id = azuread_access_package_catalog.test_catalog.id } + resource "azuread_access_package_assignment_policy" "test" { display_name = "access-package-assignment-policy-%[1]d" description = "Test Access Package Assignnment Policy %[1]d" @@ -314,7 +316,7 @@ resource "azuread_access_package_assignment_policy" "test" { requestor { object_id = azuread_group.requestor.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" } } @@ -330,12 +332,12 @@ resource "azuread_access_package_assignment_policy" "test" { enable_alternative_approval_in_days = 8 primary_approver { - subject_type = "requestorManager" + subject_type = "RequestorManager" } alternative_approver { object_id = azuread_group.second_approver.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" } } @@ -344,12 +346,12 @@ resource "azuread_access_package_assignment_policy" "test" { primary_approver { object_id = azuread_group.second_approver.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" } primary_approver { object_id = azuread_group.first_approver.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" backup = true } } @@ -359,13 +361,13 @@ resource "azuread_access_package_assignment_policy" "test" { enabled = true review_frequency = "annual" review_type = "Reviewers" - duration_in_days = "10" + duration_in_days = 10 access_recommendation_enabled = true access_review_timeout_behavior = "acceptAccessRecommendation" reviewer { object_id = azuread_group.first_approver.object_id - subject_type = "groupMembers" + subject_type = "GroupMembers" } } diff --git a/internal/services/identitygovernance/access_package_catalog_data_source.go b/internal/services/identitygovernance/access_package_catalog_data_source.go index 377dc9b17..ec3199b2f 100644 --- a/internal/services/identitygovernance/access_package_catalog_data_source.go +++ b/internal/services/identitygovernance/access_package_catalog_data_source.go @@ -5,16 +5,19 @@ package identitygovernance import ( "context" + "errors" "fmt" "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalog" "github.com/hashicorp/go-azure-sdk/sdk/odata" "github.com/hashicorp/terraform-provider-azuread/internal/clients" - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) func accessPackageCatalogDataSource() *pluginsdk.Resource { @@ -70,36 +73,38 @@ func accessPackageCatalogDataRead(ctx context.Context, d *pluginsdk.ResourceData objectId := d.Get("object_id").(string) displayName := d.Get("display_name").(string) - var catalog *msgraph.AccessPackageCatalog - var err error + var catalog *beta.AccessPackageCatalog if objectId != "" { - catalog, _, err = client.Get(ctx, objectId, odata.Query{}) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(objectId) + resp, err := client.GetEntitlementManagementAccessPackageCatalog(ctx, id, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Error retrieving access package catalog with id %q", objectId) + return tf.ErrorDiagF(err, "Retrieving %s", id) } + + if resp.Model == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", id) + } + catalog = resp.Model + } else if displayName != "" { - query := odata.Query{ - Filter: fmt.Sprintf("displayName eq '%s'", displayName), + options := entitlementmanagementaccesspackagecatalog.ListEntitlementManagementAccessPackageCatalogsOperationOptions{ + Filter: pointer.To(fmt.Sprintf("displayName eq '%s'", odata.EscapeSingleQuote(displayName))), } - result, _, err := client.List(ctx, query) + resp, err := client.ListEntitlementManagementAccessPackageCatalogs(ctx, options) if err != nil { - return tf.ErrorDiagF(err, "Error listing access package catalog with filter %s", query.Filter) + return tf.ErrorDiagF(err, "Listing access package catalogs with filter %s", *options.Filter) } - if result == nil || len(*result) == 0 { - return tf.ErrorDiagF(fmt.Errorf("no access package catalog matched with filter %s", query.Filter), "Access package catalog not found!") + + if resp.Model == nil || len(*resp.Model) == 0 { + return tf.ErrorDiagF(errors.New("no matching results"), "Listing access package catalogs with filter %s", *options.Filter) } - if len(*result) > 1 { - return tf.ErrorDiagF(fmt.Errorf("multiple access package catalog matched with filter %s", query.Filter), "Multiple access package catalog found!") + if len(*resp.Model) > 1 { + return tf.ErrorDiagF(errors.New("multiple results matched"), "Listing access package catalogs with filter %s", *options.Filter) } - for _, c := range *result { - name := c.DisplayName - if name == nil { - continue - } - - if *name == displayName { + for _, c := range *resp.Model { + if strings.EqualFold(c.DisplayName.GetOrZero(), displayName) { catalog = &c break } @@ -107,20 +112,24 @@ func accessPackageCatalogDataRead(ctx context.Context, d *pluginsdk.ResourceData } if catalog == nil { - return tf.ErrorDiagF(fmt.Errorf("no access package catalog matched with specified parameters"), "Access access package catalog not found!") + return tf.ErrorDiagF(fmt.Errorf("no access package catalog matched with specified parameters"), "Access package catalog not found") + } + if catalog.Id == nil { + return tf.ErrorDiagF(fmt.Errorf("model has nil ID"), "Access package catalog not found") } published := false - if strings.EqualFold(catalog.State, msgraph.AccessPackageCatalogStatusPublished) { + if strings.EqualFold(catalog.CatalogStatus.GetOrZero(), CatalogStatusPublished) { published = true } - d.SetId(*catalog.ID) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(*catalog.Id) + d.SetId(id.AccessPackageCatalogId) - tf.Set(d, "object_id", catalog.ID) - tf.Set(d, "display_name", catalog.DisplayName) - tf.Set(d, "description", catalog.Description) - tf.Set(d, "externally_visible", catalog.IsExternallyVisible) + tf.Set(d, "object_id", id.AccessPackageCatalogId) + tf.Set(d, "display_name", catalog.DisplayName.GetOrZero()) + tf.Set(d, "description", catalog.Description.GetOrZero()) + tf.Set(d, "externally_visible", catalog.IsExternallyVisible.GetOrZero()) tf.Set(d, "published", published) return nil diff --git a/internal/services/identitygovernance/access_package_catalog_resource.go b/internal/services/identitygovernance/access_package_catalog_resource.go index cf6545d04..f9a895ee4 100644 --- a/internal/services/identitygovernance/access_package_catalog_resource.go +++ b/internal/services/identitygovernance/access_package_catalog_resource.go @@ -5,21 +5,23 @@ package identitygovernance import ( "context" + "errors" "fmt" "log" - "net/http" "strings" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalog" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-uuid" "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/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/consistency" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) const accessPackageCatalogResourceName = "azuread_access_package_catalog" @@ -47,17 +49,17 @@ func accessPackageCatalogResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "display_name": { - Description: "The display name of the access package catalog", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The display name of the access package catalog", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "description": { - Description: "The description of the access package catalog", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The description of the access package catalog", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "externally_visible": { @@ -80,26 +82,30 @@ func accessPackageCatalogResource() *pluginsdk.Resource { func accessPackageCatalogResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - displayName := d.Get("display_name").(string) - - state := msgraph.AccessPackageCatalogStateUnpublished + status := CatalogStatusUnpublished if d.Get("published").(bool) { - state = msgraph.AccessPackageCatalogStatePublished + status = CatalogStatusPublished } - properties := msgraph.AccessPackageCatalog{ - DisplayName: pointer.To(displayName), - Description: pointer.To(d.Get("description").(string)), - State: state, - IsExternallyVisible: pointer.To(d.Get("externally_visible").(bool)), + properties := beta.AccessPackageCatalog{ + DisplayName: nullable.NoZero(d.Get("display_name").(string)), + Description: nullable.NoZero(d.Get("description").(string)), + CatalogStatus: nullable.Value(status), + IsExternallyVisible: nullable.Value(d.Get("externally_visible").(bool)), } - accessPackageCatalog, _, err := client.Create(ctx, properties) + resp, err := client.CreateEntitlementManagementAccessPackageCatalog(ctx, properties, entitlementmanagementaccesspackagecatalog.DefaultCreateEntitlementManagementAccessPackageCatalogOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Creating access package catalog %q", displayName) + return tf.ErrorDiagF(err, "Creating access package catalog") + } + + catalog := resp.Model + if catalog == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Creating access package catalog") } - d.SetId(*accessPackageCatalog.ID) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(*catalog.Id) + d.SetId(id.AccessPackageCatalogId) return accessPackageCatalogResourceRead(ctx, d, meta) } @@ -107,25 +113,25 @@ func accessPackageCatalogResourceCreate(ctx context.Context, d *pluginsdk.Resour func accessPackageCatalogResourceUpdate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - objectId := d.Id() - tf.LockByName(accessPackageCatalogResourceName, objectId) - defer tf.UnlockByName(accessPackageCatalogResourceName, objectId) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(d.Id()) + + tf.LockByName(accessPackageCatalogResourceName, id.AccessPackageCatalogId) + defer tf.UnlockByName(accessPackageCatalogResourceName, id.AccessPackageCatalogId) - state := msgraph.AccessPackageCatalogStateUnpublished + status := CatalogStatusUnpublished if d.Get("published").(bool) { - state = msgraph.AccessPackageCatalogStatePublished + status = CatalogStatusPublished } - properties := msgraph.AccessPackageCatalog{ - ID: pointer.To(d.Id()), - DisplayName: pointer.To(d.Get("display_name").(string)), - Description: pointer.To(d.Get("description").(string)), - State: state, - IsExternallyVisible: pointer.To(d.Get("externally_visible").(bool)), + properties := beta.AccessPackageCatalog{ + DisplayName: nullable.NoZero(d.Get("display_name").(string)), + Description: nullable.NoZero(d.Get("description").(string)), + CatalogStatus: nullable.Value(status), + IsExternallyVisible: nullable.Value(d.Get("externally_visible").(bool)), } - if _, err := client.Update(ctx, properties); err != nil { - return tf.ErrorDiagF(err, "Could not update access package catalog with ID: %q", objectId) + if _, err := client.UpdateEntitlementManagementAccessPackageCatalog(ctx, id, properties, entitlementmanagementaccesspackagecatalog.DefaultUpdateEntitlementManagementAccessPackageCatalogOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Updating %s", id) } return accessPackageCatalogResourceRead(ctx, d, meta) @@ -134,62 +140,58 @@ func accessPackageCatalogResourceUpdate(ctx context.Context, d *pluginsdk.Resour func accessPackageCatalogResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - objectId := d.Id() - accessPackageCatalog, status, err := client.Get(ctx, objectId, odata.Query{}) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(d.Id()) + + resp, err := client.GetEntitlementManagementAccessPackageCatalog(ctx, id, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package catalog with Object ID %q was not found - removing from state!", objectId) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] %s was not found - removing from state!", id) d.SetId("") return nil } - return tf.ErrorDiagF(err, "Retrieving access package catalog with object ID: %q", objectId) + return tf.ErrorDiagF(err, "Retrieving %s", id) + } + + catalog := resp.Model + if catalog == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", id) } published := false - if strings.EqualFold(accessPackageCatalog.State, msgraph.AccessPackageCatalogStatusPublished) { + if strings.EqualFold(catalog.CatalogStatus.GetOrZero(), CatalogStatusPublished) { published = true } - tf.Set(d, "display_name", accessPackageCatalog.DisplayName) - tf.Set(d, "description", accessPackageCatalog.Description) + tf.Set(d, "display_name", catalog.DisplayName.GetOrZero()) + tf.Set(d, "description", catalog.Description.GetOrZero()) tf.Set(d, "published", published) - tf.Set(d, "externally_visible", accessPackageCatalog.IsExternallyVisible) + tf.Set(d, "externally_visible", catalog.IsExternallyVisible.GetOrZero()) return nil } func accessPackageCatalogResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - accessPackageCatalogId := d.Id() - _, status, err := client.Get(ctx, accessPackageCatalogId, odata.Query{}) - if err != nil { - if status == http.StatusNotFound { - return tf.ErrorDiagPathF(fmt.Errorf("Access package catalog was not found"), "id", "Retrieving user with object ID %q", accessPackageCatalogId) - } - - return tf.ErrorDiagPathF(err, "id", "Retrieving access package catalog with object ID %q", accessPackageCatalogId) - } + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(d.Id()) - status, err = client.Delete(ctx, accessPackageCatalogId) - if err != nil { - return tf.ErrorDiagPathF(err, "id", "Deleting access package catalog with object ID %q, got status %d", accessPackageCatalogId, status) + if _, err := client.DeleteEntitlementManagementAccessPackageCatalog(ctx, id, entitlementmanagementaccesspackagecatalog.DefaultDeleteEntitlementManagementAccessPackageCatalogOperationOptions()); err != nil { + return tf.ErrorDiagPathF(err, "id", "Deleting %s", id) } // Wait for object to be deleted - 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, accessPackageCatalogId, odata.Query{}); err != nil { - if status == http.StatusNotFound { + if err := consistency.WaitForDeletion(ctx, func(ctx context.Context) (*bool, error) { + if resp, err := client.GetEntitlementManagementAccessPackageCatalog(ctx, id, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } return nil, err } + return pointer.To(true), nil }); err != nil { - return tf.ErrorDiagF(err, "Waiting for deletion of access package catalog with object ID %q", accessPackageCatalogId) + return tf.ErrorDiagF(err, "Waiting for deletion of %s", id) } return nil diff --git a/internal/services/identitygovernance/access_package_catalog_resource_test.go b/internal/services/identitygovernance/access_package_catalog_resource_test.go index 696d16325..8df41e5a5 100644 --- a/internal/services/identitygovernance/access_package_catalog_resource_test.go +++ b/internal/services/identitygovernance/access_package_catalog_resource_test.go @@ -6,11 +6,12 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalog" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -80,17 +81,16 @@ func TestAccAccessPackageCatalog_update(t *testing.T) { func (AccessPackageCatalogResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.IdentityGovernance.AccessPackageCatalogClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(state.ID) - _, status, err := client.Get(ctx, state.ID, odata.Query{}) + resp, err := client.GetEntitlementManagementAccessPackageCatalog(ctx, id, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - - return nil, fmt.Errorf("failed to retrieve access package catalog with ID %q: %+v", state.ID, err) + return nil, fmt.Errorf("failed to retrieve %s: %v", id, err) } + return pointer.To(true), nil } diff --git a/internal/services/identitygovernance/access_package_catalog_role_assignment_resource.go b/internal/services/identitygovernance/access_package_catalog_role_assignment_resource.go index 58e6b4578..3c9fe7863 100644 --- a/internal/services/identitygovernance/access_package_catalog_role_assignment_resource.go +++ b/internal/services/identitygovernance/access_package_catalog_role_assignment_resource.go @@ -8,18 +8,20 @@ import ( "errors" "fmt" "log" - "net/http" "strings" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/beta/entitlementmanagementroleassignment" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/beta/entitlementmanagementroledefinition" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform-provider-azuread/internal/clients" - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) func accessPackageCatalogRoleAssignmentResource() *pluginsdk.Resource { @@ -44,86 +46,121 @@ func accessPackageCatalogRoleAssignmentResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "role_id": { - Description: "The object ID of the catalog role for this assignment", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the catalog role for this assignment", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, "principal_object_id": { - Description: "The object ID of the member principal", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the member principal", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, "catalog_id": { - Description: "The unique ID of the access package catalog.", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The unique ID of the access package catalog.", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, }, } } func accessPackageCatalogRoleAssignmentResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogRoleAssignmentsClient + client := meta.(*clients.Client).IdentityGovernance.RoleAssignmentClient + roleDefinitionClient := meta.(*clients.Client).IdentityGovernance.RoleDefinitionClient catalogId := d.Get("catalog_id").(string) principalId := d.Get("principal_object_id").(string) - roleId := d.Get("role_id").(string) - properties := msgraph.UnifiedRoleAssignment{ - DirectoryScopeId: pointer.To("/"), - PrincipalId: pointer.To(principalId), - RoleDefinitionId: pointer.To(roleId), - AppScopeId: pointer.To("/AccessPackageCatalog/" + catalogId), + roleId := beta.NewRoleManagementEntitlementManagementRoleDefinitionID(d.Get("role_id").(string)) + + roleResp, err := roleDefinitionClient.GetEntitlementManagementRoleDefinition(ctx, roleId, entitlementmanagementroledefinition.DefaultGetEntitlementManagementRoleDefinitionOperationOptions()) + if err != nil { + if response.WasNotFound(roleResp.HttpResponse) { + return tf.ErrorDiagPathF(nil, "role_id", "%s was not found", roleId) + } + return tf.ErrorDiagF(err, "Retrieving %s", roleId) + } + if roleResp.Model == nil { + return tf.ErrorDiagPathF(errors.New("model was nil"), "role_id", "Retrieving %s", roleId) + } + + //roleDefinition := roleResp.Model + + properties := beta.UnifiedRoleAssignment{ + DirectoryScopeId: nullable.Value("/"), + PrincipalId: nullable.Value(principalId), + RoleDefinitionId: nullable.Value(roleId.UnifiedRoleDefinitionId), + //RoleDefinition: roleDefinition, + AppScopeId: nullable.Value(fmt.Sprintf("/AccessPackageCatalog/%s", catalogId)), + + OmitDiscriminatedValue: true, } - assignment, status, err := client.Create(ctx, properties) + createMsg := fmt.Sprintf("Assigning catalog role %q to directory principal %q on catalog %q", roleId, principalId, catalogId) + resp, err := client.CreateEntitlementManagementRoleAssignment(ctx, properties, entitlementmanagementroleassignment.DefaultCreateEntitlementManagementRoleAssignmentOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Assigning catalog role %q to directory principal %q on catalog %q, received %d with error: %+v", roleId, principalId, catalogId, status, err) + return tf.ErrorDiagF(err, createMsg) } - if assignment == nil || assignment.ID() == nil { - return tf.ErrorDiagF(errors.New("returned role assignment ID was nil"), "API Error") + + assignment := resp.Model + if assignment == nil { + return tf.ErrorDiagF(errors.New("model was nil"), createMsg) + } + if assignment.Id == nil { + return tf.ErrorDiagF(errors.New("model has nil ID"), createMsg) } - d.SetId(*assignment.ID()) + id := beta.NewRoleManagementEntitlementManagementRoleAssignmentID(*assignment.Id) + d.SetId(id.UnifiedRoleAssignmentId) + return accessPackageCatalogRoleAssignmentResourceRead(ctx, d, meta) } func accessPackageCatalogRoleAssignmentResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogRoleAssignmentsClient + client := meta.(*clients.Client).IdentityGovernance.RoleAssignmentClient + + id := beta.NewRoleManagementEntitlementManagementRoleAssignmentID(d.Id()) - id := d.Id() - assignment, status, err := client.Get(ctx, id, odata.Query{}) + resp, err := client.GetEntitlementManagementRoleAssignment(ctx, id, entitlementmanagementroleassignment.DefaultGetEntitlementManagementRoleAssignmentOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Assignment with ID %q was not found - removing from state", id) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] %s was not found - removing from state", id) d.SetId("") return nil } - return tf.ErrorDiagF(err, "Retrieving role assignment %q", id) + return tf.ErrorDiagF(err, "Retrieving %s", id) } - catalogId := strings.TrimPrefix(*assignment.AppScopeId, "/AccessPackageCatalog/") + assignment := resp.Model + if assignment == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", id) + } + + catalogId := strings.TrimPrefix(assignment.AppScopeId.GetOrZero(), "/AccessPackageCatalog/") tf.Set(d, "catalog_id", pointer.To(catalogId)) - tf.Set(d, "principal_object_id", assignment.PrincipalId) - tf.Set(d, "role_id", assignment.RoleDefinitionId) + tf.Set(d, "principal_object_id", assignment.PrincipalId.GetOrZero()) + tf.Set(d, "role_id", assignment.RoleDefinitionId.GetOrZero()) return nil } func accessPackageCatalogRoleAssignmentResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogRoleAssignmentsClient + client := meta.(*clients.Client).IdentityGovernance.RoleAssignmentClient - if _, err := client.Delete(ctx, d.Id()); err != nil { - return tf.ErrorDiagF(err, "Deleting role assignment %q: %+v", d.Id(), err) + id := beta.NewRoleManagementEntitlementManagementRoleAssignmentID(d.Id()) + + if _, err := client.DeleteEntitlementManagementRoleAssignment(ctx, id, entitlementmanagementroleassignment.DefaultDeleteEntitlementManagementRoleAssignmentOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Deleting %s", id) } + return nil } diff --git a/internal/services/identitygovernance/access_package_catalog_role_assignment_resource_test.go b/internal/services/identitygovernance/access_package_catalog_role_assignment_resource_test.go index f4468c934..20b0b0683 100644 --- a/internal/services/identitygovernance/access_package_catalog_role_assignment_resource_test.go +++ b/internal/services/identitygovernance/access_package_catalog_role_assignment_resource_test.go @@ -6,11 +6,12 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/beta/entitlementmanagementroleassignment" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -74,15 +75,14 @@ func TestAccAccessPackageCatalogRoleAssignmentResource_user(t *testing.T) { } func (r AccessPackageCatalogRoleAssignmentResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.IdentityGovernance.AccessPackageCatalogRoleAssignmentsClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.IdentityGovernance.RoleAssignmentClient + id := beta.NewRoleManagementEntitlementManagementRoleAssignmentID(state.ID) - if _, status, err := client.Get(ctx, state.ID, odata.Query{}); err != nil { - if status == http.StatusNotFound { + if resp, err := client.GetEntitlementManagementRoleAssignment(ctx, id, entitlementmanagementroleassignment.DefaultGetEntitlementManagementRoleAssignmentOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve directory role assignment %q: %+v", state.ID, err) + return nil, fmt.Errorf("failed to retrieve %s: %+v", id, err) } return pointer.To(true), nil diff --git a/internal/services/identitygovernance/access_package_catalog_role_data_source.go b/internal/services/identitygovernance/access_package_catalog_role_data_source.go index 6aab51e47..d784e3a1c 100644 --- a/internal/services/identitygovernance/access_package_catalog_role_data_source.go +++ b/internal/services/identitygovernance/access_package_catalog_role_data_source.go @@ -7,14 +7,17 @@ import ( "context" "errors" "fmt" - "net/http" + "github.com/hashicorp/go-azure-sdk/sdk/odata" "time" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/beta/entitlementmanagementroledefinition" "github.com/hashicorp/terraform-provider-azuread/internal/clients" - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) func accessPackageCatalogRoleDataSource() *pluginsdk.Resource { @@ -32,6 +35,7 @@ func accessPackageCatalogRoleDataSource() *pluginsdk.Resource { Optional: true, Computed: true, ExactlyOneOf: []string{"display_name", "object_id"}, + ValidateFunc: validation.StringIsNotEmpty, }, "object_id": { @@ -40,6 +44,7 @@ func accessPackageCatalogRoleDataSource() *pluginsdk.Resource { Optional: true, Computed: true, ExactlyOneOf: []string{"display_name", "object_id"}, + ValidateFunc: validation.IsUUID, }, "description": { @@ -58,9 +63,9 @@ func accessPackageCatalogRoleDataSource() *pluginsdk.Resource { } func accessPackageCatalogRoleDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogRoleClient + client := meta.(*clients.Client).IdentityGovernance.RoleDefinitionClient - var role msgraph.UnifiedRoleDefinition + var role *beta.UnifiedRoleDefinition var displayName string if v, ok := d.GetOk("display_name"); ok { @@ -68,47 +73,55 @@ func accessPackageCatalogRoleDataSourceRead(ctx context.Context, d *pluginsdk.Re } if displayName != "" { - filter := fmt.Sprintf("displayName eq '%s'", displayName) + options := entitlementmanagementroledefinition.ListEntitlementManagementRoleDefinitionsOperationOptions{ + Filter: pointer.To(fmt.Sprintf("displayName eq '%s'", odata.EscapeSingleQuote(displayName))), + } - roles, _, err := client.List(ctx, odata.Query{Filter: filter}) + resp, err := client.ListEntitlementManagementRoleDefinitions(ctx, options) if err != nil { - return tf.ErrorDiagPathF(err, "display_name", "No role found matching specified filter (%s)", filter) + return tf.ErrorDiagPathF(err, "display_name", "No role found matching specified filter: %s", *options.Filter) + } + if resp.Model == nil { + return tf.ErrorDiagPathF(errors.New("model was nil"), "display_name", "No role found matching specified filter: %s", *options.Filter) } - count := len(*roles) + count := len(*resp.Model) if count > 1 { - return tf.ErrorDiagPathF(err, "display_name", "More than one role found matching specified filter (%s)", filter) + return tf.ErrorDiagPathF(err, "display_name", "More than one role found matching specified filter: %s", *options.Filter) } else if count == 0 { - return tf.ErrorDiagPathF(err, "display_name", "No role found matching specified filter (%s)", filter) + return tf.ErrorDiagPathF(err, "display_name", "No role found matching specified filter: %s", *options.Filter) } - role = (*roles)[0] + role = &(*resp.Model)[0] } else if objectId, ok := d.Get("object_id").(string); ok && objectId != "" { - r, status, err := client.Get(ctx, objectId, odata.Query{}) + resp, err := client.GetEntitlementManagementRoleDefinition(ctx, beta.NewRoleManagementEntitlementManagementRoleDefinitionID(objectId), entitlementmanagementroledefinition.DefaultGetEntitlementManagementRoleDefinitionOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return tf.ErrorDiagPathF(nil, "object_id", "No role found with object ID: %q", objectId) } return tf.ErrorDiagF(err, "Retrieving role with object ID: %q", objectId) } - if r == nil { + if resp.Model == nil { return tf.ErrorDiagPathF(nil, "object_id", "Role not found with object ID: %q", objectId) } - role = *r + role = resp.Model } - if role.ID() == nil { + + if role == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "No role found") + } + if role.Id == nil { return tf.ErrorDiagF(errors.New("API returned role with nil object ID"), "Bad API Response") } - d.SetId(*role.ID()) + id := beta.NewRoleManagementEntitlementManagementRoleDefinitionID(*role.Id) + d.SetId(id.UnifiedRoleDefinitionId) - tf.Set(d, "object_id", role.ID()) - tf.Set(d, "display_name", role.DisplayName) - tf.Set(d, "description", role.Description) - tf.Set(d, "template_id", role.TemplateId) + tf.Set(d, "object_id", id.UnifiedRoleDefinitionId) + tf.Set(d, "display_name", role.DisplayName.GetOrZero()) + tf.Set(d, "description", role.Description.GetOrZero()) + tf.Set(d, "template_id", role.TemplateId.GetOrZero()) return nil } - -// TODO replace role diff --git a/internal/services/identitygovernance/access_package_data_source.go b/internal/services/identitygovernance/access_package_data_source.go index 8e16e0c0e..1f8e69b6a 100644 --- a/internal/services/identitygovernance/access_package_data_source.go +++ b/internal/services/identitygovernance/access_package_data_source.go @@ -5,15 +5,19 @@ package identitygovernance import ( "context" + "errors" "fmt" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/sdk/odata" + "strings" "time" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" "github.com/hashicorp/terraform-provider-azuread/internal/clients" - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) func accessPackageDataSource() *pluginsdk.Resource { @@ -39,6 +43,7 @@ func accessPackageDataSource() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, Computed: true, + ValidateFunc: validation.StringIsNotEmpty, AtLeastOneOf: []string{"object_id", "display_name", "catalog_id"}, ConflictsWith: []string{"object_id"}, RequiredWith: []string{"catalog_id"}, @@ -48,6 +53,7 @@ func accessPackageDataSource() *pluginsdk.Resource { Description: "The ID of the Catalog this access package is in", Type: pluginsdk.TypeString, Optional: true, + ValidateFunc: validation.IsUUID, AtLeastOneOf: []string{"object_id", "display_name", "catalog_id"}, ConflictsWith: []string{"object_id"}, RequiredWith: []string{"display_name"}, @@ -71,42 +77,44 @@ func accessPackageDataSource() *pluginsdk.Resource { func accessPackageDataRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageClient - var err error objectId := d.Get("object_id").(string) displayName := d.Get("display_name").(string) catalogId := d.Get("catalog_id").(string) - var accessPackage *msgraph.AccessPackage + var accessPackage *beta.AccessPackage if objectId != "" { - accessPackage, _, err = client.Get(ctx, objectId, odata.Query{}) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(objectId) + resp, err := client.GetEntitlementManagementAccessPackage(ctx, id, entitlementmanagementaccesspackage.DefaultGetEntitlementManagementAccessPackageOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Error retrieving access package with id %q", objectId) + return tf.ErrorDiagF(err, "Retrieving %s", id) } + + if resp.Model == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", id) + } + accessPackage = resp.Model + } else if displayName != "" && catalogId != "" { - query := odata.Query{ - // Filter: fmt.Sprintf("displayName eq '%s' and catalogId eq '%s'", displayName, catalogId), - // Filter: fmt.Sprintf("catalogId eq '%s'", catalogId), + // We can only filter on displayName + options := entitlementmanagementaccesspackage.ListEntitlementManagementAccessPackagesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("displayName eq '%s'", odata.EscapeSingleQuote(displayName))), } - result, _, err := client.List(ctx, query) + resp, err := client.ListEntitlementManagementAccessPackages(ctx, options) if err != nil { - return tf.ErrorDiagF(err, "Error listing access package with filter %s", query.Filter) + return tf.ErrorDiagF(err, "Listing access packages") } - if result == nil || len(*result) == 0 { - return tf.ErrorDiagF(fmt.Errorf("no access package matched with filter %s", query.Filter), "Access access package not found!") + + if resp.Model == nil || len(*resp.Model) == 0 { + return tf.ErrorDiagF(errors.New("no matching results"), "Listing access packages") + } + if len(*resp.Model) > 1 { + return tf.ErrorDiagF(fmt.Errorf("multiple access package matched with filter %s", *options.Filter), "Unexpected number of results") } - // if len(*result) > 1 { - // return tf.ErrorDiagF(fmt.Errorf("Multiple access package matched with filter %s", query.Filter), "Multitple access package found!") - // } - - for _, c := range *result { - name := c.DisplayName - catalog := c.CatalogId - if name == nil || catalog == nil { - continue - } - if *name == displayName && *c.CatalogId == catalogId { + for _, c := range *resp.Model { + // Check the displayName and catalogId + if strings.EqualFold(c.DisplayName.GetOrZero(), displayName) && c.CatalogId.GetOrZero() == catalogId { accessPackage = &c break } @@ -114,16 +122,20 @@ func accessPackageDataRead(ctx context.Context, d *pluginsdk.ResourceData, meta } if accessPackage == nil { - return tf.ErrorDiagF(fmt.Errorf("no access package matched with specified parameters"), "Access access package not found!") + return tf.ErrorDiagF(fmt.Errorf("no access package matched with specified parameters"), "Access package not found") + } + if accessPackage.Id == nil { + return tf.ErrorDiagF(fmt.Errorf("model has nil ID"), "Access package not found") } - d.SetId(*accessPackage.ID) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(*accessPackage.Id) + d.SetId(id.AccessPackageId) - tf.Set(d, "object_id", accessPackage.ID) - tf.Set(d, "display_name", accessPackage.DisplayName) - tf.Set(d, "description", accessPackage.Description) - tf.Set(d, "hidden", accessPackage.IsHidden) - tf.Set(d, "catalog_id", accessPackage.CatalogId) + tf.Set(d, "object_id", id.AccessPackageId) + tf.Set(d, "catalog_id", accessPackage.CatalogId.GetOrZero()) + tf.Set(d, "display_name", accessPackage.DisplayName.GetOrZero()) + tf.Set(d, "description", accessPackage.Description.GetOrZero()) + tf.Set(d, "hidden", accessPackage.IsHidden.GetOrZero()) return nil } diff --git a/internal/services/identitygovernance/access_package_resource.go b/internal/services/identitygovernance/access_package_resource.go index f80d847d0..a39a148fd 100644 --- a/internal/services/identitygovernance/access_package_resource.go +++ b/internal/services/identitygovernance/access_package_resource.go @@ -5,20 +5,23 @@ package identitygovernance import ( "context" + "errors" "fmt" "log" - "net/http" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalog" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-uuid" "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/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/consistency" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) const accessPackageResourceName = "azuread_access_package" @@ -46,25 +49,25 @@ func accessPackageResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "catalog_id": { - Description: "The ID of the Catalog this access package will be created in", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The ID of the Catalog this access package will be created in", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, "display_name": { - Description: "The display name of the access package", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The display name of the access package", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "description": { - Description: "The description of the access package", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The description of the access package", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "hidden": { @@ -81,28 +84,38 @@ func accessPackageResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, client := meta.(*clients.Client).IdentityGovernance.AccessPackageClient accessPackageCatalogClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - displayName := d.Get("display_name").(string) - catalogId := d.Get("catalog_id").(string) + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(d.Get("catalog_id").(string)) - accessPackageCatalog, _, err := accessPackageCatalogClient.Get(ctx, catalogId, odata.Query{}) + catalogResp, err := accessPackageCatalogClient.GetEntitlementManagementAccessPackageCatalog(ctx, catalogId, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()) if err != nil { return tf.ErrorDiagF(err, "Retrieving access package catalog with object ID: %q", catalogId) } - properties := msgraph.AccessPackage{ - DisplayName: pointer.To(displayName), - Description: pointer.To(d.Get("description").(string)), - IsHidden: pointer.To(d.Get("hidden").(bool)), - Catalog: accessPackageCatalog, - CatalogId: accessPackageCatalog.ID, + catalog := catalogResp.Model + if catalog == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", catalogId) } - accessPackage, _, err := client.Create(ctx, properties) + properties := beta.AccessPackage{ + DisplayName: nullable.Value(d.Get("display_name").(string)), + Description: nullable.NoZero(d.Get("description").(string)), + IsHidden: nullable.Value(d.Get("hidden").(bool)), + + AccessPackageCatalog: catalog, + CatalogId: nullable.Value(pointer.From(catalog.Id)), + } + + resp, err := client.CreateEntitlementManagementAccessPackage(ctx, properties, entitlementmanagementaccesspackage.DefaultCreateEntitlementManagementAccessPackageOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Creating access package %q", displayName) + return tf.ErrorDiagF(err, "Creating access package") + } + + if resp.Model == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Creating access package") } - d.SetId(*accessPackage.ID) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(pointer.From(resp.Model.Id)) + d.SetId(id.AccessPackageId) return accessPackageResourceRead(ctx, d, meta) } @@ -111,28 +124,33 @@ func accessPackageResourceUpdate(ctx context.Context, d *pluginsdk.ResourceData, client := meta.(*clients.Client).IdentityGovernance.AccessPackageClient accessPackageCatalogClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - objectId := d.Id() - catalogId := d.Get("catalog_id").(string) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(d.Id()) + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(d.Get("catalog_id").(string)) - accessPackageCatalog, _, err := accessPackageCatalogClient.Get(ctx, catalogId, odata.Query{}) + catalogResp, err := accessPackageCatalogClient.GetEntitlementManagementAccessPackageCatalog(ctx, catalogId, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Retrieving access package catalog with ID: %q", catalogId) + return tf.ErrorDiagF(err, "Retrieving access package catalog with object ID: %q", catalogId) + } + + catalog := catalogResp.Model + if catalog == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", catalogId) } - tf.LockByName(accessPackageResourceName, objectId) - defer tf.UnlockByName(accessPackageResourceName, objectId) + tf.LockByName(accessPackageResourceName, id.AccessPackageId) + defer tf.UnlockByName(accessPackageResourceName, id.AccessPackageId) + + properties := beta.AccessPackage{ + DisplayName: nullable.Value(d.Get("display_name").(string)), + Description: nullable.NoZero(d.Get("description").(string)), + IsHidden: nullable.Value(d.Get("hidden").(bool)), - properties := msgraph.AccessPackage{ - ID: pointer.To(objectId), - DisplayName: pointer.To(d.Get("display_name").(string)), - Description: pointer.To(d.Get("description").(string)), - IsHidden: pointer.To(d.Get("hidden").(bool)), - Catalog: accessPackageCatalog, - CatalogId: accessPackageCatalog.ID, + AccessPackageCatalog: catalog, + CatalogId: nullable.Value(pointer.From(catalog.Id)), } - if _, err := client.Update(ctx, properties); err != nil { - return tf.ErrorDiagF(err, "Could not update access package with ID: %q", objectId) + if _, err := client.UpdateEntitlementManagementAccessPackage(ctx, id, properties, entitlementmanagementaccesspackage.DefaultUpdateEntitlementManagementAccessPackageOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Updating %s", id) } return accessPackageResourceRead(ctx, d, meta) @@ -141,58 +159,51 @@ func accessPackageResourceUpdate(ctx context.Context, d *pluginsdk.ResourceData, func accessPackageResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageClient - objectId := d.Id() - accessPackage, status, err := client.Get(ctx, objectId, odata.Query{}) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(d.Id()) + + resp, err := client.GetEntitlementManagementAccessPackage(ctx, id, entitlementmanagementaccesspackage.DefaultGetEntitlementManagementAccessPackageOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package with Object ID %q was not found - removing from state!", objectId) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] %s was not found - removing from state!", id) d.SetId("") return nil } - return tf.ErrorDiagF(err, "Retrieving access package with object ID: %q", objectId) + return tf.ErrorDiagF(err, "Retrieving %s", id) + } + + accessPackage := resp.Model + if accessPackage == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", id) } - tf.Set(d, "display_name", accessPackage.DisplayName) - tf.Set(d, "description", accessPackage.Description) - tf.Set(d, "hidden", accessPackage.IsHidden) - // v1.0 graph API doesn't contain this info however beta contains - tf.Set(d, "catalog_id", accessPackage.CatalogId) + tf.Set(d, "catalog_id", accessPackage.CatalogId.GetOrZero()) // only beta API returns this field + tf.Set(d, "description", accessPackage.Description.GetOrZero()) + tf.Set(d, "display_name", accessPackage.DisplayName.GetOrZero()) + tf.Set(d, "hidden", accessPackage.IsHidden.GetOrZero()) return nil } func accessPackageResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageClient - accessPackageId := d.Id() - - _, status, err := client.Get(ctx, accessPackageId, odata.Query{}) - if err != nil { - if status == http.StatusNotFound { - return tf.ErrorDiagPathF(fmt.Errorf("Access package was not found"), "id", "Retrieving user with object ID %q", accessPackageId) - } - return tf.ErrorDiagPathF(err, "id", "Retrieving access package with object ID %q", accessPackageId) - } + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(d.Id()) - status, err = client.Delete(ctx, accessPackageId) - if err != nil { - return tf.ErrorDiagPathF(err, "id", "Deleting access package with object ID %q, got status %d", accessPackageId, status) + if _, err := client.DeleteEntitlementManagementAccessPackage(ctx, id, entitlementmanagementaccesspackage.DefaultDeleteEntitlementManagementAccessPackageOperationOptions()); err != nil { + return tf.ErrorDiagPathF(err, "id", "Deleting %s", id) } - // Wait for object to be deleted - 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, accessPackageId, odata.Query{}); err != nil { - if status == http.StatusNotFound { + if err := consistency.WaitForDeletion(ctx, func(ctx context.Context) (*bool, error) { + if resp, err := client.GetEntitlementManagementAccessPackage(ctx, id, entitlementmanagementaccesspackage.DefaultGetEntitlementManagementAccessPackageOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } return nil, err } return pointer.To(true), nil }); err != nil { - return tf.ErrorDiagF(err, "Waiting for deletion of access package with object ID %q", accessPackageId) + return tf.ErrorDiagF(err, "Waiting for deletion of %s", id) } return nil diff --git a/internal/services/identitygovernance/access_package_resource_catalog_association_resource.go b/internal/services/identitygovernance/access_package_resource_catalog_association_resource.go index c75e036a2..e586ceffd 100644 --- a/internal/services/identitygovernance/access_package_resource_catalog_association_resource.go +++ b/internal/services/identitygovernance/access_package_resource_catalog_association_resource.go @@ -5,18 +5,24 @@ package identitygovernance import ( "context" + "errors" + "fmt" "log" - "net/http" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalog" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalogaccesspackageresource" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageresourcerequest" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/terraform-provider-azuread/internal/clients" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/consistency" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance/parse" "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance/validate" - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/manicminer/hamilton/msgraph" ) func accessPackageResourceCatalogAssociationResource() *pluginsdk.Resource { @@ -35,24 +41,27 @@ func accessPackageResourceCatalogAssociationResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "resource_origin_id": { - Description: "The unique identifier of the resource in the origin system. In the case of an Azure AD group, this is the identifier of the group", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, + Description: "The unique identifier of the resource in the origin system. In the case of an Azure AD group, this is the identifier of the group", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, "resource_origin_system": { - Description: "The type of the resource in the origin system, such as SharePointOnline, AadApplication or AadGroup", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, + Description: "The type of the resource in the origin system, such as SharePointOnline, AadApplication or AadGroup", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, "catalog_id": { - Description: "The unique ID of the access package catalog", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, + Description: "The unique ID of the access package catalog", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, }, } @@ -61,106 +70,146 @@ func accessPackageResourceCatalogAssociationResource() *pluginsdk.Resource { func accessPackageResourceCatalogAssociationResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceRequestClient accessPackageCatalogClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogClient - resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceClient + resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogResourceClient - catalogId := d.Get("catalog_id").(string) resourceOriginId := d.Get("resource_origin_id").(string) resourceOriginSystem := d.Get("resource_origin_system").(string) - _, status, err := accessPackageCatalogClient.Get(ctx, catalogId, odata.Query{}) - if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package catalog with Object ID %q was not found - removing from state!", catalogId) - return nil - } + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(d.Get("catalog_id").(string)) + catalogResp, err := accessPackageCatalogClient.GetEntitlementManagementAccessPackageCatalog(ctx, catalogId, entitlementmanagementaccesspackagecatalog.DefaultGetEntitlementManagementAccessPackageCatalogOperationOptions()) + if err != nil { return tf.ErrorDiagF(err, "Retrieving access package catalog with object ID: %q", catalogId) } - if existing, _, err := resourceClient.Get(ctx, catalogId, resourceOriginId); err == nil && existing != nil { - id := parse.NewAccessPackageResourceCatalogAssociationID(catalogId, resourceOriginId) - return tf.ImportAsExistsDiag("azuread_access_package_resource_catalog_association", id.ID()) + catalog := catalogResp.Model + if catalog == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", catalogId) } - properties := msgraph.AccessPackageResourceRequest{ - CatalogId: &catalogId, - RequestType: pointer.To("AdminAdd"), - AccessPackageResource: &msgraph.AccessPackageResource{ - OriginId: &resourceOriginId, - OriginSystem: resourceOriginSystem, + options := entitlementmanagementaccesspackagecatalogaccesspackageresource.ListEntitlementManagementAccessPackageCatalogResourcesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("originId eq '%s'", resourceOriginId)), + } + existingResp, err := resourceClient.ListEntitlementManagementAccessPackageCatalogResources(ctx, catalogId, options) + if err != nil { + return tf.ErrorDiagF(err, "Checking for existing Access Package Resource Catalog Association") + } + if existingResp.Model != nil && len(*existingResp.Model) > 0 { + importId := parse.NewAccessPackageResourceCatalogAssociationID(resourceOriginId, resourceOriginId) + return tf.ImportAsExistsDiag("azuread_access_package_resource_catalog_association", importId.ID()) + } + + properties := beta.AccessPackageResourceRequest{ + CatalogId: nullable.Value(catalogId.AccessPackageCatalogId), + ExecuteImmediately: nullable.Value(true), + RequestType: nullable.Value("AdminAdd"), + AccessPackageResource: &beta.AccessPackageResource{ + OriginId: nullable.Value(resourceOriginId), + OriginSystem: nullable.Value(resourceOriginSystem), }, } - resourceCatalogAssociation, _, err := client.Create(ctx, properties, true) - if err != nil { - return tf.ErrorDiagF(err, "Failed to link resource %q@%q with access catalog %q.", resourceOriginId, resourceOriginSystem, catalogId) + if _, err = client.CreateEntitlementManagementAccessPackageResourceRequest(ctx, properties, entitlementmanagementaccesspackageresourcerequest.DefaultCreateEntitlementManagementAccessPackageResourceRequestOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Failed to request Access Package Resource Catalog Association (Catalog ID: %q / Origin ID: %q)", catalogId, resourceOriginId) } - id := parse.NewAccessPackageResourceCatalogAssociationID(*resourceCatalogAssociation.CatalogId, *resourceCatalogAssociation.AccessPackageResource.OriginId) - d.SetId(id.ID()) + // Poll for processed request + if err = consistency.WaitForUpdate(ctx, func(ctx context.Context) (*bool, error) { + options := entitlementmanagementaccesspackagecatalogaccesspackageresource.ListEntitlementManagementAccessPackageCatalogResourcesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("startswith(originId, '%s')", resourceOriginId)), + } + resp, err := resourceClient.ListEntitlementManagementAccessPackageCatalogResources(ctx, catalogId, options) + if err != nil { + return nil, err + } + if resp.Model == nil || len(*resp.Model) == 0 { + return pointer.To(false), nil + } + return pointer.To(true), nil + }); err != nil { + return tf.ErrorDiagF(err, "Waiting for processing of Access Package Resource Request (Catalog ID: %q / Origin ID: %q)", catalogId, resourceOriginId) + } + + resourceId := parse.NewAccessPackageResourceCatalogAssociationID(catalogId.AccessPackageCatalogId, resourceOriginId) + d.SetId(resourceId.ID()) return accessPackageResourceCatalogAssociationResourceRead(ctx, d, meta) } func accessPackageResourceCatalogAssociationResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceClient + resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogResourceClient id, err := parse.AccessPackageResourceCatalogAssociationID(d.Id()) if err != nil { return tf.ErrorDiagPathF(err, "id", "Failed to parse resource ID %q", d.Id()) } - accessPackageRes, status, err := resourceClient.Get(ctx, id.CatalogId, id.OriginId) + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(id.CatalogId) + options := entitlementmanagementaccesspackagecatalogaccesspackageresource.ListEntitlementManagementAccessPackageCatalogResourcesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("originId eq '%s'", id.OriginId)), + } + resp, err := resourceClient.ListEntitlementManagementAccessPackageCatalogResources(ctx, catalogId, options) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package resource and catalog association with resource origin ID %q and catalog ID %q was not found - removing from state!", id.OriginId, id.CatalogId) - d.SetId("") - return nil - } + return tf.ErrorDiagF(err, "Retrieving Access Package Resource Catalog Association") + } + + var resource *beta.AccessPackageResource + if resp.Model != nil && len(*resp.Model) > 0 { + resource = pointer.To((*resp.Model)[0]) + } - return tf.ErrorDiagF(err, "Error retrieving access package resource and catalog association with resource origin id %q and catalog id %q.", id.OriginId, id.CatalogId) + if resource == nil { + log.Printf("[DEBUG] Access Package Resource Catalog Associations was not found - removing from state!") + d.SetId("") + return nil } tf.Set(d, "catalog_id", id.CatalogId) tf.Set(d, "resource_origin_id", id.OriginId) - tf.Set(d, "resource_origin_system", accessPackageRes.OriginSystem) + tf.Set(d, "resource_origin_system", resource.OriginSystem.GetOrZero()) return nil } func accessPackageResourceCatalogAssociationResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceRequestClient - resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceClient + resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogResourceClient id, err := parse.AccessPackageResourceCatalogAssociationID(d.Id()) if err != nil { return tf.ErrorDiagPathF(err, "id", "Failed to parse resource ID %q", d.Id()) } - resource, status, err := resourceClient.Get(ctx, id.CatalogId, id.OriginId) + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(id.CatalogId) + options := entitlementmanagementaccesspackagecatalogaccesspackageresource.ListEntitlementManagementAccessPackageCatalogResourcesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("originId eq '%s'", id.OriginId)), + } + resp, err := resourceClient.ListEntitlementManagementAccessPackageCatalogResources(ctx, catalogId, options) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package resource and catalog association with resource %q@%q and catalog id %q was not found - removing from state!", id.OriginId, resource.OriginSystem, id.CatalogId) - d.SetId("") - return nil - } + return tf.ErrorDiagF(err, "Retrieving Access Package Resource Catalog Association") + } - return tf.ErrorDiagF(err, "Retrieving access package resource and catalog association with resource %q@%q and catalog id %q.", id.OriginId, resource.OriginSystem, id.CatalogId) + var resource *beta.AccessPackageResource + if resp.Model != nil && len(*resp.Model) > 0 { + resource = pointer.To((*resp.Model)[0]) } - if err != nil { - return tf.ErrorDiagF(err, "Error retrieving access package resource with origin ID %q in catalog %q.", id.OriginId, id.CatalogId) + if resource == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving Access Package Resource Catalog Association") } - resourceCatalogAssociation := msgraph.AccessPackageResourceRequest{ - CatalogId: &id.CatalogId, - AccessPackageResource: resource, + properties := beta.AccessPackageResourceRequest{ + CatalogId: nullable.Value(id.CatalogId), + ExecuteImmediately: nullable.Value(true), + RequestType: nullable.Value("AdminRemove"), + AccessPackageResource: &beta.AccessPackageResource{ + OriginId: nullable.Value(id.OriginId), + OriginSystem: resource.OriginSystem, + }, } - _, err = client.Delete(ctx, resourceCatalogAssociation) - if err != nil { - return tf.ErrorDiagPathF(err, "id", "Deleting access package resource and catalog association with resource %q@%q and catalog id %q.", - *resourceCatalogAssociation.AccessPackageResource.OriginId, resourceCatalogAssociation.AccessPackageResource.OriginSystem, *resourceCatalogAssociation.CatalogId) + if _, err = client.CreateEntitlementManagementAccessPackageResourceRequest(ctx, properties, entitlementmanagementaccesspackageresourcerequest.DefaultCreateEntitlementManagementAccessPackageResourceRequestOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Failed to request removal for Access Package Resource Catalog Association (Catalog ID: %q / Origin ID: %q)", id.CatalogId, id.OriginId) } return nil diff --git a/internal/services/identitygovernance/access_package_resource_catalog_association_resource_test.go b/internal/services/identitygovernance/access_package_resource_catalog_association_resource_test.go index c37c36232..84b2b8851 100644 --- a/internal/services/identitygovernance/access_package_resource_catalog_association_resource_test.go +++ b/internal/services/identitygovernance/access_package_resource_catalog_association_resource_test.go @@ -6,10 +6,11 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalogaccesspackageresource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -50,22 +51,25 @@ func TestAccAccessPackageResourceCatalogAssociation_requiresImport(t *testing.T) } func (r AccessPackageResourceCatalogAssociationResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.IdentityGovernance.AccessPackageResourceClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.IdentityGovernance.AccessPackageCatalogResourceClient id, err := parse.AccessPackageResourceCatalogAssociationID(state.ID) if err != nil { return nil, err } - _, status, err := client.Get(ctx, id.CatalogId, id.OriginId) + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(id.CatalogId) + options := entitlementmanagementaccesspackagecatalogaccesspackageresource.ListEntitlementManagementAccessPackageCatalogResourcesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("originId eq '%s'", id.OriginId)), + } + + resp, err := client.ListEntitlementManagementAccessPackageCatalogResources(ctx, catalogId, options) if err != nil { - if status == http.StatusNotFound { - return pointer.To(false), nil - } + return nil, fmt.Errorf("retrieving Access Package Resource Catalog Association: %v", err) + } - return nil, fmt.Errorf("failed to retrieve access package catalog association with ID %q: %+v", id.ID(), err) + if resp.Model == nil || len(*resp.Model) == 0 { + return pointer.To(false), nil } return pointer.To(true), nil diff --git a/internal/services/identitygovernance/access_package_resource_package_association_resource.go b/internal/services/identitygovernance/access_package_resource_package_association_resource.go index fb9f9579e..bb4f1bd23 100644 --- a/internal/services/identitygovernance/access_package_resource_package_association_resource.go +++ b/internal/services/identitygovernance/access_package_resource_package_association_resource.go @@ -5,20 +5,24 @@ package identitygovernance import ( "context" + "errors" "fmt" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/consistency" "log" - "net/http" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageaccesspackageresourcerolescope" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalogaccesspackageresource" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/terraform-provider-azuread/internal/clients" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance/parse" "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance/validate" - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" ) func accessPackageResourcePackageAssociationResource() *pluginsdk.Resource { @@ -39,16 +43,17 @@ func accessPackageResourcePackageAssociationResource() *pluginsdk.Resource { "access_package_id": { Description: "The ID of access package this resource association is configured to", Type: pluginsdk.TypeString, - ValidateFunc: validation.IsUUID, Required: true, ForceNew: true, + ValidateFunc: validation.IsUUID, }, "catalog_resource_association_id": { - Description: "The ID of the access package catalog association", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, + Description: "The ID of the access package catalog association", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, "access_type": { @@ -68,7 +73,8 @@ func accessPackageResourcePackageAssociationResource() *pluginsdk.Resource { func accessPackageResourcePackageAssociationResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceRoleScopeClient - resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceClient + accessPackageClient := meta.(*clients.Client).IdentityGovernance.AccessPackageClient + resourceClient := meta.(*clients.Client).IdentityGovernance.AccessPackageCatalogResourceClient catalogResourceAssociationId, err := parse.AccessPackageResourceCatalogAssociationID(d.Get("catalog_resource_association_id").(string)) if err != nil { @@ -76,70 +82,111 @@ func accessPackageResourcePackageAssociationResourceCreate(ctx context.Context, } accessType := d.Get("access_type").(string) - accessPackageId := d.Get("access_package_id").(string) + accessPackageId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(d.Get("access_package_id").(string)) - resource, _, err := resourceClient.Get(ctx, catalogResourceAssociationId.CatalogId, catalogResourceAssociationId.OriginId) + catalogId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageCatalogID(catalogResourceAssociationId.CatalogId) + options := entitlementmanagementaccesspackagecatalogaccesspackageresource.ListEntitlementManagementAccessPackageCatalogResourcesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("originId eq '%s'", catalogResourceAssociationId.OriginId)), + } + resourceResp, err := resourceClient.ListEntitlementManagementAccessPackageCatalogResources(ctx, catalogId, options) if err != nil { - return tf.ErrorDiagF(err, "Error retrieving access package resource and catalog association with resource ID %q and catalog ID %q.", catalogResourceAssociationId.CatalogId, catalogResourceAssociationId.OriginId) + return tf.ErrorDiagF(err, "Retrieving Access Package Resource Catalog Association") + } + + if resourceResp.Model == nil || len(*resourceResp.Model) == 0 { + return tf.ErrorDiagF(errors.New("no matching resource found"), "Retrieving Access Package Resources for %s", catalogId) } - properties := msgraph.AccessPackageResourceRoleScope{ - AccessPackageId: &accessPackageId, - AccessPackageResourceRole: &msgraph.AccessPackageResourceRole{ - DisplayName: pointer.To(accessType), - OriginId: pointer.To(fmt.Sprintf("%s_%s", accessType, catalogResourceAssociationId.OriginId)), + resource := pointer.To((*resourceResp.Model)[0]) + + properties := beta.AccessPackageResourceRoleScope{ + AccessPackageResourceRole: &beta.AccessPackageResourceRole{ + DisplayName: nullable.NoZero(accessType), + OriginId: nullable.Value(fmt.Sprintf("%s_%s", accessType, catalogResourceAssociationId.OriginId)), OriginSystem: resource.OriginSystem, - AccessPackageResource: &msgraph.AccessPackageResource{ - ID: resource.ID, + AccessPackageResource: &beta.AccessPackageResource{ + Id: resource.Id, ResourceType: resource.ResourceType, OriginId: resource.OriginId, }, }, - AccessPackageResourceScope: &msgraph.AccessPackageResourceScope{ + AccessPackageResourceScope: &beta.AccessPackageResourceScope{ OriginSystem: resource.OriginSystem, - OriginId: &catalogResourceAssociationId.OriginId, + OriginId: nullable.Value(catalogResourceAssociationId.OriginId), }, } - resourcePackageAssociation, _, err := client.Create(ctx, properties) + createMsg := fmt.Sprintf("Creating Access Package Resource Association from resource %q@%q to access package %q", catalogResourceAssociationId.OriginId, resource.OriginSystem.GetOrZero(), accessPackageId) + + resp, err := client.CreateEntitlementManagementAccessPackageResourceRoleScope(ctx, accessPackageId, properties, entitlementmanagementaccesspackageaccesspackageresourcerolescope.DefaultCreateEntitlementManagementAccessPackageResourceRoleScopeOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Error creating access package resource association from resource %q@%q to access package %q.", catalogResourceAssociationId.OriginId, resource.OriginSystem, accessPackageId) + return tf.ErrorDiagF(err, createMsg) + } + + resourceRoleScope := resp.Model + if resourceRoleScope == nil { + return tf.ErrorDiagF(errors.New("model was nil"), createMsg) + } + if resourceRoleScope.Id == nil { + return tf.ErrorDiagF(errors.New("model has nil ID"), createMsg) + } + + resourceId := parse.NewAccessPackageResourcePackageAssociationID(accessPackageId.AccessPackageId, *resourceRoleScope.Id, catalogResourceAssociationId.OriginId, accessType) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageIdAccessPackageResourceRoleScopeID(resourceId.AccessPackageId, resourceId.ResourceRoleScopeId) + + // Poll for AccessPackageResourceRoleScope + if err = consistency.WaitForUpdate(ctx, func(ctx context.Context) (*bool, error) { + roleScope, err := GetAccessPackageResourcesRoleScope(ctx, accessPackageClient, id) + if err != nil { + return nil, err + } + return pointer.To(roleScope != nil), nil + }); err != nil { + return tf.ErrorDiagF(err, "Waiting for creation of %s", id) } - id := parse.NewAccessPackageResourcePackageAssociationID(accessPackageId, *resourcePackageAssociation.ID, *resource.OriginId, accessType) - d.SetId(id.ID()) + d.SetId(resourceId.ID()) return accessPackageResourcePackageAssociationResourceRead(ctx, d, meta) } func accessPackageResourcePackageAssociationResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceRoleScopeClient accessPackageClient := meta.(*clients.Client).IdentityGovernance.AccessPackageClient - id, err := parse.AccessPackageResourcePackageAssociationID(d.Id()) + resourceId, err := parse.AccessPackageResourcePackageAssociationID(d.Id()) if err != nil { return tf.ErrorDiagPathF(err, "id", "Failed to parse resource ID %q", d.Id()) } - resourcePackage, status, err := client.Get(ctx, id.AccessPackageId, id.ResourcePackageAssociationId) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageIdAccessPackageResourceRoleScopeID(resourceId.AccessPackageId, resourceId.ResourceRoleScopeId) + + roleScope, err := GetAccessPackageResourcesRoleScope(ctx, accessPackageClient, id) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Access package resource association with ID %q was not found - removing from state!", d.Id()) - d.SetId("") - return nil - } - return tf.ErrorDiagF(err, "Error retrieving resource id %v in access package %v", id.ResourcePackageAssociationId, id.AccessPackageId) + return tf.ErrorDiagF(err, "Retrieving %s", id) } - accessPackage, _, err := accessPackageClient.Get(ctx, id.AccessPackageId, odata.Query{}) + if roleScope == nil { + log.Printf("[DEBUG] %s was not found - removing from state!", id) + d.SetId("") + return nil + } + + accessPackageId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(resourceId.AccessPackageId) + + accessPackageResp, err := accessPackageClient.GetEntitlementManagementAccessPackage(ctx, accessPackageId, entitlementmanagementaccesspackage.DefaultGetEntitlementManagementAccessPackageOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Err retrieving access package with id %v", id.AccessPackageId) + return tf.ErrorDiagF(err, "Retrieving %s", accessPackageId) } - catalogResourceAssociationId := parse.NewAccessPackageResourceCatalogAssociationID(*accessPackage.CatalogId, id.OriginId) + accessPackage := accessPackageResp.Model + if accessPackage == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Retrieving %s", accessPackageId) + } - tf.Set(d, "access_package_id", resourcePackage.AccessPackageId) - tf.Set(d, "access_type", id.AccessType) + catalogResourceAssociationId := parse.NewAccessPackageResourceCatalogAssociationID(accessPackage.CatalogId.GetOrZero(), resourceId.OriginId) + + tf.Set(d, "access_package_id", resourceId.AccessPackageId) + tf.Set(d, "access_type", resourceId.AccessType) tf.Set(d, "catalog_resource_association_id", catalogResourceAssociationId.ID()) return nil @@ -148,14 +195,15 @@ func accessPackageResourcePackageAssociationResourceRead(ctx context.Context, d func accessPackageResourcePackageAssociationResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { client := meta.(*clients.Client).IdentityGovernance.AccessPackageResourceRoleScopeClient - id, err := parse.AccessPackageResourcePackageAssociationID(d.Id()) + resourceId, err := parse.AccessPackageResourcePackageAssociationID(d.Id()) if err != nil { return tf.ErrorDiagPathF(err, "id", "Failed to parse resource ID %q", d.Id()) } - status, err := client.Delete(ctx, id.AccessPackageId, id.ResourcePackageAssociationId) - if err != nil { - return tf.ErrorDiagPathF(err, "id", "Deleting access package resource association with object ID %q, got status %d", id.ResourcePackageAssociationId, status) + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageIdAccessPackageResourceRoleScopeID(resourceId.AccessPackageId, resourceId.ResourceRoleScopeId) + + if _, err = client.DeleteEntitlementManagementAccessPackageResourceRoleScope(ctx, id, entitlementmanagementaccesspackageaccesspackageresourcerolescope.DefaultDeleteEntitlementManagementAccessPackageResourceRoleScopeOperationOptions()); err != nil { + return tf.ErrorDiagPathF(err, "id", "Deleting %s", id) } return nil diff --git a/internal/services/identitygovernance/access_package_resource_package_association_resource_test.go b/internal/services/identitygovernance/access_package_resource_package_association_resource_test.go index 9b42c72e1..6b99605c4 100644 --- a/internal/services/identitygovernance/access_package_resource_package_association_resource_test.go +++ b/internal/services/identitygovernance/access_package_resource_package_association_resource_test.go @@ -6,10 +6,11 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" + "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -36,25 +37,21 @@ func TestAccAccessPackageResourcePackageAssociation_complete(t *testing.T) { } func (AccessPackageResourcePackageAssociationResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.IdentityGovernance.AccessPackageResourceRoleScopeClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.IdentityGovernance.AccessPackageClient - id, err := parse.AccessPackageResourcePackageAssociationID(state.ID) + resourceId, err := parse.AccessPackageResourcePackageAssociationID(state.ID) if err != nil { return nil, err } - _, status, err := client.Get(ctx, id.AccessPackageId, id.ResourcePackageAssociationId) - if err != nil { - if status == http.StatusNotFound { - return pointer.To(false), nil - } + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageIdAccessPackageResourceRoleScopeID(resourceId.AccessPackageId, resourceId.ResourceRoleScopeId) - return nil, fmt.Errorf("failed to retrieve access package resource association with ID %q: %+v", id.ID(), err) + roleScope, err := identitygovernance.GetAccessPackageResourcesRoleScope(ctx, client, id) + if err != nil { + return nil, fmt.Errorf("failed to retrieve %s: %v", id, err) } - return pointer.To(true), nil + return pointer.To(roleScope != nil), nil } func (AccessPackageResourcePackageAssociationResource) complete(data acceptance.TestData) string { diff --git a/internal/services/identitygovernance/access_package_resource_test.go b/internal/services/identitygovernance/access_package_resource_test.go index 924ae4921..08414abd1 100644 --- a/internal/services/identitygovernance/access_package_resource_test.go +++ b/internal/services/identitygovernance/access_package_resource_test.go @@ -6,11 +6,12 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" - "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -80,16 +81,15 @@ func TestAccAccessPackage_update(t *testing.T) { func (AccessPackageResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.IdentityGovernance.AccessPackageClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + id := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(state.ID) - _, status, err := client.Get(ctx, state.ID, odata.Query{}) - if err != nil { - if status == http.StatusNotFound { + if resp, err := client.GetEntitlementManagementAccessPackage(ctx, id, entitlementmanagementaccesspackage.DefaultGetEntitlementManagementAccessPackageOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve access package with ID %q: %+v", state.ID, err) + return nil, fmt.Errorf("failed to retrieve %s: %+v", id, err) } + return pointer.To(true), nil } diff --git a/internal/services/identitygovernance/client/client.go b/internal/services/identitygovernance/client/client.go index fdb37c852..62698c3bb 100644 --- a/internal/services/identitygovernance/client/client.go +++ b/internal/services/identitygovernance/client/client.go @@ -5,93 +5,146 @@ package client import ( "github.com/hashicorp/terraform-provider-azuread/internal/common" - "github.com/manicminer/hamilton/msgraph" +) + +import ( + // Beta clients + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageaccesspackageresourcerolescope" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageassignmentpolicy" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalog" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackagecatalogaccesspackageresource" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackageresourcerequest" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/beta/entitlementmanagementroleassignment" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/beta/entitlementmanagementroledefinition" + + // Stable clients + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupassignmentschedule" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupassignmentscheduleinstance" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupassignmentschedulerequest" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupeligibilityschedule" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupeligibilityscheduleinstance" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupeligibilityschedulerequest" ) type Client struct { - AccessPackageAssignmentPolicyClient *msgraph.AccessPackageAssignmentPolicyClient - AccessPackageCatalogClient *msgraph.AccessPackageCatalogClient - AccessPackageCatalogRoleAssignmentsClient *msgraph.EntitlementRoleAssignmentsClient - AccessPackageCatalogRoleClient *msgraph.EntitlementRoleDefinitionsClient - AccessPackageClient *msgraph.AccessPackageClient - AccessPackageResourceClient *msgraph.AccessPackageResourceClient - AccessPackageResourceRequestClient *msgraph.AccessPackageResourceRequestClient - AccessPackageResourceRoleScopeClient *msgraph.AccessPackageResourceRoleScopeClient - PrivilegedAccessGroupAssignmentScheduleClient *msgraph.PrivilegedAccessGroupAssignmentScheduleClient - PrivilegedAccessGroupAssignmentScheduleInstancesClient *msgraph.PrivilegedAccessGroupAssignmentScheduleInstancesClient - PrivilegedAccessGroupAssignmentScheduleRequestsClient *msgraph.PrivilegedAccessGroupAssignmentScheduleRequestsClient - PrivilegedAccessGroupEligibilityScheduleClient *msgraph.PrivilegedAccessGroupEligibilityScheduleClient - PrivilegedAccessGroupEligibilityScheduleInstancesClient *msgraph.PrivilegedAccessGroupEligibilityScheduleInstancesClient - PrivilegedAccessGroupEligibilityScheduleRequestsClient *msgraph.PrivilegedAccessGroupEligibilityScheduleRequestsClient + AccessPackageAssignmentPolicyClient *entitlementmanagementaccesspackageassignmentpolicy.EntitlementManagementAccessPackageAssignmentPolicyClient + AccessPackageCatalogClient *entitlementmanagementaccesspackagecatalog.EntitlementManagementAccessPackageCatalogClient + AccessPackageCatalogResourceClient *entitlementmanagementaccesspackagecatalogaccesspackageresource.EntitlementManagementAccessPackageCatalogAccessPackageResourceClient + AccessPackageClient *entitlementmanagementaccesspackage.EntitlementManagementAccessPackageClient + AccessPackageResourceRequestClient *entitlementmanagementaccesspackageresourcerequest.EntitlementManagementAccessPackageResourceRequestClient + AccessPackageResourceRoleScopeClient *entitlementmanagementaccesspackageaccesspackageresourcerolescope.EntitlementManagementAccessPackageAccessPackageResourceRoleScopeClient + RoleAssignmentClient *entitlementmanagementroleassignment.EntitlementManagementRoleAssignmentClient + RoleDefinitionClient *entitlementmanagementroledefinition.EntitlementManagementRoleDefinitionClient + + PrivilegedAccessGroupAssignmentScheduleClient *privilegedaccessgroupassignmentschedule.PrivilegedAccessGroupAssignmentScheduleClient + PrivilegedAccessGroupAssignmentScheduleInstanceClient *privilegedaccessgroupassignmentscheduleinstance.PrivilegedAccessGroupAssignmentScheduleInstanceClient + PrivilegedAccessGroupAssignmentScheduleRequestClient *privilegedaccessgroupassignmentschedulerequest.PrivilegedAccessGroupAssignmentScheduleRequestClient + PrivilegedAccessGroupEligibilityScheduleClient *privilegedaccessgroupeligibilityschedule.PrivilegedAccessGroupEligibilityScheduleClient + PrivilegedAccessGroupEligibilityScheduleInstanceClient *privilegedaccessgroupeligibilityscheduleinstance.PrivilegedAccessGroupEligibilityScheduleInstanceClient + PrivilegedAccessGroupEligibilityScheduleRequestClient *privilegedaccessgroupeligibilityschedulerequest.PrivilegedAccessGroupEligibilityScheduleRequestClient } -func NewClient(o *common.ClientOptions) *Client { - // Resource only available in beta API - accessPackageAssignmentPolicyClient := msgraph.NewAccessPackageAssignmentPolicyClient() - o.ConfigureClient(&accessPackageAssignmentPolicyClient.BaseClient) - accessPackageAssignmentPolicyClient.BaseClient.ApiVersion = msgraph.VersionBeta +func NewClient(o *common.ClientOptions) (*Client, error) { + accessPackageAssignmentPolicyClient, err := entitlementmanagementaccesspackageassignmentpolicy.NewEntitlementManagementAccessPackageAssignmentPolicyClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(accessPackageAssignmentPolicyClient.Client) - accessPackageCatalogClient := msgraph.NewAccessPackageCatalogClient() - o.ConfigureClient(&accessPackageCatalogClient.BaseClient) + accessPackageCatalogClient, err := entitlementmanagementaccesspackagecatalog.NewEntitlementManagementAccessPackageCatalogClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(accessPackageCatalogClient.Client) - accessPackageCatalogRoleAssignmentsClient := msgraph.NewEntitlementRoleAssignmentsClient() - o.ConfigureClient(&accessPackageCatalogRoleAssignmentsClient.BaseClient) + accessPackageCatalogResourceClient, err := entitlementmanagementaccesspackagecatalogaccesspackageresource.NewEntitlementManagementAccessPackageCatalogAccessPackageResourceClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(accessPackageCatalogResourceClient.Client) - accessPackageCatalogRoleClient := msgraph.NewEntitlementRoleDefinitionsClient() - o.ConfigureClient(&accessPackageCatalogRoleClient.BaseClient) + accessPackageClient, err := entitlementmanagementaccesspackage.NewEntitlementManagementAccessPackageClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(accessPackageClient.Client) - // Use beta version because it replies more info than v1.0 - accessPackageClient := msgraph.NewAccessPackageClient() - o.ConfigureClient(&accessPackageClient.BaseClient) - accessPackageClient.BaseClient.ApiVersion = msgraph.VersionBeta + accessPackageResourceRequestClient, err := entitlementmanagementaccesspackageresourcerequest.NewEntitlementManagementAccessPackageResourceRequestClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(accessPackageResourceRequestClient.Client) - // Use beta version because it replies more info than v1.0 and the URL is different - accessPackageResourceClient := msgraph.NewAccessPackageResourceClient() - o.ConfigureClient(&accessPackageResourceClient.BaseClient) - accessPackageResourceClient.BaseClient.ApiVersion = msgraph.VersionBeta + accessPackageResourceRoleScopeClient, err := entitlementmanagementaccesspackageaccesspackageresourcerolescope.NewEntitlementManagementAccessPackageAccessPackageResourceRoleScopeClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(accessPackageResourceRoleScopeClient.Client) - // Resource only available in beta API - accessPackageResourceRequestClient := msgraph.NewAccessPackageResourceRequestClient() - o.ConfigureClient(&accessPackageResourceRequestClient.BaseClient) - accessPackageResourceRequestClient.BaseClient.ApiVersion = msgraph.VersionBeta + roleAssignmentClient, err := entitlementmanagementroleassignment.NewEntitlementManagementRoleAssignmentClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(roleAssignmentClient.Client) - // Resource only available in beta API - accessPackageResourceRoleScopeClient := msgraph.NewAccessPackageResourceRoleScopeClient() - o.ConfigureClient(&accessPackageResourceRoleScopeClient.BaseClient) - accessPackageResourceRoleScopeClient.BaseClient.ApiVersion = msgraph.VersionBeta + roleDefinitionClient, err := entitlementmanagementroledefinition.NewEntitlementManagementRoleDefinitionClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(roleDefinitionClient.Client) - privilegedAccessGroupAssignmentScheduleClient := msgraph.NewPrivilegedAccessGroupAssignmentScheduleClient() - o.ConfigureClient(&privilegedAccessGroupAssignmentScheduleClient.BaseClient) + privilegedAccessGroupAssignmentScheduleClient, err := privilegedaccessgroupassignmentschedule.NewPrivilegedAccessGroupAssignmentScheduleClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(privilegedAccessGroupAssignmentScheduleClient.Client) - privilegedAccessGroupAssignmentScheduleInstancesClient := msgraph.NewPrivilegedAccessGroupAssignmentScheduleInstancesClient() - o.ConfigureClient(&privilegedAccessGroupAssignmentScheduleInstancesClient.BaseClient) + privilegedAccessGroupAssignmentScheduleInstanceClient, err := privilegedaccessgroupassignmentscheduleinstance.NewPrivilegedAccessGroupAssignmentScheduleInstanceClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(privilegedAccessGroupAssignmentScheduleInstanceClient.Client) - privilegedAccessGroupAssignmentScheduleRequestsClient := msgraph.NewPrivilegedAccessGroupAssignmentScheduleRequestsClient() - o.ConfigureClient(&privilegedAccessGroupAssignmentScheduleRequestsClient.BaseClient) + privilegedAccessGroupAssignmentScheduleRequestClient, err := privilegedaccessgroupassignmentschedulerequest.NewPrivilegedAccessGroupAssignmentScheduleRequestClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(privilegedAccessGroupAssignmentScheduleRequestClient.Client) - privilegedAccessGroupEligibilityScheduleClient := msgraph.NewPrivilegedAccessGroupEligibilityScheduleClient() - o.ConfigureClient(&privilegedAccessGroupEligibilityScheduleClient.BaseClient) + privilegedAccessGroupEligibilityScheduleClient, err := privilegedaccessgroupeligibilityschedule.NewPrivilegedAccessGroupEligibilityScheduleClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(privilegedAccessGroupEligibilityScheduleClient.Client) - privilegedAccessGroupEligibilityScheduleInstancesClient := msgraph.NewPrivilegedAccessGroupEligibilityScheduleInstancesClient() - o.ConfigureClient(&privilegedAccessGroupEligibilityScheduleInstancesClient.BaseClient) + privilegedAccessGroupEligibilityScheduleInstanceClient, err := privilegedaccessgroupeligibilityscheduleinstance.NewPrivilegedAccessGroupEligibilityScheduleInstanceClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(privilegedAccessGroupEligibilityScheduleInstanceClient.Client) - privilegedAccessGroupEligibilityScheduleRequestsClient := msgraph.NewPrivilegedAccessGroupEligibilityScheduleRequestsClient() - o.ConfigureClient(&privilegedAccessGroupEligibilityScheduleRequestsClient.BaseClient) + privilegedAccessGroupEligibilityScheduleRequestClient, err := privilegedaccessgroupeligibilityschedulerequest.NewPrivilegedAccessGroupEligibilityScheduleRequestClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(privilegedAccessGroupEligibilityScheduleRequestClient.Client) return &Client{ - AccessPackageAssignmentPolicyClient: accessPackageAssignmentPolicyClient, - AccessPackageCatalogClient: accessPackageCatalogClient, - AccessPackageCatalogRoleAssignmentsClient: accessPackageCatalogRoleAssignmentsClient, - AccessPackageCatalogRoleClient: accessPackageCatalogRoleClient, - AccessPackageClient: accessPackageClient, - AccessPackageResourceClient: accessPackageResourceClient, - AccessPackageResourceRequestClient: accessPackageResourceRequestClient, - AccessPackageResourceRoleScopeClient: accessPackageResourceRoleScopeClient, - PrivilegedAccessGroupAssignmentScheduleClient: privilegedAccessGroupAssignmentScheduleClient, - PrivilegedAccessGroupAssignmentScheduleInstancesClient: privilegedAccessGroupAssignmentScheduleInstancesClient, - PrivilegedAccessGroupAssignmentScheduleRequestsClient: privilegedAccessGroupAssignmentScheduleRequestsClient, - PrivilegedAccessGroupEligibilityScheduleClient: privilegedAccessGroupEligibilityScheduleClient, - PrivilegedAccessGroupEligibilityScheduleInstancesClient: privilegedAccessGroupEligibilityScheduleInstancesClient, - PrivilegedAccessGroupEligibilityScheduleRequestsClient: privilegedAccessGroupEligibilityScheduleRequestsClient, - } + AccessPackageAssignmentPolicyClient: accessPackageAssignmentPolicyClient, + AccessPackageCatalogClient: accessPackageCatalogClient, + AccessPackageCatalogResourceClient: accessPackageCatalogResourceClient, + AccessPackageClient: accessPackageClient, + AccessPackageResourceRequestClient: accessPackageResourceRequestClient, + AccessPackageResourceRoleScopeClient: accessPackageResourceRoleScopeClient, + RoleAssignmentClient: roleAssignmentClient, + RoleDefinitionClient: roleDefinitionClient, + + PrivilegedAccessGroupAssignmentScheduleClient: privilegedAccessGroupAssignmentScheduleClient, + PrivilegedAccessGroupAssignmentScheduleInstanceClient: privilegedAccessGroupAssignmentScheduleInstanceClient, + PrivilegedAccessGroupAssignmentScheduleRequestClient: privilegedAccessGroupAssignmentScheduleRequestClient, + PrivilegedAccessGroupEligibilityScheduleClient: privilegedAccessGroupEligibilityScheduleClient, + PrivilegedAccessGroupEligibilityScheduleInstanceClient: privilegedAccessGroupEligibilityScheduleInstanceClient, + PrivilegedAccessGroupEligibilityScheduleRequestClient: privilegedAccessGroupEligibilityScheduleRequestClient, + }, nil } diff --git a/internal/services/identitygovernance/constants.go b/internal/services/identitygovernance/constants.go new file mode 100644 index 000000000..735f7abc7 --- /dev/null +++ b/internal/services/identitygovernance/constants.go @@ -0,0 +1,70 @@ +package identitygovernance + +const ( + AccessReviewRecurrenceTypeAnnual = "annual" + AccessReviewRecurrenceTypeHalfYearly = "halfyearly" + AccessReviewRecurrenceTypeMonthly = "monthly" + AccessReviewRecurrenceTypeQuarterly = "quarterly" + AccessReviewRecurrenceTypeWeekly = "weekly" +) + +var possibleValuesForAccessReviewRecurrenceType = []string{ + AccessReviewRecurrenceTypeAnnual, + AccessReviewRecurrenceTypeHalfYearly, + AccessReviewRecurrenceTypeMonthly, + AccessReviewRecurrenceTypeQuarterly, + AccessReviewRecurrenceTypeWeekly, +} + +const ( + AccessReviewReviewerTypeManager = "Manager" + AccessReviewReviewerTypeReviewers = "Reviewers" + AccessReviewReviewerTypeSelf = "Self" +) + +var possibleValuesForAccessReviewReviewerType = []string{ + AccessReviewReviewerTypeManager, + AccessReviewReviewerTypeReviewers, + AccessReviewReviewerTypeSelf, +} + +const ( + RequestorScopeTypeAllConfiguredConnectedOrganizationSubjects = "AllConfiguredConnectedOrganizationSubjects" + RequestorScopeTypeAllExistingConnectedOrganizationSubjects = "AllExistingConnectedOrganizationSubjects" + RequestorScopeTypeAllExistingDirectoryMemberUsers = "AllExistingDirectoryMemberUsers" + RequestorScopeTypeAllExistingDirectorySubjects = "AllExistingDirectorySubjects" + RequestorScopeTypeAllExternalSubjects = "AllExternalSubjects" + RequestorScopeTypeNoSubjects = "NoSubjects" + RequestorScopeTypeSpecificConnectedOrganizationSubjects = "SpecificConnectedOrganizationSubjects" + RequestorScopeTypeSpecificDirectorySubjects = "SpecificDirectorySubjects" +) + +var possibleValuesForRequestorScopeType = []string{ + RequestorScopeTypeAllConfiguredConnectedOrganizationSubjects, + RequestorScopeTypeAllExistingConnectedOrganizationSubjects, + RequestorScopeTypeAllExistingDirectoryMemberUsers, + RequestorScopeTypeAllExistingDirectorySubjects, + RequestorScopeTypeAllExternalSubjects, + RequestorScopeTypeNoSubjects, + RequestorScopeTypeSpecificConnectedOrganizationSubjects, + RequestorScopeTypeSpecificDirectorySubjects, +} + +const ( + CatalogStatusPublished = "published" + CatalogStatusUnpublished = "unpublished" +) + +const ( + PrivilegedAccessGroupScheduleRequestStatusCanceled = "Canceled" + PrivilegedAccessGroupScheduleRequestStatusDenied = "Denied" + PrivilegedAccessGroupScheduleRequestStatusFailed = "Failed" + PrivilegedAccessGroupScheduleRequestStatusGranted = "Granted" + PrivilegedAccessGroupScheduleRequestStatusPendingAdminDecision = "PendingAdminDecision" + PrivilegedAccessGroupScheduleRequestStatusPendingApproval = "PendingApproval" + PrivilegedAccessGroupScheduleRequestStatusPendingProvisioning = "PendingProvisioning" + PrivilegedAccessGroupScheduleRequestStatusPendingScheduleCreation = "PendingScheduleCreation" + PrivilegedAccessGroupScheduleRequestStatusProvisioned = "Provisioned" + PrivilegedAccessGroupScheduleRequestStatusRevoked = "Revoked" + PrivilegedAccessGroupScheduleRequestStatusScheduleCreated = "ScheduleCreated" +) diff --git a/internal/services/identitygovernance/identitygovernance.go b/internal/services/identitygovernance/identitygovernance.go index 8142de5a6..76f0d6b14 100644 --- a/internal/services/identitygovernance/identitygovernance.go +++ b/internal/services/identitygovernance/identitygovernance.go @@ -4,195 +4,295 @@ package identitygovernance import ( + "context" "fmt" - "time" + "strings" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/beta/entitlementmanagementaccesspackage" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-azure-sdk/sdk/odata" - "github.com/manicminer/hamilton/msgraph" + "golang.org/x/text/cases" + "golang.org/x/text/language" ) -func expandRequestorSettings(input []interface{}) *msgraph.RequestorSettings { +func GetAccessPackageResourcesRoleScope(ctx context.Context, client *entitlementmanagementaccesspackage.EntitlementManagementAccessPackageClient, id beta.IdentityGovernanceEntitlementManagementAccessPackageIdAccessPackageResourceRoleScopeId) (*beta.AccessPackageResourceRoleScope, error) { + accessPackageId := beta.NewIdentityGovernanceEntitlementManagementAccessPackageID(id.AccessPackageId) + options := entitlementmanagementaccesspackage.GetEntitlementManagementAccessPackageOperationOptions{ + Expand: &odata.Expand{ + Relationship: "accessPackageResourceRoleScopes($expand=accessPackageResourceRole,accessPackageResourceScope)", + }, + } + resp, err := client.GetEntitlementManagementAccessPackage(ctx, accessPackageId, options) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %v", accessPackageId, err) + } + + if resp.Model == nil { + return nil, fmt.Errorf("retrieving %s: model was nil", accessPackageId) + } + + if resp.Model.AccessPackageResourceRoleScopes == nil { + return nil, fmt.Errorf("retrieving %s: AccessPackageResourceRoleScopes was nil", accessPackageId) + } + + // There is only a select and expand method on this endpoint, we iterate the result to find the RoleScope + for _, roleScope := range *resp.Model.AccessPackageResourceRoleScopes { + if roleScope.Id != nil && *roleScope.Id == id.AccessPackageResourceRoleScopeId { + return &roleScope, nil + } + } + + return nil, nil +} + +func expandRequestorSettings(input []interface{}) (*beta.RequestorSettings, error) { if len(input) == 0 { - return nil + return nil, nil } in := input[0].(map[string]interface{}) - result := msgraph.RequestorSettings{ - ScopeType: in["scope_type"].(string), - AcceptRequests: pointer.To(in["requests_accepted"].(bool)), + result := beta.RequestorSettings{ + ScopeType: nullable.NoZero(in["scope_type"].(string)), + AcceptRequests: nullable.NoZero(in["requests_accepted"].(bool)), } - result.AllowedRequestors = expandUserSets(in["requestor"].([]interface{})) - return &result + if _, ok := in["requestor"]; ok { + allowedRequestors, err := expandUserSets(in["requestor"].([]interface{})) + if err != nil { + return nil, fmt.Errorf("building `requestor`: %v", err) + } + result.AllowedRequestors = allowedRequestors + } + + return &result, nil } -func flattenRequestorSettings(input *msgraph.RequestorSettings) []map[string]interface{} { +func flattenRequestorSettings(input *beta.RequestorSettings) []map[string]interface{} { if input == nil { return nil } return []map[string]interface{}{{ - "requests_accepted": input.AcceptRequests, - "scope_type": input.ScopeType, + "requests_accepted": input.AcceptRequests.GetOrZero(), + "scope_type": input.ScopeType.GetOrZero(), "requestor": flattenUserSets(input.AllowedRequestors), }} } -func expandApprovalSettings(input []interface{}) *msgraph.ApprovalSettings { +func expandApprovalSettings(input []interface{}) (*beta.ApprovalSettings, error) { if len(input) == 0 { - return nil + return nil, nil } in := input[0].(map[string]interface{}) - result := msgraph.ApprovalSettings{ - IsApprovalRequired: pointer.To(in["approval_required"].(bool)), - IsApprovalRequiredForExtension: pointer.To(in["approval_required_for_extension"].(bool)), - IsRequestorJustificationRequired: pointer.To(in["requestor_justification_required"].(bool)), + result := beta.ApprovalSettings{ + IsApprovalRequired: nullable.Value(in["approval_required"].(bool)), + IsApprovalRequiredForExtension: nullable.Value(in["approval_required_for_extension"].(bool)), + IsRequestorJustificationRequired: nullable.Value(in["requestor_justification_required"].(bool)), } - approvalStages := make([]msgraph.ApprovalStage, 0) - for _, v := range in["approval_stage"].([]interface{}) { - v_map := v.(map[string]interface{}) + approvalStages := make([]beta.ApprovalStage, 0) + if _, ok := in["approval_stage"]; ok { + for _, raw := range in["approval_stage"].([]interface{}) { + v := raw.(map[string]interface{}) - stage := msgraph.ApprovalStage{ - ApprovalStageTimeOutInDays: pointer.To(int32(v_map["approval_timeout_in_days"].(int))), - EscalationTimeInMinutes: pointer.To(int32(v_map["enable_alternative_approval_in_days"].(int) * 24 * 60)), - IsApproverJustificationRequired: pointer.To(v_map["approver_justification_required"].(bool)), - IsEscalationEnabled: pointer.To(v_map["alternative_approval_enabled"].(bool)), - } + stage := beta.ApprovalStage{ + ApprovalStageTimeOutInDays: nullable.NoZero(int64(v["approval_timeout_in_days"].(int))), + EscalationTimeInMinutes: nullable.NoZero(int64(v["enable_alternative_approval_in_days"].(int) * 24 * 60)), + IsApproverJustificationRequired: nullable.NoZero(v["approver_justification_required"].(bool)), + IsEscalationEnabled: nullable.NoZero(v["alternative_approval_enabled"].(bool)), + } - stage.PrimaryApprovers = expandUserSets(v_map["primary_approver"].([]interface{})) - stage.EscalationApprovers = expandUserSets(v_map["alternative_approver"].([]interface{})) + if _, ok := v["primary_approver"]; ok { + primaryApprovers, err := expandUserSets(v["primary_approver"].([]interface{})) + if err != nil { + return nil, fmt.Errorf("building `primary_approver`: %v", err) + } + stage.PrimaryApprovers = primaryApprovers + } - approvalStages = append(approvalStages, stage) + if _, ok := v["escalation_approver"]; ok { + escalationApprovers, err := expandUserSets(v["escalation_approver"].([]interface{})) + if err != nil { + return nil, fmt.Errorf("building `escalation_approver`: %v", err) + } + stage.EscalationApprovers = escalationApprovers + } + + approvalStages = append(approvalStages, stage) + } } result.ApprovalStages = &approvalStages - return &result + return &result, nil } -func flattenApprovalSettings(input *msgraph.ApprovalSettings) []map[string]interface{} { +func flattenApprovalSettings(input *beta.ApprovalSettings) []map[string]interface{} { if input == nil { return nil } result := []map[string]interface{}{{ - "approval_required": input.IsApprovalRequired, - "approval_required_for_extension": input.IsApprovalRequiredForExtension, - "requestor_justification_required": input.IsRequestorJustificationRequired, + "approval_required": input.IsApprovalRequired.GetOrZero(), + "approval_required_for_extension": input.IsApprovalRequiredForExtension.GetOrZero(), + "requestor_justification_required": input.IsRequestorJustificationRequired.GetOrZero(), }} approvalStages := make([]interface{}, 0) for _, v := range *input.ApprovalStages { + var alternativeApprovalInDays int + if w := v.EscalationTimeInMinutes.GetOrZero(); w > 0 { + alternativeApprovalInDays = int(w) / 60 / 24 + } approvalStage := map[string]interface{}{ - "approval_timeout_in_days": v.ApprovalStageTimeOutInDays, - "approver_justification_required": v.IsApproverJustificationRequired, - "alternative_approval_enabled": v.IsEscalationEnabled, - "enable_alternative_approval_in_days": *v.EscalationTimeInMinutes / 60 / 24, + "approval_timeout_in_days": int(v.ApprovalStageTimeOutInDays.GetOrZero()), + "approver_justification_required": v.IsApproverJustificationRequired.GetOrZero(), + "alternative_approval_enabled": v.IsEscalationEnabled.GetOrZero(), + "enable_alternative_approval_in_days": alternativeApprovalInDays, "primary_approver": flattenUserSets(v.PrimaryApprovers), "alternative_approver": flattenUserSets(v.EscalationApprovers), } approvalStages = append(approvalStages, approvalStage) } + result[0]["approval_stage"] = approvalStages return result } -func expandAssignmentReviewSettings(input []interface{}) (*msgraph.AssignmentReviewSettings, error) { +func expandAssignmentReviewSettings(input []interface{}) (*beta.AssignmentReviewSettings, error) { if len(input) == 0 { return nil, nil } in := input[0].(map[string]interface{}) - result := msgraph.AssignmentReviewSettings{ - AccessReviewTimeoutBehavior: in["access_review_timeout_behavior"].(string), - DurationInDays: pointer.To(int32(in["duration_in_days"].(int))), - IsAccessRecommendationEnabled: pointer.To(in["access_recommendation_enabled"].(bool)), - IsApprovalJustificationRequired: pointer.To(in["approver_justification_required"].(bool)), - IsEnabled: pointer.To(in["enabled"].(bool)), - RecurrenceType: in["review_frequency"].(string), - ReviewerType: in["review_type"].(string), + result := beta.AssignmentReviewSettings{ + AccessReviewTimeoutBehavior: pointer.To(beta.AccessReviewTimeoutBehavior(in["access_review_timeout_behavior"].(string))), + DurationInDays: nullable.Value(int64(in["duration_in_days"].(int))), + IsAccessRecommendationEnabled: nullable.Value(in["access_recommendation_enabled"].(bool)), + IsApprovalJustificationRequired: nullable.Value(in["approver_justification_required"].(bool)), + IsEnabled: nullable.Value(in["enabled"].(bool)), + RecurrenceType: nullable.NoZero(in["review_frequency"].(string)), + ReviewerType: nullable.NoZero(in["review_type"].(string)), + StartDateTime: nullable.NoZero(in["starting_on"].(string)), } - startOnDate := in["starting_on"].(string) - if startOnDate != "" { - startOn, err := time.Parse(time.RFC3339, startOnDate) + if _, ok := in["reviewer"]; ok { + reviewers, err := expandUserSets(in["reviewer"].([]interface{})) if err != nil { - return nil, fmt.Errorf("converting starting date %q to a valid date: %q", in["starting_on"].(string), err) + return nil, fmt.Errorf("building `reviewer`: %v", err) } - - result.StartDateTime = &startOn + result.Reviewers = reviewers } - result.Reviewers = expandUserSets(in["reviewer"].([]interface{})) - - if result.AccessReviewTimeoutBehavior == "" && - (result.DurationInDays == nil || *result.DurationInDays == 0) && - (result.IsAccessRecommendationEnabled == nil || !*result.IsAccessRecommendationEnabled) && - (result.IsApprovalJustificationRequired == nil || !*result.IsApprovalJustificationRequired) && - (result.IsEnabled == nil || !*result.IsEnabled) && - result.RecurrenceType == "" && - result.ReviewerType == "" && - (result.Reviewers == nil || len(*result.Reviewers) == 0) { + if pointer.From(result.AccessReviewTimeoutBehavior) == "" && result.DurationInDays.GetOrZero() == 0 && + !result.IsAccessRecommendationEnabled.GetOrZero() && !result.IsApprovalJustificationRequired.GetOrZero() && + !result.IsEnabled.GetOrZero() && result.RecurrenceType.GetOrZero() == "" && + result.ReviewerType.GetOrZero() == "" && len(pointer.From(result.Reviewers)) == 0 { return nil, nil } return &result, nil } -func flattenAssignmentReviewSettings(input *msgraph.AssignmentReviewSettings) []map[string]interface{} { +func flattenAssignmentReviewSettings(input *beta.AssignmentReviewSettings) []map[string]interface{} { if input == nil { return nil } return []map[string]interface{}{{ - "access_recommendation_enabled": input.IsAccessRecommendationEnabled, - "access_review_timeout_behavior": input.AccessReviewTimeoutBehavior, - "approver_justification_required": input.IsApprovalJustificationRequired, - "duration_in_days": input.DurationInDays, - "enabled": input.IsEnabled, - "review_frequency": input.RecurrenceType, - "review_type": input.ReviewerType, + "access_recommendation_enabled": input.IsAccessRecommendationEnabled.GetOrZero(), + "access_review_timeout_behavior": pointer.From(input.AccessReviewTimeoutBehavior), + "approver_justification_required": input.IsApprovalJustificationRequired.GetOrZero(), + "duration_in_days": input.DurationInDays.GetOrZero(), + "enabled": input.IsEnabled.GetOrZero(), + "review_frequency": input.RecurrenceType.GetOrZero(), + "review_type": input.ReviewerType.GetOrZero(), "reviewer": flattenUserSets(input.Reviewers), - "starting_on": input.StartDateTime.Format(time.RFC3339), + "starting_on": input.StartDateTime.GetOrZero(), }} } -func expandUserSets(input []interface{}) *[]msgraph.UserSet { - userSets := make([]msgraph.UserSet, 0) - for _, v := range input { - v_map := v.(map[string]interface{}) - oDataType, needId := userSetODataType(v_map["subject_type"].(string)) - userSet := msgraph.UserSet{ - ODataType: oDataType, - IsBackup: pointer.To(v_map["backup"].(bool)), - } - if needId { - userSet.ID = pointer.To(v_map["object_id"].(string)) +func expandUserSets(input []interface{}) (*[]beta.UserSet, error) { + userSets := make([]beta.UserSet, 0) + for _, raw := range input { + v := raw.(map[string]interface{}) + + isBackup := v["backup"].(bool) + objectId := v["object_id"].(string) + odataType := formatODataType(v["subject_type"].(string)) + + var userSet beta.UserSet + switch odataType { + case "ConnectedOrganizationMembers": + userSet = beta.ConnectedOrganizationMembers{ + Id: nullable.Value(objectId), + IsBackup: nullable.Value(isBackup), + } + case "ExternalSponsors": + userSet = beta.ExternalSponsors{ + IsBackup: nullable.Value(isBackup), + } + case "GroupMembers": + userSet = beta.GroupMembers{ + Id: nullable.Value(objectId), + IsBackup: nullable.Value(isBackup), + } + case "InternalSponsors": + userSet = beta.InternalSponsors{ + IsBackup: nullable.Value(isBackup), + } + case "RequestorManager": + userSet = beta.RequestorManager{ + IsBackup: nullable.Value(isBackup), + } + case "SingleUser": + userSet = beta.SingleUser{ + Id: nullable.Value(objectId), + IsBackup: nullable.Value(isBackup), + } + case "TargetUserSponsors": + userSet = beta.TargetUserSponsors{ + IsBackup: nullable.Value(isBackup), + } + default: + return nil, fmt.Errorf("unknown `subject_type`: %s", odataType) } userSets = append(userSets, userSet) } - return &userSets + return &userSets, nil } -func flattenUserSets(input *[]msgraph.UserSet) []interface{} { +func flattenUserSets(input *[]beta.UserSet) []map[string]interface{} { if input == nil || len(*input) == 0 { return nil } - userSets := make([]interface{}, 0) - for _, v := range *input { + userSets := make([]map[string]interface{}, 0) + for _, raw := range *input { + v := raw.UserSet() + var id *string + + switch impl := raw.(type) { + case beta.ConnectedOrganizationMembers: + id = impl.Id.Get() + case beta.GroupMembers: + id = impl.Id.Get() + case beta.SingleUser: + id = impl.Id.Get() + } + userSet := map[string]interface{}{ - "subject_type": userSetShortType(*v.ODataType), - "backup": v.IsBackup, - "object_id": v.ID, + "subject_type": formatODataType(pointer.From(v.ODataType)), + "backup": v.IsBackup.GetOrZero(), + "object_id": pointer.From(id), } userSets = append(userSets, userSet) @@ -201,120 +301,81 @@ func flattenUserSets(input *[]msgraph.UserSet) []interface{} { return userSets } -func userSetODataType(in string) (*string, bool) { - odataType := odata.TypeSingleUser - needId := true - switch in { - case odata.ShortTypeGroupMembers: - odataType = odata.TypeGroupMembers - case odata.ShortTypeConnectedOrganizationMembers: - odataType = odata.TypeConnectedOrganizationMembers - case odata.ShortTypeRequestorManager: - odataType = odata.TypeRequestorManager - needId = false - case odata.ShortTypeInternalSponsors: - odataType = odata.TypeInternalSponsors - needId = false - case odata.ShortTypeExternalSponsors: - odataType = odata.TypeExternalSponsors - needId = false - } - - return &odataType, needId -} - -func userSetShortType(in string) *string { - shortType := odata.ShortTypeSingleUser - switch in { - case odata.TypeGroupMembers: - shortType = odata.ShortTypeGroupMembers - case odata.TypeConnectedOrganizationMembers: - shortType = odata.ShortTypeConnectedOrganizationMembers - case odata.TypeRequestorManager: - shortType = odata.ShortTypeRequestorManager - case odata.TypeInternalSponsors: - shortType = odata.ShortTypeInternalSponsors - case odata.TypeExternalSponsors: - shortType = odata.ShortTypeExternalSponsors - } - - return &shortType -} - -func expandAccessPackageQuestions(questions []interface{}) *[]msgraph.AccessPackageQuestion { - result := make([]msgraph.AccessPackageQuestion, 0) +func expandAccessPackageQuestions(input []interface{}) *[]beta.AccessPackageQuestion { + result := make([]beta.AccessPackageQuestion, 0) - for _, questionRaw := range questions { - question := questionRaw.(map[string]interface{}) - textList := question["text"].([]interface{}) + for _, raw := range input { + v := raw.(map[string]interface{}) + textList := v["text"].([]interface{}) if len(textList) == 0 { continue } - text := textList[0].(map[string]interface{}) - resultQuestion := msgraph.AccessPackageQuestion{ - ODataType: pointer.To(odata.TypeAccessPackageTextInputQuestion), - IsRequired: pointer.To(question["required"].(bool)), - Sequence: pointer.To(int32(question["sequence"].(int))), - Text: expandAccessPackageLocalizedContent(text), - } - - if choicesRaw := question["choice"].([]interface{}); len(choicesRaw) > 0 { - resultQuestion.ODataType = pointer.To(odata.TypeAccessPackageMultipleChoiceQuestion) - choices := make([]msgraph.AccessPackageMultipleChoiceQuestions, 0) + var question beta.AccessPackageQuestion + if choicesRaw, ok := v["choice"].([]interface{}); ok && len(choicesRaw) > 0 { + choices := make([]beta.AccessPackageAnswerChoice, 0) for _, choiceRaw := range choicesRaw { choice := choiceRaw.(map[string]interface{}) displayValue := make(map[string]interface{}) if v := choice["display_value"].([]interface{}); len(v) > 0 { displayValue = v[0].(map[string]interface{}) } - choices = append(choices, msgraph.AccessPackageMultipleChoiceQuestions{ - ActualValue: pointer.To(choice["actual_value"].(string)), - DisplayValue: expandAccessPackageLocalizedContent(displayValue), + + choices = append(choices, beta.AccessPackageAnswerChoice{ + ActualValue: nullable.NoZero(choice["actual_value"].(string)), + DisplayValue: pointer.From(expandAccessPackageLocalizedContent(displayValue)), }) } - if len(choices) > 0 { - resultQuestion.Choices = pointer.To(choices) + question = beta.AccessPackageMultipleChoiceQuestion{ + Choices: &choices, + IsRequired: nullable.Value(v["required"].(bool)), + Sequence: nullable.Value(int64(v["sequence"].(int))), + Text: expandAccessPackageLocalizedContent(text), + } + } else { + question = beta.AccessPackageTextInputQuestion{ + IsRequired: nullable.Value(v["required"].(bool)), + Sequence: nullable.Value(int64(v["sequence"].(int))), + Text: expandAccessPackageLocalizedContent(text), } } - result = append(result, resultQuestion) + result = append(result, question) } return &result } -func flattenAccessPackageQuestions(input *[]msgraph.AccessPackageQuestion) []map[string]interface{} { +func flattenAccessPackageQuestions(input *[]beta.AccessPackageQuestion) []map[string]interface{} { if input == nil || len(*input) == 0 { return nil } questions := make([]map[string]interface{}, 0) - for _, v := range *input { + for _, raw := range *input { + v := raw.AccessPackageQuestion() + question := map[string]interface{}{ - "required": v.IsRequired, - "sequence": v.Sequence, + "required": v.IsRequired.GetOrZero(), + "sequence": int(v.Sequence.GetOrZero()), "text": flattenAccessPackageLocalizedContent(v.Text), } - if c_array := v.Choices; c_array != nil && len(*c_array) > 0 { + switch impl := raw.(type) { + case beta.AccessPackageMultipleChoiceQuestion: choices := make([]map[string]interface{}, 0) - - for _, c := range *c_array { - choice := map[string]interface{}{ - "actual_value": c.ActualValue, - "display_value": flattenAccessPackageLocalizedContent(c.DisplayValue), - } - - choices = append(choices, choice) + for _, choice := range pointer.From(impl.Choices) { + choices = append(choices, map[string]interface{}{ + "actual_value": choice.ActualValue.GetOrZero(), + "display_value": flattenAccessPackageLocalizedContent(&choice.DisplayValue), + }) } - - question["choice"] = choices + question["choices"] = choices } questions = append(questions, question) @@ -323,23 +384,26 @@ func flattenAccessPackageQuestions(input *[]msgraph.AccessPackageQuestion) []map return questions } -func expandAccessPackageLocalizedContent(input map[string]interface{}) *msgraph.AccessPackageLocalizedContent { +func expandAccessPackageLocalizedContent(input map[string]interface{}) *beta.AccessPackageLocalizedContent { if len(input) == 0 { return nil } - result := msgraph.AccessPackageLocalizedContent{ - DefaultText: pointer.To(input["default_text"].(string)), + result := beta.AccessPackageLocalizedContent{ + DefaultText: nullable.NoZero(input["default_text"].(string)), } - texts := make([]msgraph.AccessPackageLocalizedTexts, 0) + texts := make([]beta.AccessPackageLocalizedText, 0) - for _, v := range input["localized_text"].([]interface{}) { - v_map := v.(map[string]interface{}) - texts = append(texts, msgraph.AccessPackageLocalizedTexts{ - LanguageCode: pointer.To(v_map["language_code"].(string)), - Text: pointer.To(v_map["content"].(string)), - }) + if _, ok := input["localized_text"]; ok { + for _, raw := range input["localized_text"].([]interface{}) { + v := raw.(map[string]interface{}) + + texts = append(texts, beta.AccessPackageLocalizedText{ + LanguageCode: nullable.NoZero(v["language_code"].(string)), + Text: nullable.NoZero(v["content"].(string)), + }) + } } result.LocalizedTexts = &texts @@ -347,17 +411,17 @@ func expandAccessPackageLocalizedContent(input map[string]interface{}) *msgraph. return &result } -func flattenAccessPackageLocalizedContent(input *msgraph.AccessPackageLocalizedContent) []map[string]interface{} { +func flattenAccessPackageLocalizedContent(input *beta.AccessPackageLocalizedContent) []map[string]interface{} { result := []map[string]interface{}{{ - "default_text": input.DefaultText, + "default_text": input.DefaultText.GetOrZero(), }} texts := make([]map[string]interface{}, 0) for _, v := range *input.LocalizedTexts { text := map[string]interface{}{ - "language_code": v.LanguageCode, - "content": v.Text, + "language_code": v.LanguageCode.GetOrZero(), + "content": v.Text.GetOrZero(), } texts = append(texts, text) @@ -367,3 +431,7 @@ func flattenAccessPackageLocalizedContent(input *msgraph.AccessPackageLocalizedC return result } + +func formatODataType(in string) string { + return cases.Title(language.AmericanEnglish, cases.NoLower).String(strings.TrimPrefix(in, "#microsoft.graph.")) +} diff --git a/internal/services/identitygovernance/parse/access_package_resource_package_association_id.go b/internal/services/identitygovernance/parse/access_package_resource_package_association_id.go index 8ea7b20cc..d18a9bad4 100644 --- a/internal/services/identitygovernance/parse/access_package_resource_package_association_id.go +++ b/internal/services/identitygovernance/parse/access_package_resource_package_association_id.go @@ -11,22 +11,22 @@ import ( ) type AccessPackageResourcePackageAssociationId struct { - AccessPackageId string - ResourcePackageAssociationId string - OriginId string - AccessType string + AccessPackageId string + ResourceRoleScopeId string + OriginId string + AccessType string } func (id AccessPackageResourcePackageAssociationId) ID() string { - return fmt.Sprintf("%s/%s/%s/%s", id.AccessPackageId, id.ResourcePackageAssociationId, id.OriginId, id.AccessType) + return fmt.Sprintf("%s/%s/%s/%s", id.AccessPackageId, id.ResourceRoleScopeId, id.OriginId, id.AccessType) } -func NewAccessPackageResourcePackageAssociationID(catalogId, resourcePackageAssociationId, originId, accessType string) AccessPackageResourcePackageAssociationId { +func NewAccessPackageResourcePackageAssociationID(catalogId, resourceRoleScopeId, originId, accessType string) AccessPackageResourcePackageAssociationId { return AccessPackageResourcePackageAssociationId{ - AccessPackageId: catalogId, - ResourcePackageAssociationId: resourcePackageAssociationId, - OriginId: originId, - AccessType: accessType, + AccessPackageId: catalogId, + ResourceRoleScopeId: resourceRoleScopeId, + OriginId: originId, + AccessType: accessType, } } @@ -45,9 +45,9 @@ func AccessPackageResourcePackageAssociationID(idString string) (*AccessPackageR } return &AccessPackageResourcePackageAssociationId{ - AccessPackageId: parts[0], - ResourcePackageAssociationId: parts[1], - OriginId: parts[2], - AccessType: parts[3], + AccessPackageId: parts[0], + ResourceRoleScopeId: parts[1], + OriginId: parts[2], + AccessType: parts[3], }, nil } diff --git a/internal/services/identitygovernance/parse/privileged_access_group_schedule.go b/internal/services/identitygovernance/parse/privileged_access_group_schedule.go index 722057f91..436b121ea 100644 --- a/internal/services/identitygovernance/parse/privileged_access_group_schedule.go +++ b/internal/services/identitygovernance/parse/privileged_access_group_schedule.go @@ -7,8 +7,8 @@ import ( "fmt" "strings" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) type PrivilegedAccessGroupScheduleId struct { @@ -37,8 +37,8 @@ func ParsePrivilegedAccessGroupScheduleID(idString string) (*PrivilegedAccessGro return nil, fmt.Errorf("parsing GroupScheduleId: %+v", err) } - if parts[1] != msgraph.PrivilegedAccessGroupRelationshipOwner && - parts[1] != msgraph.PrivilegedAccessGroupRelationshipMember { + if parts[1] != string(stable.PrivilegedAccessGroupRelationships_Member) && + parts[1] != string(stable.PrivilegedAccessGroupRelationships_Owner) { return nil, fmt.Errorf("parsing GroupScheduleId: invalid Relationship") } diff --git a/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource.go b/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource.go index eb801c945..2ee43a495 100644 --- a/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource.go +++ b/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource.go @@ -6,15 +6,18 @@ package identitygovernance import ( "context" "fmt" - "net/http" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupassignmentschedule" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupassignmentschedulerequest" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azuread/internal/sdk" "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance/parse" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/manicminer/hamilton/msgraph" ) var _ sdk.ResourceWithUpdate = PrivilegedAccessGroupAssignmentScheduleResource{} @@ -45,7 +48,7 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Create() sdk.ResourceFu return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestsClient + client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestClient var model PrivilegedAccessGroupScheduleModel if err := metadata.Decode(&model); err != nil { @@ -57,40 +60,45 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Create() sdk.ResourceFu return err } - properties := msgraph.PrivilegedAccessGroupAssignmentScheduleRequest{ - AccessId: model.AssignmentType, - PrincipalId: &model.PrincipalId, - GroupId: &model.GroupId, - Action: msgraph.PrivilegedAccessGroupActionAdminAssign, - Justification: &model.Justification, + properties := stable.PrivilegedAccessGroupAssignmentScheduleRequest{ + AccessId: stable.PrivilegedAccessGroupRelationships(model.AssignmentType), + PrincipalId: nullable.Value(model.PrincipalId), + GroupId: nullable.Value(model.GroupId), + Action: pointer.To(stable.ScheduleRequestActions_AdminAssign), + Justification: nullable.NoZero(model.Justification), ScheduleInfo: schedule, } if model.TicketNumber != "" || model.TicketSystem != "" { - properties.TicketInfo = &msgraph.TicketInfo{ - TicketNumber: &model.TicketNumber, - TicketSystem: &model.TicketSystem, + properties.TicketInfo = &stable.TicketInfo{ + TicketNumber: nullable.NoZero(model.TicketNumber), + TicketSystem: nullable.NoZero(model.TicketSystem), } } - req, _, err := client.Create(ctx, properties) + resp, err := client.CreatePrivilegedAccessGroupAssignmentScheduleRequest(ctx, properties, privilegedaccessgroupassignmentschedulerequest.DefaultCreatePrivilegedAccessGroupAssignmentScheduleRequestOperationOptions()) if err != nil { - return fmt.Errorf("Could not create assignment schedule request, %+v", err) + return fmt.Errorf("creating assignment schedule request: %v", err) } - if req.ID == nil || *req.ID == "" { - return fmt.Errorf("ID returned for assignment schedule request is nil/empty") + request := resp.Model + if request == nil { + return fmt.Errorf("creating assignment schedule request: model was nil") + } + if request.Id == nil || *request.Id == "" { + return fmt.Errorf("creating assignment schedule request: ID returned for request is nil/empty") } - if req.Status == msgraph.PrivilegedAccessGroupAssignmentStatusFailed { - return fmt.Errorf("Assignment schedule request is in a failed state") + if pointer.From(request.Status) == PrivilegedAccessGroupScheduleRequestStatusFailed { + return fmt.Errorf("creating assignment schedule request: request is in a failed state") } - id, err := parse.ParsePrivilegedAccessGroupScheduleID(*req.TargetScheduleId) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(request.TargetScheduleId.GetOrZero()) if err != nil { return err } - metadata.SetID(id) + + metadata.SetID(resourceId) return nil }, @@ -102,9 +110,9 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Read() sdk.ResourceFunc Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { scheduleClient := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleClient - requestsClient := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestsClient + requestsClient := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestClient - id, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) if err != nil { return err } @@ -114,76 +122,80 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Read() sdk.ResourceFunc return fmt.Errorf("decoding: %+v", err) } - schedule, scheduleStatus, err := scheduleClient.Get(ctx, id.ID()) - if err != nil && scheduleStatus != http.StatusNotFound { + id := stable.NewIdentityGovernancePrivilegedAccessGroupAssignmentScheduleID(resourceId.ID()) + + scheduleResp, err := scheduleClient.GetPrivilegedAccessGroupAssignmentSchedule(ctx, id, privilegedaccessgroupassignmentschedule.DefaultGetPrivilegedAccessGroupAssignmentScheduleOperationOptions()) + if err != nil && !response.WasNotFound(scheduleResp.HttpResponse) { return fmt.Errorf("retrieving %s: %+v", id, err) } - var request *msgraph.PrivilegedAccessGroupAssignmentScheduleRequest + schedule := scheduleResp.Model // Some details are only available on the request which is used for the create/update of the schedule. // Schedule requests are never deleted. New ones are created when changes are made. - // Therefore on a read, we need to find the latest version of the request. + // Therefore, on read, we need to find the latest version of the request. // This is to cater for changes being made outside of Terraform. - requests, _, err := requestsClient.List(ctx, odata.Query{ - Filter: fmt.Sprintf("groupId eq '%s' and targetScheduleId eq '%s'", id.GroupId, id.ID()), - OrderBy: odata.OrderBy{ + options := privilegedaccessgroupassignmentschedulerequest.ListPrivilegedAccessGroupAssignmentScheduleRequestsOperationOptions{ + Filter: pointer.To(fmt.Sprintf("groupId eq '%s' and targetScheduleId eq '%s'", resourceId.GroupId, resourceId.ID())), + OrderBy: pointer.To(odata.OrderBy{ Field: "createdDateTime", Direction: odata.Descending, - }, - }) + }), + } + requestsResp, err := requestsClient.ListPrivilegedAccessGroupAssignmentScheduleRequests(ctx, options) if err != nil { - return fmt.Errorf("listing requests: %+v", err) + return fmt.Errorf("listing requests: %v", err) } + + var request *stable.PrivilegedAccessGroupAssignmentScheduleRequest + + requests := requestsResp.Model if requests == nil || len(*requests) == 0 { - if scheduleStatus == http.StatusNotFound { + if response.WasNotFound(scheduleResp.HttpResponse) { // No request and no schedule was found - return metadata.MarkAsGone(id) + return metadata.MarkAsGone(resourceId) } } else { request = pointer.To((*requests)[0]) } - var scheduleInfo *msgraph.RequestSchedule + var scheduleInfo *stable.RequestSchedule if request != nil { // The request is still present, populate from the request scheduleInfo = request.ScheduleInfo - model.AssignmentType = request.AccessId - model.GroupId = pointer.From(request.GroupId) - model.Justification = pointer.From(request.Justification) - model.PrincipalId = pointer.From(request.PrincipalId) - model.Status = request.Status + model.AssignmentType = string(request.AccessId) + model.GroupId = request.GroupId.GetOrZero() + model.Justification = request.Justification.GetOrZero() + model.PrincipalId = request.PrincipalId.GetOrZero() + model.Status = pointer.From(request.Status) if ticketInfo := request.TicketInfo; ticketInfo != nil { - model.TicketNumber = pointer.From(ticketInfo.TicketNumber) - model.TicketSystem = pointer.From(ticketInfo.TicketSystem) + model.TicketNumber = ticketInfo.TicketNumber.GetOrZero() + model.TicketSystem = ticketInfo.TicketSystem.GetOrZero() } - } else { + } else if schedule != nil { // The request has likely expired, so populate from the schedule - scheduleInfo = schedule.ScheduleInfo + scheduleInfo = &schedule.ScheduleInfo - model.AssignmentType = schedule.AccessId - model.GroupId = pointer.From(schedule.GroupId) - model.PrincipalId = pointer.From(schedule.PrincipalId) - model.Status = schedule.Status + model.AssignmentType = string(schedule.AccessId) + model.GroupId = schedule.GroupId.GetOrZero() + model.PrincipalId = schedule.PrincipalId.GetOrZero() + model.Status = schedule.Status.GetOrZero() } if scheduleInfo != nil { + model.StartDate = scheduleInfo.StartDateTime.GetOrZero() + if expiration := scheduleInfo.Expiration; expiration != nil { - model.Duration = pointer.From(expiration.Duration) + model.Duration = expiration.Duration.GetOrZero() + model.ExpirationDate = expiration.EndDateTime.GetOrZero() - if expiration.EndDateTime != nil { - model.ExpirationDate = expiration.EndDateTime.Format(time.RFC3339) - } if expiration.Type != nil { - model.PermanentAssignment = *expiration.Type == msgraph.ExpirationPatternTypeNoExpiration + model.PermanentAssignment = *expiration.Type == stable.ExpirationPatternType_NoExpiration } } - if scheduleInfo.StartDateTime != nil { - model.StartDate = scheduleInfo.StartDateTime.Format(time.RFC3339) - } } return metadata.Encode(&model) @@ -195,10 +207,15 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Update() sdk.ResourceFu return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestsClient + client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestClient + + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) + if err != nil { + return err + } var model PrivilegedAccessGroupScheduleModel - if err := metadata.Decode(&model); err != nil { + if err = metadata.Decode(&model); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -207,33 +224,37 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Update() sdk.ResourceFu return err } - properties := msgraph.PrivilegedAccessGroupAssignmentScheduleRequest{ - AccessId: model.AssignmentType, - PrincipalId: &model.PrincipalId, - GroupId: &model.GroupId, - Action: msgraph.PrivilegedAccessGroupActionAdminAssign, - Justification: &model.Justification, + properties := stable.PrivilegedAccessGroupAssignmentScheduleRequest{ + AccessId: stable.PrivilegedAccessGroupRelationships(resourceId.Relationship), + PrincipalId: nullable.Value(model.PrincipalId), + GroupId: nullable.Value(resourceId.GroupId), + Action: pointer.To(stable.ScheduleRequestActions_AdminAssign), + Justification: nullable.NoZero(model.Justification), ScheduleInfo: schedule, } if model.TicketNumber != "" || model.TicketSystem != "" { - properties.TicketInfo = &msgraph.TicketInfo{ - TicketNumber: &model.TicketNumber, - TicketSystem: &model.TicketSystem, + properties.TicketInfo = &stable.TicketInfo{ + TicketNumber: nullable.NoZero(model.TicketNumber), + TicketSystem: nullable.NoZero(model.TicketSystem), } } - req, _, err := client.Create(ctx, properties) + resp, err := client.CreatePrivilegedAccessGroupAssignmentScheduleRequest(ctx, properties, privilegedaccessgroupassignmentschedulerequest.DefaultCreatePrivilegedAccessGroupAssignmentScheduleRequestOperationOptions()) if err != nil { - return fmt.Errorf("Could not create assignment schedule request, %+v", err) + return fmt.Errorf("creating updated assignment schedule request: %v", err) } - if req.ID == nil || *req.ID == "" { - return fmt.Errorf("ID returned for assignment schedule request is nil/empty") + request := resp.Model + if request == nil { + return fmt.Errorf("creating updated assignment schedule request: model was nil") + } + if request.Id == nil || *request.Id == "" { + return fmt.Errorf("creating updated assignment schedule request: ID returned for request is nil/empty") } - if req.Status == msgraph.PrivilegedAccessGroupAssignmentStatusFailed { - return fmt.Errorf("Assignment schedule request is in a failed state") + if pointer.From(request.Status) == PrivilegedAccessGroupScheduleRequestStatusFailed { + return fmt.Errorf("creating updated assignment schedule request: request is in a failed state") } return nil @@ -245,33 +266,33 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Delete() sdk.ResourceFu return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestsClient + client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleRequestClient - id, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) if err != nil { return err } var model PrivilegedAccessGroupScheduleModel - if err := metadata.Decode(&model); err != nil { + if err = metadata.Decode(&model); err != nil { return fmt.Errorf("decoding: %+v", err) } switch model.Status { - case msgraph.PrivilegedAccessGroupAssignmentStatusDenied, - msgraph.PrivilegedAccessGroupAssignmentStatusFailed, - msgraph.PrivilegedAccessGroupAssignmentStatusGranted, - msgraph.PrivilegedAccessGroupAssignmentStatusPendingAdminDecision, - msgraph.PrivilegedAccessGroupAssignmentStatusPendingApproval, - msgraph.PrivilegedAccessGroupAssignmentStatusPendingProvisioning, - msgraph.PrivilegedAccessGroupAssignmentStatusPendingScheduledCreation: - return cancelAssignmentRequest(ctx, metadata, client, id) - case msgraph.PrivilegedAccessGroupAssignmentStatusProvisioned, - msgraph.PrivilegedAccessGroupAssignmentStatusScheduleCreated: - return revokeAssignmentRequest(ctx, metadata, client, id, &model) - case msgraph.PrivilegedAccessGroupAssignmentStatusCanceled, - msgraph.PrivilegedAccessGroupAssignmentStatusRevoked: - return metadata.MarkAsGone(id) + case PrivilegedAccessGroupScheduleRequestStatusDenied, + PrivilegedAccessGroupScheduleRequestStatusFailed, + PrivilegedAccessGroupScheduleRequestStatusGranted, + PrivilegedAccessGroupScheduleRequestStatusPendingAdminDecision, + PrivilegedAccessGroupScheduleRequestStatusPendingApproval, + PrivilegedAccessGroupScheduleRequestStatusPendingProvisioning, + PrivilegedAccessGroupScheduleRequestStatusPendingScheduleCreation: + return cancelAssignmentRequest(ctx, metadata, client, stable.NewIdentityGovernancePrivilegedAccessGroupAssignmentScheduleRequestID(resourceId.ID())) + case PrivilegedAccessGroupScheduleRequestStatusProvisioned, + PrivilegedAccessGroupScheduleRequestStatusScheduleCreated: + return revokeAssignmentRequest(ctx, metadata, client, *resourceId, model) + case PrivilegedAccessGroupScheduleRequestStatusCanceled, + PrivilegedAccessGroupScheduleRequestStatusRevoked: + return metadata.MarkAsGone(resourceId) } return fmt.Errorf("unable to destroy due to unknown status: %s", model.Status) @@ -279,33 +300,33 @@ func (r PrivilegedAccessGroupAssignmentScheduleResource) Delete() sdk.ResourceFu } } -func cancelAssignmentRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *msgraph.PrivilegedAccessGroupAssignmentScheduleRequestsClient, id *parse.PrivilegedAccessGroupScheduleId) error { - status, err := client.Cancel(ctx, id.ID()) - if err != nil { - if status == http.StatusNotFound { +func cancelAssignmentRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *privilegedaccessgroupassignmentschedulerequest.PrivilegedAccessGroupAssignmentScheduleRequestClient, id stable.IdentityGovernancePrivilegedAccessGroupAssignmentScheduleRequestId) error { + if resp, err := client.CancelPrivilegedAccessGroupAssignmentScheduleRequest(ctx, id, privilegedaccessgroupassignmentschedulerequest.DefaultCancelPrivilegedAccessGroupAssignmentScheduleRequestOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return metadata.MarkAsGone(id) } - return fmt.Errorf("cancelling %s: %+v", id, err) + + return fmt.Errorf("canceling %s: %v", id, err) } + return nil } -func revokeAssignmentRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *msgraph.PrivilegedAccessGroupAssignmentScheduleRequestsClient, id *parse.PrivilegedAccessGroupScheduleId, model *PrivilegedAccessGroupScheduleModel) error { - result, status, err := client.Create(ctx, msgraph.PrivilegedAccessGroupAssignmentScheduleRequest{ - ID: pointer.To(id.ID()), - AccessId: model.AssignmentType, - PrincipalId: &model.PrincipalId, - GroupId: &model.GroupId, - Action: msgraph.PrivilegedAccessGroupActionAdminRemove, - }) - if err != nil { - if status == http.StatusNotFound { - return metadata.MarkAsGone(id) - } - return fmt.Errorf("retrieving %s: %+v", id, err) +func revokeAssignmentRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *privilegedaccessgroupassignmentschedulerequest.PrivilegedAccessGroupAssignmentScheduleRequestClient, id parse.PrivilegedAccessGroupScheduleId, model PrivilegedAccessGroupScheduleModel) error { + request := stable.PrivilegedAccessGroupAssignmentScheduleRequest{ + AccessId: stable.PrivilegedAccessGroupRelationships(id.Relationship), + PrincipalId: nullable.Value(model.PrincipalId), + GroupId: nullable.Value(id.GroupId), + Action: pointer.To(stable.ScheduleRequestActions_AdminRemove), } - if result == nil { - return fmt.Errorf("retrieving %s: API error, result was nil", id) + + if resp, err := client.CreatePrivilegedAccessGroupAssignmentScheduleRequest(ctx, request, privilegedaccessgroupassignmentschedulerequest.DefaultCreatePrivilegedAccessGroupAssignmentScheduleRequestOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(&id) + } + + return fmt.Errorf("creating schedule removal request: %v", err) } + return nil } diff --git a/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource_test.go b/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource_test.go index 372927026..861a8c195 100644 --- a/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource_test.go +++ b/internal/services/identitygovernance/privileged_access_group_assignment_schedule_resource_test.go @@ -6,11 +6,13 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupassignmentschedule" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -64,21 +66,22 @@ func TestPrivilegedAccessGroupAssignmentSchedule_owner(t *testing.T) { func (PrivilegedAccessGroupAssignmentScheduleResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.IdentityGovernance.PrivilegedAccessGroupAssignmentScheduleClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() - id, err := parse.ParsePrivilegedAccessGroupScheduleID(state.ID) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(state.ID) if err != nil { return nil, fmt.Errorf("failed to parse privileged group assignment schedule ID %q: %+v", state.ID, err) } - _, status, err := client.Get(ctx, id.ID()) + id := stable.NewIdentityGovernancePrivilegedAccessGroupAssignmentScheduleID(resourceId.ID()) + + resp, err := client.GetPrivilegedAccessGroupAssignmentSchedule(ctx, id, privilegedaccessgroupassignmentschedule.DefaultGetPrivilegedAccessGroupAssignmentScheduleOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve privileged group assignment schedule with ID %q: %+v", id.ID(), err) + return nil, fmt.Errorf("failed to retrieve %s: %v", id, err) } + return pointer.To(true), nil } diff --git a/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource.go b/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource.go index 18bba1d5a..ddcbef88a 100644 --- a/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource.go +++ b/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource.go @@ -6,15 +6,18 @@ package identitygovernance import ( "context" "fmt" - "net/http" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupeligibilityschedule" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupeligibilityschedulerequest" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-azure-sdk/sdk/odata" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azuread/internal/sdk" "github.com/hashicorp/terraform-provider-azuread/internal/services/identitygovernance/parse" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/manicminer/hamilton/msgraph" ) var _ sdk.ResourceWithUpdate = PrivilegedAccessGroupEligibilityScheduleResource{} @@ -45,7 +48,7 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Create() sdk.ResourceF return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestsClient + client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestClient var model PrivilegedAccessGroupScheduleModel if err := metadata.Decode(&model); err != nil { @@ -57,40 +60,45 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Create() sdk.ResourceF return err } - properties := msgraph.PrivilegedAccessGroupEligibilityScheduleRequest{ - AccessId: model.AssignmentType, - PrincipalId: &model.PrincipalId, - GroupId: &model.GroupId, - Action: msgraph.PrivilegedAccessGroupActionAdminAssign, - Justification: &model.Justification, + properties := stable.PrivilegedAccessGroupEligibilityScheduleRequest{ + AccessId: stable.PrivilegedAccessGroupRelationships(model.AssignmentType), + PrincipalId: nullable.Value(model.PrincipalId), + GroupId: nullable.Value(model.GroupId), + Action: pointer.To(stable.ScheduleRequestActions_AdminAssign), + Justification: nullable.NoZero(model.Justification), ScheduleInfo: schedule, } if model.TicketNumber != "" || model.TicketSystem != "" { - properties.TicketInfo = &msgraph.TicketInfo{ - TicketNumber: &model.TicketNumber, - TicketSystem: &model.TicketSystem, + properties.TicketInfo = &stable.TicketInfo{ + TicketNumber: nullable.NoZero(model.TicketNumber), + TicketSystem: nullable.NoZero(model.TicketSystem), } } - req, _, err := client.Create(ctx, properties) + resp, err := client.CreatePrivilegedAccessGroupEligibilityScheduleRequest(ctx, properties, privilegedaccessgroupeligibilityschedulerequest.DefaultCreatePrivilegedAccessGroupEligibilityScheduleRequestOperationOptions()) if err != nil { - return fmt.Errorf("Could not create assignment schedule request, %+v", err) + return fmt.Errorf("creating eligibility schedule request: %v", err) } - if req.ID == nil || *req.ID == "" { - return fmt.Errorf("ID returned for assignment schedule request is nil/empty") + request := resp.Model + if request == nil { + return fmt.Errorf("creating eligibility schedule request: model was nil") + } + if request.Id == nil || *request.Id == "" { + return fmt.Errorf("creating eligibility schedule request: ID returned for request is nil/empty") } - if req.Status == msgraph.PrivilegedAccessGroupEligibilityStatusFailed { - return fmt.Errorf("Assignment schedule request is in a failed state") + if pointer.From(request.Status) == PrivilegedAccessGroupScheduleRequestStatusFailed { + return fmt.Errorf("creating eligibility schedule request: request is in a failed state") } - id, err := parse.ParsePrivilegedAccessGroupScheduleID(*req.TargetScheduleId) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(request.TargetScheduleId.GetOrZero()) if err != nil { return err } - metadata.SetID(id) + + metadata.SetID(resourceId) return nil }, @@ -102,9 +110,9 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Read() sdk.ResourceFun Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { scheduleClient := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleClient - requestsClient := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestsClient + requestsClient := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestClient - id, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) if err != nil { return err } @@ -114,76 +122,80 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Read() sdk.ResourceFun return fmt.Errorf("decoding: %+v", err) } - schedule, scheduleStatus, err := scheduleClient.Get(ctx, id.ID()) - if err != nil && scheduleStatus != http.StatusNotFound { + id := stable.NewIdentityGovernancePrivilegedAccessGroupEligibilityScheduleID(resourceId.ID()) + + scheduleResp, err := scheduleClient.GetPrivilegedAccessGroupEligibilitySchedule(ctx, id, privilegedaccessgroupeligibilityschedule.DefaultGetPrivilegedAccessGroupEligibilityScheduleOperationOptions()) + if err != nil && !response.WasNotFound(scheduleResp.HttpResponse) { return fmt.Errorf("retrieving %s: %+v", id, err) } - var request *msgraph.PrivilegedAccessGroupEligibilityScheduleRequest + schedule := scheduleResp.Model // Some details are only available on the request which is used for the create/update of the schedule. // Schedule requests are never deleted. New ones are created when changes are made. - // Therefore on a read, we need to find the latest version of the request. + // Therefore, on read, we need to find the latest version of the request. // This is to cater for changes being made outside of Terraform. - requests, _, err := requestsClient.List(ctx, odata.Query{ - Filter: fmt.Sprintf("groupId eq '%s' and targetScheduleId eq '%s'", id.GroupId, id.ID()), - OrderBy: odata.OrderBy{ + options := privilegedaccessgroupeligibilityschedulerequest.ListPrivilegedAccessGroupEligibilityScheduleRequestsOperationOptions{ + Filter: pointer.To(fmt.Sprintf("groupId eq '%s' and targetScheduleId eq '%s'", resourceId.GroupId, resourceId.ID())), + OrderBy: pointer.To(odata.OrderBy{ Field: "createdDateTime", Direction: odata.Descending, - }, - }) + }), + } + requestsResp, err := requestsClient.ListPrivilegedAccessGroupEligibilityScheduleRequests(ctx, options) if err != nil { - return fmt.Errorf("listing requests: %+v", err) + return fmt.Errorf("listing requests: %v", err) } + + var request *stable.PrivilegedAccessGroupEligibilityScheduleRequest + + requests := requestsResp.Model if requests == nil || len(*requests) == 0 { - if scheduleStatus == http.StatusNotFound { + if response.WasNotFound(scheduleResp.HttpResponse) { // No request and no schedule was found - return metadata.MarkAsGone(id) + return metadata.MarkAsGone(resourceId) } } else { request = pointer.To((*requests)[0]) } - var scheduleInfo *msgraph.RequestSchedule + var scheduleInfo *stable.RequestSchedule if request != nil { // The request is still present, populate from the request scheduleInfo = request.ScheduleInfo - model.AssignmentType = request.AccessId - model.GroupId = pointer.From(request.GroupId) - model.Justification = pointer.From(request.Justification) - model.PrincipalId = pointer.From(request.PrincipalId) - model.Status = request.Status + model.AssignmentType = string(request.AccessId) + model.GroupId = request.GroupId.GetOrZero() + model.Justification = request.Justification.GetOrZero() + model.PrincipalId = request.PrincipalId.GetOrZero() + model.Status = pointer.From(request.Status) if ticketInfo := request.TicketInfo; ticketInfo != nil { - model.TicketNumber = pointer.From(ticketInfo.TicketNumber) - model.TicketSystem = pointer.From(ticketInfo.TicketSystem) + model.TicketNumber = ticketInfo.TicketNumber.GetOrZero() + model.TicketSystem = ticketInfo.TicketSystem.GetOrZero() } - } else { + } else if request != nil { // The request has likely expired, so populate from the schedule - scheduleInfo = schedule.ScheduleInfo + scheduleInfo = &schedule.ScheduleInfo - model.AssignmentType = schedule.AccessId - model.GroupId = pointer.From(schedule.GroupId) - model.PrincipalId = pointer.From(schedule.PrincipalId) - model.Status = schedule.Status + model.AssignmentType = string(schedule.AccessId) + model.GroupId = schedule.GroupId.GetOrZero() + model.PrincipalId = schedule.PrincipalId.GetOrZero() + model.Status = schedule.Status.GetOrZero() } if scheduleInfo != nil { + model.StartDate = scheduleInfo.StartDateTime.GetOrZero() + if expiration := scheduleInfo.Expiration; expiration != nil { - model.Duration = pointer.From(expiration.Duration) + model.Duration = expiration.Duration.GetOrZero() + model.ExpirationDate = expiration.EndDateTime.GetOrZero() - if expiration.EndDateTime != nil { - model.ExpirationDate = expiration.EndDateTime.Format(time.RFC3339) - } if expiration.Type != nil { - model.PermanentAssignment = *expiration.Type == msgraph.ExpirationPatternTypeNoExpiration + model.PermanentAssignment = *expiration.Type == stable.ExpirationPatternType_NoExpiration } } - if scheduleInfo.StartDateTime != nil { - model.StartDate = scheduleInfo.StartDateTime.Format(time.RFC3339) - } } return metadata.Encode(&model) @@ -195,10 +207,15 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Update() sdk.ResourceF return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestsClient + client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestClient + + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) + if err != nil { + return err + } var model PrivilegedAccessGroupScheduleModel - if err := metadata.Decode(&model); err != nil { + if err = metadata.Decode(&model); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -207,33 +224,37 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Update() sdk.ResourceF return err } - properties := msgraph.PrivilegedAccessGroupEligibilityScheduleRequest{ - AccessId: model.AssignmentType, - PrincipalId: &model.PrincipalId, - GroupId: &model.GroupId, - Action: msgraph.PrivilegedAccessGroupActionAdminAssign, - Justification: &model.Justification, + properties := stable.PrivilegedAccessGroupEligibilityScheduleRequest{ + AccessId: stable.PrivilegedAccessGroupRelationships(resourceId.Relationship), + PrincipalId: nullable.Value(model.PrincipalId), + GroupId: nullable.Value(resourceId.GroupId), + Action: pointer.To(stable.ScheduleRequestActions_AdminAssign), + Justification: nullable.NoZero(model.Justification), ScheduleInfo: schedule, } if model.TicketNumber != "" || model.TicketSystem != "" { - properties.TicketInfo = &msgraph.TicketInfo{ - TicketNumber: &model.TicketNumber, - TicketSystem: &model.TicketSystem, + properties.TicketInfo = &stable.TicketInfo{ + TicketNumber: nullable.NoZero(model.TicketNumber), + TicketSystem: nullable.NoZero(model.TicketSystem), } } - req, _, err := client.Create(ctx, properties) + resp, err := client.CreatePrivilegedAccessGroupEligibilityScheduleRequest(ctx, properties, privilegedaccessgroupeligibilityschedulerequest.DefaultCreatePrivilegedAccessGroupEligibilityScheduleRequestOperationOptions()) if err != nil { - return fmt.Errorf("Could not create assignment schedule request, %+v", err) + return fmt.Errorf("creating updated eligibility schedule request: %v", err) } - if req.ID == nil || *req.ID == "" { - return fmt.Errorf("ID returned for assignment schedule request is nil/empty") + request := resp.Model + if request == nil { + return fmt.Errorf("creating updated eligibility schedule request: model was nil") + } + if request.Id == nil || *request.Id == "" { + return fmt.Errorf("creating updated eligibility schedule request: ID returned for request is nil/empty") } - if req.Status == msgraph.PrivilegedAccessGroupEligibilityStatusFailed { - return fmt.Errorf("Assignment schedule request is in a failed state") + if pointer.From(request.Status) == PrivilegedAccessGroupScheduleRequestStatusFailed { + return fmt.Errorf("creating updated eligibility schedule request: request is in a failed state") } return nil @@ -245,33 +266,33 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Delete() sdk.ResourceF return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestsClient + client := metadata.Client.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleRequestClient - id, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(metadata.ResourceData.Id()) if err != nil { return err } var model PrivilegedAccessGroupScheduleModel - if err := metadata.Decode(&model); err != nil { + if err = metadata.Decode(&model); err != nil { return fmt.Errorf("decoding: %+v", err) } switch model.Status { - case msgraph.PrivilegedAccessGroupEligibilityStatusDenied, - msgraph.PrivilegedAccessGroupEligibilityStatusFailed, - msgraph.PrivilegedAccessGroupEligibilityStatusGranted, - msgraph.PrivilegedAccessGroupEligibilityStatusPendingAdminDecision, - msgraph.PrivilegedAccessGroupEligibilityStatusPendingApproval, - msgraph.PrivilegedAccessGroupEligibilityStatusPendingProvisioning, - msgraph.PrivilegedAccessGroupEligibilityStatusPendingScheduledCreation: - return cancelEligibilityRequest(ctx, metadata, client, id) - case msgraph.PrivilegedAccessGroupEligibilityStatusProvisioned, - msgraph.PrivilegedAccessGroupEligibilityStatusScheduleCreated: - return revokeEligibilityRequest(ctx, metadata, client, id, &model) - case msgraph.PrivilegedAccessGroupEligibilityStatusCanceled, - msgraph.PrivilegedAccessGroupEligibilityStatusRevoked: - return metadata.MarkAsGone(id) + case PrivilegedAccessGroupScheduleRequestStatusDenied, + PrivilegedAccessGroupScheduleRequestStatusFailed, + PrivilegedAccessGroupScheduleRequestStatusGranted, + PrivilegedAccessGroupScheduleRequestStatusPendingAdminDecision, + PrivilegedAccessGroupScheduleRequestStatusPendingApproval, + PrivilegedAccessGroupScheduleRequestStatusPendingProvisioning, + PrivilegedAccessGroupScheduleRequestStatusPendingScheduleCreation: + return cancelEligibilityRequest(ctx, metadata, client, stable.NewIdentityGovernancePrivilegedAccessGroupEligibilityScheduleRequestID(resourceId.ID())) + case PrivilegedAccessGroupScheduleRequestStatusProvisioned, + PrivilegedAccessGroupScheduleRequestStatusScheduleCreated: + return revokeEligibilityRequest(ctx, metadata, client, *resourceId, model) + case PrivilegedAccessGroupScheduleRequestStatusCanceled, + PrivilegedAccessGroupScheduleRequestStatusRevoked: + return metadata.MarkAsGone(resourceId) } return fmt.Errorf("unknown status: %s", model.Status) @@ -279,33 +300,33 @@ func (r PrivilegedAccessGroupEligibilityScheduleResource) Delete() sdk.ResourceF } } -func cancelEligibilityRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *msgraph.PrivilegedAccessGroupEligibilityScheduleRequestsClient, id *parse.PrivilegedAccessGroupScheduleId) error { - status, err := client.Cancel(ctx, id.ID()) - if err != nil { - if status == http.StatusNotFound { +func cancelEligibilityRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *privilegedaccessgroupeligibilityschedulerequest.PrivilegedAccessGroupEligibilityScheduleRequestClient, id stable.IdentityGovernancePrivilegedAccessGroupEligibilityScheduleRequestId) error { + if resp, err := client.CancelPrivilegedAccessGroupEligibilityScheduleRequest(ctx, id, privilegedaccessgroupeligibilityschedulerequest.DefaultCancelPrivilegedAccessGroupEligibilityScheduleRequestOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { return metadata.MarkAsGone(id) } - return fmt.Errorf("cancelling %s: %+v", id, err) + + return fmt.Errorf("canceling %s: %v", id, err) } + return nil } -func revokeEligibilityRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *msgraph.PrivilegedAccessGroupEligibilityScheduleRequestsClient, id *parse.PrivilegedAccessGroupScheduleId, model *PrivilegedAccessGroupScheduleModel) error { - result, status, err := client.Create(ctx, msgraph.PrivilegedAccessGroupEligibilityScheduleRequest{ - ID: pointer.To(id.ID()), - AccessId: model.AssignmentType, - PrincipalId: &model.PrincipalId, - GroupId: &model.GroupId, - Action: msgraph.PrivilegedAccessGroupActionAdminRemove, - }) - if err != nil { - if status == http.StatusNotFound { - return metadata.MarkAsGone(id) - } - return fmt.Errorf("retrieving %s: %+v", id, err) +func revokeEligibilityRequest(ctx context.Context, metadata sdk.ResourceMetaData, client *privilegedaccessgroupeligibilityschedulerequest.PrivilegedAccessGroupEligibilityScheduleRequestClient, id parse.PrivilegedAccessGroupScheduleId, model PrivilegedAccessGroupScheduleModel) error { + request := stable.PrivilegedAccessGroupEligibilityScheduleRequest{ + AccessId: stable.PrivilegedAccessGroupRelationships(id.Relationship), + PrincipalId: nullable.Value(model.PrincipalId), + GroupId: nullable.Value(id.GroupId), + Action: pointer.To(stable.ScheduleRequestActions_AdminRemove), } - if result == nil { - return fmt.Errorf("retrieving %s: API error, result was nil", id) + + if resp, err := client.CreatePrivilegedAccessGroupEligibilityScheduleRequest(ctx, request, privilegedaccessgroupeligibilityschedulerequest.DefaultCreatePrivilegedAccessGroupEligibilityScheduleRequestOperationOptions()); err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(&id) + } + + return fmt.Errorf("creating schedule removal request: %v", err) } + return nil } diff --git a/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource_test.go b/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource_test.go index 0bfac03d4..a314257d2 100644 --- a/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource_test.go +++ b/internal/services/identitygovernance/privileged_access_group_eligiblity_schedule_resource_test.go @@ -6,11 +6,13 @@ package identitygovernance_test import ( "context" "fmt" - "net/http" "testing" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/identitygovernance/stable/privilegedaccessgroupeligibilityschedule" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -63,21 +65,22 @@ func TestPrivilegedAccessGroupEligibilitySchedule_owner(t *testing.T) { func (PrivilegedAccessGroupEligibilityScheduleResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.IdentityGovernance.PrivilegedAccessGroupEligibilityScheduleClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() - id, err := parse.ParsePrivilegedAccessGroupScheduleID(state.ID) + resourceId, err := parse.ParsePrivilegedAccessGroupScheduleID(state.ID) if err != nil { return nil, fmt.Errorf("failed to parse privileged group assignment schedule ID %q: %+v", state.ID, err) } - _, status, err := client.Get(ctx, id.ID()) + id := stable.NewIdentityGovernancePrivilegedAccessGroupEligibilityScheduleID(resourceId.ID()) + + resp, err := client.GetPrivilegedAccessGroupEligibilitySchedule(ctx, id, privilegedaccessgroupeligibilityschedule.DefaultGetPrivilegedAccessGroupEligibilityScheduleOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve privileged group assignment schedule request with ID %q: %+v", id.ID(), err) + return nil, fmt.Errorf("failed to retrieve %s: %v", id, err) } + return pointer.To(true), nil } diff --git a/internal/services/identitygovernance/privileged_access_group_schedule.go b/internal/services/identitygovernance/privileged_access_group_schedule.go index 73156d90a..7234942e3 100644 --- a/internal/services/identitygovernance/privileged_access_group_schedule.go +++ b/internal/services/identitygovernance/privileged_access_group_schedule.go @@ -8,10 +8,11 @@ import ( "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" "github.com/hashicorp/terraform-provider-azuread/internal/sdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" ) type PrivilegedAccessGroupScheduleModel struct { @@ -47,14 +48,11 @@ func privilegedAccessGroupScheduleArguments() map[string]*pluginsdk.Schema { }, "assignment_type": { - Description: "The ID of the assignment to the group", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringInSlice([]string{ - msgraph.PrivilegedAccessGroupRelationshipMember, - msgraph.PrivilegedAccessGroupRelationshipOwner, - }, false)), + Description: "The ID of the assignment to the group", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: validation.ValidateDiag(validation.StringInSlice(stable.PossibleValuesForPrivilegedAccessGroupRelationships(), false)), }, "start_date": { @@ -139,51 +137,49 @@ func privilegedAccessGroupScheduleAttributes() map[string]*pluginsdk.Schema { } } -func buildScheduleRequest(model *PrivilegedAccessGroupScheduleModel, metadata *sdk.ResourceMetaData) (*msgraph.RequestSchedule, error) { - schedule := msgraph.RequestSchedule{} - schedule.Expiration = &msgraph.ExpirationPattern{} +func buildScheduleRequest(model *PrivilegedAccessGroupScheduleModel, metadata *sdk.ResourceMetaData) (*stable.RequestSchedule, error) { + schedule := stable.RequestSchedule{ + Expiration: &stable.ExpirationPattern{}, + StartDateTime: nullable.NoZero(model.StartDate), + } var startDate, expiryDate time.Time - var err error if model.StartDate != "" { + var err error startDate, err = time.Parse(time.RFC3339, model.StartDate) if err != nil { return nil, fmt.Errorf("parsing %s: %+v", model.StartDate, err) } - if metadata.ResourceData.HasChange("start_date") { - if startDate.Before(time.Now()) { - return nil, fmt.Errorf("start_date must be in the future") - } - } - schedule.StartDateTime = &startDate } switch { case model.ExpirationDate != "": + var err error expiryDate, err = time.Parse(time.RFC3339, model.ExpirationDate) if err != nil { return nil, fmt.Errorf("parsing %s: %+v", model.ExpirationDate, err) } - if metadata.ResourceData.HasChange("expiry_date") { - if expiryDate.Before(time.Now().Add(5 * time.Minute)) { - return nil, fmt.Errorf("expiry_date must be at least 5 minutes in the future") - } + + if model.StartDate != "" && expiryDate.Before(startDate.Add(5*time.Minute)) { + return nil, fmt.Errorf("`expiration_date` must be at least 5 minutes after `start_date`") + } + + if metadata.ResourceData.HasChange("expiry_date") && expiryDate.Before(time.Now().Add(5*time.Minute)) { + return nil, fmt.Errorf("`expiration_date` must be at least 5 minutes in the future") } - schedule.Expiration.EndDateTime = &expiryDate - schedule.Expiration.Type = pointer.To(msgraph.ExpirationPatternTypeAfterDateTime) + + schedule.Expiration.EndDateTime = nullable.Value(model.ExpirationDate) + schedule.Expiration.Type = pointer.To(stable.ExpirationPatternType_AfterDateTime) + case model.Duration != "": - schedule.Expiration.Duration = &model.Duration - schedule.Expiration.Type = pointer.To(msgraph.ExpirationPatternTypeAfterDuration) + schedule.Expiration.Duration = nullable.Value(model.Duration) + schedule.Expiration.Type = pointer.To(stable.ExpirationPatternType_AfterDuration) + case model.PermanentAssignment: - schedule.Expiration.Type = pointer.To(msgraph.ExpirationPatternTypeNoExpiration) - default: - return nil, fmt.Errorf("either expiration_date or duration must be set, or permanent_assignment must be true") - } + schedule.Expiration.Type = pointer.To(stable.ExpirationPatternType_NoExpiration) - if model.StartDate != "" && model.ExpirationDate != "" { - if expiryDate.Before(startDate.Add(5 * time.Minute)) { - return nil, fmt.Errorf("expiration_date must be at least 5 minutes after start_date") - } + default: + return nil, fmt.Errorf("either `expiration_date` or `duration` must be set, or `permanent_assignment` must be true") } return &schedule, nil diff --git a/internal/services/identitygovernance/registration.go b/internal/services/identitygovernance/registration.go index 1ad6f3916..865a8d51b 100644 --- a/internal/services/identitygovernance/registration.go +++ b/internal/services/identitygovernance/registration.go @@ -4,8 +4,8 @@ package identitygovernance import ( + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azuread/internal/sdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" ) type Registration struct{} diff --git a/internal/services/identitygovernance/schema.go b/internal/services/identitygovernance/schema.go index d72b1003f..157d64756 100644 --- a/internal/services/identitygovernance/schema.go +++ b/internal/services/identitygovernance/schema.go @@ -4,9 +4,9 @@ package identitygovernance import ( - "github.com/hashicorp/go-azure-sdk/sdk/odata" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/suppress" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/validation" ) func schemaLocalizedContent() *pluginsdk.Resource { @@ -25,10 +25,10 @@ func schemaLocalizedContent() *pluginsdk.Resource { Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "language_code": { - Description: "The language code of this question content", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ISO639Language, + Description: "The language code of this question content", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.ISO639Language, }, "content": { @@ -47,16 +47,18 @@ func schemaUserSet() *pluginsdk.Resource { return &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "subject_type": { - Description: "Type of users", - Type: pluginsdk.TypeString, - Required: true, + Description: "Type of users", + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ - odata.ShortTypeConnectedOrganizationMembers, - odata.ShortTypeExternalSponsors, - odata.ShortTypeGroupMembers, - odata.ShortTypeInternalSponsors, - odata.ShortTypeRequestorManager, - odata.ShortTypeSingleUser, + "ConnectedOrganizationMembers", + "ExternalSponsors", + "GroupMembers", + "InternalSponsors", + "RequestorManager", + "SingleUser", + "TargetUserSponsors", }, true), },