Skip to content

[Core] support complex types in Fn.conditionIf  #8396

Closed

Description

@aaroncarlucci

I am using the CDK to output a raw CloudFormation template as a final deliverable and am using primarily Cfn* resource constructs. We have a CfnPipeline object where I want to conditionally add a stage depending on a CfnParameter value. I have the following code:

codepipeline = aws_codepipeline.CfnPipeline(
            self,
            "Pipeline",
            artifact_store=aws_codepipeline.CfnPipeline.ArtifactStoreProperty(...),
                type="S3"
            ),
            role_arn=codepipeline_role.role_arn,
            stages=[
                aws_codepipeline.CfnPipeline.StageDeclarationProperty(...),
                aws_codepipeline.CfnPipeline.StageDeclarationProperty(...),
                aws_codepipeline.CfnPipeline.StageDeclarationProperty(...),
                core.Fn.condition_if(
                    cloudfront_enable_condition.logical_id,
                    aws_codepipeline.CfnPipeline.StageDeclarationProperty(
                        name="Finalize",
                        actions=[
                            aws_codepipeline.CfnPipeline.ActionDeclarationProperty(
                                action_type_id=aws_codepipeline.CfnPipeline.ActionTypeIdProperty(
			            category="Invoke",
                                    owner="AWS",
                                    provider="Lambda",
                                    version="1"
                                ),
                                configuration={
                                    "FunctionName": cloudfront_invalidation_lambda_function.ref
                                },
                                name="CloudFrontInvalidationAction",
                                role_arn=codepipeline_finalize_stage_role.attr_arn
                            )
                        ]
                    ),
                    core.Aws.NO_VALUE
                )
            ]
)

Which produces the following output in my CloudFormation template:

          {
            "Fn::If": [
              "CloudFrontEnableCondition",
              {
                "actions": [
                  {
                    "actionTypeId": {
                      "category": "Invoke",
                      "owner": "AWS",
                      "provider": "Lambda",
                      "version": "1"
                    },
                    "name": "CloudFrontInvalidationAction",
                    "configuration": {
                      "FunctionName": {
                        "Ref": "CloudFrontInvalidationLambdaFunction"
                      }
                    },
                    "roleArn": {
                      "Fn::GetAtt": [
                        "CodePipelineFinalizeStageRole",
                        "Arn"
                      ]
                    }
                  }
                ],
                "name": "Finalize"
              },
              {
                "Ref": "AWS::NoValue"
              }
            ]
          }
        ]

The keys of the CfnPipeline.StageDeclarationProperty and its children (CfnPipeline.ActionDeclarationProperty and CfnPipeline.ActionTypeIdProperty) are printed in camelCase, making for invalid CloudFormation objects on submission. I've also tried by wrapping the conditional statement in core.Token.as_any() with the same result.

I have resorted to using the following code:

codepipeline.add_override(
            "Properties.Stages.3",
            {
                "Fn::If": [
                    cloudfront_enable_condition.logical_id,
                    {
                        "Actions": [
                            {
                                "ActionTypeId": {
                                    "Category": "Invoke",
                                    "Owner": "AWS",
                                    "Provider": "Lambda",
                                    "Version": 1
                                },
                                "Configuration": {
                                    "FunctionName": cloudfront_invalidation_lambda_function.ref
                                },
                                "Name": "CloudFrontInvalidationAction",
                                "RoleArn": codepipeline_finalize_stage_role.attr_arn
                            }
                        ],
                        "Name": "Finalize"
                    },
                    core.Aws.NO_VALUE
                ]
            }
        )

which fixes my issue and proves that my conditional is valid in CloudFormation, but it feels messy and like the Cfn* properties should be supported in conditionals (IResolvable?).

Feels like this is a bug, but might be a feature request -- perhaps something is missing in the translation layer between nodejs and the Python API? If I'm supposed to be using the API differently, please advise.

Reproduction Steps

Run the above code patterns and inspect the cloudformation template in cdk.out

Error Log

CloudFormation complains of invalid object syntax.

Environment

  • CLI Version : 1.42.1
  • Framework Version:
  • Node.js Version: 12.16.2
  • OS : Ubuntu
  • Language (Version): Python3

Other

https://github.com/ordinaryexperts/aws-marketplace-oe-patterns-drupal/blob/f0201c49583a553832d72267ba07850b1bbe4aca/cdk/drupal/drupal_stack.py#L1870


This is 🐛 Bug Report

