简体   繁体   English

Cloudformation KeyValuePair 列表作为参数

[英]Cloudformation KeyValuePair List as a parameter

When creating ECS infrastructure we describe our Task Definitions with CloudFormation.在创建 ECS 基础设施时,我们使用 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.根据文档, Environment具有KeyValuePair类型,但 CloudFormation 参数没有这种类型。 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 .到目前为止,我看到的唯一可能方法是将所有 arguments 作为 CommaDelimitedList 传递,然后使用 CloudFormation 函数以某种方式解析和 map。 I can Fn::Split every entity in key and value, but how to dynamically build an array of KeyValuePair in CloudFormation?我可以 Fn::Split 键和值中的每个实体,但是如何在 CloudFormation 中动态构建 KeyValuePair 数组?

Or maybe there is an easier way, and I'm missing something?或者也许有更简单的方法,我错过了什么? Thanks in advance for any ideas.提前感谢您的任何想法。

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"

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. 您可能需要考虑使用EC2参数存储来创建CloudFormation支持的安全密钥/值对,并且可以与ECS环境集成。

AWS Systems Manager Parameter Store AWS Systems Manager参数存储

AWS Systems Manager Parameter Store provides secure, hierarchical storage for configuration data management and secrets management. AWS Systems Manager参数存储为配置数据管理和机密管理提供安全的分层存储。 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. AWS Store具有高度可扩展性,可用性和耐用性,并受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. 虽然Parameter Store具有用于存储应用程序机密的强大安全功能,但它也可用于存储非敏感应用程序字符串,如公钥,环境设置,许可证代码等。

And it is supported directly by CloudFormation, allowing you to easily capture, store and manage application configuration strings which can be accessed by ECS. CloudFormation直接支持它,允许您轻松捕获,存储和管理ECS可以访问的应用程序配置字符串。 This template allows you provide the Parameter store key values at stack creation time via the console or CLI: 此模板允许您通过控制台或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. 任何AWS运行时环境(EC2,ECS,Lambda)都可以轻松安全地检索值。 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: 它与IAM集成,因此权限由标准IAM策略语法控制:

{
    "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. 最后,这篇博客文章展示了一种在运行时将权限读入Dockerfile的技术。 They suggest a secure way to handle environment variables in Docker with AWS Parameter Store. 他们建议使用AWS参数存储在Docker中处理环境变量的安全方法。 For reference, I am including their Dockerfile here: 作为参考,我在这里包括他们的Dockerfile:

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. 您的app可能需要也可能不需要包装器来从环境变量中读取参数。

I was facing the same problem ,I needed to create a lambda resource with environment variables. 我遇到了同样的问题,我需要用环境变量创建一个lambda资源。 We decided to fix initial set of environment variable and keys name are also decided in advance. 我们决定修复初始环境变量集,并且密钥名称也是事先确定的。 So I added four parameters , and used Ref for values while keeping fixed keys name. 所以我添加了四个参数,并在保留固定键名的同时使用Ref表示值。

There is another way too - which may sound overkill but it allows to put whatever env.还有另一种方法 - 这听起来有点矫枉过正,但它允许放置任何环境。 to the function you wish, no need to "predefine" how many env.到您想要的 function,无需“预定义”多少环境。 variables, only restriction in sample below - can not use :::: or ||||变量,仅限制在下面的示例中 - 不能使用::::|||| inside value of the key.键的内部值。 Key can't have such symbols by AWS docs already. AWS 文档中键不能有这样的符号。

Gameplan:比赛计划:

Make an inline CF Lambda Function with code which accepts all env in any format you wish as a string and uses any code you want to use inside that function (i use JS with NodeJS env) and while it's your code, parse how you wish that string passing in and use aws-sdk to update the function. Call function once inside the CF template.制作一个内联 CF Lambda Function,其代码接受您希望的任何格式的所有 env 作为字符串,并使用您想要在 function 中使用的任何代码(我将 JS 与 NodeJS env 一起使用),虽然它是您的代码,但解析您希望的方式传入的字符串并使用 aws-sdk 更新 function。在 CF 模板中调用一次 function。

In this sample you pass in env as such string:在此示例中,您将 env 作为这样的字符串传递:

key1::::value1||||key2::::value2 If you need to use :::: or |||| key1::::value1||||key2::::value2如果需要使用::::|||| in your value, of course update to some other divider.在你的价值中,当然会更新到其他一些分频器。

Not big fan of running lambda for such task, yet i want to have option of passing in virtually any env.不太喜欢为这样的任务运行 lambda,但我希望可以选择传入几乎任何环境。 to the CF template and this works.到 CF 模板,这有效。

  LambdaToSetEnvRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy
      Policies:
        - PolicyName: cloudwatch-logs
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource:
                  - !Sub "arn:aws:logs:*:${AWS::AccountId}:log-group:*:*"
                  - !Sub "arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda-insights:*"
        - PolicyName: trigger-lambda-by-cloud-events
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'lambda:UpdateFunctionConfiguration'
                Resource:
                  - !GetAtt OriginalLambda.Arn
      Tags:
        - { Key: managed-by, Value: !Ref AWS::StackId }
  LambdaToSetEnv:
    Type: AWS::Lambda::Function
    DeletionPolicy: Delete
    Properties:
      Code:
        ZipFile: |
          const response = require('cfn-response');
          const aws = require('aws-sdk');
          exports.handler = (event, context) => {
            console.log(JSON.stringify({event, context}));
            try {
              if (event.RequestType === "Delete") {
                response.send(event, context, response.SUCCESS, {RequestType: event.RequestType});
              } else {
                const client = new aws.Lambda({apiVersion: '2015-03-31'});
                const Variables = {
                  "All": process.env.FunctionEnvVariables,
                };
                console.log('process.env.FunctionEnvVariables: ', process.env.FunctionEnvVariables);
                if(process.env.FunctionEnvVariables){
                  process.env.FunctionEnvVariables.split('||||').forEach((pair) => {
                    if(pair && pair.trim() !== ''){
                      Variables[pair.split('::::')[0]] = pair.split('::::')[1];
                    }
                  })
                }
                const result = client.updateFunctionConfiguration({ FunctionName: process.env.LambdaToUpdateArn, Environment: { Variables } }, function (error, data){
                  console.log('data: ', data);
                  console.log('error: ', error);
                  if(error){
                    console.error(error);
                    response.send(event, context, response.ERROR, {});      
                  } else {
                    response.send(event, context, response.SUCCESS, {});
                  }
                });
              }
            } catch (e) {
              response.send(event, context, response.ERROR, e.stack);
            }
          }
      Role: !GetAtt LambdaToSetEnvRole.Arn
      Handler: index.handler
      Runtime: nodejs14.x
      Timeout: '300'
      Environment:
        Variables:
          LambdaToUpdateArn: !GetAtt OriginalLambda.Arn
          FunctionEnvVariables: !Ref FunctionEnvVariables
  LambdaCall:
    DependsOn:
      - OriginalLambda
      - LambdaToSetEnv
    Type: Custom::LambdaCallout
    Properties:
      ServiceToken: !GetAtt LambdaToSetEnv.Arn

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM