diff --git a/aws-cloudformation-stackset/docs/deploymenttargets.md b/aws-cloudformation-stackset/docs/deploymenttargets.md index 56ee0bd..2c61e7f 100644 --- a/aws-cloudformation-stackset/docs/deploymenttargets.md +++ b/aws-cloudformation-stackset/docs/deploymenttargets.md @@ -11,7 +11,7 @@ To declare this entity in your AWS CloudFormation template, use the following sy
{ "Accounts" : [ String, ... ], - "AccountsUrl" : String, + "AccountsUrl" : String, "OrganizationalUnitIds" : [ String, ... ], "AccountFilterType" : String } @@ -48,9 +48,11 @@ _Required_: No _Type_: String -_Length Constraints_: Minimum length of 1. Maximum length of 5120. +_Minimum Length_:1
-_Pattern_: `(s3://|http(s?)://).+` +_Maximum Length_:5120
+ +_Pattern_:(s3://|http(s?)://).+
_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt) diff --git a/aws-cloudformation-stackset/resource-role.yaml b/aws-cloudformation-stackset/resource-role.yaml index deefee6..dd872b4 100644 --- a/aws-cloudformation-stackset/resource-role.yaml +++ b/aws-cloudformation-stackset/resource-role.yaml @@ -45,6 +45,7 @@ Resources: - "cloudformation:UntagResource" - "cloudformation:UpdateStackInstances" - "cloudformation:UpdateStackSet" + - "iam:PassRole" Resource: "*" Outputs: ExecutionRoleArn: diff --git a/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzer.java b/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzer.java index a58a256..a25b5ce 100644 --- a/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzer.java +++ b/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzer.java @@ -9,6 +9,8 @@ import lombok.Builder; import lombok.Data; import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; +import software.amazon.cloudformation.stackset.Parameter; import software.amazon.cloudformation.stackset.ResourceModel; import software.amazon.cloudformation.stackset.StackInstances; @@ -53,6 +55,8 @@ public void analyze(final StackInstancesPlaceHolder placeHolder) { region -> stackInstancesToCreate.addAll(currentStackInstancesByRegion.get(region)) ); + HashMap> ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); + setInter(currentRegions, previousRegions).forEach( region -> new AltStackInstancesCalculator(region, previousStackInstancesByRegion.get(region), @@ -60,7 +64,8 @@ public void analyze(final StackInstancesPlaceHolder placeHolder) { .calculate( stackInstancesToDelete, stackInstancesToCreate, - stackInstancesToUpdate) + stackInstancesToUpdate, + ouDeploymentParametersMap) ); placeHolder.setCreateStackInstances(new ArrayList<>(stackInstancesToCreate)); @@ -68,6 +73,35 @@ public void analyze(final StackInstancesPlaceHolder placeHolder) { placeHolder.setUpdateStackInstances(new ArrayList<>(stackInstancesToUpdate)); } + /* + * If an OU is associated with different parameter sets, will raise an error. + * 1. This is the original process logic when ALT is not enabled. + * 2. Although users logically CAN associate an OU with different with ALT filter, but we cannot check if the input is valid + * 2.1 For example, (OU - account1) and (OU - account2). It's up to OU's structure if these two targets can be + * associated with two parameters -- if OU is set(account1, account2, account3), then not valid. + * 2.2 So we chose to raise and error to align with previous implementation and avoid possible ambiguity + * */ + + private static HashMap > findDeploymentParametersForOUs(final HashMap > currentStackInstancesByRegion) { + HashMap > ouDeploymentParameters = new HashMap<>(); + + for (final String region : currentStackInstancesByRegion.keySet()) { + for (final StackInstances stackInstances : currentStackInstancesByRegion.get(region)) { + Set parameters = stackInstances.getParameterOverrides(); + + stackInstances.getDeploymentTargets().getOrganizationalUnitIds().forEach( + ou -> { + if (ouDeploymentParameters.containsKey(ou) && ouDeploymentParameters.get(ou) != parameters) { + throw new CfnInvalidRequestException("An OrganizationalUnitIds cannot be associated with more than one Parameters set"); + } + ouDeploymentParameters.put(ou, parameters); + } + ); + } + } + return ouDeploymentParameters; + } + private static HashMap > regroupStackInstancesByRegion (final Collection stackInstancesGroup) { HashMap > stackInstancesGroupsByRegion = new HashMap<>(); if (CollectionUtils.isNullOrEmpty(stackInstancesGroup)) return stackInstancesGroupsByRegion; diff --git a/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculator.java b/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculator.java index bb736db..389c69d 100644 --- a/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculator.java +++ b/aws-cloudformation-stackset/src/main/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculator.java @@ -9,7 +9,6 @@ import java.util.stream.Collectors; import lombok.Data; import software.amazon.awssdk.utils.CollectionUtils; -import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; import software.amazon.cloudformation.stackset.DeploymentTargets; import software.amazon.cloudformation.stackset.Parameter; import software.amazon.cloudformation.stackset.StackInstances; @@ -30,7 +29,6 @@ public class AltStackInstancesCalculator { private final HashMap , Set
> previousStackInstancesByOuFilter; private final HashMap , Set
> currentStackInstancesByOuFilter; - private final HashMap > ouDeploymentParametersMap; private final static String NONE = "NONE"; private final static String INTER = "INTERSECTION"; @@ -52,12 +50,11 @@ public AltStackInstancesCalculator (String region, Set previousS this.currentStackInstancesGroup = currentStackInstancesGroup; this.previousOUs = findAllOus(previousStackInstancesGroup); this.currentOUs = findAllOus(currentStackInstancesGroup); - this.ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesGroup); this.previousStackInstancesByOuFilter = mergeDifferentFilters(mergeSameFilters(previousStackInstancesGroup)); this.currentStackInstancesByOuFilter = mergeDifferentFilters(mergeSameFilters(currentStackInstancesGroup)); } - public void calculate (Set instancesToDelete, Set instancesToCreate, Set instancesToUpdate) { + public void calculate (Set instancesToDelete, Set instancesToCreate, Set instancesToUpdate, HashMap > ouDeploymentParametersMap) { setDiff(previousOUs, currentOUs).forEach(ou -> { String filter = getFilterType(ou, previousStackInstancesByOuFilter); Set accounts = previousStackInstancesByOuFilter.get(Arrays.asList(ou, filter)); @@ -85,33 +82,6 @@ public void calculate (Set instancesToDelete, Set > findDeploymentParametersForOUs(final Set stackInstancesGroup) { - HashMap > ouDeploymentParameters = new HashMap<>(); - - for (final StackInstances stackInstances : stackInstancesGroup) { - Set parameters = stackInstances.getParameterOverrides(); - - stackInstances.getDeploymentTargets().getOrganizationalUnitIds().forEach( - ou -> { - if (ouDeploymentParameters.containsKey(ou) && ouDeploymentParameters.get(ou) != parameters) { - throw new CfnInvalidRequestException("An OrganizationalUnitIds cannot be associated with more than one Parameters set"); - } - ouDeploymentParameters.put(ou, parameters); - } - ); - } - return ouDeploymentParameters; - } - private static Set findAllOus(final Set stackInstancesGroup) { final HashSet OUs = new HashSet<>(); stackInstancesGroup.forEach(stackInstances -> { diff --git a/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzerTest.java b/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzerTest.java index 5acc191..0890772 100644 --- a/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzerTest.java +++ b/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltResourceModelAnalyzerTest.java @@ -26,6 +26,8 @@ import static software.amazon.cloudformation.stackset.util.AltTestUtils.generateInstances; import static software.amazon.cloudformation.stackset.util.AltTestUtils.generateInstancesWithRegions; import static software.amazon.cloudformation.stackset.util.AltTestUtils.generateModel; +import static software.amazon.cloudformation.stackset.util.AltTestUtils.parameters_1; +import static software.amazon.cloudformation.stackset.util.AltTestUtils.parameters_2; import static software.amazon.cloudformation.stackset.util.AltTestUtils.region_1; import static software.amazon.cloudformation.stackset.util.AltTestUtils.region_2; import static software.amazon.cloudformation.stackset.util.AltTestUtils.region_3; @@ -280,4 +282,18 @@ public void test_Alt_In_Both_Models() { assertThat(Comparator.equals(new HashSet<>(placeHolder.getUpdateStackInstances()), desiredUpdateInstances)).isTrue(); } + @Test + public void test_No_Alt_Filter_Multiple_Parameters_In_One_Group() { + ResourceModel currentModel = generateModel(new HashSet<>(Arrays.asList( + generateInstances(OU_1, parameters_1), + generateInstances(Arrays.asList(OU_1, OU_2), parameters_2))) + ); + StackInstancesPlaceHolder placeHolder = new StackInstancesPlaceHolder(); + + Exception ex = assertThrows( + CfnInvalidRequestException.class, + () -> AltResourceModelAnalyzer.builder().currentModel(currentModel).build().analyze(placeHolder) + ); + assertThat(ex.getMessage()).contains("An OrganizationalUnitIds cannot be associated with more than one Parameters set"); + } } diff --git a/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculatorTest.java b/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculatorTest.java index 63fb686..ced45fd 100644 --- a/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculatorTest.java +++ b/aws-cloudformation-stackset/src/test/java/software/amazon/cloudformation/stackset/util/AltStackInstancesCalculatorTest.java @@ -2,13 +2,15 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; + import org.junit.jupiter.api.Test; import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; +import software.amazon.cloudformation.stackset.Parameter; import software.amazon.cloudformation.stackset.StackInstances; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; import static software.amazon.cloudformation.stackset.util.AltTestUtils.DIFF; import static software.amazon.cloudformation.stackset.util.AltTestUtils.INTER; import static software.amazon.cloudformation.stackset.util.AltTestUtils.NONE; @@ -51,8 +53,10 @@ public void test_No_Alt_Filter() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -80,32 +84,16 @@ public void test_No_Alt_Filter_Multiple_Parameters_Across_Groups() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToUpdate, desiredUpdateInstances)).isTrue(); } - @Test - public void test_No_Alt_Filter_Multiple_Parameters_In_One_Group() { - Set previousGroup = new HashSet<>(Collections.singletonList(generateInstances(OU_1))); - Set currentGroup = new HashSet<>(Arrays.asList( - generateInstances(OU_1, parameters_1), - generateInstances(Arrays.asList(OU_1, OU_2), parameters_2))); - - HashSet stackInstancesSetToDelete = new HashSet<>(); - HashSet stackInstancesSetToCreate = new HashSet<>(); - HashSet stackInstancesSetToUpdate = new HashSet<>(); - Exception ex = assertThrows( - CfnInvalidRequestException.class, - () -> new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate) - ); - assertThat(ex.getMessage()).contains("An OrganizationalUnitIds cannot be associated with more than one Parameters set"); - } - @Test public void test_None_Filters_Take_Over_For_OUs() { Set previousGroup = new HashSet<>(Arrays.asList( @@ -127,8 +115,10 @@ public void test_None_Filters_Take_Over_For_OUs() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -155,9 +145,11 @@ public void test_Merge_Inter_Filters_For_OUs() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -184,8 +176,10 @@ public void test_Merge_Diff_Filters_For_OUs() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -212,8 +206,10 @@ public void test_Merge_Inter_Diff_Filters_For_OUs() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -243,8 +239,10 @@ public void test_Update_Previous_no_Alt_Filter() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -274,8 +272,10 @@ public void test_Update_Previous_None_Filters() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -308,8 +308,10 @@ public void test_Update_Previous_Inter_Filter() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -342,8 +344,10 @@ public void test_Update_Previous_Diff_Filter() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); @@ -382,12 +386,28 @@ public void test_A_Unnecessarily_And_Unrealistically_Complicated_Case() { HashSet stackInstancesSetToDelete = new HashSet<>(); HashSet stackInstancesSetToCreate = new HashSet<>(); HashSet stackInstancesSetToUpdate = new HashSet<>(); + HashMap > currentStackInstancesByRegion = new HashMap >(){{put(region_1, currentGroup);}}; + HashMap > ouDeploymentParametersMap = findDeploymentParametersForOUs(currentStackInstancesByRegion); new AltStackInstancesCalculator(region_1, previousGroup, currentGroup) - .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate); + .calculate(stackInstancesSetToDelete, stackInstancesSetToCreate, stackInstancesSetToUpdate, ouDeploymentParametersMap); assertThat(Comparator.equals(stackInstancesSetToDelete, desiredDeleteInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToCreate, desiredCreateInstances)).isTrue(); assertThat(Comparator.equals(stackInstancesSetToUpdate, desiredUpdateInstances)).isTrue(); } + private static HashMap > findDeploymentParametersForOUs(final HashMap > currentStackInstancesByRegion) { + HashMap > ouDeploymentParameters = new HashMap<>(); + + for (final String region : currentStackInstancesByRegion.keySet()) { + for (final StackInstances stackInstances : currentStackInstancesByRegion.get(region)) { + Set parameters = stackInstances.getParameterOverrides(); + + stackInstances.getDeploymentTargets().getOrganizationalUnitIds().forEach( + ou -> ouDeploymentParameters.put(ou, parameters) + ); + } + } + return ouDeploymentParameters; + } }