9️⃣

AWS Cloud9をCloudFormationで作成する事によって出来る事、マネジメントコンソールとの挙動の違い

2023/06/05に公開

CloudFormationでCloud9を作成してみる

DevelopersIO BASECAMP参加者の加藤です。

本記事ではCloudFormationCloud9の統合開発環境(IDE)を作成して、挙動を確認してみたいと思います!


CLoud9の概要

Cloud9の概要についてはこちらを参照ください。


マネジメントコンソールで作成する場合

以下が2023年6月5日現在、コンソールから作成する際に選択可能な項目です。

まずはこれをテンプレートで再現してみたいと思います。


CloudFormationで再現

テンプレート

以下がテンプレートになります。

一点、先ほどのマネジメントコンソールでは、既存のサブネットを指定可能(指定しない場合はデフォルトVPCに存在するサブネット中から自動選択。)でしたが、指定有無の分岐をConditionsで表現するのは少々やりすぎかと思いますので、ここでは無指定としました。

その他コメントアウトしているプロパティについては、後ほど触れたいと思います。

Create_Cloud9.yml
AWSTemplateFormatVersion: "2010-09-09"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:

      - Label:
          default: Cloud9
        Parameters:
          - AutomaticStopTimeMinutes
          - ConnectionType
          - Description
          - ImageId
          - InstanceType
          - Name

Parameters:
  AutomaticStopTimeMinutes:
    Type: Number
    MinValue: 0
    MaxValue: 20160
    Default: 30

  ConnectionType:
    Type: String 
    AllowedValues: [CONNECT_SSM, CONNECT_SSH]
    Default: CONNECT_SSM

  Description:
    Type: String

  ImageId:
    Type: String
    AllowedValues: [amazonlinux-1-x86_64, amazonlinux-2-x86_64, ubuntu-18.04-x86_64]
    Default: amazonlinux-2-x86_64

  InstanceType:
    Type: String
    AllowedPattern: ^[a-z][1-9][.][a-z0-9]+$
    Default: t2.micro

  Name:
    Type: String

Resources: 
  Cloud9:
    Type: AWS::Cloud9::EnvironmentEC2
    Properties: 
      AutomaticStopTimeMinutes: !Ref AutomaticStopTimeMinutes
      ConnectionType: !Ref ConnectionType
      Description: !Ref Description
      ImageId: !Ref ImageId 
      InstanceType: !Ref InstanceType
      Name: !Ref Name
      # OwnerArn: !Sub arn:aws:iam::${AWS::AccountId}:user/${[IAMユーザーの論理ID]} # 環境所有者を別に指定したい場合に利用
      # Repositories: # CodeCommitリポジトリをクローンする場合指定
      #   - PathComponent: string # 必須: はい AWS CodeCommit リポジトリのクローンを作成する、開発環境のデフォルトのファイル システムの場所内のパス。
      #     RepositoryUrl: string # 必須: はい クローンを作成する AWS CodeCommit リポジトリのクローン URL。
      # SubnetId: !Ref [サブネットの論理ID]
      # Tags: # Keyに”Name”は指定不可。
      #   - Key: string
      #     Value: string
Outputs:
  Cloud9Arn:
    Value: !GetAtt Cloud9.Arn

パラメータについて

デフォルト値は現在のマネジメントコンソールでの初期値と一致させてあります。
NameとDescriptionだけ指定して実行ください。


スタック実行時の挙動

今回は「test1」というスタック名とNameを指定して実行しました。

test1スタックを実行すると、同スタックの作成が「CREATE_COMPLETE」になる前に以下のように「aws-cloud9-test1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」スタックが出現し、「CREATE_COMPLETE」になり、それを受けて、test1スタックが「CREATE_COMPLETE」となりました。

「aws-cloud9-test1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」スタック内のリソースは
・EC2インスタンス
・セキュリティグループ
となり、マネジメントコンソールでCloud9を立てた時と同じスタックである事が確認出来ます。

セキュリティグループのインバウンドルール、アウトバウンドルールはデフォルト、
テンプレートにインスタンスプロファイル等を指定する項目はありませんが、通常通り"AWSCloud9SSMInstanceProfile"が指定されています。

