OPS

PythonとBoto3を使用して、AWSのインフラ構築を自動化してみた

2022.03.16

本記事のポイント

Amazon Web Services(以下、AWS)では、EC2などのサービスをPythonから操作することができる「Boto3(AWS SDK for Python)」というライブラリを提供しています。

本記事では、AWSのインフラ構築をPythonとBoto3で自動化する方法をご紹介します。



はじめに

昨今、サーバ運用・構築などをオンプレミスから、AWSやGoogle Cloud、Microsoft Azure などのクラウドへ移行する企業が増え続けています。

クラウドはオンプレミスと比べて導入コストを抑えることができメリットがたくさんありますが、コンソール上でボタンクリックやフォームに情報を入力するなど、手動で行う作業を面倒に感じる方もいらっしゃるかと思います。

そこで、今回はクラウドの中でも特にシェア率が高いAWSのインフラ構築を、PythonとBoto3を使用して自動化する方法をご紹介します。

Boto3(AWS SDK for Python)とは?

今回使用するBoto3について簡単に説明します。 Boto3とは、「AWS SDK for Python」のことで、AWS の各種サービス(Amazon S3、Amazon EC2、Amazon RDSなど)をPythonから操作するためのライブラリです。これはAWSが公式で提供しています。

内部では、AWS CLIにも利用されている Botocore というライブラリを利用しています。

構成

今回はWEBアプリケーションを運用する想定で、VPC内にパブリックサブネットとプライベートサブネットがあり、インターネットゲートウェイ、EC2、RDSがあるシンプルなインフラ環境を構築します。

事前準備

1. 前提条件

