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

New Resource - azurerm_role_assignment_marketplace #22398

Merged
merged 14 commits into from
Jul 24, 2023
Merged
2 changes: 1 addition & 1 deletion .github/labeler-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ service/attestation:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_attestation_provider((.|\n)*)###'

service/authorization:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(client_config|federated_identity_credential|pim_|role_|user_assigned_identity)((.|\n)*)###'
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(client_config|federated_identity_credential|marketplace_role_assignment|pim_|role_|user_assigned_identity)((.|\n)*)###'

service/automanage:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_automanage_configuration((.|\n)*)###'
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration {
applicationinsights.Registration{},
appservice.Registration{},
arckubernetes.Registration{},
authorization.Registration{},
automanage.Registration{},
automation.Registration{},
authorization.Registration{},
batch.Registration{},
bot.Registration{},
cognitive.Registration{},
Expand Down
18 changes: 18 additions & 0 deletions internal/services/authorization/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"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"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2020-10-01/roleeligibilityschedulerequests"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roleassignments"
"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roledefinitions"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
)

Expand All @@ -21,6 +23,8 @@ type Client struct {
RoleAssignmentScheduleInstancesClient *roleassignmentscheduleinstances.RoleAssignmentScheduleInstancesClient
RoleEligibilityScheduleRequestClient *roleeligibilityschedulerequests.RoleEligibilityScheduleRequestsClient
RoleEligibilityScheduleInstancesClient *roleeligibilityscheduleinstances.RoleEligibilityScheduleInstancesClient
ScopedRoleAssignmentsClient *roleassignments.RoleAssignmentsClient
ScopedRoleDefinitionsClient *roledefinitions.RoleDefinitionsClient
}

func NewClient(o *common.ClientOptions) (*Client, error) {
Expand Down Expand Up @@ -55,12 +59,26 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
}
o.Configure(roleEligibilityScheduleInstancesClient.Client, o.Authorizers.ResourceManager)

scopedRoleAssignmentsClient, err := roleassignments.NewRoleAssignmentsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Role Assignment Client: %+v", err)
}
o.Configure(scopedRoleAssignmentsClient.Client, o.Authorizers.ResourceManager)

scopedRoleDefinitionsClient, err := roledefinitions.NewRoleDefinitionsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Role Definition Client: %+v", err)
}
o.Configure(scopedRoleDefinitionsClient.Client, o.Authorizers.ResourceManager)