Activity

  1. added
    bugThis issue is a bug.
    needs-triageThis issue or PR still needs to be triaged.
    on Jun 5, 2020
  2. self-assigned this
    on Jun 8, 2020
  3. immmapig commented on Jun 27, 2020

    @immmapig

    I am dealing with a similar issue.

    Code before the change:

    .provisionedThroughput(
                                ProvisionedThroughputProperty.builder()
                                        .readCapacityUnits(1)
                                        .writeCapacityUnits(1)
                                        .build())
    

    Code after the change:

     .provisionedThroughput(
                                    Fn.conditionIf(enableFeaturesCond.getLogicalId(), ProvisionedThroughputProperty.builder()
                                            .readCapacityUnits(1)
                                            .writeCapacityUnits(1)
                                            .build(), Aws.NO_VALUE))
    

    Template before the change:

    "ProvisionedThroughput": {
          "ReadCapacityUnits": 1,
          "WriteCapacityUnits": 1
      },
    

    Template after the change:

    "ProvisionedThroughput": {
          "Fn::If": [
            "UseEngagementTable",
            {
              "readCapacityUnits": 1,
              "writeCapacityUnits": 1
            },
            {
              "Ref": "AWS::NoValue"
            }
          ]
        },
    

    Notice that the template after the change incorrectly uses readCapacityUnits and writeCapacityUnits as the keys instead of ReadCapacityUnits and WriteCapacityUnits.

  4. added and removed
    needs-triageThis issue or PR still needs to be triaged.
    on Jun 29, 2020
  5. changed the title [-]CfnPipeline.StageDeclarationProperty does not translate to correct CloudFormation syntax when returned from conditional in Python[/-] [+][Core] support complex types in Fn.If conditions [/+] on Aug 17, 2020
  6. self-assigned this
    on Aug 17, 2020
  7. changed the title [-][Core] support complex types in Fn.If conditions [/-] [+][Core] support complex types in Fn. conditionIf [/+] on Aug 17, 2020
  8. changed the title [-][Core] support complex types in Fn. conditionIf [/-] [+][Core] support complex types in Fn.conditionIf [/+] on Aug 17, 2020
  9. 8 remaining items

  10. added
    effort/largeLarge work item – several weeks of effort
    feature-requestA feature should be added or improved.
    on Oct 5, 2020
  11. aaroncarlucci commented on Oct 5, 2020

    @aaroncarlucci
    Author

    I will reopen this issue for reference but I will reiterate that the happy path for CDK is writing ifs in TypeScript, not in CloudFormation.

    @rix0rrr Thanks for reconsidering. A reminder that the use case presented in the ticket description is using Cfn* resources to generate a CloudFormation template final deliverable and the suggested happy path of doing if statements in TypeScript or Python isn't an option for us.

  12. bollig commented on Feb 14, 2021

    @bollig

    +1 for the use-case to generate standalone CFN templates. I had to use the same sort of workaround for conditionals on the FSx Lustre resource configuration block. Red line is desired.

    image

  13. namedgraph commented on Jun 9, 2021

    @namedgraph

    @bollig what is the workaround mechanism here?

    I want to set NetworkLoadBalancedEc2Service.domainZone() if HostedZone is provided, but not sure how to do it with Fn.conditionIf() since it provides a string and not an object :/

  14. bollig commented on Jun 9, 2021

    @bollig
  15. drobbins-ancile commented on Mar 5, 2022

    @drobbins-ancile

    @rix0rrr Any chance of getting this one addressed? We are also using CDK to generate CloudFormation templates to be manually deployed in other environments so using if conditions in the language won't work for us.

    This seems to be a valid use case.

  16. savanbthakkar commented on Mar 6, 2022

    @savanbthakkar

    @rix0rrr We are having the same problem with DynamoDB. Depending on the input parameter value, we need to set sse_specification. It generates the cloudformation yaml file, but the output is camelCase yaml attributes, and it errors out.

     sse_specification = cdk.Fn.condition_if(
                    "NotEncryptedSearchTable",
                    aws_dynamodb.CfnTable.SSESpecificationProperty(
                        sse_enabled = False
                    ),
                    aws_dynamodb.CfnTable.SSESpecificationProperty(
                        sse_enabled = True,
                        kms_master_key_id=inputParams.EncryptionKeyArn,
                        sse_type="KMS"
                    )
                )

    Here is the yaml output, which is invalid.

    SSESpecification:
            Fn::If:
              - NotEncryptedSearchTable
              - sseEnabled: false
              - kmsMasterKeyId:
                  Ref: EncryptionKeyArn
                sseEnabled: true
                sseType: KMS
  17. drobbins-ancile commented on Aug 12, 2022

    @drobbins-ancile

    @rix0rrr Can we get an official decision on the validity of this use case? Is using cdk synth to generate cloudformation templates for deployment in other environments a supported use case? If so, is there any chance this issue will be worked on?

  18. madeline-k commented on Feb 28, 2023

    @madeline-k
    Contributor

    Thank you for submitting this feature request! After closer examination of the issue by CDK product and development team, there are no plans to implement this in the near feature.

    I am moving this to a GitHub discussion so the information in this thread can continue to be accessible, and people can find the helpful workaround posted here.

  19. locked and limited conversation to collaborators on Feb 28, 2023
  20. converted this issue into a discussion #24381 on Feb 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/coreRelated to core CDK functionalityeffort/largeLarge work item – several weeks of effortfeature-requestA feature should be added or improved.p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions