AWS Systems Manager マネージドインスタンス化

ssm_managed_instance

AWS Systems Manager(以下SSM)のマネージドインスタンス化についての解説をしていきたいと思います。

SSMのマネージドインスタンスとは

マネージドする = 管理される という意味です。

SSMでEC2を管理しますよ、という意味に置き換えられます。

ではどのように管理すればいいのか?

マネージド化するにはいくつか条件があります。

その条件が以下です。

マネージドインスタンス化の条件
  • SSM Agentの導入
  • SSM APIへの経路を確保
  • IAMロールを付与
  • SSM Agentの導入

    SSM APIとSSM Agentが連携し、コントロールを行います。

    SSM Agentは以下AWS公式にもある通り、すでにプリインストールされたAMIが存在します。

    Amazon Linux2などはすでにインストールされているため、インストール作業などは必要ありません。

    SSM Agent がプリインストールされた Amazon Machine Images (AMIs)

    SSM Agent は AWS が提供するいくつかの AMIs にプリインストールされています。

    SSM APIへの経路を確保

    SSM APIとの通信をするためにSSM AgentからAWSサービスエンドポイントに対してHTTPS(443)のアウトバウンドの通信経路を確保してあげる必要があります。

    ここででいうサービスエンドポイントへの通信経路の確保には以下の2パターンがあります。

    プライベートサブネットに配置したEC2にVPCエンドポイントを経由して通信する方法

    パブリックサブネットにNAT gatewayを配置してインターネットを経由して通信する方法

    IAM Roleの付与

    AmazonSSMManagedInstanceCoreというロールをEC2にアタッチする必要があります。

    AmazonSSMManagedInstanceCoreをアタッチすることで、AWSリソースへのアクセスを許可を得ることができます。

    この場合、EC2インスタンスはSSMへアクセスするための権限を引き受けたため、SSMとの通信のやり取りが可能になります。

    IAMロールをEC2へ引き渡すための信頼関係を表しているのが、以下の信頼ポリシーです。

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "sts:AssumeRole"
                ],
                "Principal": {
                    "Service": [
                        "ec2.amazonaws.com"
                    ]
                }
            }
        ]
    }

    どうする: sts:AssumeRole: 一時的な役割をAllow(許可)します
    誰に: ec2.amazonaws.com(EC2インスタンスに)

    直訳すると、主要なサービスはEC2です。一時的なAmazonSSMManagedInstanceCoreという役割を許可します。という意味合いになるかと思います。

    ここまでできればSSMのマネージドインスタンスの前準備が整いました。

    以下CloudFormationを使用していただくと、VPCエンドポイント経由するマネージドインスタンス化されたEC2の作成が可能です。

    AWSTemplateFormatVersion: 2010-09-09
    Description: "VPC Template"
    
    Parameters:
    
      #---------------------------------------------------------------------#
      # Common
      #---------------------------------------------------------------------#
      Prefix:
        Type: String
        Default: "stg"
      
      ec2Prefix:
        Type: String
        Default: "step-server"
    
      
      #---------------------------------------------------------------------#
      # Network
      #---------------------------------------------------------------------#
      VpcCidr:
        Type: String
        Default: "10.0.0.0/16"
    
      PrivateSubnet1Cidr: 
        Type: String
        Default: "10.0.2.0/24"
      
      #---------------------------------------------------------------------#
      # EC2
      #---------------------------------------------------------------------#
      EC2InstanceName:
        Type: String
        Default: "step-server"
      EC2InstanceAMI:
        Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
        Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
      EC2InstanceType:
        Type: String
        Default: "t2.micro"
      EC2InstanceVolumeType:
        Type: String
        Default: "gp2"
      EC2InstanceVolumeSize:
        Type: String
        Default: "8"
    
    Resources: 
      #---------------------------------------------------------------------#
      # Network
      #---------------------------------------------------------------------#
    
      StgVPC:
        Type: AWS::EC2::VPC
        Properties:
          CidrBlock: !Ref VpcCidr
          EnableDnsSupport: true
          EnableDnsHostnames: true
          Tags:
            - Key: Name
              Value: !Sub "${Prefix}-vpc"
    
      StgPrivateSubnet1:
        Type: AWS::EC2::Subnet
        Properties:
          AvailabilityZone: !Sub ${AWS::Region}a
          VpcId: !Ref StgVPC
          CidrBlock: !Ref PrivateSubnet1Cidr
          Tags:
            - Key: Name
              Value: !Sub "${Prefix}-private-subnet1"
      
      StgPrivateRouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId: !Ref StgVPC
          Tags:
            - Key: Name
              Value: !Sub "${Prefix}-rtb-private"
    
      # ルートテーブルをサブネットに関連付け
      PrivateSubnet1TableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          # どのサブネットを指定するか
          SubnetId: !Ref StgPrivateSubnet1
          RouteTableId: !Ref StgPrivateRouteTable
      
      EC2SecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          GroupName: !Sub "${ec2Prefix}-sg"
          GroupDescription: !Sub "${ec2Prefix}-sg"
          VpcId: !Ref StgVPC
          SecurityGroupEgress:
            - CidrIp: 0.0.0.0/0
              IpProtocol: -1
              FromPort: -1
              ToPort: -1
          Tags:
            - Key: Name
              Value: !Sub "${ec2Prefix}-sg"
    
      #---------------------------------------------------------------------#
      # VPC Endpoint
      #---------------------------------------------------------------------#
      SsmVpcEndpointSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          GroupName: !Sub "${Prefix}-ssm-vpc-endpoint-sg"
          GroupDescription: !Sub "${Prefix}-ssm-vpc-endpoint-sg"
          VpcId: !Ref StgVPC
          SecurityGroupIngress:
            - CidrIp: !Ref VpcCidr
              IpProtocol: tcp
              FromPort: 443
              ToPort: 443
          SecurityGroupEgress:
            - CidrIp: 0.0.0.0/0
              FromPort: -1
              IpProtocol: -1
              ToPort: -1
          Tags:
            - Key: Name
              Value: !Sub "${Prefix}-ssm-vpc-endpoint-sg"
    
      SsmVpcEndpoint: 
        Type: AWS::EC2::VPCEndpoint
        Properties: 
          PrivateDnsEnabled: true
          VpcEndpointType: Interface
          SecurityGroupIds: 
            - !Ref SsmVpcEndpointSecurityGroup
          ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
          SubnetIds: 
            - !Ref StgPrivateSubnet1
          VpcId: !Ref StgVPC
    
      SsmMessagesVpcEndpoint: 
        Type: AWS::EC2::VPCEndpoint
        Properties: 
          PrivateDnsEnabled: true
          VpcEndpointType: Interface
          SecurityGroupIds: 
            - !Ref SsmVpcEndpointSecurityGroup
          ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
          SubnetIds: 
            - !Ref StgPrivateSubnet1
          VpcId: !Ref StgVPC
    
      Ec2MessagesVpcEndpoint: 
        Type: AWS::EC2::VPCEndpoint
        Properties: 
          # PolicyDocument: Json
          PrivateDnsEnabled: true
          VpcEndpointType: Interface
          SecurityGroupIds: 
            - !Ref SsmVpcEndpointSecurityGroup
          ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
          SubnetIds: 
            - !Ref StgPrivateSubnet1
          VpcId: !Ref StgVPC
    
      #---------------------------------------------------------------------#
      # Ec2InstanceProfile
      #---------------------------------------------------------------------#
      Ec2Role: 
        Type: AWS::IAM::Role
        Properties: 
          RoleName: !Sub "${Prefix}-EC2SSMRoleForRDSAccess"
          AssumeRolePolicyDocument: 
            Version: 2012-10-17
            Statement: 
              - Effect: Allow
                Principal: 
                  Service: 
                    - ec2.amazonaws.com
                Action: 
                  - "sts:AssumeRole"
          ManagedPolicyArns: 
            - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
    
      Ec2InstanceProfile:
        Type: AWS::IAM::InstanceProfile
        Properties: 
          InstanceProfileName: !Sub "${Prefix}-ec2-instance-profile"
          Roles: 
            - !Ref Ec2Role
    
      EC2Instance:
        Type: AWS::EC2::Instance
        Properties:
          Tags: 
          - Key: Name
            Value: !Sub "${ec2Prefix}"
          DisableApiTermination: false
          EbsOptimized: false
          ImageId: !Ref EC2InstanceAMI
          InstanceType: !Ref EC2InstanceType
          IamInstanceProfile: !Ref Ec2InstanceProfile
          SecurityGroupIds:
            - !Ref EC2SecurityGroup
          SubnetId: !Ref StgPrivateSubnet1
          BlockDeviceMappings: 
             - 
                DeviceName: /dev/xvda
                Ebs: 
                  DeleteOnTermination: true
                  VolumeType: !Ref EC2InstanceVolumeType
                  VolumeSize: !Ref EC2InstanceVolumeSize

    CloudFormationデプロイ方法

    以下コマンドをAWS CLIコマンドで実行します。

    aws cloudformation deploy \
    --stack-name StackName \
    --template-file filename.yml
    --capabilities CAPABILITY_NAMED_IAM

    AWS CLIコマンドをすぐに使用するためにはAmazon CloudShellを使用すると便利です。

    AWSサービスを「CloudShell」で検索します。

    次のようなコマンドライン画面が表示されます。

    右上のアクション -> ファイルのダウンロードから作成したymlファイルをアップロードします

    ファイルがアップロードされたことを確認するためにlsで確認することができます。

    ファイルがアップロードされたことを確認できたら、先ほど示したAWS CLIコマンドでdeployします。

    aws cloudformation deploy \
    --stack-name StackName \
    --template-file filename.yml
    --capabilities CAPABILITY_NAMED_IAM

    問題なくdeployが実行されると以下のように表示されます。

    deployが問題なく完了すると以下のようにステータスがCREATE_COMPLETEと表示され無事にdeployが完了です。

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です