return &Client{
RoleAssignmentsClient: &roleAssignmentsClient,
RoleDefinitionsClient: &roleDefinitionsClient,
RoleAssignmentScheduleRequestClient: roleAssignmentScheduleRequestsClient,
RoleAssignmentScheduleInstancesClient: roleAssignmentScheduleInstancesClient,
RoleEligibilityScheduleRequestClient: roleEligibilityScheduleRequestClient,
RoleEligibilityScheduleInstancesClient: roleEligibilityScheduleInstancesClient,
ScopedRoleAssignmentsClient: scopedRoleAssignmentsClient,
ScopedRoleDefinitionsClient: scopedRoleDefinitionsClient,
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package parse

import (
"fmt"
"strings"

"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roleassignments"
)

type ScopedRoleAssignmentId struct {
ScopedId roleassignments.ScopedRoleAssignmentId
TenantId string
}

func NewScopedRoleAssignmentID(scope string, roleAssignmentName string, tenantId string) ScopedRoleAssignmentId {
return ScopedRoleAssignmentId{
ScopedId: roleassignments.NewScopedRoleAssignmentID(scope, roleAssignmentName),
TenantId: tenantId,
}
}

func ScopedRoleAssignmentID(input string) (*ScopedRoleAssignmentId, error) {
azureResourceId, tenantId := DestructRoleAssignmentId(input)
scopedId, err := roleassignments.ParseScopedRoleAssignmentID(azureResourceId)
if err != nil {
return nil, err
}

return &ScopedRoleAssignmentId{ScopedId: *scopedId, TenantId: tenantId}, nil
}

func ValidateScopedRoleAssignmentID(input interface{}, key string) (warnings []string, errors []error) {
v, ok := input.(string)
if !ok {
errors = append(errors, fmt.Errorf("expected %q to be a string", key))
return
}

if _, err := ScopedRoleAssignmentID(v); err != nil {
errors = append(errors, err)
}

return
}

func (id ScopedRoleAssignmentId) ID() string {
return ConstructRoleAssignmentId(id.ScopedId.ID(), id.TenantId)
}

func (id ScopedRoleAssignmentId) String() string {
components := []string{
id.ScopedId.String(),
}

if id.TenantId != "" {
components = append(components, fmt.Sprintf("Tenant ID: %s", id.TenantId))
}

return fmt.Sprintf("Scoped Role Assignment (%s)", strings.Join(components, "\n"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package parse

import (
"testing"

"github.com/hashicorp/go-azure-sdk/resource-manager/authorization/2022-04-01/roleassignments"
)

func TestScopedRoleAssignmentID(t *testing.T) {
testData := []struct {
Input string
Error bool
Expected *ScopedRoleAssignmentId
}{
{
Input: "",
Error: true,
},

{
Input: "/",
Error: true,
},

{
Input: "/providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/",
Error: true,
},

{
Input: "/providers/Microsoft.Subscription/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
Expected: &ScopedRoleAssignmentId{
ScopedId: roleassignments.NewScopedRoleAssignmentID("/providers/Microsoft.Subscription", "23456781-2349-8764-5631-234567890121"),
},
},

{
Input: "/providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
Expected: &ScopedRoleAssignmentId{
ScopedId: roleassignments.NewScopedRoleAssignmentID("/providers/Microsoft.Marketplace", "23456781-2349-8764-5631-234567890121"),
},
},

{
Input: "/providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121|12345678-1234-5678-1234-567890123456",
Expected: &ScopedRoleAssignmentId{
ScopedId: roleassignments.NewScopedRoleAssignmentID("/providers/Microsoft.Marketplace", "23456781-2349-8764-5631-234567890121"),
TenantId: "12345678-1234-5678-1234-567890123456",
},
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Input)

actual, err := ScopedRoleAssignmentID(v.Input)
if err != nil {
if v.Error {
continue
}

t.Fatalf("expected a value but got an error: %+v", err)
}

if v.Error {
t.Fatal("Expect an error but didn't get one")
}

if actual.ScopedId.RoleAssignmentName != v.Expected.ScopedId.RoleAssignmentName {
t.Fatalf("Expected %q but got %q for Role Assignment Name", v.Expected.ScopedId.RoleAssignmentName, actual.ScopedId.RoleAssignmentName)
}

if actual.TenantId != v.Expected.TenantId {
t.Fatalf("Expected %q but got %q for Tenant ID", v.Expected.TenantId, actual.TenantId)
}
}
}

func TestValidateScopedRoleAssignmentID(t *testing.T) {
cases := []struct {
Input string
Valid bool
}{

{
Input: "",
Valid: false,
},

{
Input: "/",
Valid: false,
},

{
Input: "/providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/",
Valid: false,
},

{
Input: "/providers/Microsoft.Subscription/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
Valid: true,
},

{
Input: "/providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121",
Valid: true,
},

{
Input: "/providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/23456781-2349-8764-5631-234567890121|12345678-1234-5678-1234-567890123456",
Valid: true,
},
}
for _, tc := range cases {
t.Logf("[DEBUG] Testing Value %s", tc.Input)
_, errors := ValidateScopedRoleAssignmentID(tc.Input, "test")
valid := len(errors) == 0

if tc.Valid != valid {
t.Fatalf("Expected %t but got %t", tc.Valid, valid)
}
}
}
8 changes: 8 additions & 0 deletions internal/services/authorization/parse/role_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ func ConstructRoleAssignmentId(azureResourceId, tenantId string) string {
return fmt.Sprintf("%s|%s", azureResourceId, tenantId)
}

func DestructRoleAssignmentId(id string) (string, string) {
parts := strings.Split(id, "|")
if len(parts) == 2 {
return parts[0], parts[1]
}
return id, ""
}
Comment on lines +114 to +120
Copy link
Member

Choose a reason for hiding this comment

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

After internal discussion, this is fine for now. But we will want to create a composite ID type in the commonids package in the go-azure-helpers repo that can handle cases like these and will replace this in time. Just as an FYI.


func RoleAssignmentID(input string) (*RoleAssignmentId, error) {
if len(input) == 0 {
return nil, fmt.Errorf("Role Assignment ID is empty string")
Expand Down
1 change: 1 addition & 0 deletions internal/services/authorization/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (r Registration) Resources() []sdk.Resource {
resources := []sdk.Resource{
PimActiveRoleAssignmentResource{},
PimEligibleRoleAssignmentResource{},
RoleAssignmentMarketplaceResource{},
}
return resources
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package authorization

import (
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/authorization/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

const (
MarketplaceScope = "/providers/Microsoft.Marketplace"
)

var _ sdk.Resource = RoleAssignmentMarketplaceResource{}

type RoleAssignmentMarketplaceResource struct {
base roleAssignmentBaseResource
}

func (r RoleAssignmentMarketplaceResource) Arguments() map[string]*pluginsdk.Schema {
return r.base.arguments()
}

func (r RoleAssignmentMarketplaceResource) Attributes() map[string]*pluginsdk.Schema {
return r.base.attributes()
}

func (r RoleAssignmentMarketplaceResource) Create() sdk.ResourceFunc {
return r.base.createFunc(r.ResourceType(), MarketplaceScope)
}

func (r RoleAssignmentMarketplaceResource) Delete() sdk.ResourceFunc {
return r.base.deleteFunc()
}

func (r RoleAssignmentMarketplaceResource) Read() sdk.ResourceFunc {
return r.base.readFunc(MarketplaceScope, true)
}

func (r RoleAssignmentMarketplaceResource) ResourceType() string {
return "azurerm_marketplace_role_assignment"
}

func (r RoleAssignmentMarketplaceResource) ModelObject() interface{} {
return nil
}

func (r RoleAssignmentMarketplaceResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return parse.ValidateScopedRoleAssignmentID
}
Loading