挙動としては一見、AWS::Serverless transformを利用した場合に似ていますが、スタックはネストされておらず、下図のような動きになっている事がわかります。

この事から、マネジメントコーンソールから環境を削除したり、「aws-cloud9-test1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」スタックを削除したりすると、「test1」スタックは残る(実態のリソースは削除された状態)事が確認出来ました。

注意点があるとすると、transformを使った場合と違い、マネジメントコンソール上でスタック同士に事実上の親子関係がある事が一見してわからない事かと思います。


CloudFormationでは「amazonlinux-1-x86_64」を指定する事も可能

現状マネジメントコンソールでは、以下2種類が選択可能となっており、意図してUbuntsを選択した場合を除き、「Amazon Linux 2」=「Amazon Linux 2:amazonlinux-2-x86_64」を選択した事となります。(AMIを比較して確認済み)

一方CloudFormationではユーザーガイド上、無指定の場合「Amazon Linux (デフォルト):amazonlinux-1-x86_64」の挙動となっているようです。(※記事作成時点)

(「AMIの場所」に”Default”の文字)


コメントアウトしていた部分

「Tags:」

Tagsプロパティですが、いつもであれば確認の為にNameタグを指定する所ですが、Cloud9リソース作成にあたっては、以下エラーが起きる事が確認出来ました。

「キー"Name"はEC2が予約したキーなので、Tagには持たせられません。」
Tag cannot have the key 'Name' as it is a reserved key by EC2.

「OwnerArn:」

この値を明示的に指定した場合、指定のユーザーが所有権を持ったCloud9が作成されます。
つまり、指定の値がスタック作成ユーザーを指す値でなかった場合は、スタック作成者のCloud9コンソールにはCloud9リソースは表示されません。

今回のテンプレートでは、${AWS::AccountId}を利用していますので、作成されたEC2インスタンスなどは閲覧出来るようになっていますが、何らかスタック作成者に権限を付与する必要はあるかと思いますが、パラメーター値に別のアカウントIDを指定する事で別アカウントにも作成する事が出来るのではと思います。

以下お試し用のテンプレートになります。

Create_Cloud9_add_OwnerArn.yml
AWSTemplateFormatVersion: "2010-09-09"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:

      - Label:
          default: Cloud9
        Parameters:
          - AutomaticStopTimeMinutes
          - ConnectionType
          - Description
          - ImageId
          - InstanceType
          - Name

      - Label:
          default: IAMUser
        Parameters:
          - IAMUserName
          - IAMUserLoginPassword

Parameters:
  AutomaticStopTimeMinutes:
    Type: Number
    MinValue: 0
    MaxValue: 20160
    Default: 30

  ConnectionType:
    Type: String 
    AllowedValues: [CONNECT_SSM, CONNECT_SSH]
    Default: CONNECT_SSM

  Description:
    Type: String 

  ImageId:
    Type: String
    AllowedValues: [amazonlinux-1-x86_64, amazonlinux-2-x86_64, ubuntu-18.04-x86_64]
    Default: amazonlinux-2-x86_64

  InstanceType:
    Type: String
    AllowedPattern: ^[a-z][1-9][.][a-z0-9]+$
    Default: t2.micro
    
  Name:
    Type: String

  IAMUserName:
    Type: String

  IAMUserLoginPassword:
    Type: String
    NoEcho: true

Resources: 
  Cloud9:
    Type: AWS::Cloud9::EnvironmentEC2
    Properties: 
      AutomaticStopTimeMinutes: !Ref AutomaticStopTimeMinutes
      ConnectionType: !Ref ConnectionType
      Description: !Ref Description
      ImageId: !Ref ImageId 
      InstanceType: !Ref InstanceType
      Name: !Ref Name
      OwnerArn: !Sub arn:aws:iam::${AWS::AccountId}:user/${User} 
  User:
    Type: AWS::IAM::User
    Properties: 
      UserName: !Ref IAMUserName
      LoginProfile:
        Password: !Ref IAMUserLoginPassword
        PasswordResetRequired: false
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AdministratorAccess
Outputs:
  Cloud9Arn:
    Value: !GetAtt Cloud9.Arn