今回検証するにあたり、以下3つの条件が必要になります。

  • 自分のPCにPython3系がインストールされていること
  • Boto3がインストールされていること
  • AWSアカウントを作成していること

  • 今回、Pythonのバージョンは3.9を使用しています。

    2. AWSアクセスキー・シークレットアクセスキーの用意

    以下のアクセスポリシーが付与されているIAMユーザー、IAMグループを用意し、そのユーザーのアクセスキー、シークレットアクセスキーを用意してください。

    アクセスポリシー名
  • AmazonEC2FullAccess
  • AmazonRDSFullAccess
  • コーディング方法

    全体の流れは以下になります。

    1. VPCを作成
    2. 作成したVPCにプライベートサブネットを作成
    3. 作成したVPCにパブリックサブネットを作成
    4. 作成したパブリックサブネットにEC2インスタンスを作成
    5. RDS用のサブネットグループを作成
    6. RDSインスタンスを作成

    今回作成するプログラムはこれら6つの工程をBoto3で作成していきます。

    まず、使用したいサービス、リージョン、アベイラビリティゾーンなどを設定するメソッドと、タグを作成するメソッドを作成します。

    
    # config(設定)メソッド
    def __awsConfig(self, service):
      # 引数で使用するサービスを指定、リージョン・アベイラビリティゾーンはインスタンス変数を指定
      client = boto3.client(
        service,
        aws_access_key_id = self.access_key,
        aws_secret_access_key = self.secret_access_key,
        region_name = self.region_name
      )
      return client
    

    config設定メソッドは、引数にサービス(ec2、rdsなど)を指定します。

    
    # タグ作成メソッド
    def __createTags(self, client, id, name):
      # 指定したidの各サービスにNameタグ、Ownerタグをつける
      tags = client.create_tags(
        Resources=[id], 
        Tags=[
          {'Key': 'Name', 'Value': name},
          {'Key': 'Owner', 'Value': '〓メールアドレス〓'}
        ]
      )
      return tags
    

    タグ作成メソッドは、引数にconfigメソッドのサービスと、各サービス(EC2)などのid、タグ名を指定します。

    1. VPCの作成

    東京リージョンにVPCを作成します。

    
    # VPC作成メソッド
    def __createVpc(self):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # vpcの作成
      vpc = client.create_vpc(CidrBlock='10.0.0.0/16', InstanceTenancy='default')
      # vpcのidを取得
      id = vpc['Vpc']['VpcId']
      
      # 作成したVPCにタグをつける
      tags = self.__createTags(client, id, 'test-vpc')
      return id
    

    create_vpc()でVPCを作成します。引数にはCIDRブロック、テナンシーを指定します。

    VPCの次はプライベートサブネットを作成します。

    2. プライベートサブネットの作成

    「1. VPCの作成」で作成したVPCにプライベートサブネットを作成します。 後続でパブリックサブネットも作成するので、サブネット作成を使いまわせるようにサブネット作成のみのメソッドを作成しました。

    
    # サブネット作成メソッド
    def __createSubnet(self, vpc_id, availability_zone, cidr_block, name):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # サブネットの作成
      subnet = client.create_subnet(AvailabilityZone=availability_zone, CidrBlock=cidr_block, VpcId=vpc_id)
      # サブネットのidを取得
      id = subnet['Subnet']['SubnetId']
      
      # 作成したサブネットにタグをつける
      tags = self.__createTags(client, id, name)
      return id
    

    create_subnet()でサブネットを作成します。引数にはアベイラビリティゾーン、CIDRブロック、作成したVPCのidを指定します。

    CIDRブロックが「10.0.2.0/24」「10.0.20.0/24」の2つのサブネットを、サブネット作成メソッドを使用して作成していきます。

    
    # プライベートサブネット作成メソッド
    def __createPrivateSubnet(self, vpc_id):
      availability_zone_list = [self.availability_zone_1, self.availability_zone_2]
      cidr_block_list = ['10.0.2.0/24', '10.0.20.0/24']
      name_list = ['private_subnet_1a', 'private_subnet_1c']
      subnet_id_list = []
      
      # アベイラビリティゾーン、CIDRブロックのリストをループさせ、作成したVPCにサブネットを作成
      for availability_zone, cidr_block, name in zip(availability_zone_list, cidr_block_list, name_list):
        
        # CIDRブロックが「10.0.2.0/24」「'10.0.20.0/24'」の2つのプライベートサブネットを作成
        subnet_id = self.__createSubnet(vpc_id, availability_zone, cidr_block, name)
        subnet_id_list.append(subnet_id)
        
      return subnet_id_list
    

    プライベートサブネットが作成できたので、次はパブリックサブネットを作成します。

    3. パブリックサブネットの作成

    パブリックサブネットもプライベートサブネット同様、作成したVPCに、CIDRブロックが「10.0.1.0/24」のサブネットを作成します。

    また、パブリックサブネット上にEC2インスタンスを作成し、インターネットと通信させる必要があるので、インターネットゲートウェイ、ルートテーブル、カスタムルートを作成します。

    # パブリックサブネット作成メソッド
    def __createPublicSubnet(self, vpc_id):
      # サブネット作成メソッドの呼び出し(CIDRブロックが「10.0.1.0/24」のパブリックサブネットを作成)
      subnet_id = self.__createSubnet(vpc_id, self.availability_zone_1, '10.0.1.0/24', 'public_subnet_1a')
      
      # インターネットゲートウェイ作成メソッドの呼び出し
      internet_gateway_id = self.__createInternetGateway(vpc_id)
      
      # ルートテーブル作成メソッドの呼び出し
      route_table_id = self.__createRouteTable(vpc_id)
      
      # カスタムルート作成メソッドの呼び出し
      create_route = self.__createRoute(internet_gateway_id, route_table_id, subnet_id)
      return subnet_id
    

    パブリックサブネット作成メソッドでは、サブネット作成メソッド、インターネットゲートウェイ作成メソッド、ルートテーブル作成メソッド、カスタムルート作成メソッドを実行しています。 インターネットゲートウェイ作成メソッドから順に説明します。

    
    # インターネットゲートウェイ作成メソッド
    def __createInternetGateway(self, vpc_id):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      #インターネットゲートウェイの作成
      internet_gateway = client.create_internet_gateway()
      # インターネットゲートウェイのidを取得
      internet_gateway_id = internet_gateway['InternetGateway']['InternetGatewayId']
      
      # 作成したインターネットゲートウェイにタグをつける
      tags = self.__createTags(client, internet_gateway_id, 'test-igw')
      
      # インターネットゲートウェイをアタッチ
      attach = client.attach_internet_gateway(InternetGatewayId=internet_gateway_id, VpcId=vpc_id)
      return internet_gateway_id
    

    create_internet_gateway()でインターネットゲートウェイを作成します。 インターネットゲートウェイの作成後、VPCにアタッチする必要があります。 アタッチはattach_internet_gateway()で行います。引数には作成したVPCのid、インターネットゲートウェイのidを指定します。

    次に、ルートテーブルを作成します。

    
    # ルートテーブル作成メソッド
    def __createRouteTable(self, vpc_id):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # ルートテーブルの作成
      route_table = client.create_route_table(VpcId=vpc_id)
      # ルートテーブルのidを取得
      id = route_table['RouteTable']['RouteTableId']
      
      # 作成したルートテーブルにタグをつける
      tags = self.__createTags(client, id, 'test-public-route-table')
      return id
    

    create_route_table()でルートテーブルを作成します。引数には作成したVPCのidを指定します。

    ルートテーブルを作成した後にカスタムルートを作成します。

    
    # カスタムルート作成メソッド
    def __createRoute(self, gateway_id, route_table_id, subnet_id):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # 作成したルートテーブルにカスタムルートを作成(送信先「0.0.0.0/0」)
      create_route = client.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=gateway_id, RouteTableId=route_table_id)
      
      # サブネットの関連付け
      associate_route_subnet = client.associate_route_table(RouteTableId=route_table_id, SubnetId=subnet_id)
      
      # パブリックIPアドレスを自動的に受信するように設定
      attribute_subnet = client.modify_subnet_attribute(
        MapPublicIpOnLaunch={'Value': True},
        SubnetId=subnet_id
      )
      
      return create_route
    

    create_route()でカスタムルートを作成します。引数にCIDRブロック(送信先)、作成したインターネットゲートウェイのid、ルートテーブルのidを指定します。

    作成したカスタムルートに、サブネットの関連付けを行います。このサブネットの関連付けを行うことにより、対象のサブネットがルートテーブルとインターネットゲートウェイによってインターネットに接続ができます。

    associate_route_table()でサブネットの関連付けを行います。 引数にはサブネットのid、ルートテーブルのidを指定します。

    4. EC2インスタンスの作成

    「3. パブリックサブネットの作成」で作成したパブリックサブネット内に、EC2インスタンスを作成します。

    
    # EC2インスタンス作成メソッド
    def __createInstance(self, subnet_id, vpc_id, security_group_name):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # セキュリティグループ作成メソッドの呼び出し
      security_group_id = self.__createSecurityGroup(vpc_id, security_group_name)
      
      # キーペア作成メソッドの呼び出し
      key_name = self.__createKeyPair()
      
      # パブリックIPアドレスの自動割り当ての有効
      valid_auto_public_ip = client.modify_subnet_attribute(
        MapPublicIpOnLaunch={'Value': True},
        SubnetId=subnet_id
      )
      
      # AMIは「Amazon Linux2」を使用
      ami_id = 'ami-04204a8960917fd92'
      
      # EC2インスタンス作成
      ec2_instance = client.run_instances(
        ImageId=ami_id, 
        MinCount=1, 
        MaxCount=1, 
        InstanceType="t2.micro", 
        SecurityGroupIds=[security_group_id], 
        KeyName=key_name, 
        SubnetId=subnet_id
      )
      
      # インスタンスidの取得
      instance_id = ec2_instance['Instances'][0]['InstanceId']
      
      # インバウンドルール設定メソッド呼び出し
      inbound_rule = self.__settingInboundRules(security_group_id, 22)
      
      # 作成したインスタンスにタグをつける
      tags = self.__createTags(client, instance_id, 'test-web-server')
      return instance_id, security_group_id# EC2インスタンス作成メソッド
    def __createInstance(self, subnet_id, vpc_id, security_group_name):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # セキュリティグループ作成メソッドの呼び出し
      security_group_id = self.__createSecurityGroup(vpc_id, security_group_name)
      
      # キーペア作成メソッドの呼び出し
      key_name = self.__createKeyPair()
      
      # パブリックIPアドレスの自動割り当ての有効
      valid_auto_public_ip = client.modify_subnet_attribute(
        MapPublicIpOnLaunch={'Value': True},
        SubnetId=subnet_id
      )
      
      # AMIは「Amazon Linux2」を使用
      ami_id = 'ami-04204a8960917fd92'
      
      # EC2インスタンス作成
      ec2_instance = client.run_instances(
        ImageId=ami_id, 
        MinCount=1, 
        MaxCount=1, 
        InstanceType="t2.micro", 
        SecurityGroupIds=[security_group_id], 
        KeyName=key_name, 
        SubnetId=subnet_id
      )
      
      # インスタンスidの取得
      instance_id = ec2_instance['Instances'][0]['InstanceId']
      
      # インバウンドルール設定メソッド呼び出し
      inbound_rule = self.__settingInboundRules(security_group_id, 22)
      
      # 作成したインスタンスにタグをつける
      tags = self.__createTags(client, instance_id, 'test-web-server')
      return instance_id, security_group_id
    

    まず、インスタンス作成時に使用するEC2用のセキュリティグループを作成します。

    
    # セキュリティグループ作成メソッド
    def __createSecurityGroup(self, vpc_id, name):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # セキュリティグループ作成
      security_group = client.create_security_group(Description=name, GroupName=name, VpcId=vpc_id)
      
      # 作成したセキュリティグループのidを取得
      security_group_id = security_group['GroupId']
      
      # 作成したセキュリティグループにタグをつける
      tags = self.__createTags(client, security_group_id, name)
      return security_group_id
    

    create_security_group()でセキュリティグループを作成します。引数にVPCのid、セキュリティグループ名、説明を指定します。

    次に、EC2インスタンスにSSH接続するためのキーペアを作成します。

    
    # SSH用のキーペア作成メソッド
    def __createKeyPair(self):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # 鍵の名前
      key_name = 'test-key.pem'
      # キーペアの作成
      key = client.create_key_pair(KeyName=key_name)
      
      # キーペアidの取得
      key_pair_id = key['KeyPairId']
      
      # 作成したキーペアを取得し、pemファイルを作成
      key_material = key['KeyMaterial']
      with open(key_name, mode='w') as f:
        f.write(key_material)
      
      # 作成したキーペアにタグをつける
      tags = self.__createTags(client, key_pair_id, 'test-key-pair')
      return key_name
    

    create_key_pair()でキーペアを作成します。引数には鍵のファイル名を指定します。 キーペアを作成した後、キーペアの情報を取得し作成します。

    キーペアを作成した後に、パブリックIPアドレスの自動割り当てを有効化します。 modify_subnet_attribute()でパブリックIPアドレスを自動的に受信(自動割り当て)するように設定します。引数にはパブリックIPアドレスの割り当てをするのでTrue、作成したパブリックサブネットのidを指定します。

    run_instances()でEC2インスタンスを作成します。 引数にAMIのid、インスタンスのタイプ、作成したセキュリティグループのリスト、作成したキーペア、作成したパブリックサブネットのidを指定します。

    EC2インスタンスを作成した後に、SSH接続用のインバウンドルールを設定します。

    
    # インバウンドルール設定メソッド
    def __settingInboundRules(self, security_group_id, port):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # マイIP(現在のグローバルIPアドレス)を取得
      url = 'https://ifconfig.me'
      request = requests.get(url)
      ip_address = '{}/32'.format(request.text)
      
      # インバウンドルールの作成
      inbound_rule = client.authorize_security_group_ingress(
        GroupId=security_group_id,
        IpPermissions=[
          {
            'FromPort': port,
            'IpProtocol': 'tcp',
            'IpRanges': [
              {
                'CidrIp': ip_address,
                'Description': 'test inbound rule',
              },
            ],
            'ToPort': port
          }
        ]
      )
      
      # セキュリティグループルール(インバウンドルール)idの取得
      inbound_rule_id = inbound_rule['SecurityGroupRules'][0]['SecurityGroupRuleId']
      
      # 作成したインバウンドルールにタグをつける
      tags = self.__createTags(client, inbound_rule_id, 'test-inbound-rule-ssh')
      return inbound_rule
    

    下記サイトでマイIP(現在使用しているグローバルIPアドレス)を取得します。

    https://ifconfig.me

    authorize_security_group_ingress()でインバウンドルールの設定を行います。 引数には対象のセキュリティグループid、許可するポート、許可するIPアドレス(マイIP)、説明を指定します。

    マイIPから設定した22番ポート(SSH)への接続を許可します。

    5. サブネットグループの作成

    RDSを起動するために、「2. プライベートサブネットの作成」で作成した2つプライベートサブネットをグループ化します。

    
    # サブネットグループ作成メソッド
    def __createSubnetGroup(self, subnet_id_list):
      # client のサービスをRDSに指定
      client = self.__awsConfig('rds')
      
      # サブネットグループの作成
      subnet_group = client.create_db_subnet_group(
        DBSubnetGroupName='rds-subnet-group', 
        DBSubnetGroupDescription='rds-subnet-group', 
        SubnetIds=subnet_id_list,
        Tags=[
          {'Key': 'Name', 'Value': 'test-rds-subnet-group'},
          {'Key': 'Owner', 'Value': '〓メールアドレス〓'}
        ]
      )
      
      #サブネットグループの名前を取得
      subnet_group_name = subnet_group['DBSubnetGroup']['DBSubnetGroupName']
      return subnet_group_name
    

    create_db_subnet_group()でサブネットグループを作成します。引数には名前、説明、対象のサブネット(プライベートサブネット)のidのリスト、タグの名前、メールアドレスを指定します。

    6. RDSインスタンスの作成

    「2. プライベートサブネットの作成」で作成したプライベートサブネットにRDSインスタンス(データベース)を作成します。「5. サブネットグループの作成」で作成したサブネットグループ作成メソッドと、「4. EC2インスタンスの作成 」で使用したセキュリティグループ作成メソッドを使用します。

    
    # データベース(RDS)の作成メソッド
    def __createRds(self, subnet_id_list, vpc_id, ec2_security_group_id):
      # client のサービスをRDSに指定
      client = self.__awsConfig('rds')
      
      # サブネットグループメソッドの呼び出し
      subnet_group_name = self.__createSubnetGroup(subnet_id_list)
      
      # セキュリティグループ作成メソッドの呼び出し(RDS用を作成)
      security_group_id = self.__createSecurityGroup(vpc_id, 'test-rds-sg')
      
      # データベースの作成
      rds = client.create_db_instance(
        DBName='test_db',
        DBInstanceIdentifier='test-rds',
        DBInstanceClass='db.t2.micro',
        AllocatedStorage=20,
        Engine='mysql',
        MasterUsername='〓任意のマスターユーザー名〓',
        MasterUserPassword='〓任意のパスワード(8文字以上)〓',
        VpcSecurityGroupIds=[security_group_id],
        AvailabilityZone=self.availability_zone_1,
        DBSubnetGroupName=subnet_group_name,
        Port=3306,
        EngineVersion='8.0.27'
      )
      
      # RDS用セキュリティグループインバウンドルール設定メソッドの呼び出し
      inbound_rule = self.__settingRdsInboundRules(security_group_id, ec2_security_group_id, vpc_id)
      
      rds_name = rds['DBInstance']['DBInstanceIdentifier']
      return rds_name
    

    まず、データベースで使用するRDS用のセキュリティグループを作成します。 EC2インスタンス作成時に使用した、セキュリティグループ作成メソッドを使用します。

    create_db_instance()でデータベースを作成します。 引数にはデータベース名、DBインスタンス識別子、DBインスタンスクラス、ストレージ割り当てサイズ(GiB)、エンジン、マスターユーザー名、マスターユーザーのパスワード、作成したRDSのVPCセキュリティグループのid、アベイラビリティゾーン、サブネットグループ名、ポート、エンジンのバーションを指定します。

    次に、作成したRDS用のセキュリティグループにインバウンドルールを設定します。

    
    # RDS用セキュリティグループインバウンドルール設定メソッド
    def __settingRdsInboundRules(self, security_group_id, ec2_security_group_id, vpc_id):
      # client のサービスをEC2に指定
      client = self.__awsConfig('ec2')
      
      # インバウンドルールの作成
      inbound_rule = client.authorize_security_group_ingress(
        GroupId=security_group_id,
        IpPermissions=[
          {
            'FromPort': 3306,
            'IpProtocol': 'tcp',
            'ToPort': 3306,
            'UserIdGroupPairs': [
              {
                'Description': 'test rds inbound rule',
                'GroupId': ec2_security_group_id,
                'UserId': '〓AWSアカウントID〓',
                'VpcId': vpc_id
              }
            ]
          }
        ],
      )
      
      # セキュリティグループルール(インバウンドルール)idの取得
      inbound_rule_id = inbound_rule['SecurityGroupRules'][0]['SecurityGroupRuleId']
      
      # 作成したインバウンドルールにタグをつける
      tags = self.__createTags(client, inbound_rule_id, 'test-inbound-rule-rds')
      return inbound_rule
    

    EC2インスタンス作成時に使用したセキュリティグループ作成と同様に、 authorize_security_group_ingress()を使用します。引数には作成したRDS用のセキュリティグループのid、ポート、プロトコル、説明、EC2インスタンスセキュリティグループのid、アカウントID、VPCのidを指定します。

    RDS用のセキュリティグループでは、作成したEC2インスタンスからのみ接続を許可したいので、「UserIdGroupPairs」にEC2インスタンスのセキュリティグループのid、AWSアカウントID、VPCのidを指定する必要があります。

    上記の指定することにより、インバウンドルールの「ソース」項目にセキュリティグループが指定され、指定したセキュリティグループを設定してあるEC2インスタンスからのみの接続を許可することができます。

    これでインフラ構築自動化の準備が整いましたので、プログラムを実行し、実際にインスタンスなどが作成されているか検証を行います。

    ソースコード全文は以下です。
    ※ファイル名を「create_aws_infra.py」としています。

    
    # Boto3のインポート
    import boto3
    import requests
    
    
    # インフラ環境作成クラス
    class CreateAwsInfra():
      def __init__(self, access_key, secret_access_key):
        
        # インスタンス変数(アクセスキー・シークレットアクセスキー・リージョン・アベイラビリティゾーン)
        self.access_key = access_key
        self.secret_access_key = secret_access_key
        self.region_name = 'ap-northeast-1'
        self.availability_zone_1 = 'ap-northeast-1a'
        self.availability_zone_2 = 'ap-northeast-1c'
      
      
      # config(設定)メソッド
      def __awsConfig(self, service):
        # 引数で使用するサービスを指定、リージョン・アベイラビリティゾーンはインスタンス変数を指定
        client = boto3.client(
          service,
          aws_access_key_id = self.access_key,
          aws_secret_access_key = self.secret_access_key,
          region_name = self.region_name
        )
        return client
      
      
      # タグ作成メソッド
      def __createTags(self, client, id, name):
        # 指定したidの各サービスにNameタグ、Ownerタグをつける
        tags = client.create_tags(
          Resources=[id], 
          Tags=[
            {'Key': 'Name', 'Value': name},
            {'Key': 'Owner', 'Value': '〓メールアドレス〓'}
          ]
        )
        return tags
      
      
      # VPC作成メソッド
      def __createVpc(self):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # vpcの作成
        vpc = client.create_vpc(CidrBlock='10.0.0.0/16', InstanceTenancy='default')
        # vpcのidを取得
        id = vpc['Vpc']['VpcId']
        
        # 作成したVPCにタグをつける
        tags = self.__createTags(client, id, 'test-vpc')
        return id
      
      
      # サブネット作成メソッド
      def __createSubnet(self, vpc_id, availability_zone, cidr_block, name):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # サブネットの作成
        subnet = client.create_subnet(AvailabilityZone=availability_zone, CidrBlock=cidr_block, VpcId=vpc_id)
        # サブネットのidを取得
        id = subnet['Subnet']['SubnetId']
        
        # 作成したサブネットにタグをつける
        tags = self.__createTags(client, id, name)
        return id
      
      
      # インターネットゲートウェイ作成メソッド
      def __createInternetGateway(self, vpc_id):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        #インターネットゲートウェイの作成
        internet_gateway = client.create_internet_gateway()
        # インターネットゲートウェイのidを取得
        internet_gateway_id = internet_gateway['InternetGateway']['InternetGatewayId']
        
        # 作成したインターネットゲートウェイにタグをつける
        tags = self.__createTags(client, internet_gateway_id, 'test-igw')
        
        # インターネットゲートウェイをアタッチ
        attach = client.attach_internet_gateway(InternetGatewayId=internet_gateway_id, VpcId=vpc_id)
        return internet_gateway_id
      
      
      # ルートテーブル作成メソッド
      def __createRouteTable(self, vpc_id):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # ルートテーブルの作成
        route_table = client.create_route_table(VpcId=vpc_id)
        # ルートテーブルのidを取得
        id = route_table['RouteTable']['RouteTableId']
        
        # 作成したルートテーブルにタグをつける
        tags = self.__createTags(client, id, 'test-public-route-table')
        return id
      
      
      # カスタムルート作成メソッド
      def __createRoute(self, gateway_id, route_table_id, subnet_id):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # 作成したルートテーブルにカスタムルートを作成(送信先「0.0.0.0/0」)
        create_route = client.create_route(DestinationCidrBlock='0.0.0.0/0', GatewayId=gateway_id, RouteTableId=route_table_id)
        
        # サブネットの関連付け
        associate_route_subnet = client.associate_route_table(RouteTableId=route_table_id, SubnetId=subnet_id)
        
        # パブリックIPアドレスを自動的に受信するように設定
        attribute_subnet = client.modify_subnet_attribute(
          MapPublicIpOnLaunch={'Value': True},
          SubnetId=subnet_id
        )
        
        return create_route
      
      
      # プライベートサブネット作成メソッド
      def __createPrivateSubnet(self, vpc_id):
        availability_zone_list = [self.availability_zone_1, self.availability_zone_2]
        cidr_block_list = ['10.0.2.0/24', '10.0.20.0/24']
        name_list = ['private_subnet_1a', 'private_subnet_1c']
        subnet_id_list = []
        
        # アベイラビリティゾーン、CIDRブロックのリストをループさせ、作成したVPCにサブネットを作成
        for availability_zone, cidr_block, name in zip(availability_zone_list, cidr_block_list, name_list):
        
          # CIDRブロックが「10.0.2.0/24」「'10.0.20.0/24'」の2つのプライベートサブネットを作成
          subnet_id = self.__createSubnet(vpc_id, availability_zone, cidr_block, name)
          subnet_id_list.append(subnet_id)
        return subnet_id_list
      
      
      # パブリックサブネット作成メソッド
      def __createPublicSubnet(self, vpc_id):
        # サブネット作成メソッドの呼び出し(CIDRブロックが「10.0.1.0/24」のパブリックサブネットを作成)
        subnet_id = self.__createSubnet(vpc_id, self.availability_zone_1, '10.0.1.0/24', 'public_subnet_1a')
        
        # インターネットゲートウェイ作成メソッドの呼び出し
        internet_gateway_id = self.__createInternetGateway(vpc_id)
        
        # ルートテーブル作成メソッドの呼び出し
        route_table_id = self.__createRouteTable(vpc_id)
        
        # カスタムルート作成メソッドの呼び出し
        create_route = self.__createRoute(internet_gateway_id, route_table_id, subnet_id)
        return subnet_id
      
      
      # セキュリティグループ作成メソッド
      def __createSecurityGroup(self, vpc_id, name):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # セキュリティグループ作成
        security_group = client.create_security_group(Description=name, GroupName=name, VpcId=vpc_id)
        
        # 作成したセキュリティグループのidを取得
        security_group_id = security_group['GroupId']
        
        # 作成したセキュリティグループにタグをつける
        tags = self.__createTags(client, security_group_id, name)
        return security_group_id
      
      
      # インバウンドルール設定メソッド
      def __settingInboundRules(self, security_group_id, port):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # マイIP(現在のグローバルIPアドレス)を取得
        url = 'https://ifconfig.me'
        request = requests.get(url)
        ip_address = '{}/32'.format(request.text)
        
        # インバウンドルールの作成
        inbound_rule = client.authorize_security_group_ingress(
          GroupId=security_group_id,
          IpPermissions=[
            {
              'FromPort': port,
              'IpProtocol': 'tcp',
              'IpRanges': [
                {
                  'CidrIp': ip_address,
                  'Description': 'test inbound rule',
                },
              ],
              'ToPort': port
            }
          ]
        )
        
        # セキュリティグループルール(インバウンドルール)idの取得
        inbound_rule_id = inbound_rule['SecurityGroupRules'][0]['SecurityGroupRuleId']
        
        # 作成したインバウンドルールにタグをつける
        tags = self.__createTags(client, inbound_rule_id, 'test-inbound-rule-ssh')
        return inbound_rule
      
      
      # SSH用のキーペア作成メソッド
      def __createKeyPair(self):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # 鍵の名前
        key_name = 'test-key.pem'
        # キーペアの作成
        key = client.create_key_pair(KeyName=key_name)
        
        # キーペアidの取得
        key_pair_id = key['KeyPairId']
        
        # 作成したキーペアを取得し、pemファイルを作成
        key_material = key['KeyMaterial']
        with open(key_name, mode='w') as f:
          f.write(key_material)
        
        # 作成したキーペアにタグをつける
        tags = self.__createTags(client, key_pair_id, 'test-key-pair')
        return key_name
      
      
      # EC2インスタンス作成メソッド
      def __createInstance(self, subnet_id, vpc_id, security_group_name):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # セキュリティグループ作成メソッドの呼び出し
        security_group_id = self.__createSecurityGroup(vpc_id, security_group_name)
        
        # キーペア作成メソッドの呼び出し
        key_name = self.__createKeyPair()
        
        # パブリックIPアドレスの自動割り当ての有効
        valid_auto_public_ip = client.modify_subnet_attribute(
          MapPublicIpOnLaunch={'Value': True},
          SubnetId=subnet_id
        )
        
        # AMIは「Amazon Linux2」を使用
        ami_id = 'ami-04204a8960917fd92'
        
        # EC2インスタンス作成
        ec2_instance = client.run_instances(
          ImageId=ami_id, 
          MinCount=1, 
          MaxCount=1, 
          InstanceType="t2.micro", 
          SecurityGroupIds=[security_group_id], 
          KeyName=key_name, 
          SubnetId=subnet_id
        )
        
        # インスタンスidの取得
        instance_id = ec2_instance['Instances'][0]['InstanceId']
        
        # インバウンドルール設定メソッド呼び出し
        inbound_rule = self.__settingInboundRules(security_group_id, 22)
        
        # 作成したインスタンスにタグをつける
        tags = self.__createTags(client, instance_id, 'test-web-server')
        return instance_id, security_group_id
      
      
      # サブネットグループ作成メソッド
      def __createSubnetGroup(self, subnet_id_list):
        # client のサービスをRDSに指定
        client = self.__awsConfig('rds')
        
        # サブネットグループの作成
        subnet_group = client.create_db_subnet_group(
          DBSubnetGroupName='rds-subnet-group', 
          DBSubnetGroupDescription='rds-subnet-group', 
          SubnetIds=subnet_id_list,
          Tags=[
            {'Key': 'Name', 'Value': 'test-rds-subnet-group'},
            {'Key': 'Owner', 'Value': '〓メールアドレス〓'}
          ]
        )
        
        #サブネットグループの名前を取得
        subnet_group_name = subnet_group['DBSubnetGroup']['DBSubnetGroupName']
        return subnet_group_name
      
      
      # RDS用セキュリティグループインバウンドルール設定メソッド
      def __settingRdsInboundRules(self, security_group_id, ec2_security_group_id, vpc_id):
        # client のサービスをEC2に指定
        client = self.__awsConfig('ec2')
        
        # インバウンドルールの作成
        inbound_rule = client.authorize_security_group_ingress(
          GroupId=security_group_id,
          IpPermissions=[
            {
              'FromPort': 3306,
              'IpProtocol': 'tcp',
              'ToPort': 3306,
              'UserIdGroupPairs': [
                {
                  'Description': 'test rds inbound rule',
                  'GroupId': ec2_security_group_id,
                  'UserId': '077698332591',
                  'VpcId': vpc_id
                }
              ]
            }
          ],
        )
        
        # セキュリティグループルール(インバウンドルール)idの取得
        inbound_rule_id = inbound_rule['SecurityGroupRules'][0]['SecurityGroupRuleId']
        
        # 作成したインバウンドルールにタグをつける
        tags = self.__createTags(client, inbound_rule_id, 'test-inbound-rule-rds')
        return inbound_rule
      
      
      # データベース(RDS)の作成メソッド
      def __createRds(self, subnet_id_list, vpc_id, ec2_security_group_id):
        # client のサービスをRDSに指定
        client = self.__awsConfig('rds')
        
        # サブネットグループメソッドの呼び出し
        subnet_group_name = self.__createSubnetGroup(subnet_id_list)
        
        # セキュリティグループ作成メソッドの呼び出し(RDS用を作成)
        security_group_id = self.__createSecurityGroup(vpc_id, 'test-rds-sg')
        
        # データベースの作成
        rds = client.create_db_instance(
          DBName='test_db',
          DBInstanceIdentifier='test-rds',
          DBInstanceClass='db.t2.micro',
          AllocatedStorage=20,
          Engine='mysql',
          MasterUsername='〓任意のマスターユーザー名〓',
          MasterUserPassword='〓任意のパスワード(8文字以上)〓',
          VpcSecurityGroupIds=[security_group_id],
          AvailabilityZone=self.availability_zone_1,
          DBSubnetGroupName=subnet_group_name,
          Port=3306,
          EngineVersion='8.0.27'
        )
        
        # RDS用セキュリティグループインバウンドルール設定メソッドの呼び出し
        inbound_rule = self.__settingRdsInboundRules(security_group_id, ec2_security_group_id, vpc_id)
        
        rds_name = rds['DBInstance']['DBInstanceIdentifier']
        return rds_name
    
    
      # インフラ環境構築を自動化するメソッド
      def createInfra(self):
        # vpc作成
        vpc_id = self.__createVpc()
        print('VPCを作成しました。')
        
        # プライベートサブネット作成
        private_subnet_id_list = self.__createPrivateSubnet(vpc_id)
        print('プライベートサブネットを作成しました。')
        
        # パブリックサブネット作成
        public_subnet_id = self.__createPublicSubnet(vpc_id)
        print('パブリックサブネットを作成しました。')
        
        # EC2インスタンス作成
        instance = self.__createInstance(public_subnet_id , vpc_id, 'test-web-server-sg')
        print('EC2インスタンスを作成しました。')
        
        # EC2セキュリティグループidの取得
        ec2_security_group_id = instance[1]
        
        # RDSインスタンス作成(データベース作成)
        rds_instance = self.__createRds(private_subnet_id_list, vpc_id, ec2_security_group_id)
        print('RDSインスタンスを作成しました。')
    
    
    if __name__ == '__main__':
      access_key = '〓アクセスキー〓'
      secret_access_key = '〓シークレットアクセスキー〓'
      aws = CreateAwsInfra(access_key, secret_access_key)
      create_infra = aws.createInfra()
      print('インフラ環境を構築しました。')
    

    参考記事:
    https://docs.aws.amazon.com/ja_jp/
    autoscaling/ec2/APIReference/API_Operations.html

    https://docs.aws.amazon.com/ja_jp/
    AmazonRDS/latest/APIReference/API_Operations.html

    https://docs.aws.amazon.com/ja_jp/
    AmazonRDS/latest/UserGuide/Overview.RDSSecurityGroups.html#Overview.RDSSecurityGroups.Compare

    https://boto3.amazonaws.com/v1/
    documentation/api/latest/reference/services/ec2.html

    https://boto3.amazonaws.com/v1/
    documentation/api/latest/reference/services/rds.html

    検証・結果

    プログラムの実行結果は以下のようになります。

    プログラムで作成が成功しているので、実際にインスタンスなどが作られているか、AWSコンソールにログインして確認してみましょう。

    1. VPCの確認

    VPCが作成されていることを確認できました。

    2. プライベートサブネットの確認

    10.0.2.0/24、10.0.20.0/24のプライベートサブネットが作成されていることを確認できました。

    3. パブリックサブネットの確認

    10.0.1.0/24のパブリックサブネットが作成されていることを確認できました。

    4. インターネットゲートウェイの確認

    インターネットゲートウェイが作成されていることを確認できました。

    5. ルートテーブルの確認

    ルートテーブルが作成されていることを確認できました。

    6. カスタムルートの確認

    ルートテーブルの「サブネットの関連付け」タブに、作成したパブリックサブネットが関連付けられていることを確認できました。

    7. セキュリティグループの確認

    セキュリティグループと設定したインバウンドルールが作成されていることを確認できました。

    8. キーペアの確認

    キーペアが作成されていることを確認できました。

    9. EC2インスタンスの確認

    EC2インスタンスが作成されていることを確認できました。 また、作成したVPC、サブネット、セキュリティグループが紐づいていること、パブリックIPアドレスが割り当てられていることも確認できました。

    10. サブネットグループの確認

    サブネットグループが作成され、プライベートサブネットがグループ化されていることを確認できました。

    11. RDS用のセキュリティグループの確認

    RDS用のセキュリティグループが作成され、設定したインバウンドルールが反映されていることを確認できました。

    12. RDSインスタンスの確認

    RDSインスタンスが作成され、作成したセキュリティグループのインバウンドルールが設定されていることを確認できました。

    Boto3で作成した全てのサービスが作成されていることを確認できました。

    終わりに

    今回は、PythonとBoto3を使用してAWSのインフラ構築を自動化する方法を紹介いたしました。 Boto3を使用してインフラ環境構築を自動化してみたい、と思う方はぜひ参考にしてみてください。

    また、Boto3で作成するのにとても時間がかかりましたが、Boto3で何ができるのかが分かり、さらにはコンソール上で作成していた時にはあまり理解できていなかった部分を、Boto3で作成することにより理解することができたので、自分にとってとてもためになりました。

    AWSにはCloudFormationというサービスがあるので、次はそちらを使用して構築の自動化をしてみたいなと思いました。なお、自社によるAWSの導入や設計、運用が難しいという企業様は、お気軽にご相談ください。ネットワーク構成の設計、サーバの設計、スクリプトの設計から構築、運用監視までご対応させていただきます。

    最後までお読みいただきありがとうございました。

    > AWSの構築や運用に関するご相談はこちら