LoginSignup
17

More than 5 years have passed since last update.

CloudFormation Tips for つまづきやすいところ

Last updated at Posted at 2017-09-28

何この記事

CloudFormationでそれなりな規模の構成を作成することになったので、困って調べて分かったことをつらつらと書いていって後で見直せるようにした記事。なので自分用メモの色が強いです。が、たまたま記事を見て解決になったら良いかなともちょっと思います。
そんな性質の記事なのでふとした瞬間に増えたり減ったりします。

CloudFormationとは

AWSの公式ドキュメントかQiitaでCloudFormationタグを漁ってください

Tips

スタックを小分けにする際の1案

今はいくつか方法がありますが、1案として。

  • 共通のIDを決定し、Parametersに設定してスタック作成時に入力をする
    • 同じスクリプトを使って2つ以上の環境を作成する際、共通のIDを各インスタンスの名前に利用すると楽
  • 他のスタックで必要そうな情報をOutputsに記載してエクスポート
  • 他のスタックでエクスポートした情報はFn::ImportValue!Subを用いてインポート

VPC作成

Parameters:
  ProjectId:
    Description: "Project name id."
    Type: String
    AllowedPattern: '^[a-zA-Z0-9-/:-@\[-\`\{-\~]+$'
    ConstraintDescription: "InvalidValue[ProjectId]"

Resources:
  MyVPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: "default"
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectId}-vpc"

Outputs:
  MyVPC:
    Value: !Ref MyVPC
    Export:
      Name: !Sub "${ProjectId}-vpc"

SecurityGroup作成

Parameters:
  ProjectId:
    Description: "Project name id."
    Type: String
    AllowedPattern: '^[a-zA-Z0-9-/:-@\[-\`\{-\~]+$'
    ConstraintDescription: "InvalidValue[ProjectId]"

Resources:
  MySecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: {"Fn::ImportValue": !Sub "${ProjectId}-vpc"}
      GroupName: !Sub "${ProjectId}-sg"
      GroupDescription: "pass ssh from specific IP"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: "x.x.x.x/32"
      Tags:
        - Key: Name
          Value: !Sub "${ProjectId}-sg"

Parametersとの文字結合

Fn::Joinの方がネット上のノウハウ的に多いけど、せっかく新しいFn::Subができたんだし使っていきましょ

Fn::Sub
${Param}Paramパラメータの文字列が展開される。
複雑な文字結合の場合はListを作成すると楽。
1つ目に、生成する文字列
2つ目に、生成する文字列に対してマッピングする変数定義
(たぶん例を見た方が早い)


簡単な結合
MyParam=testparamと設定した状態でtestparam-instanceという文字列を作成

!Sub "${MyParam}-instance

複雑な結合
DBEngine=postgres DBVersion=9.6.3と設定した状態でpostgres9.6という文字列を作成

!Sub
  - ${DBEngine}${DBVersion1}.${DBVersion2}
  - DBEngine: !Ref DBEngine
    DBVersion1: !Select [0, !Split [".", !Ref DBVersion] ]
    DBVersion2: !Select [1, !Split [".", !Ref DBVersion] ]

Joinの場合より可読性も高い(と思う)

Policy作る時は基本的には管理ポリシーを作成

CFnに限った話ではないですが、インラインポリシーではなく管理ポリシーを作りましょうという話。

インラインポリシー : AWS::IAM::Policy
Role/User/Groupと1対1の関係。作成した後に別のエンティティにアタッチできない
管理ポリシー : AWS::IAM::ManagedPolicy
ポリシーは単体で存在し、複数のエンティティにアタッチできる

IAMRoleをEC2につけたい

InstanceProfileを作成してIAMRoleとEC2をつなげる必要がある。
AWSコンソールからはInstanceProfileを意識せずアタッチできるので気づきにくい。

(抜粋 parametersで指定して!Refで参照してるところもあります)

Resources:
  MyRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: "my-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
               - "ec2.amazonaws.com"
            Action: "sts:AssumeRole"
      Path: "/"

  MyInstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    DependsOn: MyRole
    Properties:
      InstanceProfileName: "my-instance-profile"
      Path: "/"
      Roles:
        - !Ref MyRole

  MyEC2:
    Type: "AWS::EC2::Instance"
    DependsOn: MyInstanceProfile
    Properties:
      ImageId: !Ref MyEC2AmiId
      IamInstanceProfile: !Ref MyInstanceProfile
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          PrivateIpAddress: "10.0.0.250"
          SubnetId: !Ref MySubnet
          GroupSet:
            - !Ref MySecurityGroup
      InstanceType: !Ref MyEC2Type
      KeyName: !Ref EC2KeyPair
      Tags:
        - Key: "Name"
          Value: "MyEC2"

RDSのセキュリティグループ

AWS::RDS::DBInstanceのPropertiesにセキュリティグループ関連のものは二つ

  • DBSecurityGroups
  • VPCSecurityGroups

ちなみにどちらも指定することはできない。

DBSecurityGroups
DB用に独自にSecurityGroupを作成して反映したい場合

VPCSecurityGroups
AWS::EC2::SecurityGroupをアタッチする場合
こちらの方が利用シーンは多そう

DBParameterGroup, DBSubnetGroupのName

  • AWS::RDS::DBParameterGroup
  • AWS::RDS::DBSubnetGroup

の名前が現時点では設定できなさそう・・・?
AWSのドキュメントにも名前を設定するプロパティはなさげ。
aws-cliで作成する場合は名前設定する用のプロパティあるのに。

ちなみに作成すると論理IDとランダム文字列が結合した名前が生成されて設定されます。

ELBのLogを保管する先のS3のBucketPolicy

これもCFnに限った話ではないですが。
ELBのログ出力を設定する場合(AccessLoggingPolicyプロパティにて設定)、S3にログファイルを吐くのは特定のAWSアカウントなので、それを許可するようS3のBucketyPolicyを設定する必要があります。
詳しくはこのあたり
AWS - Classic Load Balancer のアクセスログの有効化 - ステップ 2: S3 バケットにポリシーをアタッチする

(抜粋, ap-northeast-1リージョンの場合)

Resources:
  ELBLogS3:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: "hogehoge-server-log"

  ELBLogS3BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    DependsOn: ELBLogS3
    Properties:
      Bucket: !Ref ELBLogS3
      PolicyDocument:
        Version: "2012-10-17"
        Id: "ELBLogS3BucketPolicy"
        Statement:
          - Sid: "ELBLogWriteAccess"
            Action:
              - "s3:PutObject"
            Effect: "Allow"
            Resource: !Sub "arn:aws:s3:::${ELBLogS3}/AWSLogs/${AWS::AccountId}/*"
            Principal:
              AWS: "arn:aws:iam::582318560864:root"

以降、何かあったら追記予定

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17