上記テンプレートでは「DeletionPolicy:に Retain」を指定していませんので、スタック削除するとユーザーも一緒に消えます。

Cloud9の環境の所有者が指定のIAMユーザーであっても、所有者以外がスタック削除による環境削除を不可にするものではなさそうです。

ちなみに、公式が出しているテンプレートから

OwnerArn: !Sub arn:${AWS::Partition}:sts::${AWS::AccountId}:assumed-role/TeamRole/MasterKey

のようにユーザー以外(プリンシパルタイプに「Role sessions」を選択して作成されたロールのARN)を指定している例もありました。


「SubnetId:」

当記事ではテンプレート例は作成していませんが、パラメーターでSubnetIdを渡すか、スタック内で作成して!Refとするかでサブネットを指定可能です。


「Repositories:」

最後に「Repositories:」ですが、ユーザーガイドには、


とあります。


以下お試し用のテンプレートとなります。

「PathComponent」パラメーター値は"/REPOSITORY_NAME"としていますが、変更いただいて問題ありません。

Create_Cloud9_add_Repositories.yml
AWSTemplateFormatVersion: "2010-09-09"
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:

      - Label:
          default: Cloud9
        Parameters:
          - AutomaticStopTimeMinutes
          - ConnectionType
          - Description
          - ImageId
          - InstanceType
          - Name
          - PathComponent

      - Label:
          default: CodeCommit
        Parameters:
          - CodeCommitRepositoryName

Parameters:
  AutomaticStopTimeMinutes:
    Type: Number
    MinValue: 0
    MaxValue: 20160
    Default: 30

  ConnectionType:
    Type: String 
    AllowedValues: [CONNECT_SSM, CONNECT_SSH]
    Default: CONNECT_SSM

  Description:
    Type: String 

  ImageId:
    Type: String
    AllowedValues: [amazonlinux-1-x86_64, amazonlinux-2-x86_64, ubuntu-18.04-x86_64]
    Default: amazonlinux-2-x86_64

  InstanceType:
    Type: String
    AllowedPattern: ^[a-z][1-9][.][a-z0-9]+$
    Default: t2.micro

  Name:
    Type: String

  PathComponent:
    Type: String
    Default: /REPOSITORY_NAME

  CodeCommitRepositoryName:
    Type: String

Resources: 
  Cloud9:
    Type: AWS::Cloud9::EnvironmentEC2
    Properties: 
      AutomaticStopTimeMinutes: !Ref AutomaticStopTimeMinutes
      ConnectionType: !Ref ConnectionType
      Description: !Ref Description
      ImageId: !Ref ImageId 
      InstanceType: !Ref InstanceType
      Name: !Ref Name
      Repositories:
        - PathComponent: !Ref PathComponent
          RepositoryUrl: !GetAtt Repository.CloneUrlHttp

  Repository:
    Type: AWS::CodeCommit::Repository
    Properties: 
      RepositoryName: !Ref CodeCommitRepositoryName

Outputs:
  Cloud9Arn:
    Value: !GetAtt Cloud9.Arn

(当該プロパティを指定しなかった場合)

(当該プロパティを指定した場合)

「warning: You appear to have cloned an empty repository.」の表示が出てしまいましたが、起動時上記のようにbashが走る事まで確認する事が出来ました。


出来なさそうだった事

CloudFormationで作成したからといって、test1スタックでCloud9と合わせてIAMロールを作成しても、RoleのArnなど渡すプロパティが見当たらない為「aws-cloud9-test1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」が作成したEC2インスタンスに予めロールをアタッチして作成させる事は出来なさそうでした。
(マネジメントコンソールでも然り)

また、AWS managed temporary credentialsの有効化/無効化を指定するプロパティもありませんでした。

参考:AMTCについての技術記事
https://dev.classmethod.jp/articles/aws-cloud9-aws-managed-temporary-credentials/


終わりに

細かい部分で色々と勉強になった所もあり、触ってみる前よりもCloud9の事を少し深ぼって知る事が出来た気がします。

どなたかの知識や理解のお役に立てている部分があれば幸いです。

お読みいただき有難うございました!

デベキャン

Discussion