Skip to content

CloudFormationProduct: product_versions only support ONE version #16892

@klang

Description

@klang

What is the problem?

When adding more than one CloudFormationProductVersion element the sequence of product_versions in the CloudFormationProduct constructor, CDK errors out during synth.

Reproduction Steps

With the following in ./example/service_catalog.py

from aws_cdk import core as cdk
from aws_cdk.aws_servicecatalog import CloudFormationTemplate, Portfolio, CloudFormationProduct, CloudFormationProductVersion
    class ServiceCatalogStack(cdk.Stack):
           def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
                  super().__init__(scope, construct_id, **kwargs)
                  portfolio = Portfolio(self, "ExamplePortfolio", description="Example", display_name="Example", provider_name="LocalAdmin")
    
                  version1=CloudFormationProductVersion(product_version_name="v1.0", description="ReadOnlyAccess", validate_template=False, cloud_formation_template=CloudFormationTemplate.from_asset(path="./example/products/AccountSpecificTrustRoleReadOnlyAccess.yaml"))
                  version2=CloudFormationProductVersion(product_version_name="v1.1", description="AdministratorAccess", validate_template=False, cloud_formation_template=CloudFormationTemplate.from_asset(path="./example/products/AccountSpecificTrustRole.yaml"))
    
                  product = CloudFormationProduct(self, "IamRoles",
                                                  owner="LocalAdmin",
                                                  product_name="TemporaryRole",
                                                  product_versions=[version1, version2 ]) # version2 can not be inserted here, without getting an error
    
                  portfolio.add_product(product)

...the following in ./example/products/AccountSpecificTrustRoleReadOnlyAccess.yaml"

    ---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: Loopback role
    Resources:
      LoopbackRole:
        Type: AWS::IAM::Role
        Properties:
          RoleName: temporary-trusted-account
          AssumeRolePolicyDocument:
            Statement:
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
                Sid: ''
            Version: '2012-10-17'
          Path: "/"
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/ReadOnlyAccess

.. the following in ./example/product/AccountSpecificTrustRole.yaml

    ---
    AWSTemplateFormatVersion: '2010-09-09'
    Description: Loopback role
    Resources:
      LoopbackRole:
        Type: AWS::IAM::Role
        Properties:
          RoleName: temporary-trusted-account
          AssumeRolePolicyDocument:
            Statement:
              - Action: sts:AssumeRole
                Effect: Allow
                Principal:
                  AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
                Sid: ''
            Version: '2012-10-17'
          Path: "/"
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/AdministratorAccess

.. and the following in ./app.py

    from aws_cdk import core
    from example.service_catalog import ServiceCatalogStack
    app = core.App()
    ServiceCatalogStack(app, "ServiceCatalogStack")
    app.synth()

This error will be produced:

    (.venv) ➜  service-catalog git:(master) ✗ cdk synth
    jsii.errors.JavaScriptError:
      Error: There is already a Construct with name 'Template' in CloudFormationProduct [IamRoles]
          at Node.addChild (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/constructs/lib/construct.js:531:19)
          at new Node (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/constructs/lib/construct.js:40:28)
          at new ConstructNode (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/@aws-cdk/core/lib/construct-compat.js:170:28)
          at Object.createNode (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/@aws-cdk/core/lib/construct-compat.js:39:42)
          at new Construct (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/constructs/lib/construct.js:580:32)
          at new Construct (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/@aws-cdk/core/lib/construct-compat.js:37:9)
          at new Asset (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/@aws-cdk/aws-s3-assets/lib/asset.js:26:9)
          at CloudFormationAssetTemplate.bind (/private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/@aws-cdk/aws-servicecatalog/lib/cloudformation-template.js:64:26)
          at /private/var/folders/gp/g8n90mzd4rb021d9p1jj4dhh0000gn/T/jsii-kernel-CDkviC/node_modules/@aws-cdk/aws-servicecatalog/lib/product.js:76:68
          at Array.map (<anonymous>)

    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "./service-catalog/app.py", line 36, in <module>
        ServiceCatalogStack(app, "ServiceCatalogStack")
      File "./service-catalog/.venv/lib/python3.9/site-packages/jsii/_runtime.py", line 86, in __call__
        inst = super().__call__(*args, **kwargs)
      File "./service-catalog/example/service_catalog.py", line 16, in __init__
        product = CloudFormationProduct(self, "IamRoles",
      File "./service-catalog/.venv/lib/python3.9/site-packages/jsii/_runtime.py", line 86, in __call__
        inst = super().__call__(*args, **kwargs)
      File "./service-catalog/.venv/lib/python3.9/site-packages/aws_cdk/aws_servicecatalog/__init__.py", line 6746, in __init__
        jsii.create(self.__class__, self, [scope, id, props])
      File "./service-catalog/.venv/lib/python3.9/site-packages/jsii/_kernel/__init__.py", line 290, in create
        response = self.provider.create(
      File "./service-catalog/.venv/lib/python3.9/site-packages/jsii/_kernel/providers/process.py", line 344, in create
        return self._process.send(request, CreateResponse)
      File "./service-catalog/.venv/lib/python3.9/site-packages/jsii/_kernel/providers/process.py", line 326, in send
        raise JSIIError(resp.error) from JavaScriptError(resp.stack)
    jsii.errors.JSIIError: There is already a Construct with name 'Template' in CloudFormationProduct [IamRoles]

What did you expect to happen?

I expected product_versions to be able to handle a sequence of CloudFormationProductVersion as stated in the documentation :

  • product_versions (Sequence[CloudFormationProductVersion]) – (experimental) The configuration of the product version.

What actually happened?

    jsii.errors.JavaScriptError:
      Error: There is already a Construct with name 'Template' in CloudFormationProduct [IamRoles]

The first element creates a "Template" construct, which is the same name the following elements tries to use. Which causes the problem, as the Construct names have to be unique.

CDK CLI Version

1.127.0 (build 0ea309a)

Framework Version

No response

Node.js Version

v16.10.0

OS

macOS Big Sur, Version 11.5.2

Language

Python

Language Version

Python 3.9.7

Other information

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions