in deployment/managementgroup.go [163:330]
func (mg *HierarchyManagementGroup) generatePolicyAssignmentAdditionalRoleAssignments() error {
// Make a error collection type so we can return them in the error message without stopping the process.
// Upstream code can then decide what to do with them, issue warnings in stead of hard fail, etc.
var errs *PolicyRoleAssignmentErrors
for paName, pa := range mg.policyAssignments {
// we only care about policy assignments that use an identity
if pa.IdentityType() == armpolicy.ResourceIdentityTypeNone {
continue
}
// get the policy definition name using the resource id
policyDefinitionRef, err := pa.ReferencedPolicyDefinitionResourceId()
if err != nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: error getting referenced policy definition type for policy assignment `%s`: %w", paName, err)
}
switch policyDefinitionRef.ResourceType.Type {
case "policyDefinitions":
// check the definition exists in the AlzLib
pd := mg.hierarchy.alzlib.PolicyDefinition(policyDefinitionRef.Name)
if pd == nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: policy definition `%s`, referenced by `%s` not found in AlzLib", policyDefinitionRef.Name, paName)
}
// get the role definition ids from the policy definition and add to the additional role assignment data
rdids, err := pd.NormalizedRoleDefinitionResourceIds()
if err != nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, error getting role definition ids for policy definition `%s`: %w", paName, *pd.Name, err)
}
if len(rdids) == 0 {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, policy definition `%s` has no role definition ids", paName, *pd.Name)
}
for _, rdid := range rdids {
mg.policyRoleAssignments.Add(PolicyRoleAssignment{
Scope: mg.ResourceId(),
RoleDefinitionId: rdid,
AssignmentName: paName,
ManagementGroupId: mg.id,
})
}
// for each parameter with assignPermissions = true
// add the additional role assignment data unless the parameter value is empty
assignPermissionParams, err := pd.AssignPermissionsParameterNames()
if err != nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: error getting assign permissions parameter names for policy definition `%s`: %w", *pd.Name, err)
}
for _, paramName := range assignPermissionParams {
paramIsOptional, err := pd.ParameterIsOptional(paramName)
if err != nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: error getting parameter %s optional status for policy definition `%s`: %w", paramName, *pd.Name, err)
}
paParamVal, err := pa.ParameterValueAsString(paramName)
if err != nil && !paramIsOptional {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: error getting parameter value for parameter `%s` in policy assignment `%s`: %w", paramName, paName, err)
}
// We should assign permissions but the parameter os optional and doesn't have a value in the assignment, so skip.
if err != nil && paramIsOptional {
continue
}
if paParamVal == "" {
continue
}
resId, err := arm.ParseResourceID(paParamVal)
if err != nil {
continue
}
for _, rdid := range rdids {
mg.policyRoleAssignments.Add(PolicyRoleAssignment{
Scope: resId.String(),
RoleDefinitionId: rdid,
AssignmentName: paName,
ManagementGroupId: mg.id,
})
}
}
case "policySetDefinitions":
psd := mg.hierarchy.alzlib.PolicySetDefinition(policyDefinitionRef.Name)
if psd == nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, policy set `%s` not found in AlzLib", paName, policyDefinitionRef.Name)
}
pdRefs := psd.PolicyDefinitionReferences()
if pdRefs == nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, error getting referenced policy definition names for policy set definition %s", paName, *psd.Name)
}
// for each policy definition in the policy set definition
for _, pdRef := range pdRefs {
pdName, err := assets.NameFromResourceId(*pdRef.PolicyDefinitionID)
if err != nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, error getting policy definition name from policy set definition `%s`, with id `%s`: %w", paName, *psd.Name, *pdRef.PolicyDefinitionID, err)
}
pd := mg.hierarchy.alzlib.PolicyDefinition(pdName)
if pd == nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, policy definition `%s`, referenced by `%s` not found in AlzLib", paName, pdName, *psd.Name)
}
// get the role definition ids from the policy definition and add to the additional role assignment data
rdids, err := pd.NormalizedRoleDefinitionResourceIds()
if err != nil {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s`, error getting role definition ids referenced in policy set `%s` for policy definition %s: %w", paName, *psd.Name, pdName, err)
}
for _, rdid := range rdids {
mg.policyRoleAssignments.Add(PolicyRoleAssignment{
Scope: mg.ResourceId(),
RoleDefinitionId: rdid,
AssignmentName: paName,
ManagementGroupId: mg.id,
})
}
// for each parameter with assignPermissions = true
// add the additional scopes to the additional role assignment data
// to do this we have to map the assignment parameter value to the policy definition parameter value.
for paramName, paramVal := range pd.Properties.Parameters {
// If assignPermissions is not set then skip.
if paramVal.Metadata == nil || paramVal.Metadata.AssignPermissions == nil || !*paramVal.Metadata.AssignPermissions {
continue
}
// get the parameter value from the policy reference within the set definition.
if _, ok := pd.Properties.Parameters[paramName]; !ok {
return fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s` for policy set `%s`, parameter `%s` not found in refernced policy definition `%s`", paName, *psd.Name, paramName, *pd.Name)
}
// use goarmfunctions to evaluate the ARM expression in the parameter value in the set definition reference.
scope, err := parseArmFunctionInPolicySetParameter(*pdRef.PolicyDefinitionReferenceID, paramName, &pa.Assignment, &psd.SetDefinition)
if err != nil {
if errs == nil {
errs = NewPolicyRoleAssignmentErrors()
}
errs.Add(NewPolicyRoleAssignmentError(paName, mg.id, paramName, *pdRef.PolicyDefinitionReferenceID, rdids, err))
continue
}
// The value should be a string.
scopeStr, ok := scope.(string)
if !ok {
if errs == nil {
errs = NewPolicyRoleAssignmentErrors()
}
errs.Add(NewPolicyRoleAssignmentError(paName, mg.id, paramName, *pdRef.PolicyDefinitionReferenceID, rdids, fmt.Errorf("ManagementGroup.GeneratePolicyAssignmentAdditionalRoleAssignments: assignment `%s` for policy set `%s`, parameter `%s` value in policy definition `%s` is not a string", paName, *psd.Name, paramName, *pd.Name)))
continue
}
// The value should be an ARM resource ID.
resid, err := arm.ParseResourceID(scopeStr)
if err != nil {
if errs == nil {
errs = NewPolicyRoleAssignmentErrors()
}
errs.Add(NewPolicyRoleAssignmentError(paName, mg.id, paramName, *pdRef.PolicyDefinitionReferenceID, rdids, err))
continue
}
// if we got this far then we can add the role assignments.
for _, rdid := range rdids {
mg.policyRoleAssignments.Add(PolicyRoleAssignment{
Scope: resid.String(),
RoleDefinitionId: rdid,
AssignmentName: paName,
ManagementGroupId: mg.id,
})
}
}
}
}
}
if errs != nil {
return errs
}
return nil
}