diff --git a/alzmanagementgroup.go b/alzmanagementgroup.go index c4f3b0c..f8f7d65 100644 --- a/alzmanagementgroup.go +++ b/alzmanagementgroup.go @@ -4,7 +4,6 @@ package alzlib import ( - "context" "encoding/json" "errors" "fmt" @@ -299,84 +298,42 @@ func (alzmg *AlzManagementGroup) update(az *AlzLib, papv PolicyAssignmentsParame return nil } -// UpsertPolicyAssignments adds policy assignments to the management group. -// These can be net-new assignments, or amendments to existing assignments. +// ModifyPolicyAssignment modifies an existing policy assignment in the management group. // It will deep merge the supplied assignments with the existing assignments. -// If the assignment already exists, its attributes will be updated, but not entirely replaced. -func (alzmg *AlzManagementGroup) UpsertPolicyAssignments(ctx context.Context, pas map[string]*armpolicy.Assignment, az *AlzLib) error { - papv := make(PolicyAssignmentsParameterValues) - defsToGet := mapset.NewSet[string]() - - for name, pa := range pas { - if _, ok := alzmg.policyAssignments[name]; !ok { - alzmg.policyAssignments[name] = pa - continue - } - if pa.Properties == nil { - continue - } - if alzmg.policyAssignments[name].Properties == nil { - alzmg.policyAssignments[name].Properties = new(armpolicy.AssignmentProperties) - } - - if pa.Properties.DisplayName != nil { - alzmg.policyAssignments[name].Properties.DisplayName = pa.Properties.DisplayName - } - - if pa.Properties.Description != nil { - alzmg.policyAssignments[name].Properties.Description = pa.Properties.Description - } - - if pa.Properties.Metadata != nil { - alzmg.policyAssignments[name].Properties.Metadata = pa.Properties.Metadata - } - - // Update policy assignment parameter values map. - if pa.Properties.Parameters != nil && len(pa.Properties.Parameters) > 0 { - papv[name] = pa.Properties.Parameters - } - - if pa.Properties.EnforcementMode != nil { - alzmg.policyAssignments[name].Properties.EnforcementMode = pa.Properties.EnforcementMode - } +func (alzmg *AlzManagementGroup) ModifyPolicyAssignment( + name string, + parameters map[string]*armpolicy.ParameterValuesValue, + enforcementMode *armpolicy.EnforcementMode, + nonComplianceMessages []*armpolicy.NonComplianceMessage, + identity *armpolicy.Identity, +) error { + if _, ok := alzmg.policyAssignments[name]; !ok { + return fmt.Errorf("policy assignment %s not found in management group %s", name, alzmg.name) + } - if pa.Properties.NonComplianceMessages != nil { - if alzmg.policyAssignments[name].Properties.NonComplianceMessages == nil { - alzmg.policyAssignments[name].Properties.NonComplianceMessages = make([]*armpolicy.NonComplianceMessage, len(pa.Properties.NonComplianceMessages)) - } - alzmg.policyAssignments[name].Properties.NonComplianceMessages = pa.Properties.NonComplianceMessages - } + if alzmg.policyAssignments[name].Properties == nil { + return fmt.Errorf("properties for policy assignment %s in management group %s is nil", name, alzmg.name) + } - if pa.Properties.PolicyDefinitionID != nil { - alzmg.policyAssignments[name].Properties.PolicyDefinitionID = pa.Properties.PolicyDefinitionID - switch lastButOneSegment(*pa.Properties.PolicyDefinitionID) { - case "policyDefinitions": - if !az.PolicyDefinitionExists(lastSegment(*pa.Properties.PolicyDefinitionID)) { - defsToGet.Add(*pa.Properties.PolicyDefinitionID) - } - case "policySetDefinitions": - if !az.PolicySetDefinitionExists(lastSegment(*pa.Properties.PolicyDefinitionID)) { - defsToGet.Add(*pa.Properties.PolicyDefinitionID) - } - } - } + if alzmg.policyAssignments[name].Properties.Parameters == nil && len(parameters) > 0 { + alzmg.policyAssignments[name].Properties.Parameters = make(map[string]*armpolicy.ParameterValuesValue, len(parameters)) } - // fetch defs that don't exist - if defsToGet.Cardinality() > 0 { - if err := az.GetDefinitionsFromAzure(ctx, defsToGet.ToSlice()); err != nil { - return err - } + for k, v := range parameters { + alzmg.policyAssignments[name].Properties.Parameters[k] = v } - // update the policy assignments - pd2mg := az.Deployment.policyDefinitionToMg() - psd2mg := az.Deployment.policySetDefinitionToMg() + if enforcementMode != nil { + alzmg.policyAssignments[name].Properties.EnforcementMode = enforcementMode + } - if err := modifyPolicyAssignments(alzmg, pd2mg, psd2mg, papv); err != nil { - return err + if nonComplianceMessages != nil { + alzmg.policyAssignments[name].Properties.NonComplianceMessages = nonComplianceMessages } + if identity != nil { + alzmg.policyAssignments[name].Identity = identity + } return nil } diff --git a/alzmanagementgroup_test.go b/alzmanagementgroup_test.go index 4633655..5a4697e 100644 --- a/alzmanagementgroup_test.go +++ b/alzmanagementgroup_test.go @@ -673,98 +673,77 @@ func TestModifyRoleDefinitions(t *testing.T) { assert.Empty(t, alzmg.roleDefinitions) } -func TestUpsertPolicyAssignments(t *testing.T) { - // Create a new AlzLib instance. - az := NewAlzLib() - az.policyDefinitions = map[string]*armpolicy.Definition{ - "test-policy-definition": {}, +func TestCopyMap(t *testing.T) { + // Create a new map. + m := map[string]*int{ + "foo": to.Ptr(1), + "bar": to.Ptr(2), + "baz": to.Ptr(3), } - // Create a new AlzManagementGroup instance. + // Copy the map. + m2 := copyMap[string, int](m) + + // Verify that the original map and the copied map are equal. + assert.Equal(t, len(m), len(m2)) + for k, v := range m { + assert.Equal(t, *v, m2[k]) + } + + // Modify the original map. + m["foo"] = to.Ptr(4) + + // Verify that the original map and the copied map are no longer equal. + assert.NotEqual(t, m, m2) +} + +func TestModifyPolicyAssignment(t *testing.T) { + // Create a new AlzManagementGroup instance alzmg := &AlzManagementGroup{ policyAssignments: make(map[string]*armpolicy.Assignment), } - // Create a new policy assignment to upsert. + // Add a policy assignment to the management group pa := &armpolicy.Assignment{ Name: to.Ptr("test-policy-assignment"), Type: to.Ptr("Microsoft.Authorization/policyAssignments"), - - Identity: &armpolicy.Identity{Type: to.Ptr(armpolicy.ResourceIdentityTypeSystemAssigned)}, Properties: &armpolicy.AssignmentProperties{ - PolicyDefinitionID: to.Ptr("/providers/Microsoft.Authorization/policyDefinitions/test-policy-definition"), Parameters: map[string]*armpolicy.ParameterValuesValue{ - "parameter1": {Value: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-rg"}, - "parameter2": {Value: "value2"}, + "parameter1": {Value: "value1"}, }, }, } + alzmg.policyAssignments["test-policy-assignment"] = pa - // Upsert the policy assignment. - err := alzmg.UpsertPolicyAssignments(context.Background(), map[string]*armpolicy.Assignment{"test-policy-assignment": pa}, az) - assert.NoError(t, err) - - // Verify that the policy assignment was added to the management group. - assert.Equal(t, 1, len(alzmg.policyAssignments)) - assert.Equal(t, pa, alzmg.policyAssignments["test-policy-assignment"]) - - // Update the policy assignment. - pa.Properties.Parameters["parameter1"].Value = "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/my-rg" - pa.Properties.Parameters["parameter2"].Value = "value3" - - // Upsert the updated policy assignment. - err = alzmg.UpsertPolicyAssignments(context.Background(), map[string]*armpolicy.Assignment{"test-policy-assignment": pa}, az) - assert.NoError(t, err) - - // Verify that the policy assignment was updated in the management group. - assert.Equal(t, 1, len(alzmg.policyAssignments)) - assert.Equal(t, pa, alzmg.policyAssignments["test-policy-assignment"]) - - // Add a new policy assignment. - pa2 := &armpolicy.Assignment{ - Name: to.Ptr("test-policy-assignment-2"), + // Define the expected modified policy assignment + expected := &armpolicy.Assignment{ + Name: to.Ptr("test-policy-assignment"), Type: to.Ptr("Microsoft.Authorization/policyAssignments"), - - Identity: &armpolicy.Identity{Type: to.Ptr(armpolicy.ResourceIdentityTypeSystemAssigned)}, Properties: &armpolicy.AssignmentProperties{ - PolicyDefinitionID: to.Ptr("/providers/Microsoft.Authorization/policyDefinitions/test-policy-definition-2"), Parameters: map[string]*armpolicy.ParameterValuesValue{ - "parameter1": {Value: "/subscriptions/22222222-2222-2222-2222-222222222222/resourceGroups/my-rg"}, - "parameter2": {Value: "value4"}, + "parameter1": {Value: "value1"}, + "parameter2": {Value: "value2"}, }, + EnforcementMode: to.Ptr(armpolicy.EnforcementModeDefault), + NonComplianceMessages: []*armpolicy.NonComplianceMessage{}, }, + Identity: &armpolicy.Identity{Type: to.Ptr(armpolicy.ResourceIdentityTypeSystemAssigned)}, } - // Upsert the new policy assignment. - err = alzmg.UpsertPolicyAssignments(context.Background(), map[string]*armpolicy.Assignment{"test-policy-assignment-2": pa2}, az) - assert.NoError(t, err) - - // Verify that the new policy assignment was added to the management group. - assert.Equal(t, 2, len(alzmg.policyAssignments)) - assert.Equal(t, pa, alzmg.policyAssignments["test-policy-assignment"]) - assert.Equal(t, pa2, alzmg.policyAssignments["test-policy-assignment-2"]) -} - -func TestCopyMap(t *testing.T) { - // Create a new map. - m := map[string]*int{ - "foo": to.Ptr(1), - "bar": to.Ptr(2), - "baz": to.Ptr(3), - } - - // Copy the map. - m2 := copyMap[string, int](m) - - // Verify that the original map and the copied map are equal. - assert.Equal(t, len(m), len(m2)) - for k, v := range m { - assert.Equal(t, *v, m2[k]) - } + // Call the ModifyPolicyAssignment function + err := alzmg.ModifyPolicyAssignment( + "test-policy-assignment", + map[string]*armpolicy.ParameterValuesValue{ + "parameter2": {Value: "value2"}, + }, + to.Ptr(armpolicy.EnforcementModeDefault), + []*armpolicy.NonComplianceMessage{}, + &armpolicy.Identity{Type: to.Ptr(armpolicy.ResourceIdentityTypeSystemAssigned)}, + ) - // Modify the original map. - m["foo"] = to.Ptr(4) + // Check for errors + assert.NoError(t, err) - // Verify that the original map and the copied map are no longer equal. - assert.NotEqual(t, m, m2) + // Check if the policy assignment was modified correctly + assert.Equal(t, expected, alzmg.policyAssignments["test-policy-assignment"]) }