Cloudformation 堆栈默认参数 SSM Parameter store

Cloudformation stack default parameter SSM Parameter store

我正在尝试使用 cloudformation 启动一个 jupyterlab 实例(这是我经常做的事情,而 sagemaker 没有 1y 免费套餐)所以开头看起来像这样不起作用。特别是密码参数

# AWSTemplateFormatVersion: "2010-09-09"
Description: Creates a Jupyter Lab Instance with an Elastic Load Balancer


Parameters:
  KeyName:
    Description: >-
      Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: Must be the name of an existing EC2 KeyPair.
    Default: eduinstance
  VPC:
    Description: VPC ID of the VPC in which to deploy this stack.
    Type: AWS::EC2::VPC::Id
    ConstraintDescription: Must be the name of a valid VPC
    Default: vpc-10a7ac6a
  Subnets:
    Type: List<AWS::EC2::Subnet::Id>
    Default: subnet-8cde25d3,subnet-531fda72,subnet-4bbe3006
    Description: >-
      Subnets for the Elastic Load Balancer.
      Please include at least two subnets
  Password: 
    Type: String
    NoEcho: false
    MinLength: 4
    Default: '{{resolve:ssm:JLabPassword:1}}'
    Description: Password to set for Jupyter Lab
  EBSVolumeSize:
    Type: Number
    Description: EBS Volume Size (in GiB) for the instance
    Default: 8
    MinValue: 8
    MaxValue: 64000
    ConstraintDescription: Please enter a value between 8 GB and 64 TB
  EC2InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - c5.large
      - m5.large
    Description: Enter t2.micro, c5.large or m5.large. Default is t2.micro.


Conditions:
  JupyterPasswordDefault: !Equals
    - !Ref Password
    - DEFAULT

Resources:
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties: 
      IpAddressType: ipv4
      Scheme: internet-facing
      SecurityGroups: 
        - !GetAtt [ALBSG, GroupId]
      Subnets: !Ref Subnets
      Type: application
  
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions: 
        - Type: forward
          TargetGroupArn: !Ref ALBTargetGroup
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP
  
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Port: 8888
      Protocol: HTTP
      Targets: 
        - Id: !Ref ComputeInstance
      TargetType: instance
      VpcId: !Ref VPC
  
  ComputeInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref ComputeIAMRole

  
  ComputeInstance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      SubnetId: !Select [0, !Ref Subnets]
      KeyName: !Ref KeyName
      ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2:33}}'
      SecurityGroupIds: 
        - !GetAtt [ComputeSG, GroupId]
      IamInstanceProfile: !Ref ComputeInstanceProfile
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: gp2
            VolumeSize: !Ref EBSVolumeSize
            DeleteOnTermination: true
      UserData:
        Fn::Base64: !Sub
          - |
            #!/bin/bash
            yum update -y
            yum install python3-pip -y
            yum install java-1.8.0-openjdk -y
            cd /home/ec2-user/
            wget https://repo.anaconda.com/archive/Anaconda3-2020.11-Linux-x86_64.sh
            sudo -u ec2-user bash Anaconda3-2020.11-Linux-x86_64.sh -b -p /home/ec2-user/anaconda
            echo "PATH=/home/ec2-user/anaconda/bin:$PATH" >> /etc/environment
            source /etc/environment
            jupyter notebook --generate-config
            mkdir .jupyter
            cp /root/.jupyter/jupyter_notebook_config.py /home/ec2-user/.jupyter/
            echo "c = get_config()" >> .jupyter/jupyter_notebook_config.py
            echo "c.NotebookApp.ip = '*'" >> .jupyter/jupyter_notebook_config.py
            NB_PASSWORD=$(python3 -c "from notebook.auth import passwd; print(passwd('${password}'))")
            echo "c.NotebookApp.password = u'$NB_PASSWORD'" >> .jupyter/jupyter_notebook_config.py
            rm Anaconda3-2020.11-Linux-x86_64.sh
            mkdir Notebooks
            chmod 777 -R Notebooks .jupyter
            su -c "jupyter lab" -s /bin/sh ec2-user
          - password: !Ref Password #!If [JupyterPasswordDefault, '{{resolve:ssm:JupyterLabPassword:1}}', !Ref Password]

  
  ALBSG:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: Security Group for JupyterLab ALB. Created Automatically.
      SecurityGroupIngress: 
        - CidrIp: 0.0.0.0/0
          Description: Allows HTTP Traffic from anywhere
          FromPort: 80
          ToPort: 80
          IpProtocol: tcp

  ComputeSG:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: Security Group for JupyterLab EC2 Instance. Created Automatically.
      SecurityGroupIngress: 
        - Description: Allows JupyterLab Server Traffic from ALB.
          FromPort: 8888
          IpProtocol: tcp
          SourceSecurityGroupId: !GetAtt [ALBSG, GroupId]
          ToPort: 8890
        - CidrIp: 0.0.0.0/0
          Description: Allows SSH Access from Anywhere
          FromPort: 22
          ToPort: 22
          IpProtocol: tcp
  
  ComputeIAMRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Description: Allows EC2 Access to S3. Created Automatically.
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AmazonS3FullAccess
        - arn:aws:iam::aws:policy/AmazonSageMakerFullAccess

Outputs:
  URL:
    Description: URL of the ALB
    Value: !Join 
      - ''
      - - 'http://'
        - !GetAtt 
          - ALB
          - DNSName
  
  ConnectionString:
    Description: Connection String For SSH On EC2
    Value: !Join
      - ''
      - - 'ssh -i "'
        - !Ref KeyName
        - '.pem" ec2-user@'
        - !GetAtt
          - ComputeInstance
          - PublicDnsName

然而,它按字面解释字符串,所以我实际上并没有得到我的密码,而是解析...本身。

您的密码参数的默认值缺少服务名称 (ssm) 和单引号。

// What you have:
Password:
  Default: {{resolve:JupyterPassword:1}}
  ...

// What it should be:
Password:
  Default: '{{resolve:ssm:JupyterPassword:1}}'
  ...

更新:您已修复问题中的代码。我的回答和下面的评论是否解决了您的问题?如果没有,我不确定你还需要什么。

基于 OP 的评论和新的、更新的模板,并扩展@DennisTraub 的回答。

SSM parameters 在模板中的几乎所有情况下都会解析,但 UserData 除外(顺便说一句,Init 也不起作用)。这意味着,当在 UserData 的上下文中使用时,动态引用将 不会解析 。这是由于安全问题。

UserData 任何可以查看实例基本属性的人都可以在 纯文本 中阅读。这意味着,您的 JLabPassword 将以纯文本形式在 UserData 中提供给所有人查看,如果这样的解决方案是可能的话。

要纠正此问题,应在 UserData 中使用 SSM 参数,如下所示:

  1. 将 IAM 权限 ssm:GetParameter 附加到实例 role/profile 以允许实例访问 SSM Parameter Store。

  2. 而不是 {{resolve:ssm:JLabPassword:1}} 在你的参数中,你可以只传递 JLabPassword 这样 SSM 参数的名称就会传递到 UserData,而不是它的实际价值。

  3. UserData中,请使用AWS CLI get-parameter获取你的JLabPassword的实际值。

以上确保 JLabPassword 的值在 UserData.

中保持私有并且在纯文本中不可见