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

Conversation

ms-zhenhua
Copy link
Contributor

@ms-zhenhua ms-zhenhua commented Jul 7, 2023

Related Links

#18387
#22178
#18439

Test Result

image

--- PASS: TestAccRoleAssignmentMarketplace_roleName (79.79s)
--- PASS: TestAccRoleAssignmentMarketplace_requiresImport (66.15s)
--- PASS: TestAccRoleAssignmentMarketplace_emptyName (71.63s)
--- PASS: TestAccRoleAssignmentMarketplace_ServicePrincipalWithType (71.94s)
--- PASS: TestAccRoleAssignmentMarketplace_builtin (72.01s)
--- PASS: TestAccRoleAssignmentMarketplace_ServicePrincipalGroup (78.87s)
--- PASS: TestAccRoleAssignmentMarketplace_ServicePrincipal (83.01s)
PASS
ok github.com/hashicorp/terraform-provider-azurerm/internal/services/authorization 249.838s

@ms-zhenhua ms-zhenhua reopened this Jul 7, 2023
Copy link
Member

@stephybun stephybun left a comment

Choose a reason for hiding this comment

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

Thanks for this PR @ms-zhenhua! I made a first pass and this looks mostly good. I'm unsure about the scoped ID parser that we're adding in this PR though and will need some time to discuss this internally.

Other than that just a few minor comments/suggestions/questions left in line.

Comment on lines +65 to +70
"delegated_managed_identity_resource_id": {
Type: pluginsdk.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
},
Copy link
Member

Choose a reason for hiding this comment

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

Could we use commonids.ValidateUserAssignedIdentityID here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably no. According to this issue: this property does not mean the managed identity resource id, but the resource which contains a managed identity.

Comment on lines 119 to 135
if v, ok := metadata.ResourceData.GetOk("role_definition_id"); ok {
roleDefinitionId = v.(string)
} else if v, ok := metadata.ResourceData.GetOk("role_definition_name"); ok {
roleName := v.(string)
roleDefinitions, err := roleDefinitionsClient.List(ctx, commonids.NewScopeID(scope), roledefinitions.ListOperationOptions{Filter: pointer.To(fmt.Sprintf("roleName eq '%s'", roleName))})
if err != nil {
return fmt.Errorf("loading Role Definition List: %+v", err)
}

if roleDefinitions.Model != nil && len(*roleDefinitions.Model) == 1 && (*roleDefinitions.Model)[0].Id != nil {
roleDefinitionId = *(*roleDefinitions.Model)[0].Id
} else {
return fmt.Errorf("loading Role Definition List: failed to find role '%s'", roleName)
}
} else {
return fmt.Errorf("either 'role_definition_id' or 'role_definition_name' needs to be set")
}
Copy link
Member

Choose a reason for hiding this comment

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

This is personal preference but the multiple layers of if/else make this difficult to understand at first glance - could we refactor this to

