Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_role_definition - swap to go-azure-sdk #24266

Merged
merged 8 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 67 additions & 99 deletions internal/services/authorization/azuresdkhacks/definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,151 +7,119 @@ import (
"context"
"net/http"

"github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2020-04-01-preview/authorization" // nolint: staticcheck
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2018-01-01-preview/roledefinitions"
"github.com/hashicorp/go-azure-sdk/sdk/client"
"github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager"
"github.com/hashicorp/go-azure-sdk/sdk/odata"
)

type RoleDefinitionsWorkaroundClient struct {
sdkClient *authorization.RoleDefinitionsClient
client *resourcemanager.Client
}

func NewRoleDefinitionsWorkaroundClient(client *authorization.RoleDefinitionsClient) RoleDefinitionsWorkaroundClient {
func NewRoleDefinitionsWorkaroundClient(resourcemanagerClient *resourcemanager.Client) RoleDefinitionsWorkaroundClient {
return RoleDefinitionsWorkaroundClient{
sdkClient: client,
client: resourcemanagerClient,
}
}

// CreateOrUpdate creates or updates a role definition.
// Parameters:
// scope - the scope of the role definition.
// roleDefinitionID - the ID of the role definition.
// roleDefinition - the values for the role definition.
func (client RoleDefinitionsWorkaroundClient) CreateOrUpdate(ctx context.Context, scope string, roleDefinitionID string, roleDefinition authorization.RoleDefinition) (result RoleDefinitionUpdateResponse, err error) {
req, err := client.sdkClient.CreateOrUpdatePreparer(ctx, scope, roleDefinitionID, roleDefinition)
// CreateOrUpdate ...
func (c RoleDefinitionsWorkaroundClient) CreateOrUpdate(ctx context.Context, id roledefinitions.ScopedRoleDefinitionId, input RoleDefinition) (result CreateOrUpdateOperationResponse, err error) {
opts := client.RequestOptions{
ContentType: "application/json; charset=utf-8",
ExpectedStatusCodes: []int{
http.StatusCreated,
},
HttpMethod: http.MethodPut,
Path: id.ID(),
}

req, err := c.client.NewRequest(ctx, opts)
if err != nil {
err = autorest.NewErrorWithError(err, "authorization.RoleDefinitionsClient", "CreateOrUpdate", nil, "Failure preparing request")
return
}

resp, err := client.sdkClient.CreateOrUpdateSender(req)
if err != nil {
result.Response = autorest.Response{Response: resp}
err = autorest.NewErrorWithError(err, "authorization.RoleDefinitionsClient", "CreateOrUpdate", resp, "Failure sending request")
if err = req.Marshal(input); err != nil {
return
}

result, err = client.CreateOrUpdateResponder(resp)
var resp *client.Response
resp, err = req.Execute(ctx)
if resp != nil {
result.OData = resp.OData
result.HttpResponse = resp.Response
}
if err != nil {
err = autorest.NewErrorWithError(err, "authorization.RoleDefinitionsClient", "CreateOrUpdate", resp, "Failure responding to request")
return
}

return
}
if err = resp.Unmarshal(&result.Model); err != nil {
return
}

// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always
// closes the http.Response Body.
func (client RoleDefinitionsWorkaroundClient) CreateOrUpdateResponder(resp *http.Response) (result RoleDefinitionUpdateResponse, err error) {
err = autorest.Respond(
resp,
azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
}

// Get get role definition by name (GUID).
// Parameters:
// scope - the scope of the role definition.
// roleDefinitionID - the ID of the role definition.
func (client RoleDefinitionsWorkaroundClient) Get(ctx context.Context, scope string, roleDefinitionID string) (result RoleDefinitionGetResponse, err error) {
req, err := client.sdkClient.GetPreparer(ctx, scope, roleDefinitionID)
func (c RoleDefinitionsWorkaroundClient) Get(ctx context.Context, id roledefinitions.ScopedRoleDefinitionId) (result GetOperationResponse, err error) {
opts := client.RequestOptions{
ContentType: "application/json; charset=utf-8",
ExpectedStatusCodes: []int{
http.StatusOK,
},
HttpMethod: http.MethodGet,
Path: id.ID(),
}

req, err := c.client.NewRequest(ctx, opts)
if err != nil {
err = autorest.NewErrorWithError(err, "authorization.RoleDefinitionsClient", "Get", nil, "Failure preparing request")
return
}

resp, err := client.sdkClient.GetSender(req)
var resp *client.Response
resp, err = req.Execute(ctx)
if resp != nil {
result.OData = resp.OData
result.HttpResponse = resp.Response
}
if err != nil {
result.Response = autorest.Response{Response: resp}
err = autorest.NewErrorWithError(err, "authorization.RoleDefinitionsClient", "Get", resp, "Failure sending request")
return
}

result, err = client.GetResponder(resp)
if err != nil {
err = autorest.NewErrorWithError(err, "authorization.RoleDefinitionsClient", "Get", resp, "Failure responding to request")
if err = resp.Unmarshal(&result.Model); err != nil {
return
}

return
}

// GetResponder handles the response to the Get request. The method always
// closes the http.Response Body.
func (client RoleDefinitionsWorkaroundClient) GetResponder(resp *http.Response) (result RoleDefinitionGetResponse, err error) {
err = autorest.Respond(
resp,
azure.WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&result),
autorest.ByClosing())
result.Response = autorest.Response{Response: resp}
return
type CreateOrUpdateOperationResponse struct {
HttpResponse *http.Response
OData *odata.OData
Model *RoleDefinition
}

// RoleDefinition role definition.
type RoleDefinitionGetResponse struct {
autorest.Response `json:"-"`
// ID - READ-ONLY; The role definition ID.
ID *string `json:"id,omitempty"`
// Name - READ-ONLY; The role definition name.
Name *string `json:"name,omitempty"`
// Type - READ-ONLY; The role definition type.
Type *string `json:"type,omitempty"`
// RoleDefinitionProperties - Role definition properties.
*RoleDefinitionProperties `json:"properties,omitempty"`
type GetOperationResponse struct {
HttpResponse *http.Response
OData *odata.OData
Model *RoleDefinition
}

type RoleDefinitionUpdateResponse struct {
autorest.Response `json:"-"`
// ID - READ-ONLY; The role definition ID.
ID *string `json:"id,omitempty"`
// Name - READ-ONLY; The role definition name.
Name *string `json:"name,omitempty"`
// Type - READ-ONLY; The role definition type.
Type *string `json:"type,omitempty"`
// RoleDefinitionProperties - Role definition properties.
*RoleDefinitionProperties `json:"properties,omitempty"`
type RoleDefinition struct {
Id *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Properties *RoleDefinitionProperties `json:"properties,omitempty"`
Type *string `json:"type,omitempty"`
}

// RoleDefinitionProperties role definition properties.
type RoleDefinitionProperties struct {
// RoleName - The role name.
RoleName *string `json:"roleName,omitempty"`
// Description - The role definition description.
Description *string `json:"description,omitempty"`
// RoleType - The role type.
RoleType *string `json:"type,omitempty"`
// Permissions - Role definition permissions.
Permissions *[]Permission `json:"permissions,omitempty"`
// AssignableScopes - Role definition assignable scopes.
AssignableScopes *[]string `json:"assignableScopes,omitempty"`

AssignableScopes *[]string `json:"assignableScopes,omitempty"`
Description *string `json:"description,omitempty"`
Permissions *[]roledefinitions.Permission `json:"permissions,omitempty"`
RoleName *string `json:"roleName,omitempty"`
Type *string `json:"type,omitempty"`
// not exposed in the sdk
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fields are available in the newer API version which is already used by the Provider - as such I believe this workaround is no longer needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I planed to remove them in another PR, and do the API version upgrade at that PR, how do you think?

CreatedOn *string `json:"createdOn,omitempty"`
UpdatedOn *string `json:"updatedOn,omitempty"`
CreatedBy *string `json:"createdBy,omitempty"`
UpdatedBy *string `json:"updatedBy,omitempty"`
}

// Permission role definition permissions.
type Permission struct {
// Actions - Allowed actions.
Actions *[]string `json:"actions,omitempty"`
// NotActions - Denied actions.
NotActions *[]string `json:"notActions,omitempty"`
// DataActions - Allowed Data actions.
DataActions *[]string `json:"dataActions,omitempty"`
// NotDataActions - Denied Data actions.
NotDataActions *[]string `json:"notDataActions,omitempty"`
}
13 changes: 9 additions & 4 deletions internal/services/authorization/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"fmt"

"github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2020-04-01-preview/authorization" // nolint: staticcheck // nolint: staticcheck
// To swap sdk for `azurerm_role_definition` without changing API version
oldRoleDefinitions "github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2018-01-01-preview/roledefinitions"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleassignmentscheduleinstances"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleassignmentschedulerequests"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleeligibilityscheduleinstances"
Expand All @@ -18,7 +20,7 @@ import (

type Client struct {
RoleAssignmentsClient *authorization.RoleAssignmentsClient
RoleDefinitionsClient *authorization.RoleDefinitionsClient
RoleDefinitionsClient *oldRoleDefinitions.RoleDefinitionsClient
RoleAssignmentScheduleRequestClient *roleassignmentschedulerequests.RoleAssignmentScheduleRequestsClient
RoleAssignmentScheduleInstancesClient *roleassignmentscheduleinstances.RoleAssignmentScheduleInstancesClient
RoleEligibilityScheduleRequestClient *roleeligibilityschedulerequests.RoleEligibilityScheduleRequestsClient
Expand All @@ -31,8 +33,11 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
roleAssignmentsClient := authorization.NewRoleAssignmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&roleAssignmentsClient.Client, o.ResourceManagerAuthorizer)

roleDefinitionsClient := authorization.NewRoleDefinitionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&roleDefinitionsClient.Client, o.ResourceManagerAuthorizer)
roleDefinitionsClient, err := oldRoleDefinitions.NewRoleDefinitionsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("creating roleDefinitionsClient: %+v", err)
}
o.Configure(roleDefinitionsClient.Client, o.Authorizers.ResourceManager)

roleAssignmentScheduleRequestsClient, err := roleassignmentschedulerequests.NewRoleAssignmentScheduleRequestsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
Expand Down Expand Up @@ -73,7 +78,7 @@ func NewClient(o *common.ClientOptions) (*Client, error) {

return &Client{
RoleAssignmentsClient: &roleAssignmentsClient,
RoleDefinitionsClient: &roleDefinitionsClient,
RoleDefinitionsClient: roleDefinitionsClient,
RoleAssignmentScheduleRequestClient: roleAssignmentScheduleRequestsClient,
RoleAssignmentScheduleInstancesClient: roleAssignmentScheduleInstancesClient,
RoleEligibilityScheduleRequestClient: roleEligibilityScheduleRequestClient,
Expand Down
35 changes: 26 additions & 9 deletions internal/services/authorization/role_assignment_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2020-04-01-preview/authorization" // nolint: staticcheck
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2018-01-01-preview/roledefinitions"
"github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-12-01/subscriptions"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
Expand Down Expand Up @@ -163,20 +164,26 @@ func resourceArmRoleAssignmentCreate(d *pluginsdk.ResourceData, meta interface{}

name := d.Get("name").(string)
scope := d.Get("scope").(string)
scopeId, err := commonids.ParseScopeID(scope)
if err != nil {
return fmt.Errorf("parsing %s: %+v", scopeId, err)
}

var roleDefinitionId string
if v, ok := d.GetOk("role_definition_id"); ok {
roleDefinitionId = v.(string)
} else if v, ok := d.GetOk("role_definition_name"); ok {
roleName := v.(string)
roleDefinitions, err := roleDefinitionsClient.List(ctx, scope, fmt.Sprintf("roleName eq '%s'", roleName))
roleDefinitions, err := roleDefinitionsClient.List(ctx, *scopeId, roledefinitions.ListOperationOptions{
Filter: pointer.To(fmt.Sprintf("roleName eq '%s'", roleName)),
})
if err != nil {
return fmt.Errorf("loading Role Definition List: %+v", err)
}
if len(roleDefinitions.Values()) != 1 {
if roleDefinitions.Model == nil || len(*roleDefinitions.Model) != 1 {
return fmt.Errorf("loading Role Definition List: could not find role '%s'", roleName)
}
roleDefinitionId = *roleDefinitions.Values()[0].ID
roleDefinitionId = *(*roleDefinitions.Model)[0].Id
} else {
return fmt.Errorf("Error: either role_definition_id or role_definition_name needs to be set")
}
Expand Down Expand Up @@ -296,15 +303,25 @@ func resourceArmRoleAssignmentRead(d *pluginsdk.ResourceData, meta interface{})
d.Set("condition_version", props.ConditionVersion)

// allows for import when role name is used (also if the role name changes a plan will show a diff)
if roleId := props.RoleDefinitionID; roleId != nil {
roleResp, err := roleDefinitionsClient.GetByID(ctx, *roleId)
if roleDefResourceId := props.RoleDefinitionID; roleDefResourceId != nil {
// Workaround for https://github.com/hashicorp/pandora/issues/3257
// The role definition id returned does not contain scope when the role definition was on tenant level (management group or tenant).
// And adding tenant id as scope will cause 404 response, so just adding a slash to parse that.
if strings.HasPrefix(*roleDefResourceId, "/providers") {
roleDefResourceId = pointer.To(fmt.Sprintf("/%s", *roleDefResourceId))
}
parsedRoleDefId, err := roledefinitions.ParseScopedRoleDefinitionID(*roleDefResourceId)
if err != nil {
return fmt.Errorf("loading Role Definition %q: %+v", *roleId, err)
return fmt.Errorf("parsing %q: %+v", *roleDefResourceId, err)
}

if roleProps := roleResp.RoleDefinitionProperties; roleProps != nil {
d.Set("role_definition_name", roleProps.RoleName)
roleResp, err := roleDefinitionsClient.Get(ctx, *parsedRoleDefId)
if err != nil {
return fmt.Errorf("loading Role Definition %q: %+v", *roleDefResourceId, err)
}
if roleResp.Model != nil && roleResp.Model.Properties != nil {
d.Set("role_definition_name", pointer.From(roleResp.Model.Properties.RoleName))
}

}
}

Expand Down
Loading
Loading