Kashyap
Kashyap

Reputation: 17441

Conditionally define Elastic Beanstalk environment variable using CloudFormation

I want to create an Elastic Beanstalk using CloudFormation template. I want to define an environment variable ENV_VAR_1 and set it's value to value of template parameter var1. But don't want ENV_VAR_1 to exist at all if var1 is an empty string. I.e. I don't want ENV_VAR_1 with no value.

First I tried the Conditions, but I get "Encountered unsupported property Condition" during creation of ElasticBeanstalkEnvironment resource.

Parameters:
  var1:
    Type: String

Conditions:
  isVar1Empty: !Equals [ !Ref var1, "" ]

Resources:
  ElasticBeanstalkEnvironment:
    Type: 'AWS::ElasticBeanstalk::Environment'
    Properties:
      OptionSettings:
        - Namespace: 'aws:elasticbeanstalk:application:environment'
          Condition: isVar1Empty
          OptionName: ENV_VAR_1
          Value: !Ref var1

Then I tried AWS::NoValue

Parameters:
  var1:
    Type: String

Resources:
  ElasticBeanstalkEnvironment:
    Type: 'AWS::ElasticBeanstalk::Environment'
    Properties:
      OptionSettings:
        - Namespace: 'aws:elasticbeanstalk:application:environment'
          OptionName: ENV_VAR_1
          Value: !If [[!Equals [ !Ref var1, "" ]], !Ref 'AWS::NoValue', !Ref var1]

and many permutation combinations of this. With the same result: When var1 is empty, Elastic Beanstalk gets created with ENV_VAR_1 set to ""

Upvotes: 6

Views: 1279

Answers (3)

djvg
djvg

Reputation: 14255

Yet another option is to use ConfigurationTemplate "inheritance", using the SourceConfiguration property.

Basically, you create a parent configuration template with all desired properties except the environment property, and you conditionally create a child template that specifies the parent as its SourceConfiguration. The child template just adds the desired environment property. Then you use the same condition to assign the appropriate template to the environment.

Here it is applied to the OP's example :

Parameters:
  var1:
    Type: String

Conditions:
  Var1NotEmpty: !Not [!Equals [ !Ref var1, "" ]]

Resources:
  ElasticBeanstalkEnvironment:
    Type: 'AWS::ElasticBeanstalk::Environment'
    Properties:
      ApplicationName: !Ref ApplicationName
      TemplateName: !If [Var1NotEmpty, !Ref TemplateWithVar, !Ref TemplateWithoutVar]

  TemplateWithoutVar:
    Type: AWS::ElasticBeanstalk::ConfigurationTemplate
    Properties:
      ApplicationName: !Ref ApplicationName
      Description: "specifies all configuration settings except the optional ENV_VAR_1"
      SolutionStackName: "64bit Amazon Linux 2023 v4.1.0 running Python 3.11"
      OptionSettings:
        - Namespace: "aws:autoscaling:launchconfiguration"
          OptionName: "IamInstanceProfile"
          Value: "aws-elasticbeanstalk-ec2-role"

  TemplateWithVar:
    Type: AWS::ElasticBeanstalk::ConfigurationTemplate
    Condition: Var1NotEmpty
    Properties:
      ApplicationName: !Ref ApplicationName
      Description: "only adds the optional ENV_VAR_1"
      SourceConfiguration:
        ApplicationName: !Ref ApplicationName
        TemplateName: !Ref TemplateWithoutVar
      OptionSettings:
        - Namespace: 'aws:elasticbeanstalk:application:environment'
          OptionName: ENV_VAR_1
          Value: !Ref var1

Clearly this becomes too cumbersome if you've got multiple optional variables.

An interesting use-case would be the implementation of an optional SSH key, as in

...
        - Namespace: "aws:autoscaling:launchconfiguration"
          OptionName: "EC2KeyName"
          Value: !Ref KeyName

where KeyName can be an empty String.

Upvotes: 1

Faqir Hussain
Faqir Hussain

Reputation: 66

Another workaround to handle conditions at option level:

Conditions:    
    CreateProdResources: !Equals [!Ref Env, "prod"]

EBEnvironment:
    Type: AWS::ElasticBeanstalk::Environment
    Properties: 
      OptionSettings: 
       - Namespace : "aws:elasticbeanstalk:command"
         OptionName: Timeout
         Value : 1200
       - Namespace : !If [CreateProdResources, "aws:elbv2:listener:443", "aws:elasticbeanstalk:command"]
         OptionName: !If [CreateProdResources, Protocol, Timeout]
         Value : !If [CreateProdResources, HTTPS, 1200]
       - Namespace : !If [CreateProdResources, "aws:elbv2:listener:443", "aws:elasticbeanstalk:command"]
         OptionName: !If [CreateProdResources, SSLPolicy, Timeout]
         Value : !If [CreateProdResources, "ELBSecurityPolicy-2016-08", 1200]
       - Namespace : !If [CreateProdResources, "aws:elbv2:listener:443", "aws:elasticbeanstalk:command"]
         OptionName: !If [CreateProdResources, SSLCertificateArns, Timeout]
         Value : !If [CreateProdResources, !Ref ACMCertificate, 1200]

Repeated options are considered only once in Elastic Beanstalk.

Upvotes: 3

nicholas.hauschild
nicholas.hauschild

Reputation: 42849

Conditions are going to be applied at the Resource level...currently, you cannot apply a condition to a specific property.

What you could do to satisfy these exact requirements (and this is a bit ugly), is create two conditions, one negating the other. Then with these two conditions, have them conditionally create the specific resource.

Parameters:
  var1:
    Type: String

Conditions:
  isVar1Empty: !Equals [ !Ref var1, "" ]
  isVar1NonEmpty: !Not [ !Equals [ !Ref var1, "" ] ]

Resources:
  ElasticBeanstalkEnvironmentWithVar1:
    Type: 'AWS::ElasticBeanstalk::Environment'
    Condition: isVar1NonEmpty
    Properties:
      OptionSettings:
        - Namespace: 'aws:elasticbeanstalk:application:environment'
          OptionName: ENV_VAR_1
          Value: !Ref var1
  ElasticBeanstalkEnvironmentWithoutVar1:
    Type: 'AWS::ElasticBeanstalk::Environment'
    Condition: isVar1Empty
    Properties:
      OptionSettings:
        - Namespace: 'aws:elasticbeanstalk:application:environment'

Like I said...a bit ugly. Note that this will only really work well if you have one or two variables like this. As soon as you add a second or third 'optional' parameter, this quickly starts spiraling out of control.

A better option might be to generate your CloudFormation template using a templating library like mustache.

Upvotes: 5

Related Questions