Suggested change
if v, ok := metadata.ResourceData.GetOk("role_definition_id"); ok {
roleDefinitionId = v.(string)
} else if v, ok := metadata.ResourceData.GetOk("role_definition_name"); ok {
roleName := v.(string)
roleDefinitions, err := roleDefinitionsClient.List(ctx, commonids.NewScopeID(scope), roledefinitions.ListOperationOptions{Filter: pointer.To(fmt.Sprintf("roleName eq '%s'", roleName))})
if err != nil {
return fmt.Errorf("loading Role Definition List: %+v", err)
}
if roleDefinitions.Model != nil && len(*roleDefinitions.Model) == 1 && (*roleDefinitions.Model)[0].Id != nil {
roleDefinitionId = *(*roleDefinitions.Model)[0].Id
} else {
return fmt.Errorf("loading Role Definition List: failed to find role '%s'", roleName)
}
} else {
return fmt.Errorf("either 'role_definition_id' or 'role_definition_name' needs to be set")
}
if v, ok := metadata.ResourceData.GetOk("role_definition_id"); ok {
roleDefinitionId = v.(string)
}
if v, ok := metadata.ResourceData.GetOk("role_definition_name"); ok {
roleName := v.(string)
roleDefinitions, err := roleDefinitionsClient.List(ctx, commonids.NewScopeID(scope), roledefinitions.ListOperationOptions{Filter: pointer.To(fmt.Sprintf("roleName eq '%s'", roleName))})
if err != nil {
return fmt.Errorf("loading Role Definition List for %s: %+v", roleName, err)
}
if roleDefinitions.Model == nil || len(*roleDefinitions.Model) != 1 || (*roleDefinitions.Model)[0].Id == nil {
return fmt.Errorf("loading Role Definition List: failed to find role '%s'", roleName)
}
roleDefinitionId = *(*roleDefinitions.Model)[0].Id
}
if roleDefinitionId == "" {
return fmt.Errorf("either 'role_definition_id' or 'role_definition_name' needs to be set")
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

code updated

Comment on lines 141 to 158
if name == "" {
uuid, err := uuid.GenerateUUID()
if err != nil {
return fmt.Errorf("generating UUID for Role Assignment: %+v", err)
}

name = uuid
}

tenantId := ""
delegatedManagedIdentityResourceID := metadata.ResourceData.Get("delegated_managed_identity_resource_id").(string)
if len(delegatedManagedIdentityResourceID) > 0 {
var err error
tenantId, err = getTenantIdBySubscriptionId(ctx, subscriptionClient, subscriptionId)
if err != nil {
return err
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if name == "" {
uuid, err := uuid.GenerateUUID()
if err != nil {
return fmt.Errorf("generating UUID for Role Assignment: %+v", err)
}
name = uuid
}
tenantId := ""
delegatedManagedIdentityResourceID := metadata.ResourceData.Get("delegated_managed_identity_resource_id").(string)
if len(delegatedManagedIdentityResourceID) > 0 {
var err error
tenantId, err = getTenantIdBySubscriptionId(ctx, subscriptionClient, subscriptionId)
if err != nil {
return err
}
}
var err error
if name == "" {
name, err = uuid.GenerateUUID()
if err != nil {
return fmt.Errorf("generating UUID for Role Assignment: %+v", err)
}
}
tenantId := ""
delegatedManagedIdentityResourceID := metadata.ResourceData.Get("delegated_managed_identity_resource_id").(string)
if len(delegatedManagedIdentityResourceID) > 0 {
tenantId, err = getTenantIdBySubscriptionId(ctx, subscriptionClient, subscriptionId)
if err != nil {
return err
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

code updated

Comment on lines 195 to 197
} else if condition != "" || conditionVersion != "" {
return fmt.Errorf("`condition` and `conditionVersion` should be both set or unset")
}
Copy link
Member

Choose a reason for hiding this comment

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

Won't the RequiredWith in the schema already validate this/prevent this scenario from happening?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, duplicated. Code updated.

resp, err := client.GetById(ctx, commonids.NewScopeID(id.ScopedId.ID()), options)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
log.Printf("[DEBUG] Role Assignment ID %s was not found - removing from state", id)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
log.Printf("[DEBUG] Role Assignment ID %s was not found - removing from state", id)
log.Printf("[DEBUG] %s was not found - removing from state", id)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

code updated

Comment on lines 258 to 260
if props.PrincipalType != nil {
metadata.ResourceData.Set("principal_type", string(*props.PrincipalType))
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if props.PrincipalType != nil {
metadata.ResourceData.Set("principal_type", string(*props.PrincipalType))
}
metadata.ResourceData.Set("principal_type", pointer.From(props.PrincipalType))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

code updated

Comment on lines 6 to 11
Assigns a given Principal (User or Group) to a given Role in Private Azure Marketplace.
---

# azurerm_role_assignment_marketplace

Assigns a given Principal (User or Group) to a given Role in Private Azure Marketplace.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Assigns a given Principal (User or Group) to a given Role in Private Azure Marketplace.
---
# azurerm_role_assignment_marketplace
Assigns a given Principal (User or Group) to a given Role in Private Azure Marketplace.
Assigns a given Principal (User or Group) to a given Role in a Private Azure Marketplace.
---
# azurerm_role_assignment_marketplace
Assigns a given Principal (User or Group) to a given Role in a Private Azure Marketplace.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

doc updated


* `delegated_managed_identity_resource_id` - (Optional) The delegated Azure Resource ID which contains a Managed Identity. Changing this forces a new resource to be created.

~> **NOTE:** this field is only used in cross tenant scenario.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
~> **NOTE:** this field is only used in cross tenant scenario.
~> **NOTE:** This field is only used in cross tenant scenarios.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

doc updated


* `skip_service_principal_aad_check` - (Optional) If the `principal_id` is a newly provisioned `Service Principal` set this value to `true` to skip the `Azure Active Directory` check which may fail due to replication lag. This argument is only valid if the `principal_id` is a `Service Principal` identity. Defaults to `false`. Changing this forces a new resource to be created.

~> **NOTE:** If it is not a `Service Principal` identity it will cause the role assignment to fail.
Copy link
Member

Choose a reason for hiding this comment

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

This note isn't clear on what we're expecting from the user - is this note saying that the principal_id cannot be set to any other type other than a Service Principal? Or should skip_service_principal_aad_check always be set to false if principal_id is something other than a Service Principal?

Suggested change
~> **NOTE:** If it is not a `Service Principal` identity it will cause the role assignment to fail.
~> **NOTE:** If it is not a `Service Principal` identity it will cause the role assignment to fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

doc updated: This field takes effect only when principal_id is a Service Principal identity.

terraform import azurerm_role_assignment_marketplace.example /providers/Microsoft.Marketplace/providers/Microsoft.Authorization/roleAssignments/00000000-0000-0000-0000-000000000000
```

~> **NOTE:** for cross tenant scenario, the format of `resource id` is composed of Azure resource ID and tenantId. for example:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
~> **NOTE:** for cross tenant scenario, the format of `resource id` is composed of Azure resource ID and tenantId. for example:
~> **NOTE:** For cross tenant scenarios, the format of the `resource id` consists of the Azure resource ID and and the tenant ID, for example:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

doc updated

@ms-zhenhua
Copy link
Contributor Author

Hi @stephybun, thank you for your review. I have resolved all comments. Kindly help take another review. Thanks.

@ms-zhenhua ms-zhenhua requested a review from stephybun July 12, 2023 05:04
Comment on lines +114 to +120
func DestructRoleAssignmentId(id string) (string, string) {
parts := strings.Split(id, "|")
if len(parts) == 2 {
return parts[0], parts[1]
}
return id, ""
}
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 (r RoleAssignmentMarketplaceResource) ResourceType() string {
return "azurerm_role_assignment_marketplace"
Copy link
Member

Choose a reason for hiding this comment

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

We have other role assignment resources where the scope comes before the resource name, can we do that here aswell please?

Suggested change
return "azurerm_role_assignment_marketplace"
return "azurerm_marketplace_role_assignment"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

make sense. Code updated.

@ms-zhenhua
Copy link
Contributor Author

Hi @stephybun, thank you for your review. I have renamed the resource. Kindly take another review. Thanks.

Copy link
Member

@stephybun stephybun left a comment

Choose a reason for hiding this comment

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

Thanks @ms-zhenhua LGTM 🥟

@stephybun stephybun merged commit 49bf0dc into hashicorp:main Jul 24, 2023
@github-actions github-actions bot added this to the v3.67.0 milestone Jul 24, 2023
stephybun added a commit that referenced this pull request Jul 24, 2023
Copy link

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unable to create role assignment at resource provider scopeof /providers/Microsoft.Marketplace
2 participants