From a44f123927f0c26a408ebcea2f0e69fe4e090917 Mon Sep 17 00:00:00 2001 From: Ting Qi Date: Mon, 18 Sep 2023 13:00:14 -0700 Subject: [PATCH] Fix AltStackInstances check for create (#119) * Fix AltStackInstances check for create * Fix AltStackInstances check for creation * Delete unnecessary throw from unit test helper function --- .../docs/deploymenttargets.md | 8 +- .../resource-role.yaml | 1 + .../util/AltResourceModelAnalyzer.java | 36 ++++++++- .../util/AltStackInstancesCalculator.java | 32 +------- .../util/AltResourceModelAnalyzerTest.java | 16 ++++ .../util/AltStackInstancesCalculatorTest.java | 80 ++++++++++++------- 6 files changed, 108 insertions(+), 65 deletions(-) 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;
+    }
 }