スキップしてコンテンツを表示

AWS CloudFormation を使用し、AWS PrivateLink エンドポイントを使用する Amazon EC2 に DataSync エージェントを自動でデプロイする方法を教えてください。

所要時間4分
0

AWS CloudFormation を使用し、AWS PrivateLink エンドポイントを使用するプライベートサブネット内の Amazon Elastic Compute Cloud (Amazon EC2) 上に、AWS DataSync エージェントを作成したいと考えています。

解決策

AWS CloudFormation を使用すると、Amazon EC2 インスタンス上で実行され、AWS PrivateLink エンドポイントを使用する DataSync エージェントを自動で作成、有効化できます。

Lambda 関数を使用するカスタムリソースが組み込まれた YAML テンプレートを使用し、DataSync エージェントの有効化キーを取得します。次の YAML コードをコピーし、AWS CloudFormation テンプレートとして保存します。

AWSTemplateFormatVersion: '2010-09-09'  
Description: >-  
  This CloudFormation template deploys an EC2 instance with the AWS DataSync  
  agent AMI.  

Metadata:  
  AWS::CloudFormation::Interface:  
    ParameterGroups:  
    - Label:  
        default: Deploy DataSync agent on Amazon EC2 via PrivateLink VPC endpoint  
      Parameters:  
      - InstanceType  
      - ImageId  
      - SubnetId  
      - VpcId  
    ParameterLabels:  
      InstanceType:  
        default: Agent EC2 Instance Size  
      SubnetId:  
        default: Select your Subnet  
      VpcId:  
        default: Select your VPC Contains Above Subnet  
      ImageId:  
        default: Agent Image (Keep default - Do NOT change)  

Parameters:  
  InstanceType:  
    Description: Instance type for the DataSync agent EC2 instance  
    Type: String  
    Default: m5.2xlarge  
    AllowedValues:  
      - m5.2xlarge  
      - m5.4xlarge  
    ConstraintDescription: must be a valid EC2 instance type.  

  ImageId:  
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>  
    Default: /aws/service/datasync/ami  

  SubnetId:  
    Type: AWS::EC2::Subnet::Id  
    Description: The subnet where to deploy the instance  

  VpcId:   
    Type: AWS::EC2::VPC::Id  
    Description: Select the VPC containing the subnet selected above.  

