diff --git a/internal/services/directoryroles/client/client.go b/internal/services/directoryroles/client/client.go index 1f7633811..0afb52447 100644 --- a/internal/services/directoryroles/client/client.go +++ b/internal/services/directoryroles/client/client.go @@ -4,44 +4,76 @@ package client import ( + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryobjects/stable/directoryobject" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroles/stable/directoryrole" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroles/stable/member" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroletemplates/stable/directoryroletemplate" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroleassignment" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroledefinition" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroleeligibilityschedulerequest" "github.com/hashicorp/terraform-provider-azuread/internal/common" - "github.com/manicminer/hamilton/msgraph" ) type Client struct { - DirectoryObjectsClient *msgraph.DirectoryObjectsClient - DirectoryRolesClient *msgraph.DirectoryRolesClient - DirectoryRoleTemplatesClient *msgraph.DirectoryRoleTemplatesClient - RoleAssignmentsClient *msgraph.RoleAssignmentsClient - RoleDefinitionsClient *msgraph.RoleDefinitionsClient - RoleEligibilityScheduleRequestClient *msgraph.RoleEligibilityScheduleRequestClient + DirectoryObjectClient *directoryobject.DirectoryObjectClient + DirectoryRoleAssignmentClient *directoryroleassignment.DirectoryRoleAssignmentClient + DirectoryRoleClient *directoryrole.DirectoryRoleClient + DirectoryRoleDefinitionClient *directoryroledefinition.DirectoryRoleDefinitionClient + DirectoryRoleEligibilityScheduleRequestClient *directoryroleeligibilityschedulerequest.DirectoryRoleEligibilityScheduleRequestClient + DirectoryRoleMemberClient *member.MemberClient + DirectoryRoleTemplateClient *directoryroletemplate.DirectoryRoleTemplateClient } -func NewClient(o *common.ClientOptions) *Client { - directoryObjectsClient := msgraph.NewDirectoryObjectsClient() - o.ConfigureClient(&directoryObjectsClient.BaseClient) +func NewClient(o *common.ClientOptions) (*Client, error) { + directoryObjectClient, err := directoryobject.NewDirectoryObjectClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(directoryObjectClient.Client) - directoryRolesClient := msgraph.NewDirectoryRolesClient() - o.ConfigureClient(&directoryRolesClient.BaseClient) + directoryRoleClient, err := directoryrole.NewDirectoryRoleClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(directoryRoleClient.Client) - directoryRoleTemplatesClient := msgraph.NewDirectoryRoleTemplatesClient() - o.ConfigureClient(&directoryRoleTemplatesClient.BaseClient) + directoryRoleAssignmentClient, err := directoryroleassignment.NewDirectoryRoleAssignmentClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(directoryRoleAssignmentClient.Client) - roleAssignmentsClient := msgraph.NewRoleAssignmentsClient() - o.ConfigureClient(&roleAssignmentsClient.BaseClient) + directoryRoleDefinitionClient, err := directoryroledefinition.NewDirectoryRoleDefinitionClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(directoryRoleDefinitionClient.Client) - roleDefinitionsClient := msgraph.NewRoleDefinitionsClient() - o.ConfigureClient(&roleDefinitionsClient.BaseClient) + directoryRoleMemberClient, err := member.NewMemberClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(directoryRoleMemberClient.Client) - roleEligibilityScheduleRequestClient := msgraph.NewRoleEligibilityScheduleRequestClient() - o.ConfigureClient(&roleEligibilityScheduleRequestClient.BaseClient) + directoryRoleEligibilityScheduleRequestClient, err := directoryroleeligibilityschedulerequest.NewDirectoryRoleEligibilityScheduleRequestClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err + } + o.Configure(directoryRoleEligibilityScheduleRequestClient.Client) - return &Client{ - DirectoryObjectsClient: directoryObjectsClient, - DirectoryRolesClient: directoryRolesClient, - DirectoryRoleTemplatesClient: directoryRoleTemplatesClient, - RoleAssignmentsClient: roleAssignmentsClient, - RoleDefinitionsClient: roleDefinitionsClient, - RoleEligibilityScheduleRequestClient: roleEligibilityScheduleRequestClient, + directoryRoleTemplateClient, err := directoryroletemplate.NewDirectoryRoleTemplateClientWithBaseURI(o.Environment.MicrosoftGraph) + if err != nil { + return nil, err } + o.Configure(directoryRoleTemplateClient.Client) + + return &Client{ + DirectoryObjectClient: directoryObjectClient, + DirectoryRoleAssignmentClient: directoryRoleAssignmentClient, + DirectoryRoleClient: directoryRoleClient, + DirectoryRoleDefinitionClient: directoryRoleDefinitionClient, + DirectoryRoleEligibilityScheduleRequestClient: directoryRoleEligibilityScheduleRequestClient, + DirectoryRoleMemberClient: directoryRoleMemberClient, + DirectoryRoleTemplateClient: directoryRoleTemplateClient, + }, nil } diff --git a/internal/services/directoryroles/constants.go b/internal/services/directoryroles/constants.go new file mode 100644 index 000000000..272f0dadd --- /dev/null +++ b/internal/services/directoryroles/constants.go @@ -0,0 +1,3 @@ +package directoryroles + +const directoryRoleMemberResourceName = "azuread_directory_role_member" diff --git a/internal/services/directoryroles/custom_directory_role_resource.go b/internal/services/directoryroles/custom_directory_role_resource.go index f850a257d..c0a5498f2 100644 --- a/internal/services/directoryroles/custom_directory_role_resource.go +++ b/internal/services/directoryroles/custom_directory_role_resource.go @@ -8,17 +8,17 @@ import ( "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/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroledefinition" + "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 customDirectoryRoleResource() *pluginsdk.Resource { @@ -44,10 +44,10 @@ func customDirectoryRoleResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "display_name": { - Description: "The display name of the custom directory role", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "The display name of the custom directory role", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "enabled": { @@ -67,8 +67,8 @@ func customDirectoryRoleResource() *pluginsdk.Resource { Type: pluginsdk.TypeSet, Required: true, Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, }, }, }, @@ -76,10 +76,10 @@ func customDirectoryRoleResource() *pluginsdk.Resource { }, "version": { - Description: "The version of the role definition.", - Type: pluginsdk.TypeString, - Required: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringLenBetween(1, 128)), + Description: "The version of the role definition.", + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 128), }, "description": { @@ -89,11 +89,11 @@ func customDirectoryRoleResource() *pluginsdk.Resource { }, "template_id": { - Description: "Custom template identifier that is typically used if one needs an identifier to be the same across different directories.", - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "Custom template identifier that is typically used if one needs an identifier to be the same across different directories.", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.IsUUID, // The template ID _can_ technically be changed but doing so mutates the role ID - essentially // causing the equivalent of a ForceNew by the API :/ @@ -110,52 +110,54 @@ func customDirectoryRoleResource() *pluginsdk.Resource { } func customDirectoryRoleResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleDefinitionsClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleDefinitionClient displayName := d.Get("display_name").(string) - properties := msgraph.UnifiedRoleDefinition{ - Description: tf.NullableString(d.Get("description").(string)), - DisplayName: pointer.To(displayName), - IsEnabled: pointer.To(d.Get("enabled").(bool)), + properties := stable.UnifiedRoleDefinition{ + Description: nullable.NoZero(d.Get("description").(string)), + DisplayName: nullable.Value(displayName), + IsEnabled: nullable.Value(d.Get("enabled").(bool)), RolePermissions: expandCustomRolePermissions(d.Get("permissions").(*pluginsdk.Set).List()), - TemplateId: pointer.To(d.Get("template_id").(string)), - Version: pointer.To(d.Get("version").(string)), + TemplateId: nullable.Value(d.Get("template_id").(string)), + Version: nullable.Value(d.Get("version").(string)), } - role, _, err := client.Create(ctx, properties) + resp, err := client.CreateDirectoryRoleDefinition(ctx, properties, directoryroledefinition.DefaultCreateDirectoryRoleDefinitionOperationOptions()) if err != nil { return tf.ErrorDiagF(err, "Creating custom directory role %q", displayName) } - if role.ID() == nil || *role.ID() == "" { + role := resp.Model + if role == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "Creating custom directory role %q", displayName) + } + + if role.Id == nil || *role.Id == "" { return tf.ErrorDiagF(errors.New("API returned custom directory role with nil ID"), "Bad API Response") } - d.SetId(*role.ID()) + d.SetId(*role.Id) return customDirectoryRoleResourceRead(ctx, d, meta) } func customDirectoryRoleResourceUpdate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleDefinitionsClient - roleId := d.Id() + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleDefinitionClient + id := stable.NewRoleManagementDirectoryRoleDefinitionID(d.Id()) displayName := d.Get("display_name").(string) - properties := msgraph.UnifiedRoleDefinition{ - DirectoryObject: msgraph.DirectoryObject{ - Id: &roleId, - }, - Description: tf.NullableString(d.Get("description").(string)), - DisplayName: pointer.To(displayName), - IsEnabled: pointer.To(d.Get("enabled").(bool)), + properties := stable.UnifiedRoleDefinition{ + Description: nullable.NoZero(d.Get("description").(string)), + DisplayName: nullable.Value(displayName), + IsEnabled: nullable.Value(d.Get("enabled").(bool)), RolePermissions: expandCustomRolePermissions(d.Get("permissions").(*pluginsdk.Set).List()), - TemplateId: pointer.To(d.Get("template_id").(string)), - Version: pointer.To(d.Get("version").(string)), + TemplateId: nullable.Value(d.Get("template_id").(string)), + Version: nullable.Value(d.Get("version").(string)), } - _, err := client.Update(ctx, properties) + _, err := client.UpdateDirectoryRoleDefinition(ctx, id, properties, directoryroledefinition.DefaultUpdateDirectoryRoleDefinitionOperationOptions()) if err != nil { return tf.ErrorDiagF(err, "Updating custom directory role %q", displayName) } @@ -164,47 +166,49 @@ func customDirectoryRoleResourceUpdate(ctx context.Context, d *pluginsdk.Resourc } func customDirectoryRoleResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleDefinitionsClient - roleId := d.Id() + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleDefinitionClient + id := stable.NewRoleManagementDirectoryRoleDefinitionID(d.Id()) - role, status, err := client.Get(ctx, roleId, odata.Query{}) + resp, err := client.GetDirectoryRoleDefinition(ctx, id, directoryroledefinition.DefaultGetDirectoryRoleDefinitionOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Custom Directory Role with ID %q was not found - removing from state", roleId) + if response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] %s was not found - removing from state", id) d.SetId("") return nil } - return tf.ErrorDiagPathF(err, "template_id", "Retrieving custom directory role with ID %q: %+v", roleId, err) + return tf.ErrorDiagPathF(err, "template_id", "Retrieving %s: %+v", id, err) } + + role := resp.Model if role == nil { - return tf.ErrorDiagF(errors.New("API error: nil unifiedDirectoryRole was returned"), "Retrieving custom directory role with ID %q", roleId) + return tf.ErrorDiagF(errors.New("API error: nil unifiedDirectoryRole was returned"), "Retrieving %s", id) } - tf.Set(d, "description", role.Description) - tf.Set(d, "display_name", role.DisplayName) - tf.Set(d, "enabled", role.IsEnabled) - tf.Set(d, "object_id", role.ID()) + tf.Set(d, "description", role.Description.GetOrZero()) + tf.Set(d, "display_name", role.DisplayName.GetOrZero()) + tf.Set(d, "enabled", role.IsEnabled.GetOrZero()) + tf.Set(d, "object_id", id.UnifiedRoleDefinitionId) tf.Set(d, "permissions", flattenCustomRolePermissions(role.RolePermissions)) - tf.Set(d, "template_id", role.TemplateId) - tf.Set(d, "version", role.Version) + tf.Set(d, "template_id", role.TemplateId.GetOrZero()) + tf.Set(d, "version", role.Version.GetOrZero()) return nil } func customDirectoryRoleResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleDefinitionsClient - roleId := d.Id() + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleDefinitionClient + id := stable.NewRoleManagementDirectoryRoleDefinitionID(d.Id()) - _, status, err := client.Get(ctx, roleId, odata.Query{}) + resp, err := client.GetDirectoryRoleDefinition(ctx, id, directoryroledefinition.DefaultGetDirectoryRoleDefinitionOperationOptions()) if err != nil { - if status == http.StatusNotFound { - return tf.ErrorDiagPathF(fmt.Errorf("Custom Directory Role was not found"), "id", "Retrieving custom directory role with ID %q", roleId) + if response.WasNotFound(resp.HttpResponse) { + return tf.ErrorDiagPathF(fmt.Errorf("Custom Directory Role was not found"), "id", "Retrieving %s", id) } - return tf.ErrorDiagPathF(err, "id", "Retrieving custom directory role with ID %q", roleId) + return tf.ErrorDiagPathF(err, "id", "Retrieving %s", id) } - if status, err := client.Delete(ctx, roleId); err != nil { - return tf.ErrorDiagPathF(err, "id", "Deleting custom directory role with ID %q, got status %d", roleId, status) + if _, err = client.DeleteDirectoryRoleDefinition(ctx, id, directoryroledefinition.DefaultDeleteDirectoryRoleDefinitionOperationOptions()); err != nil { + return tf.ErrorDiagPathF(err, "id", "Deleting %s", id) } return nil diff --git a/internal/services/directoryroles/custom_directory_role_resource_test.go b/internal/services/directoryroles/custom_directory_role_resource_test.go index b2afac4eb..487866754 100644 --- a/internal/services/directoryroles/custom_directory_role_resource_test.go +++ b/internal/services/directoryroles/custom_directory_role_resource_test.go @@ -6,11 +6,12 @@ package directoryroles_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/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroledefinition" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -129,19 +130,18 @@ func TestAccCustomDirectoryRole_templateId(t *testing.T) { } func (r CustomDirectoryRoleResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.DirectoryRoles.RoleDefinitionsClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.DirectoryRoles.DirectoryRoleDefinitionClient + id := stable.NewRoleManagementDirectoryRoleDefinitionID(state.ID) - role, status, err := client.Get(ctx, state.ID, odata.Query{}) + resp, err := client.GetDirectoryRoleDefinition(ctx, id, directoryroledefinition.DefaultGetDirectoryRoleDefinitionOperationOptions()) if err != nil { - if status == http.StatusNotFound { - return nil, fmt.Errorf("Custom Directory Role with ID %q does not exist", state.ID) + if response.WasNotFound(resp.HttpResponse) { + return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve Custom Directory Role with object ID %q: %+v", state.ID, err) + return nil, fmt.Errorf("failed to retrieve %s: %+v", id, err) } - return pointer.To(role.ID() != nil && *role.ID() == state.ID), nil + return pointer.To(true), nil } func (r CustomDirectoryRoleResource) basic(data acceptance.TestData) string { diff --git a/internal/services/directoryroles/directory_role_assignment_resource.go b/internal/services/directoryroles/directory_role_assignment_resource.go index c6ad28179..ad2b0e04c 100644 --- a/internal/services/directoryroles/directory_role_assignment_resource.go +++ b/internal/services/directoryroles/directory_role_assignment_resource.go @@ -8,16 +8,16 @@ import ( "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/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroleassignment" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "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 directoryRoleAssignmentResource() *pluginsdk.Resource { @@ -41,74 +41,74 @@ func directoryRoleAssignmentResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "role_id": { - Description: "The object ID of the directory role for this assignment", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the directory 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, }, "app_scope_id": { - Description: "Identifier of the app-specific scope when the assignment scope is app-specific", - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"app_scope_object_id", "directory_scope_id", "directory_scope_object_id"}, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "Identifier of the app-specific scope when the assignment scope is app-specific", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"app_scope_object_id", "directory_scope_id", "directory_scope_object_id"}, + ValidateFunc: validation.StringIsNotEmpty, }, "app_scope_object_id": { - Deprecated: "`app_scope_object_id` has been renamed to `app_scope_id` and will be removed in version 3.0 or the AzureAD Provider", - Description: "Identifier of the app-specific scope when the assignment scope is app-specific", - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"app_scope_id", "directory_scope_id", "directory_scope_object_id"}, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Deprecated: "`app_scope_object_id` has been renamed to `app_scope_id` and will be removed in version 3.0 or the AzureAD Provider", + Description: "Identifier of the app-specific scope when the assignment scope is app-specific", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"app_scope_id", "directory_scope_id", "directory_scope_object_id"}, + ValidateFunc: validation.StringIsNotEmpty, }, "directory_scope_id": { - Description: "Identifier of the directory object representing the scope of the assignment", - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"app_scope_id", "app_scope_object_id", "directory_scope_object_id"}, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "Identifier of the directory object representing the scope of the assignment", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"app_scope_id", "app_scope_object_id", "directory_scope_object_id"}, + ValidateFunc: validation.StringIsNotEmpty, }, "directory_scope_object_id": { - Description: "Identifier of the directory object representing the scope of the assignment", - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"app_scope_id", "app_scope_object_id", "directory_scope_id"}, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "Identifier of the directory object representing the scope of the assignment", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"app_scope_id", "app_scope_object_id", "directory_scope_id"}, + ValidateFunc: validation.StringIsNotEmpty, }, }, } } func directoryRoleAssignmentResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleAssignmentsClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleAssignmentClient roleId := d.Get("role_id").(string) principalId := d.Get("principal_object_id").(string) - properties := msgraph.UnifiedRoleAssignment{ - PrincipalId: &principalId, - RoleDefinitionId: &roleId, + properties := stable.UnifiedRoleAssignment{ + PrincipalId: nullable.Value(principalId), + RoleDefinitionId: nullable.Value(roleId), } var appScopeId, directoryScopeId string @@ -127,22 +127,25 @@ func directoryRoleAssignmentResourceCreate(ctx context.Context, d *pluginsdk.Res switch { case appScopeId != "": - properties.AppScopeId = &appScopeId + properties.AppScopeId = nullable.Value(appScopeId) case directoryScopeId != "": - properties.DirectoryScopeId = &directoryScopeId + properties.DirectoryScopeId = nullable.Value(directoryScopeId) default: - properties.DirectoryScopeId = pointer.To("/") + properties.DirectoryScopeId = nullable.Value("/") } - assignment, status, err := client.Create(ctx, properties) + resp, err := client.CreateDirectoryRoleAssignment(ctx, properties, directoryroleassignment.DefaultCreateDirectoryRoleAssignmentOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Assigning directory role %q to directory principal %q, received %d with error: %+v", roleId, principalId, status, err) + return tf.ErrorDiagF(err, "Assigning directory role %q to directory principal %q: %v", roleId, principalId, err) } - if assignment == nil || assignment.ID() == nil { + + assignment := resp.Model + if assignment == nil || assignment.Id == nil { return tf.ErrorDiagF(errors.New("returned role assignment ID was nil"), "API Error") } - d.SetId(*assignment.ID()) + id := stable.NewRoleManagementDirectoryRoleAssignmentID(*assignment.Id) + d.SetId(id.UnifiedRoleAssignmentId) // Wait for role assignment to reflect deadline, ok := ctx.Deadline() @@ -157,9 +160,9 @@ func directoryRoleAssignmentResourceCreate(ctx context.Context, d *pluginsdk.Res MinTimeout: 1 * time.Second, ContinuousTargetOccurence: 3, Refresh: func() (interface{}, string, error) { - _, status, err := client.Get(ctx, *assignment.ID(), odata.Query{}) + resp, err := client.GetDirectoryRoleAssignment(ctx, id, directoryroleassignment.DefaultGetDirectoryRoleAssignmentOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return "stub", "Waiting", nil } return nil, "Error", fmt.Errorf("retrieving role assignment") @@ -175,34 +178,41 @@ func directoryRoleAssignmentResourceCreate(ctx context.Context, d *pluginsdk.Res } func directoryRoleAssignmentResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleAssignmentsClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleAssignmentClient + id := stable.NewRoleManagementDirectoryRoleAssignmentID(d.Id()) - id := d.Id() - assignment, status, err := client.Get(ctx, id, odata.Query{}) + resp, err := client.GetDirectoryRoleAssignment(ctx, id, directoryroleassignment.DefaultGetDirectoryRoleAssignmentOperationOptions()) 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) + } + + assignment := resp.Model + if assignment == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "API Error") } - tf.Set(d, "app_scope_id", assignment.AppScopeId) - tf.Set(d, "app_scope_object_id", assignment.AppScopeId) - tf.Set(d, "directory_scope_id", assignment.DirectoryScopeId) - tf.Set(d, "directory_scope_object_id", assignment.DirectoryScopeId) - tf.Set(d, "principal_object_id", assignment.PrincipalId) - tf.Set(d, "role_id", assignment.RoleDefinitionId) + tf.Set(d, "app_scope_id", assignment.AppScopeId.GetOrZero()) + tf.Set(d, "app_scope_object_id", assignment.AppScopeId.GetOrZero()) + tf.Set(d, "directory_scope_id", assignment.DirectoryScopeId.GetOrZero()) + tf.Set(d, "directory_scope_object_id", assignment.DirectoryScopeId.GetOrZero()) + tf.Set(d, "principal_object_id", assignment.PrincipalId.GetOrZero()) + tf.Set(d, "role_id", assignment.RoleDefinitionId.GetOrZero()) return nil } func directoryRoleAssignmentResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleAssignmentsClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleAssignmentClient + id := stable.NewRoleManagementDirectoryRoleAssignmentID(d.Id()) - if _, err := client.Delete(ctx, d.Id()); err != nil { - return tf.ErrorDiagF(err, "Deleting role assignment %q: %+v", d.Id(), err) + if _, err := client.DeleteDirectoryRoleAssignment(ctx, id, directoryroleassignment.DefaultDeleteDirectoryRoleAssignmentOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Deleting %s", id) } + return nil } diff --git a/internal/services/directoryroles/directory_role_assignment_resource_test.go b/internal/services/directoryroles/directory_role_assignment_resource_test.go index c030dcc97..1139a59ee 100644 --- a/internal/services/directoryroles/directory_role_assignment_resource_test.go +++ b/internal/services/directoryroles/directory_role_assignment_resource_test.go @@ -6,11 +6,12 @@ package directoryroles_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/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroleassignment" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -145,15 +146,14 @@ func TestAccDirectoryRoleAssignment_multipleUser(t *testing.T) { } func (r DirectoryRoleAssignmentResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.DirectoryRoles.RoleAssignmentsClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.DirectoryRoles.DirectoryRoleAssignmentClient + id := stable.NewRoleManagementDirectoryRoleAssignmentID(state.ID) - if _, status, err := client.Get(ctx, state.ID, odata.Query{}); err != nil { - if status == http.StatusNotFound { + if resp, err := client.GetDirectoryRoleAssignment(ctx, id, directoryroleassignment.DefaultGetDirectoryRoleAssignmentOperationOptions()); 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/directoryroles/directory_role_eligibility_schedule_request_resource.go b/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource.go index 7c67ff15a..117d82fa6 100644 --- a/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource.go +++ b/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource.go @@ -12,14 +12,17 @@ import ( "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/rolemanagement/stable/directoryroleeligibilityschedulerequest" + "github.com/hashicorp/go-azure-sdk/sdk/nullable" "github.com/hashicorp/go-azure-sdk/sdk/odata" "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" ) func directoryRoleEligibilityScheduleRequestResource() *pluginsdk.Resource { @@ -43,85 +46,93 @@ func directoryRoleEligibilityScheduleRequestResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "role_definition_id": { - Description: "The object ID of the directory role for this role eligibility schedule request", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the directory role for this role eligibility schedule request", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, "principal_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, }, "directory_scope_id": { - Description: "Identifier of the directory object representing the scope of the role eligibility schedule request", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "Identifier of the directory object representing the scope of the role eligibility schedule request", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, "justification": { - Description: "Justification for why the role is assigned", - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + Description: "Justification for why the role is assigned", + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, }, } } func directoryRoleEligibilityScheduleRequestResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleEligibilityScheduleRequestClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleEligibilityScheduleRequestClient roleDefinitionId := d.Get("role_definition_id").(string) principalId := d.Get("principal_id").(string) justification := d.Get("justification").(string) directoryScopeId := d.Get("directory_scope_id").(string) - now := time.Now() - properties := msgraph.UnifiedRoleEligibilityScheduleRequest{ - Action: pointer.To(msgraph.UnifiedRoleScheduleRequestActionAdminAssign), - RoleDefinitionId: &roleDefinitionId, - PrincipalId: &principalId, - Justification: &justification, - DirectoryScopeId: &directoryScopeId, - ScheduleInfo: &msgraph.RequestSchedule{ - StartDateTime: &now, - Expiration: &msgraph.ExpirationPattern{ - Type: pointer.To(msgraph.ExpirationPatternTypeNoExpiration), + properties := stable.UnifiedRoleEligibilityScheduleRequest{ + Action: pointer.To(stable.UnifiedRoleScheduleRequestActions_AdminAssign), + RoleDefinitionId: nullable.Value(roleDefinitionId), + PrincipalId: nullable.Value(principalId), + Justification: nullable.Value(justification), + DirectoryScopeId: nullable.Value(directoryScopeId), + ScheduleInfo: &stable.RequestSchedule{ + StartDateTime: nullable.Value(time.Now().Format(time.RFC3339)), + Expiration: &stable.ExpirationPattern{ + Type: pointer.To(stable.ExpirationPatternType_NoExpiration), }, }, } - roleEligibilityScheduleRequest, status, err := client.Create(ctx, properties) + options := directoryroleeligibilityschedulerequest.CreateDirectoryRoleEligibilityScheduleRequestOperationOptions{ + RetryFunc: func(resp *http.Response, o *odata.OData) (bool, error) { + if response.WasNotFound(resp) && o.Error != nil { + return o.Error.Match("RoleNotFound") || o.Error.Match("SubjectNotFound"), nil + } + return false, nil + }, + } + + resp, err := client.CreateDirectoryRoleEligibilityScheduleRequest(ctx, properties, options) if err != nil { - return tf.ErrorDiagF(err, "Eligibility schedule request for role %q to principal %q, received %d with error: %+v", roleDefinitionId, principalId, status, err) + return tf.ErrorDiagF(err, "Creating eligibility schedule request for role %q to principal %q: %+v", roleDefinitionId, principalId, err) } - if roleEligibilityScheduleRequest == nil || roleEligibilityScheduleRequest.ID == nil { + + roleEligibilityScheduleRequest := resp.Model + if roleEligibilityScheduleRequest == nil || roleEligibilityScheduleRequest.Id == nil { return tf.ErrorDiagF(errors.New("returned role roleEligibilityScheduleRequest ID was nil"), "API Error") } - d.SetId(*roleEligibilityScheduleRequest.ID) - - if err := helpers.WaitForUpdate(ctx, func(ctx context.Context) (*bool, error) { - defer func() { client.BaseClient.DisableRetries = false }() - client.BaseClient.DisableRetries = true + id := stable.NewRoleManagementDirectoryRoleEligibilityScheduleRequestID(*roleEligibilityScheduleRequest.Id) + d.SetId(id.UnifiedRoleEligibilityScheduleRequestId) - resr, status, err := client.Get(ctx, *roleEligibilityScheduleRequest.ID, odata.Query{}) + if err = consistency.WaitForUpdate(ctx, func(ctx context.Context) (*bool, error) { + resp, err := client.GetDirectoryRoleEligibilityScheduleRequest(ctx, id, directoryroleeligibilityschedulerequest.DefaultGetDirectoryRoleEligibilityScheduleRequestOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } return nil, err } - return pointer.To(resr != nil), nil + return pointer.To(resp.Model != nil), nil }); err != nil { return tf.ErrorDiagF(err, "Waiting for role eligibility schedule request for %q to be created for directory role %q", principalId, roleDefinitionId) } @@ -130,40 +141,57 @@ func directoryRoleEligibilityScheduleRequestResourceCreate(ctx context.Context, } func directoryRoleEligibilityScheduleRequestResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleEligibilityScheduleRequestClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleEligibilityScheduleRequestClient + id := stable.NewRoleManagementDirectoryRoleEligibilityScheduleRequestID(d.Id()) - id := d.Id() - roleEligibilityScheduleRequest, status, err := client.Get(ctx, id, odata.Query{}) + resp, err := client.GetDirectoryRoleEligibilityScheduleRequest(ctx, id, directoryroleeligibilityschedulerequest.DefaultGetDirectoryRoleEligibilityScheduleRequestOperationOptions()) if err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] roleEligibilityScheduleRequest 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 roleEligibilityScheduleRequest %q", id) + return tf.ErrorDiagF(err, "Retrieving %s", id) } - tf.Set(d, "role_definition_id", roleEligibilityScheduleRequest.RoleDefinitionId) - tf.Set(d, "principal_id", roleEligibilityScheduleRequest.PrincipalId) - tf.Set(d, "justification", roleEligibilityScheduleRequest.Justification) - tf.Set(d, "directory_scope_id", roleEligibilityScheduleRequest.DirectoryScopeId) + roleEligibilityScheduleRequest := resp.Model + if roleEligibilityScheduleRequest == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "API Error") + } + + tf.Set(d, "role_definition_id", roleEligibilityScheduleRequest.RoleDefinitionId.GetOrZero()) + tf.Set(d, "principal_id", roleEligibilityScheduleRequest.PrincipalId.GetOrZero()) + tf.Set(d, "justification", roleEligibilityScheduleRequest.Justification.GetOrZero()) + tf.Set(d, "directory_scope_id", roleEligibilityScheduleRequest.DirectoryScopeId.GetOrZero()) return nil } func directoryRoleEligibilityScheduleRequestResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.RoleEligibilityScheduleRequestClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleEligibilityScheduleRequestClient + id := stable.NewRoleManagementDirectoryRoleEligibilityScheduleRequestID(d.Id()) - id := d.Id() - roleEligibilityScheduleRequest, _, err := client.Get(ctx, id, odata.Query{}) + resp, err := client.GetDirectoryRoleEligibilityScheduleRequest(ctx, id, directoryroleeligibilityschedulerequest.DefaultGetDirectoryRoleEligibilityScheduleRequestOperationOptions()) if err != nil { - return tf.ErrorDiagF(err, "Retrieving roleEligibilityScheduleRequest %q", id) + return tf.ErrorDiagF(err, "Retrieving %s", id) + } + + roleEligibilityScheduleRequest := resp.Model + if roleEligibilityScheduleRequest == nil { + return tf.ErrorDiagF(errors.New("model was nil"), "API Error") } - roleEligibilityScheduleRequest.Action = pointer.To(msgraph.UnifiedRoleScheduleRequestActionAdminRemove) + properties := stable.UnifiedRoleEligibilityScheduleRequest{ + Action: pointer.To(stable.UnifiedRoleScheduleRequestActions_AdminRemove), + RoleDefinitionId: roleEligibilityScheduleRequest.RoleDefinitionId, + PrincipalId: roleEligibilityScheduleRequest.PrincipalId, + Justification: roleEligibilityScheduleRequest.Justification, + DirectoryScopeId: roleEligibilityScheduleRequest.DirectoryScopeId, + } - if _, _, err := client.Create(ctx, *roleEligibilityScheduleRequest); err != nil { - return tf.ErrorDiagF(err, "Deleting role eligibility schedule request %q: %+v", d.Id(), err) + if _, err = client.CreateDirectoryRoleEligibilityScheduleRequest(ctx, properties, directoryroleeligibilityschedulerequest.DefaultCreateDirectoryRoleEligibilityScheduleRequestOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Removing role eligibility schedule request %q: %+v", d.Id(), err) } + return nil } diff --git a/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource_test.go b/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource_test.go index e8627e7ca..7a9a1012c 100644 --- a/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource_test.go +++ b/internal/services/directoryroles/directory_role_eligibility_schedule_request_resource_test.go @@ -6,11 +6,12 @@ package directoryroles_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/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/rolemanagement/stable/directoryroleeligibilityschedulerequest" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -23,7 +24,7 @@ func TestAccRoleEligibilityScheduleRequest_builtin(t *testing.T) { data := acceptance.BuildTestData(t, "azuread_directory_role_eligibility_schedule_request", "test") r := RoleEligibilityScheduleRequestResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ + data.ResourceTestIgnoreDangling(t, r, []acceptance.TestStep{ { Config: r.builtin(data), Check: acceptance.ComposeTestCheckFunc( @@ -37,7 +38,7 @@ func TestAccRoleEligibilityScheduleRequest_custom(t *testing.T) { data := acceptance.BuildTestData(t, "azuread_directory_role_eligibility_schedule_request", "test") r := RoleEligibilityScheduleRequestResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ + data.ResourceTestIgnoreDangling(t, r, []acceptance.TestStep{ { Config: r.custom(data), Check: acceptance.ComposeTestCheckFunc( @@ -48,18 +49,18 @@ func TestAccRoleEligibilityScheduleRequest_custom(t *testing.T) { } func (r RoleEligibilityScheduleRequestResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.DirectoryRoles.RoleEligibilityScheduleRequestClient + client := clients.DirectoryRoles.DirectoryRoleEligibilityScheduleRequestClient + id := stable.NewRoleManagementDirectoryRoleEligibilityScheduleRequestID(state.ID) - resr, status, err := client.Get(ctx, state.ID, odata.Query{}) + resp, err := client.GetDirectoryRoleEligibilityScheduleRequest(ctx, id, directoryroleeligibilityschedulerequest.DefaultGetDirectoryRoleEligibilityScheduleRequestOperationOptions()) if err != nil { - fmt.Printf("%s, %v\n", err.Error(), status) - if status == http.StatusNotFound { - return nil, fmt.Errorf("Role Eligibility Schedule Request with ID %q does not exist", state.ID) + if response.WasNotFound(resp.HttpResponse) { + return pointer.To(false), nil } - return nil, fmt.Errorf("failed to retrieve Role Eligibility Schedule Request with object ID %q: %+v", state.ID, err) + return nil, fmt.Errorf("failed to retrieve %s: %+v", id, err) } - return pointer.To(resr.ID != nil && *resr.ID == state.ID), nil + return pointer.To(true), nil } func (r RoleEligibilityScheduleRequestResource) builtin(data acceptance.TestData) string { diff --git a/internal/services/directoryroles/directory_role_member_resource.go b/internal/services/directoryroles/directory_role_member_resource.go index b6b2e89fb..4d781e4cf 100644 --- a/internal/services/directoryroles/directory_role_member_resource.go +++ b/internal/services/directoryroles/directory_role_member_resource.go @@ -5,25 +5,23 @@ package directoryroles 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/stable" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroles/stable/directoryrole" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroles/stable/member" "github.com/hashicorp/terraform-provider-azuread/internal/clients" - "github.com/hashicorp/terraform-provider-azuread/internal/helpers" + "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/directoryroles/parse" - "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" ) -const directoryRoleMemberResourceName = "azuread_directory_role_member" - func directoryRoleMemberResource() *pluginsdk.Resource { return &pluginsdk.Resource{ CreateContext: directoryRoleMemberResourceCreate, @@ -45,88 +43,67 @@ func directoryRoleMemberResource() *pluginsdk.Resource { Schema: map[string]*pluginsdk.Schema{ "role_object_id": { - Description: "The object ID of the directory role", - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the directory role", + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, "member_object_id": { - Description: "The object ID of the member", - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the member", + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, }, }, } } func directoryRoleMemberResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.DirectoryRolesClient - directoryObjectsClient := meta.(*clients.Client).DirectoryRoles.DirectoryObjectsClient - tenantId := meta.(*clients.Client).TenantID + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleMemberClient + directoryRoleClient := meta.(*clients.Client).DirectoryRoles.DirectoryRoleClient id := parse.NewDirectoryRoleMemberID(d.Get("role_object_id").(string), d.Get("member_object_id").(string)) + directoryRoleId := stable.NewDirectoryRoleID(id.DirectoryRoleId) tf.LockByName(directoryRoleMemberResourceName, id.DirectoryRoleId) defer tf.UnlockByName(directoryRoleMemberResourceName, id.DirectoryRoleId) - role, status, err := client.Get(ctx, id.DirectoryRoleId) + resp, err := directoryRoleClient.GetDirectoryRole(ctx, directoryRoleId, directoryrole.DefaultGetDirectoryRoleOperationOptions()) if err != nil { - if status == http.StatusNotFound { - return tf.ErrorDiagPathF(nil, "object_id", "Directory role with object ID %q was not found", id.DirectoryRoleId) + if response.WasNotFound(resp.HttpResponse) { + return tf.ErrorDiagPathF(nil, "object_id", "%s was not found", directoryRoleId) } - return tf.ErrorDiagPathF(err, "object_id", "Retrieving directory role with object ID: %q", id.DirectoryRoleId) + return tf.ErrorDiagPathF(err, "object_id", "Retrieving %s", directoryRoleId) } - if _, status, err = client.GetMember(ctx, id.DirectoryRoleId, id.MemberId); err == nil { + if member, err := directoryRoleGetMember(ctx, client, id.DirectoryRoleId, id.MemberId); err != nil { + return tf.ErrorDiagF(err, "Checking for existing membership of member %q for directory role with object ID %q", id.MemberId, id.DirectoryRoleId) + } else if member != nil { return tf.ImportAsExistsDiag("azuread_directory_role_member", id.String()) - } else if status != http.StatusNotFound { - return tf.ErrorDiagF(err, "Checking for existing membership of member %q for directory role with object ID: %q", id.MemberId, id.DirectoryRoleId) } - memberObject, _, err := directoryObjectsClient.Get(ctx, id.MemberId, odata.Query{}) - if err != nil { - return tf.ErrorDiagF(err, "Could not retrieve member principal object %q", id.MemberId) - } - if memberObject == nil { - return tf.ErrorDiagF(errors.New("returned memberObject was nil"), "Could not retrieve member principal object %q", id.MemberId) - } - memberObject.ODataId = (*odata.Id)(pointer.To(fmt.Sprintf("%s/v1.0/%s/directoryObjects/%s", - client.BaseClient.Endpoint, tenantId, id.MemberId))) + memberId := stable.NewDirectoryObjectID(id.MemberId) - role.Members = &msgraph.Members{*memberObject} + addMemberProperties := stable.ReferenceCreate{ + ODataId: pointer.To(client.Client.BaseUri + memberId.ID()), + } - if _, err := client.AddMembers(ctx, role); err != nil { - return tf.ErrorDiagF(err, "Adding role member %q to directory role %q", id.MemberId, id.DirectoryRoleId) + if _, err = client.AddMemberRef(ctx, directoryRoleId, addMemberProperties, member.DefaultAddMemberRefOperationOptions()); err != nil { + return tf.ErrorDiagF(err, "Adding member %q to directory role %q", id.MemberId, id.DirectoryRoleId) } // Wait for role membership to reflect - deadline, ok := ctx.Deadline() - if !ok { - return tf.ErrorDiagF(errors.New("context has no deadline"), "Waiting for role member %q to reflect for directory role %q", id.MemberId, id.DirectoryRoleId) - } - timeout := time.Until(deadline) - _, err = (&pluginsdk.StateChangeConf{ //nolint:staticcheck - Pending: []string{"Waiting"}, - Target: []string{"Done"}, - Timeout: timeout, - MinTimeout: 1 * time.Second, - ContinuousTargetOccurence: 3, - Refresh: func() (interface{}, string, error) { - _, status, err := client.GetMember(ctx, id.DirectoryRoleId, id.MemberId) - if err != nil { - if status == http.StatusNotFound { - return "stub", "Waiting", nil - } - return nil, "Error", fmt.Errorf("retrieving role member") - } - return "stub", "Done", nil - }, - }).WaitForStateContext(ctx) - if err != nil { + if err = consistency.WaitForUpdate(ctx, func(ctx context.Context) (*bool, error) { + if member, err := directoryRoleGetMember(ctx, client, id.DirectoryRoleId, id.MemberId); err != nil { + return nil, fmt.Errorf("retrieving member") + } else if member == nil { + return pointer.To(false), nil + } + return pointer.To(true), nil + }); err != nil { return tf.ErrorDiagF(err, "Waiting for role member %q to reflect for directory role %q", id.MemberId, id.DirectoryRoleId) } @@ -136,20 +113,19 @@ func directoryRoleMemberResourceCreate(ctx context.Context, d *pluginsdk.Resourc } func directoryRoleMemberResourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.DirectoryRolesClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleMemberClient id, err := parse.DirectoryRoleMemberID(d.Id()) if err != nil { return tf.ErrorDiagPathF(err, "id", "Parsing Directory Role Member ID %q", d.Id()) } - if _, status, err := client.GetMember(ctx, id.DirectoryRoleId, id.MemberId); err != nil { - if status == http.StatusNotFound { - log.Printf("[DEBUG] Member with ID %q was not found in directory role %q - removing from state", id.MemberId, id.DirectoryRoleId) - d.SetId("") - return nil - } - return tf.ErrorDiagF(err, "Retrieving role member %q for directory role with object ID: %q", id.MemberId, id.DirectoryRoleId) + if member, err := directoryRoleGetMember(ctx, client, id.DirectoryRoleId, id.MemberId); err != nil { + return tf.ErrorDiagF(err, "Retrieving member %q for directory role with object ID: %q", id.MemberId, id.DirectoryRoleId) + } else if member == nil { + log.Printf("[DEBUG] Member with ID %q was not found in directory role %q - removing from state", id.MemberId, id.DirectoryRoleId) + d.SetId("") + return nil } tf.Set(d, "role_object_id", id.DirectoryRoleId) @@ -159,7 +135,7 @@ func directoryRoleMemberResourceRead(ctx context.Context, d *pluginsdk.ResourceD } func directoryRoleMemberResourceDelete(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.DirectoryRolesClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleMemberClient id, err := parse.DirectoryRoleMemberID(d.Id()) if err != nil { @@ -169,19 +145,16 @@ func directoryRoleMemberResourceDelete(ctx context.Context, d *pluginsdk.Resourc tf.LockByName(directoryRoleMemberResourceName, id.DirectoryRoleId) defer tf.UnlockByName(directoryRoleMemberResourceName, id.DirectoryRoleId) - if _, err := client.RemoveMembers(ctx, id.DirectoryRoleId, &[]string{id.MemberId}); err != nil { + if _, err = client.RemoveMemberRef(ctx, stable.NewDirectoryRoleIdMemberID(id.DirectoryRoleId, id.MemberId), member.DefaultRemoveMemberRefOperationOptions()); err != nil { return tf.ErrorDiagF(err, "Removing member %q from directory role with object ID: %q", id.MemberId, id.DirectoryRoleId) } // Wait for membership link 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.GetMember(ctx, id.DirectoryRoleId, id.MemberId); err != nil { - if status == http.StatusNotFound { - return pointer.To(false), nil - } + if err = consistency.WaitForDeletion(ctx, func(ctx context.Context) (*bool, error) { + if member, err := directoryRoleGetMember(ctx, client, id.DirectoryRoleId, id.MemberId); err != nil { return nil, err + } else if member == nil { + return pointer.To(false), nil } return pointer.To(true), nil }); err != nil { diff --git a/internal/services/directoryroles/directory_role_member_resource_test.go b/internal/services/directoryroles/directory_role_member_resource_test.go index 44f5177b0..e0a8971a1 100644 --- a/internal/services/directoryroles/directory_role_member_resource_test.go +++ b/internal/services/directoryroles/directory_role_member_resource_test.go @@ -6,10 +6,12 @@ package directoryroles_test import ( "context" "fmt" - "net/http" "testing" "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/directoryroles/stable/member" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -109,23 +111,33 @@ func TestAccDirectoryRoleMember_requiresImport(t *testing.T) { } func (r DirectoryRoleMemberResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.DirectoryRoles.DirectoryRolesClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.DirectoryRoles.DirectoryRoleMemberClient id, err := parse.DirectoryRoleMemberID(state.ID) if err != nil { return nil, fmt.Errorf("parsing Directory Role Member ID: %v", err) } - if _, status, err := client.GetMember(ctx, id.DirectoryRoleId, id.MemberId); err != nil { - if status == http.StatusNotFound { + options := member.ListMembersOperationOptions{ + Filter: pointer.To(fmt.Sprintf("id eq '%s'", id.MemberId)), + } + resp, err := client.ListMembers(ctx, stable.NewDirectoryRoleID(id.DirectoryRoleId), options) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } return nil, fmt.Errorf("failed to retrieve directory role member %q (role ID: %q): %+v", id.MemberId, id.DirectoryRoleId, err) } - return pointer.To(true), nil + if resp.Model != nil { + for _, member := range *resp.Model { + if pointer.From(member.DirectoryObject().Id) == id.MemberId { + return pointer.To(true), nil + } + } + } + + return pointer.To(false), nil } func (DirectoryRoleMemberResource) templateThreeUsers(data acceptance.TestData) string { diff --git a/internal/services/directoryroles/directory_role_resource.go b/internal/services/directoryroles/directory_role_resource.go index ea39495fb..e1d9cde9f 100644 --- a/internal/services/directoryroles/directory_role_resource.go +++ b/internal/services/directoryroles/directory_role_resource.go @@ -6,17 +6,21 @@ package directoryroles import ( "context" "fmt" - "net/http" "strings" "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/directoryroles/stable/directoryrole" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroletemplates/stable/directoryroletemplate" + "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/helpers/tf/suppress" + "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/services/directoryroles/parse" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/suppress" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/validation" - "github.com/manicminer/hamilton/msgraph" ) type DirectoryRoleModel struct { @@ -52,17 +56,17 @@ func (r DirectoryRoleResource) Arguments() map[string]*pluginsdk.Schema { ForceNew: true, ExactlyOneOf: []string{"display_name", "template_id"}, DiffSuppressFunc: suppress.CaseDifference, - ValidateDiagFunc: validation.ValidateDiag(validation.StringIsNotEmpty), + ValidateFunc: validation.StringIsNotEmpty, }, "template_id": { - Description: "The object ID of the template associated with the directory role", - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ExactlyOneOf: []string{"display_name", "template_id"}, - ValidateDiagFunc: validation.ValidateDiag(validation.IsUUID), + Description: "The object ID of the template associated with the directory role", + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ExactlyOneOf: []string{"display_name", "template_id"}, + ValidateFunc: validation.IsUUID, }, } } @@ -87,11 +91,8 @@ func (r DirectoryRoleResource) Create() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.DirectoryRoles.DirectoryRolesClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() - - directoryRoleTemplatesClient := metadata.Client.DirectoryRoles.DirectoryRoleTemplatesClient + client := metadata.Client.DirectoryRoles.DirectoryRoleClient + templateClient := metadata.Client.DirectoryRoles.DirectoryRoleTemplateClient var model DirectoryRoleModel if err := metadata.Decode(&model); err != nil { @@ -99,74 +100,80 @@ func (r DirectoryRoleResource) Create() sdk.ResourceFunc { } // First we find the directory role template - var template *msgraph.DirectoryRoleTemplate + var template *stable.DirectoryRoleTemplate if model.DisplayName != "" { - templates, _, err := directoryRoleTemplatesClient.List(ctx) + resp, err := templateClient.ListDirectoryRoleTemplates(ctx, directoryroletemplate.DefaultListDirectoryRoleTemplatesOperationOptions()) if err != nil { return fmt.Errorf("listing directory role templates: %+v", err) } + + templates := resp.Model if templates == nil { return fmt.Errorf("listing directory role templates: API error, result was nil") } for _, t := range *templates { - if strings.EqualFold(model.DisplayName, pointer.From(t.DisplayName)) { + if strings.EqualFold(model.DisplayName, t.DisplayName.GetOrZero()) { template = &t break } } - - if template == nil { - return fmt.Errorf("no directory role template found with display name: %q", model.DisplayName) - } } else if model.TemplateId != "" { - var status int - var err error - template, status, err = directoryRoleTemplatesClient.Get(ctx, model.TemplateId) + resp, err := templateClient.GetDirectoryRoleTemplate(ctx, stable.NewDirectoryRoleTemplateID(model.TemplateId), directoryroletemplate.DefaultGetDirectoryRoleTemplateOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return fmt.Errorf("no directory role template with object ID %q was found", model.TemplateId) } return fmt.Errorf("retrieving directory role template with object ID %q: %+v", model.TemplateId, err) } - if template == nil { - return fmt.Errorf("retrieving directory role template with object ID %q: API error, result was nil", model.TemplateId) - } + template = resp.Model } if template == nil { return fmt.Errorf("no directory role template found") } - - if template.ID == nil { + if template.Id == nil { return fmt.Errorf("received directory role template with nil ID (API error)") } - templateId := *template.ID + templateId := *template.Id + var directoryRole *stable.DirectoryRole // Now look for the directory role created from that template - directoryRole, status, err := client.GetByTemplateId(ctx, templateId) - if err != nil { - if status == http.StatusNotFound { + options := directoryrole.ListDirectoryRolesOperationOptions{ + Filter: pointer.To(fmt.Sprintf("roleTemplateId eq '%s'", odata.EscapeSingleQuote(templateId))), + } + + if resp, err := client.ListDirectoryRoles(ctx, options); err != nil { + if response.WasNotFound(resp.HttpResponse) { // Directory role was not found, so activate it - directoryRole, _, err = client.Activate(ctx, templateId) - if err != nil { - return fmt.Errorf("activating directory role for template ID %q: %+v", templateId, err) + properties := stable.DirectoryRole{ + RoleTemplateId: nullable.Value(templateId), + } + if resp, err := client.CreateDirectoryRole(ctx, properties, directoryrole.DefaultCreateDirectoryRoleOperationOptions()); err != nil { + return fmt.Errorf("activating directory role for template ID %q: %v", templateId, err) + } else { + directoryRole = resp.Model } } else { - return fmt.Errorf("retrieving directory role with template ID %q: %+v", templateId, err) + return fmt.Errorf("retrieving directory role with template ID %q: %v", templateId, err) + } + } else if resp.Model != nil { + for _, role := range *resp.Model { + directoryRole = &role + break } } if directoryRole == nil { return fmt.Errorf("retrieving directory role for template ID %q: result was nil", templateId) } - if directoryRole.ID() == nil { + if directoryRole.Id == nil { return fmt.Errorf("retrieving directory role for template ID %q: ID was nil (API error)", templateId) } - id := parse.NewDirectoryRoleID(*directoryRole.ID()) + id := parse.NewDirectoryRoleID(*directoryRole.Id) metadata.SetID(id) return nil @@ -178,9 +185,7 @@ func (r DirectoryRoleResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { - client := metadata.Client.DirectoryRoles.DirectoryRolesClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := metadata.Client.DirectoryRoles.DirectoryRoleClient id := parse.NewDirectoryRoleID(metadata.ResourceData.Id()) @@ -189,21 +194,23 @@ func (r DirectoryRoleResource) Read() sdk.ResourceFunc { return fmt.Errorf("decoding: %+v", err) } - directoryRole, status, err := client.Get(ctx, id.ID()) + resp, err := client.GetDirectoryRole(ctx, stable.NewDirectoryRoleID(id.DirectoryRoleId), directoryrole.DefaultGetDirectoryRoleOperationOptions()) if err != nil { - if status == http.StatusNotFound { + if response.WasNotFound(resp.HttpResponse) { return metadata.MarkAsGone(id) } return fmt.Errorf("retrieving %s: %+v", id, err) } + + directoryRole := resp.Model if directoryRole == nil { return fmt.Errorf("retrieving %s: API error, result was nil", id) } - state.Description = pointer.From(directoryRole.Description) - state.DisplayName = pointer.From(directoryRole.DisplayName) - state.ObjectId = pointer.From(directoryRole.ID()) - state.TemplateId = pointer.From(directoryRole.RoleTemplateId) + state.Description = directoryRole.Description.GetOrZero() + state.DisplayName = directoryRole.DisplayName.GetOrZero() + state.ObjectId = pointer.From(directoryRole.Id) + state.TemplateId = directoryRole.RoleTemplateId.GetOrZero() return metadata.Encode(&state) }, diff --git a/internal/services/directoryroles/directory_role_resource_test.go b/internal/services/directoryroles/directory_role_resource_test.go index 301711ca9..4427bdae5 100644 --- a/internal/services/directoryroles/directory_role_resource_test.go +++ b/internal/services/directoryroles/directory_role_resource_test.go @@ -6,10 +6,12 @@ package directoryroles_test import ( "context" "fmt" - "net/http" "testing" "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/directoryroles/stable/directoryrole" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance" "github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check" @@ -53,18 +55,18 @@ func TestAccDirectoryRole_byTemplateId(t *testing.T) { } func (r DirectoryRoleResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { - client := clients.DirectoryRoles.DirectoryRolesClient - client.BaseClient.DisableRetries = true - defer func() { client.BaseClient.DisableRetries = false }() + client := clients.DirectoryRoles.DirectoryRoleClient + id := stable.NewDirectoryRoleID(state.ID) - role, status, err := client.Get(ctx, state.ID) + resp, err := client.GetDirectoryRole(ctx, id, directoryrole.DefaultGetDirectoryRoleOperationOptions()) if err != nil { - if status == http.StatusNotFound { - return nil, fmt.Errorf("Directory Role with object ID %q does not exist", state.ID) + if response.WasNotFound(resp.HttpResponse) { + return pointer.To(false), nil } return nil, fmt.Errorf("failed to retrieve Directory Role with object ID %q: %+v", state.ID, err) } - return pointer.To(role.ID() != nil && *role.ID() == state.ID), nil + + return pointer.To(true), nil } func (DirectoryRoleResource) byDisplayName(_ acceptance.TestData) string { diff --git a/internal/services/directoryroles/directory_role_templates_data_source.go b/internal/services/directoryroles/directory_role_templates_data_source.go index 2a4db0c9e..60d436c28 100644 --- a/internal/services/directoryroles/directory_role_templates_data_source.go +++ b/internal/services/directoryroles/directory_role_templates_data_source.go @@ -11,9 +11,11 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroletemplates/stable/directoryroletemplate" "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/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" ) func directoryRoleTemplatesDataSource() *pluginsdk.Resource { @@ -65,12 +67,14 @@ func directoryRoleTemplatesDataSource() *pluginsdk.Resource { } func directoryRoleTemplatesDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleTemplatesClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleTemplateClient - directoryRoleTemplates, _, err := client.List(ctx) + resp, err := client.ListDirectoryRoleTemplates(ctx, directoryroletemplate.DefaultListDirectoryRoleTemplatesOperationOptions()) if err != nil { return tf.ErrorDiagF(err, "Could not retrieve role templates") } + + directoryRoleTemplates := resp.Model if directoryRoleTemplates == nil { return tf.ErrorDiagF(errors.New("API error: nil directoryRoleTemplates were returned"), "Retrieving all directory role templates") } @@ -80,16 +84,17 @@ func directoryRoleTemplatesDataSourceRead(ctx context.Context, d *pluginsdk.Reso for _, r := range *directoryRoleTemplates { // Skip the implicit "Users" role as it's non-assignable - if r.ID == nil || r.DisplayName == nil || *r.DisplayName == "User" { + if r.Id == nil || r.DisplayName == nil || r.DisplayName.GetOrZero() == "User" { continue } - objectIds = append(objectIds, *r.ID) + objectIds = append(objectIds, *r.Id) template := make(map[string]interface{}) - template["description"] = r.Description - template["display_name"] = r.DisplayName - template["object_id"] = r.ID + template["description"] = r.Description.GetOrZero() + template["display_name"] = r.DisplayName.GetOrZero() + template["object_id"] = pointer.From(r.Id) + templateList = append(templateList, template) } diff --git a/internal/services/directoryroles/directory_roles_data_source.go b/internal/services/directoryroles/directory_roles_data_source.go index c2a280192..c8e36c27f 100644 --- a/internal/services/directoryroles/directory_roles_data_source.go +++ b/internal/services/directoryroles/directory_roles_data_source.go @@ -11,9 +11,11 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/microsoft-graph/directoryroles/stable/directoryrole" "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/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" ) func directoryRolesDataSource() *pluginsdk.Resource { @@ -80,12 +82,14 @@ func directoryRolesDataSource() *pluginsdk.Resource { } func directoryRolesDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { - client := meta.(*clients.Client).DirectoryRoles.DirectoryRolesClient + client := meta.(*clients.Client).DirectoryRoles.DirectoryRoleClient - directoryRoles, _, err := client.List(ctx) + resp, err := client.ListDirectoryRoles(ctx, directoryrole.DefaultListDirectoryRolesOperationOptions()) if err != nil { return tf.ErrorDiagF(err, "Could not retrieve roles") } + + directoryRoles := resp.Model if directoryRoles == nil { return tf.ErrorDiagF(errors.New("API error: nil directoryRoles were returned"), "Retrieving all directory roles") } @@ -95,14 +99,15 @@ func directoryRolesDataSourceRead(ctx context.Context, d *pluginsdk.ResourceData roleList := make([]map[string]interface{}, 0) for _, r := range *directoryRoles { - objectIds = append(objectIds, *r.ID()) - templateIds = append(templateIds, *r.RoleTemplateId) + objectIds = append(objectIds, pointer.From(r.Id)) + templateIds = append(templateIds, r.RoleTemplateId.GetOrZero()) role := make(map[string]interface{}) - role["description"] = r.Description - role["display_name"] = r.DisplayName - role["object_id"] = r.ID() - role["template_id"] = r.RoleTemplateId + role["description"] = r.Description.GetOrZero() + role["display_name"] = r.DisplayName.GetOrZero() + role["object_id"] = pointer.From(r.Id) + role["template_id"] = r.RoleTemplateId.GetOrZero() + roleList = append(roleList, role) } diff --git a/internal/services/directoryroles/directoryroles.go b/internal/services/directoryroles/directoryroles.go index 1fd429ac7..e366c07b5 100644 --- a/internal/services/directoryroles/directoryroles.go +++ b/internal/services/directoryroles/directoryroles.go @@ -4,43 +4,65 @@ package directoryroles import ( - "github.com/hashicorp/terraform-provider-azuread/internal/tf" - "github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk" - "github.com/manicminer/hamilton/msgraph" + "context" + "fmt" + + "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/directoryroles/stable/member" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf" + "github.com/hashicorp/terraform-provider-azuread/internal/helpers/tf/pluginsdk" ) -func expandCustomRolePermissions(in []interface{}) *[]msgraph.UnifiedRolePermission { - if in == nil { - return nil +func directoryRoleGetMember(ctx context.Context, client *member.MemberClient, id string, memberId string) (*stable.DirectoryObject, error) { + options := member.ListMembersOperationOptions{ + Filter: pointer.To(fmt.Sprintf("id eq '%s'", memberId)), + } + + resp, err := client.ListMembers(ctx, stable.NewDirectoryRoleID(id), options) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return nil, nil + } else { + return nil, err + } + } + + if resp.Model != nil { + for _, member := range *resp.Model { + if member.DirectoryObject().Id != nil && *member.DirectoryObject().Id == memberId { + return &member, nil + } + } } - result := make([]msgraph.UnifiedRolePermission, 0) + return nil, nil +} + +func expandCustomRolePermissions(in []interface{}) []stable.UnifiedRolePermission { + result := make([]stable.UnifiedRolePermission, 0) for _, permRaw := range in { perm := permRaw.(map[string]interface{}) - var allowedResourceActions *[]string + var allowedResourceActions []string if v, ok := perm["allowed_resource_actions"]; ok { - allowedResourceActions = tf.ExpandStringSlicePtr(v.(*pluginsdk.Set).List()) + allowedResourceActions = tf.ExpandStringSlice(v.(*pluginsdk.Set).List()) } - result = append(result, msgraph.UnifiedRolePermission{ + result = append(result, stable.UnifiedRolePermission{ AllowedResourceActions: allowedResourceActions, }) } - return &result + return result } -func flattenCustomRolePermissions(in *[]msgraph.UnifiedRolePermission) []interface{} { +func flattenCustomRolePermissions(in []stable.UnifiedRolePermission) []interface{} { result := make([]interface{}, 0) - - if in == nil { - return result - } - - for _, perm := range *in { + for _, perm := range in { result = append(result, map[string]interface{}{ - "allowed_resource_actions": tf.FlattenStringSlicePtr(perm.AllowedResourceActions), + "allowed_resource_actions": tf.FlattenStringSlice(perm.AllowedResourceActions), }) } diff --git a/internal/services/directoryroles/parse/directory_role.go b/internal/services/directoryroles/parse/directory_role.go index b09298772..459cea489 100644 --- a/internal/services/directoryroles/parse/directory_role.go +++ b/internal/services/directoryroles/parse/directory_role.go @@ -6,17 +6,17 @@ package parse import "fmt" type DirectoryRoleId struct { - val string + DirectoryRoleId string } func NewDirectoryRoleID(input string) DirectoryRoleId { - return DirectoryRoleId{val: input} + return DirectoryRoleId{DirectoryRoleId: input} } func (id DirectoryRoleId) ID() string { - return id.val + return id.DirectoryRoleId } func (id DirectoryRoleId) String() string { - return fmt.Sprintf("Directory Role (Object ID: %q)", id.val) + return fmt.Sprintf("Directory Role (Object ID: %q)", id.DirectoryRoleId) } diff --git a/internal/services/directoryroles/registration.go b/internal/services/directoryroles/registration.go index e2cf47f45..1bfbac38d 100644 --- a/internal/services/directoryroles/registration.go +++ b/internal/services/directoryroles/registration.go @@ -4,8 +4,8 @@ package directoryroles 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{}