Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cloudformation KeyValuePair List as a parameter

When creating ECS infrastructure we describe our Task Definitions with CloudFormation. We want to be able to dynamically pass environment variables as a parameter to the template. According to the docs, Environment has a KeyValuePair type, but CloudFormation parameters do not have this type. We can not hardcode Environment variables to the template, because this template is used as a nested stack so environment variables will be dynamically passed inside it.

The only possible way I see so far is to pass all arguments as a CommaDelimitedList, and then somehow parse and map it using CloudFormation functions. I can Fn::Split every entity in key and value, but how to dynamically build an array of KeyValuePair in CloudFormation?

Or maybe there is an easier way, and I'm missing something? Thanks in advance for any ideas.

like image 468
Ivan Avatar asked Dec 14 '17 20:12

Ivan


People also ask

How do you reference a parameter in CloudFormation?

Referencing a parameter within a template You use the Ref intrinsic function to reference a parameter, and AWS CloudFormation uses the parameter's value to provision the stack. You can reference parameters from the Resources and Outputs sections of the same template.

What is pseudo parameter in AWS CloudFormation?

Pseudo parameters are parameters that are predefined by AWS CloudFormation. You don't declare them in your template. Use them the same way as you would a parameter, as the argument for the Ref function.

What is dynamic reference in CloudFormation?

When you use a dynamic reference, CloudFormation retrieves the value of the specified reference when necessary during stack and change set operations. CloudFormation currently supports the following dynamic reference patterns: ssm, for plaintext values stored in AWS Systems Manager Parameter Store.

Which of the following options Cannot be used inside a CloudFormation template?

Which of the following are not valid CloudFormation template sections? In total there are 9 valid sections allowed within a CloudFormation template. In the answers above, only "Parameters", "Resources" and "Outputs" are considered valid. "Options" is not a template section.


2 Answers

I know it's late and you have already found a workaround. However, the following is the closest I came to solve this. Still not completely dynamic as expected parameters have to be defined as placeholders. Therefore the maximum number of environment variables expected should be known.

The answer is based on this blog. All credits to the author.

Parameters:
  EnvVar1:
    Type: String
    Description: A possible environment variable to be passed on to the container definition.
      Should be a key-value pair combined with a ':'. E.g. 'envkey:envval'
    Default: ''
  EnvVar2:
    Type: String
    Description: A possible environment variable to be passed on to the container definition.
      Should be a key-value pair combined with a ':'. E.g. 'envkey:envval'
    Default: ''
  EnvVar3:
    Type: String
    Description: A possible environment variable to be passed on to the container definition.
      Should be a key-value pair combined with a ':'. E.g. 'envkey:envval'
    Default: ''
Conditions:
  Env1Exist: !Not [ !Equals [!Ref EnvVar1, '']]
  Env2Exist: !Not [ !Equals [!Ref EnvVar2, '']]
  Env3Exist: !Not [ !Equals [!Ref EnvVar3, '']]
Resources:
  TaskDefinition:
    ContainerDefinitions:
      -
         Environment:
           - !If
             - Env1Exist
             -
               Name: !Select [0, !Split [":", !Ref EnvVar1]]
               Value: !Select [1, !Split [":", !Ref EnvVar1]]
             - !Ref "AWS::NoValue"
           - !If
             - Env2Exist
             -
               Name: !Select [0, !Split [":", !Ref EnvVar2]]
               Value: !Select [1, !Split [":", !Ref EnvVar2]]
             - !Ref "AWS::NoValue"
           - !If
             - Env3Exist
             -
               Name: !Select [0, !Split [":", !Ref EnvVar3]]
               Value: !Select [1, !Split [":", !Ref EnvVar3]]
             - !Ref "AWS::NoValue"
like image 153
madawa Avatar answered Oct 08 '22 15:10

madawa


You may want to consider using the EC2 Parameter Store to create secured key/value pairs, which is supported in CloudFormation, and can be integrated with ECS environments.

AWS Systems Manager Parameter Store

AWS Systems Manager Parameter Store provides secure, hierarchical storage for configuration data management and secrets management. You can store data such as passwords, database strings, and license codes as parameter values. You can store values as plain text or encrypted data. You can then reference values by using the unique name that you specified when you created the parameter. Highly scalable, available, and durable, Parameter Store is backed by the AWS Cloud. Parameter Store is offered at no additional charge.

While Parameter Store has great security features for storing application secrets, it can also be used to store nonsensitive application strings such as public keys, environment settings, license codes, etc.

And it is supported directly by CloudFormation, allowing you to easily capture, store and manage application configuration strings which can be accessed by ECS. This template allows you provide the Parameter store key values at stack creation time via the console or CLI:

Description: Simple SSM parameter example
Parameters:
  pSMTPServer:
    Description: SMTP Server URL eg [email-smtp.us-east-1.amazonaws.com]:587
    Type: String
    NoEcho: false
  SMTPServer:
    Type: AWS::SSM::Parameter
    Properties: 
      Name: my-smtp-server
      Type: String
      Value: !Ref pSMTPServer

Any AWS runtime environment (EC2, ECS, Lambda) can easily securely retrieve the values. From the console side, there is great Parameter manager interface that maintains parameter version history. Its intergated with IAM, so permissions are controlled with standard IAM policy syntax:

{
    "Action": [
        "ssm:GetParameterHistory",
        "ssm:GetParameter",
        "ssm:GetParameters",
        "ssm:GetParametersByPath"
    ],
    "Resource": [
        "arn:aws:ssm:us-west-2:555513456471:parameter/smtp-server"
    ],
    "Effect": "Allow"
},
{
    "Action": [
        "kms:Decrypt"
    ],
    "Resource": [
        "arn:aws:kms:us-west-2:555513456471:key/36235f94-19b5-4649-84e0-978f52242aa0a"
    ],
    "Effect": "Allow"
}

Finally, this blog article shows a technique to read the permissions into a Dockerfile at runtime. They suggest a secure way to handle environment variables in Docker with AWS Parameter Store. For reference, I am including their Dockerfile here:

FROM grafana/grafana:master

RUN curl -L -o /bin/aws-env https://github.com/Droplr/aws-env/raw/master/bin/aws-env-linux-amd64 && \
  chmod +x /bin/aws-env

ENTRYPOINT ["/bin/bash", "-c", "eval $(/bin/aws-env) && /run.sh"]

With that invocation, each of the parameters are available as an environment variable in the container. You app may or may not need a wrapper to read the parameters from the environment variables.

like image 34
Rodrigo Murillo Avatar answered Oct 08 '22 16:10

Rodrigo Murillo