From ef662ece1227d62de105dda450c98f2d0d79b83e Mon Sep 17 00:00:00 2001 From: AndyEfaa <73257849+AndyEfaa@users.noreply.github.com> Date: Mon, 5 Feb 2024 10:17:44 +0100 Subject: [PATCH] feat: add multi-org generate_params.py (#672) ## Why? When running ADF in a multi-org setup, this feature allows you to set stage-specific files in the repositories' `params` folder. This feature doesn't break existing parameter generation. It just adds a new feature. ## Example Let's assume that we have a three AWS Org setup with a dev, int and prod AWS Org. That implies that the SSM param "/adf/org/stage" will have one of the following values: [dev, int, prod]. For now, let's assume the value is set to "dev". Let's further assume that, the `params` folder of our target repository has the following structure: - params: - global_dev.yml - global_int.yml - global_prod.yml - global.yml With this feature, the param file global_dev.yml will be selected based on the value "dev" in SSM param "/adf/org/stage". --------- Co-authored-by: Andreas Falkenberg Co-authored-by: Simon Kok --- docs/user-guide.md | 36 ++++++++++++++++--- .../adf-build/shared/generate_params.py | 10 ++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/user-guide.md b/docs/user-guide.md index 68aa763a4..d2e7da23d 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -648,11 +648,12 @@ the corresponding OU parameter file will not be referenced. ```txt global.yml -└───deployment_account_region.yml (e.g. global_eu-west-1.yml) - └───ou.yml (e.g. ou-1a2b-3c4d5e.yml) - └───ou_region.yml (e.g. ou-1a2b-3c4d5e_eu-west-1.yml) - └───account.yml (e.g. dev-account-1.yml) - └───account_region.yml (e.g. dev-account-1_eu-west-1.yml) +└───deployment_org_stage.yml (e.g. global_dev.yml) + └───deployment_account_region.yml (e.g. global_eu-west-1.yml) + └───ou.yml (e.g. ou-1a2b-3c4d5e.yml) + └───ou_region.yml (e.g. ou-1a2b-3c4d5e_eu-west-1.yml) + └───account.yml (e.g. dev-account-1.yml) + └───account_region.yml (e.g. dev-account-1_eu-west-1.yml) ``` This concept also works for applying **Tags** to the resources within your @@ -712,6 +713,31 @@ the root of the repository. *Note:* Currently only Strings type values are supported as parameters to CloudFormation templates when deploying via AWS CodePipeline. +#### CloudFormation Parameters in a Multi-Organization ADF Setup + +The CloudFormation Parameter generation feature is fully compatible with +the [Multi-Organization ADF Setup](./multi-organization-guide.md) approach. + +For example, in a setup with three AWS Organizations; with a separate +`dev`, an `int`, and a `prod` AWS Organization. This implies that the +SSM parameter `/adf/org/stage` will have one of the following three +values: `dev`, `int`, or `prod`; depending on the AWS organization +you are in. Let's further assume that your application in scope +requires AWS Organization specific parameters. In that case, +the `params` folder should have the following content: + +```txt +params +└───global_dev.yml +└───global_int.yml +└───global_prod.yml +└───global.yml +``` + +Where it will prefer the AWS Organization specific configuration +`global_${org_stage}` over the `global` parameters in case they both +match the same parameter or tag. + ### Serverless Transforms If the template that is being deployed contains a transform, such as a diff --git a/src/lambda_codebase/initial_commit/bootstrap_repository/adf-build/shared/generate_params.py b/src/lambda_codebase/initial_commit/bootstrap_repository/adf-build/shared/generate_params.py index 3521d9b37..0500a6828 100644 --- a/src/lambda_codebase/initial_commit/bootstrap_repository/adf-build/shared/generate_params.py +++ b/src/lambda_codebase/initial_commit/bootstrap_repository/adf-build/shared/generate_params.py @@ -118,6 +118,7 @@ class PipelineDefinition(TypedDict): DEPLOYMENT_ACCOUNT_REGION = os.environ["AWS_REGION"] PROJECT_NAME = os.environ["ADF_PROJECT_NAME"] EMPTY_PARAMS_DICT: ParametersAndTags = {'Parameters': {}, 'Tags': {}} +ADF_ORG_STAGE = os.getenv("ADF_ORG_STAGE", "dev") class Parameters: @@ -229,6 +230,7 @@ def create_parameter_files(self) -> None: i.e. "/devsecops/security_eu-west-1" 1. f"{organization_unit_path}" i.e. "/devsecops/security" 1. f"{global}_{region}" i.e. "global_eu-west-1" + 1. f"{global}_{stage}" i.e. "global_dev" 1. f"{global}" i.e. "global" It will then generate a JSON file that holds all the parameters per @@ -299,6 +301,14 @@ def create_parameter_files(self) -> None: ), current_params ) + # Compare account_region final to global_stage + current_params = self._merge_params( + Parameters._parse( + params_root_path=self.cwd, + params_filename=f"global_{ADF_ORG_STAGE}", + ), + current_params, + ) # Compare account_region final to global current_params = self._merge_params( Parameters._parse(