func()

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
}