Resources:  
  NewKeyPair:  
    Type: 'AWS::EC2::KeyPair'  
    Properties:  
      KeyName: !Sub "ssh-key-cfn-stack-${AWS::StackName}"  

  DataSyncAgentInstance:  
    Type: AWS::EC2::Instance  
    Properties:  
      ImageId: !Ref ImageId  
      InstanceType: !Ref InstanceType  
      SubnetId: !Ref SubnetId  
      SecurityGroupIds:  
        - !Ref DataSyncAgentSecurityGroup  
      KeyName: !Ref NewKeyPair  
      Tags:  
        - Key: "Name"  
          Value: !Ref 'AWS::StackName'  

  DataSyncAgentSecurityGroup:  
    Type: AWS::EC2::SecurityGroup  
    Properties:  
      GroupDescription: Enable SSH access and DataSync agent communication  
      SecurityGroupIngress:  
        - IpProtocol: tcp  
          FromPort: 80  
          ToPort: 80  
          CidrIp: 0.0.0.0/0  
      VpcId: !Ref VpcId  

  DsInterfaceEndpoint:  
    Type: 'AWS::EC2::VPCEndpoint'  
    Properties:  
      VpcEndpointType: 'Interface'  
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.datasync'  
      VpcId: !Ref VpcId  
      SubnetIds:   
        - !Ref SubnetId  
      SecurityGroupIds:  
        - !Ref DsVpceSecurityGroup  

  DsVpceSecurityGroup:  
    Type: 'AWS::EC2::SecurityGroup'  
    Properties:  
      GroupDescription: 'Allow control traffic from agent'  
      VpcId: !Ref VpcId  
      SecurityGroupIngress:  
        - IpProtocol: tcp  
          FromPort: 443  
          ToPort: 443  
          CidrIp: !Sub "${DataSyncAgentInstance.PrivateIp}/32"  
        - IpProtocol: tcp  
          FromPort: 1024  
          ToPort: 1064  
          CidrIp: !Sub "${DataSyncAgentInstance.PrivateIp}/32"  
        - IpProtocol: tcp  
          FromPort: 22  
          ToPort: 22  
          CidrIp: !Sub "${DataSyncAgentInstance.PrivateIp}/32"  

  LambdaActivator:  
      Type: AWS::Lambda::Function  
      DependsOn: DsInterfaceEndpoint  
      Properties:  
        FunctionName: !Ref 'AWS::StackName'  
        Description: 'Lambda function to get the activation key'  
        VpcConfig:   
          SecurityGroupIds:  
            - !Ref DataSyncAgentSecurityGroup  
          SubnetIds:  
            - !Ref SubnetId  
        Environment:  
          Variables:  
            DsDnsEntryList: !Select [1, !Split [':', !Select [0, !GetAtt DsInterfaceEndpoint.DnsEntries]]]  
        Code:  
          ZipFile: !Sub |  
            import boto3, json, urllib.request, socket, time, os  
            import cfnresponse  

            print('Loading function')  
            def handler(event, context):  
                print('EVENT:')  
                print(event)  
                responseData = {}  
                try:  
                    if event['RequestType'] == "Create":  
                        agentIp = "${DataSyncAgentInstance.PrivateIp}"  
                        awsRegion = "${AWS::Region}"  
                        VpceDnsName = os.environ['DsDnsEntryList']  
                        VpceIp = socket.gethostbyname(VpceDnsName)  

                        print("Agent IP: " + agentIp)  
                        print("AWS Region: " + awsRegion)  
                        print("VPC Endpoint DNS Name: " + VpceDnsName)  
                        print("VPC IP: " + VpceIp)  

                        activateUrl = "http://" + agentIp + "/?gatewayType=SYNC&activationRegion=" + awsRegion + "&privateLinkEndpoint=" + VpceIp + "&endpointType=PRIVATE_LINK&no_redirect"  
                        print(activateUrl)  

                        time.sleep(30)  
                        print("Sending URL for Getting activation key")  
                        res = urllib.request.urlopen(urllib.request.Request(  
                            url=activateUrl,  
                            method='GET'),  
                            timeout=600)  

                        print("HTTP Response :" + str(res.status) + " " + res.reason)  
                        activationKey = res.read().decode('utf-8')  

                        responseData['Data'] = activationKey  

                    elif event['RequestType'] == "Delete":  
                        print("Lambda is being deleted by the CloudFormation stack!!")  

                    else:  
                        print("Should not perform actions!!")  

                    cfnresponse.send(event, context, cfnresponse.SUCCESS,   
                            responseData, 'cfn-customresource-id')  

                    return {  
                      'body': json.dumps('Execution completed!')  
                    }  

                except Exception as e:  
                    print(e)  
                    cfnresponse.send(event, context, cfnresponse.FAILED,   
                            responseData, 'cfn-customresource-id')  
                    raise e  

        Handler: index.handler  
        Role: !GetAtt FunctionIamRole.Arn  
        Runtime: python3.12  
        Timeout: 300  

  ActivatorInvoker:  
    DependsOn:  
      - LambdaActivator  
      - DataSyncAgentInstance  
    Type: AWS::CloudFormation::CustomResource  
    Properties:  
      ServiceToken: !GetAtt LambdaActivator.Arn  

  FunctionIamRole:  
    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/service-role/AWSLambdaBasicExecutionRole"  
      Policies:   
        - PolicyName: !Sub "ec2-permissions-lambda-${AWS::StackName}"  
          PolicyDocument:  
            Version: '2012-10-17'  
            Statement:  
              - Effect: Allow  
                Action:  
                  - ec2:CreateNetworkInterface  
                  - ec2:DeleteNetworkInterface  
                  - ec2:DescribeNetworkInterfaces  
                Resource: "*"  

  ActiveAgent:  
    Type: AWS::DataSync::Agent  
    Properties:  
      ActivationKey: !GetAtt ActivatorInvoker.Data  
      AgentName: !Sub "datasync-agent-created-from-cfn-${AWS::StackName}"  
      VpcEndpointId: !GetAtt DsInterfaceEndpoint.Id  
      SecurityGroupArns:  
        - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${DsVpceSecurityGroup}"  
      SubnetArns:  
        - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${SubnetId}"  

Outputs:  
  KeyPairId:  
    Description: KeyPairId of the newly created SSH Key to connect the EC2 (you can download the key under Parameter Store, a capability of AWS Systems Manager)  
    Value: !GetAtt NewKeyPair.KeyPairId  

  PrivateIp:  
    Description: PrivateIp of the newly created EC2 instance  
    Value: !GetAtt DataSyncAgentInstance.PrivateIp  

  ActivationKey:  
    Description: Agent Activation Key  
    Value: !GetAtt ActivatorInvoker.Data

AWS CloudFormation コンソールで AWS CloudFormation スタックを作成します。[スタックの作成] ページの [前提条件 - テンプレートの準備] で [既存のテンプレートを選択] を選択します。[テンプレートを指定] で [テンプレートファイルをアップロード] を選択し、保存したテンプレートを選択します。

注: [パラメータ] セクションで指定するプライベートサブネットは、Amazon 仮想プライベートクラウド (VPC) ゲートウェイエンドポイント経由で Amazon Simple Storage Service (Amazon S3) サービスにアクセスできる必要があります。

AWS公式更新しました 1年前
コメントはありません

関連するコンテンツ