aws-solutions / aws-control-tower-customizations

The Customizations for AWS Control Tower solution combines AWS Control Tower and other highly-available, trusted AWS services to help customers more quickly set up a secure, multi-account AWS environment using AWS best practices.
https://docs.aws.amazon.com/controltower/latest/userguide/cfct-overview.html
Apache License 2.0
353 stars 205 forks source link

export_outputs should create unique SSM parameter store keys for every account/region target #79

Open felipe1982 opened 2 years ago

felipe1982 commented 2 years ago

Describe the bug

When deploying a stackset to 2+ accounts, the export_outputs section does not create unique values for every account/region target. Instead, it overwrites the previous value with the next value.

example

  - name: vpc-pattern-b
    resource_file: templates/networking/vpc-pattern-b.cfn.yaml
    parameter_file: parameters/networking/vpc-pattern-b.json
    deploy_method: stack_set
    regions:
      - ap-southeast-2
    deployment_targets:
      accounts:
        - "Shared-Services-Non-Prod"
        - "Shared-Services-Prod"
    export_outputs:
      - name: /shared-services/VPCId
        value: $[output_VPCId]

To Reproduce

Deploy a VPC to 2+ accounts, and use export_outputs. You will find only a single value, instead of a list of values (or list of parameters), one for each target account/region

Expected behavior

I would expect a variable to be used inside of manifest.yaml which can be used to distinguish different parameter store keys. example

  - name: vpc-pattern-b
    resource_file: templates/networking/vpc-pattern-b.cfn.yaml
    parameter_file: parameters/networking/vpc-pattern-b.json
    deploy_method: stack_set
    regions:
      - ap-southeast-2
    deployment_targets:
      accounts:
        - "Shared-Services-Non-Prod"
        - "Shared-Services-Prod"
    export_outputs:
      - name: /{{ACCOUNT_ID}}/VPCId
        value: $[output_VPCId]

Other variables would be useful, too, such as OU name (i.e. Workloads), or Account Name/Alias (i.e. Banking-Prod), Region code (ap-southeast-2)

To get the version of the solution, you can look at the description of the created CloudFormation stack. For example, "(SO0158) - The AWS CloudFormation template for deployment of the Amazon CloudWatch Monitoring Framework. Version v1.0.0". You can also find the version from releases

Screenshots If applicable, add screenshots to help explain your problem (please DO NOT include sensitive information).

Additional context Add any other context about the problem here.

richturner commented 2 years ago

I initially assumed that export_outputs would write to Parameter Store of the executing account and then this problem wouldn't exist; the docs should be clear that it uses the Parameter Store of the Control Tower admin account.

Any update on this issue as the op says it is almost useless if you cannot put a unique identifier in the name

Galvin-wjw commented 1 year ago

Same issue, hope that manifest.yaml can support variables

julian-price commented 2 weeks ago

I had this same issue. I wanted to produce different SSM parameters depending on what region the stackset was deployed to. I managed to resolve the issue by patching the code to allowing the key of stack output parameters to be read as SSM parameters the in the same way that values can be:

  1. Clone the CfCT repo and modify the ssm_put_parameters function in source/src/cfct/state_machine_handler.py
  2. Add the following code (I inserted at 1033) to parse the key like an ssm parameter if it is contained within $[ ... ] (the same as values are):
        if key.startswith("$[") and key.endswith("]"):
            key = key[2:-1]
        # Iterate through all the keys in the event
        # (includes the nested keys)
        for k, v in self.nested_dictionary_iteration(self.event):
            if key.lower() == k.lower():
                ssm_key = v
                break
            else:
                ssm_key = key
  3. Now, modify lines 1050 and 1051 slightly to use the ssm-replacement key:
            self.logger.info("Adding value for SSM Parameter Store" " Key: {}".format(ssm_key))
            self.ssm.put_parameter(ssm_key, ssm_value)
  4. Package and deploy the CfCT according to the instructions in https://github.com/aws-solutions/aws-control-tower-customizations?tab=readme-ov-file#building-the-customized-solution

Once this is done, you can construct CFN outputs for both the key and value in your CloudFormation YAML, for example:

Outputs:
  oMyNewResourceArn:
    Value: !GetAtt myResource.Arn
  oMyNewResourceArnParamName:
    Value: !Sub /my/resource/${AWS::Region}/my-resource-arn

Back in the manifest.yaml, store the parameters into the management account SSM:

  - name: my-new-resource
    description: Example showing how to use dynamic export outputs for key and value pairs
    resource_file: templates/my-resource.yaml
    export_outputs: 
      - name: $[output_oMyNewResourceArnParamName]
        value: $[output_oMyNewResourceArn]

Finally, for other stacks that need to use the SSM parameters, you can read the SSM parameters stored in the management account using the alfred helper and and then pass them down to the accounts that others stacks are being provisioned in.