先日、Amazon 本社で AWS スタートアップの説明を聞く機会があった。そこで「最近では踏み台に Cloud9 を使っている方も」という話を聞いたので「本当に踏み台として使えるのか」「Terraform との相性はどうなのか」というのを確認してみた。なお、Cloud9 には新規に EC2 インスタンスを作成するタイプと既存の EC2 インスタンスなどに Cloud9 IDE をインストールして Cloud9 environment として使う SSH タイプもあるが後者については触れない。
AWS Cloud9 とは
AWS Cloud9 はブラウザで使えるクラウド上の IDE。本来は開発目的で利用するものなんだろうが EC2 インスタンスを使っているので普通に踏み台として EC2 を構築するのとあまり変わらない。実際、既存の EC2 インスタンスに Cloud9 IDE をインストールしてそのまま「environment」として使うこともできる。
一般的な SSH 接続を中継する方法以外に Cloud9 IDE のターミナルからプライベートネットワークにあるインスタンスに SSH 接続をするという選択肢がある。AWS マネジメントコンソールにアクセスでき、対象の「environment」でターミナルを使う権限があるユーザであれば SSH クライアントなしでプライベートネットワークに配置されているインスタンスにアクセスができる。ブラウザ上でファイル転送もできるので大抵のことは Cloud9 IDE 上でできるだろう。
他にも Cloud9 を使うメリットとしては以下のようなことが考えられる。
- membership というもので IDE にアクセスできる(=ターミナルを利用できる)ユーザを管理することができる。
- (Cloud9 IDE からしかアクセス出来ないと思われる)セキュリティグループを自動的に作成してくれる。名前は
aws-cloud9-環境名-乱数
のような書式。 - インアクティブな状態が続いた場合の自動シャットダウンを「30 分」「1 時間」「4 時間」「1 日」「1 週間」「なし」から設定することができる。踏み台用のインスタンスのコストを抑えることができる。
「お、これは結構使えるのでは?」と思ったが、試しているうちにデメリットも色々と出てきた。詳しくは後述するが、だいたい以下のような点。
- Cloud9 用のインスタンスを作成したらサーバに接続するための秘密鍵を保存しておく必要がある。
- 一般的な SSH 接続を中継する踏み台として利用する場合、セキュリティグループの編集が必要になったり、EIP を設定したりする必要がああるが自動化が難しい。
現時点での個人的な感想は「踏み台として『使える』か『使えない』かなら『使える』が、何か中途半端」といった感じ。
では孤独のハンズオンで色々と試していく。
Cloud9 environment の作成
Cloud9 では environment(以下、「Cloud9 environment」とする)と membership でアクセス制限を行っている。作成した Cloud9 environment の membership に IAM ユーザやロールを read-only
または read-write
のいずれかの権限で追加することによって複数のユーザで Cloud9 environment を共有することができるようになっている。
AWS マネジメントコンソールの Cloud9 メニューでは以下のように environment の種類が分かれている。
Your environment | Owner が自身の ARN で登録されている Cloud9 environment が表示される。AWS マネジメントコンソールから作成した場合は必ずこちらに登録される。 |
---|---|
Shared with you | 対象の Cloud9 environment の membership に自身の ARN が read-only または read-write 権限で登録されている Cloud9 environment が表示される。 |
Account Environments | AWS アカウントに登録されている Cloud9 environment が表示される。自身が Owner の Cloud9 environment 以外に、Owner でもなく membership でも登録されていない Cloud9 environment も表示される。Terraform や AWS CLI で Owner の ARN を指定しなかった場合はここにのみ表示される。 |
membership には read-only
と read-write
以外に owner
があり、これは Cloud9 environment 作成時に自動的に作成される membership であり変更はできない。
AWS マネジメントコンソール、Terraform、AWS CLI で Cloud9 environment 作成時の挙動が異なるので以下にまとめておく。
AWS マネジメントコンソールから Cloud9 environment を作成する
「Name」は必須だが「Description」は任意。既に同名の Cloud9 environment が存在する場合は作成できない。(Owner が異なる場合は作成可能)
インスタンスタイプや配置する VPC、サブネットなどを選択する。インスタンスタイプは「Other instance type」を選択すれば好きなものを選ぶことができる。執筆時点ではここの「Amazon Linux」は「Amazon Linux 2」ではない。VPC やサブネットはデフォルトでは「デフォルト VPC」と「デフォルトサブネット」を使うようになっている。実際にはプロジェクト用に作成した VPC とサブネットに配置すると思うが、ここで気をつけなければならないのは Cloud9 environment を配置するサブネットはパブリックサブネットでなければならない。Cloud9 によって自動的に作成されるセキュリティグループを見ればわかるがインバウンドルールに Cloud9 用に予約されたネットワークからの 22 番接続を許可する設定が追加される。Cloud9 environment への接続へはグローバルから行われることになるため NAT ゲートウェイの有無に関わらずプライベートサブネットへ配置すると Cloud9 IDE にアクセスできなくなる。
設定内容を確認して[Create environment]ボタンをクリックする。
Cloud9 用のインスタンス作成が行われて接続画面に移行する。Cloud9 用の AMI から作成されているのでだいたい 60 秒もあれば使えるようになる。
インスタンスの状態を確認してみる
Cloud9 で作成される EC2 インスタンスとセキュリティグループは CloudFormation によって作成されているのでまずは CloudFormation のスタックを確認する。スタック名は aws-cloud9-{Cloud9環境名}-{Cloud9環境ID}
となっている。Cloud9 environment の ID は Cloud9 IDE の URL か AWS CLI の cloud9 list-environments
で調べることができる。
aws cloudformation describe-stacks \ --stack-name aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
{ "Stacks": [ { "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "StackName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "CreationTime": "2019-07-14T14:59:45.535Z", "RollbackConfiguration": {}, "StackStatus": "CREATE_COMPLETE", "DisableRollback": false, "NotificationARNs": [], "Tags": [ { "Key": "aws:cloud9:environment", "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { "Key": "aws:cloud9:owner", "Value": "XXXXXXXXXXXXXXXXXXXXX:john" } ], "EnableTerminationProtection": false } ] }
スタックのリソースを確認する。
aws cloudformation describe-stack-resources --stack-name aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ここから EC2 インスタンスの ID とセキュリティグループの ID がわかる。
{ "StackResources": [ { "StackName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "LogicalResourceId": "Instance", "PhysicalResourceId": "i-xxxxxxxxxxxxxxxxx", "ResourceType": "AWS::EC2::Instance", "Timestamp": "2019-07-14T15:00:31.775Z", "ResourceStatus": "CREATE_COMPLETE" }, { "StackName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "LogicalResourceId": "InstanceSecurityGroup", "PhysicalResourceId": "sg-xxxxxxxxxxxxxxxxx", "ResourceType": "AWS::EC2::SecurityGroup", "Timestamp": "2019-07-14T14:59:55.347Z", "ResourceStatus": "CREATE_COMPLETE" } ] }
セキュリティグループは aws-cloud9-{Cloud9環境名}-{Cloud9環境ID}-InstanceSecurityGroup-{乱数}
という名前で作成されているので、GroupName
かタグ値で CloudFormation のスタック名をフィルタリングすればすぐ出てくるだろう。
aws ec2 describe-security-groups --filters 'Name=group-name,Values=aws-cloud9-foo-*'
セキュリティグループのインバウンドルールには Cloud9 で予約されていると思われるネットワークアドレスが二つ登録されている。
{ "SecurityGroups": [ { "Description": "Security group for AWS Cloud9 environment aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "GroupName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-InstanceSecurityGroup-XXXXXXXXXXXXX", "IpPermissions": [ { "FromPort": 22, "IpProtocol": "tcp", "IpRanges": [ { "CidrIp": "18.179.48.96/27" }, { "CidrIp": "18.179.48.128/27" } ], "Ipv6Ranges": [], "PrefixListIds": [], "ToPort": 22, "UserIdGroupPairs": [] } ], "OwnerId": "000000000000", "GroupId": "sg-xxxxxxxxxxxxxxxxx", "IpPermissionsEgress": [ { "IpProtocol": "-1", "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ], "Ipv6Ranges": [], "PrefixListIds": [], "UserIdGroupPairs": [] } ], "Tags": [ { "Key": "aws:cloud9:owner", "Value": "XXXXXXXXXXXXXXXXXXXXX:john" }, { "Key": "aws:cloudformation:stack-name", "Value": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { "Key": "aws:cloud9:environment", "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { "Key": "aws:cloudformation:logical-id", "Value": "InstanceSecurityGroup" }, { "Key": "aws:cloudformation:stack-id", "Value": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } ], "VpcId": "vpc-xxxxxxxx" } ] }
EC2 インスタンスも同様。
aws describe-instances --filters 'Name=tag-key,Values=Name' 'Name=tag-value,Values=aws-cloud9-foo-*'
{ "Reservations": [ { "Groups": [], "Instances": [ { "AmiLaunchIndex": 0, "ImageId": "ami-0872d0e3184cfc976", "InstanceId": "i-xxxxxxxxxxxxxxxxx", "InstanceType": "t2.micro", "LaunchTime": "2019-07-14T14:59:58.000Z", "Monitoring": { "State": "disabled" }, "Placement": { "AvailabilityZone": "ap-northeast-1a", "GroupName": "", "Tenancy": "default" }, "PrivateDnsName": "ip-172-31-40-231.ap-northeast-1.compute.internal", "PrivateIpAddress": "172.31.40.231", "ProductCodes": [], "PublicDnsName": "ec2-00-000-000-000.ap-northeast-1.compute.amazonaws.com", "PublicIpAddress": "00.000.000.000", "State": { "Code": 16, "Name": "running" }, "StateTransitionReason": "", "SubnetId": "subnet-xxxxxxxx", "VpcId": "vpc-xxxxxxxx", "Architecture": "x86_64", "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", "Ebs": { "AttachTime": "2019-07-14T14:59:59.000Z", "DeleteOnTermination": true, "Status": "attached", "VolumeId": "vol-xxxxxxxxxxxxxxxxx" } } ], "ClientToken": "aws-c-Insta-XXXXXXXXXXXX", "EbsOptimized": false, "EnaSupport": true, "Hypervisor": "xen", "NetworkInterfaces": [ { "Association": { "IpOwnerId": "amazon", "PublicDnsName": "ec2-00-000-000-000.ap-northeast-1.compute.amazonaws.com", "PublicIp": "00.000.000.000" }, "Attachment": { "AttachTime": "2019-07-14T14:59:58.000Z", "AttachmentId": "eni-attach-xxxxxxxxxxxxxxxxx", "DeleteOnTermination": true, "DeviceIndex": 0, "Status": "attached" }, "Description": "", "Groups": [ { "GroupName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-InstanceSecurityGroup-XXXXXXXXXXXXX", "GroupId": "sg-xxxxxxxxxxxxxxxxx" } ], "Ipv6Addresses": [], "MacAddress": "xx:xx:xx:xx:xx:xx", "NetworkInterfaceId": "eni-xxxxxxxxxxxxxxxxx", "OwnerId": "000000000000", "PrivateDnsName": "ip-172-31-40-231.ap-northeast-1.compute.internal", "PrivateIpAddress": "172.31.40.231", "PrivateIpAddresses": [ { "Association": { "IpOwnerId": "amazon", "PublicDnsName": "ec2-00-000-000-000.ap-northeast-1.compute.amazonaws.com", "PublicIp": "00.000.000.000" }, "Primary": true, "PrivateDnsName": "ip-172-31-40-231.ap-northeast-1.compute.internal", "PrivateIpAddress": "172.31.40.231" } ], "SourceDestCheck": true, "Status": "in-use", "SubnetId": "subnet-xxxxxxxx", "VpcId": "vpc-xxxxxxxx" } ], "RootDeviceName": "/dev/xvda", "RootDeviceType": "ebs", "SecurityGroups": [ { "GroupName": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-InstanceSecurityGroup-XXXXXXXXXXXXX", "GroupId": "sg-xxxxxxxxxxxxxxxxx" } ], "SourceDestCheck": true, "Tags": [ { "Key": "aws:cloudformation:logical-id", "Value": "Instance" }, { "Key": "Name", "Value": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { "Key": "aws:cloud9:environment", "Value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { "Key": "aws:cloudformation:stack-id", "Value": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, { "Key": "aws:cloud9:owner", "Value": "XXXXXXXXXXXXXXXXXXXXX:john" }, { "Key": "aws:cloudformation:stack-name", "Value": "aws-cloud9-foo-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ], "VirtualizationType": "hvm", "CpuOptions": { "CoreCount": 1, "ThreadsPerCore": 1 } } ], "OwnerId": "000000000000", "RequesterId": "000000000000", "ReservationId": "r-xxxxxxxxxxxxxxxxx" } ] }
いずれもタグに Cloud9 environment の ID が入っているのでフィルタリングはしやすい。
Cloud9 environment の menbership について学ぶ
AWS マネジメントコンソールから Cloud9 environment を作成した場合は現在の IAM ユーザやロールの ARN が Owner として設定されるようになっている。
aws cloud9 describe-environments \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
{ "environments": [ { "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "name": "AWS Management Console", "description": "", "type": "ec2", "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john" } ] }
上記の Cloud9 environment の membership がどうなっているか確認してみる。
aws cloud9 describe-environment-memberships \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
{ "memberships": [ { "permissions": "owner", "userId": "XXXXXXXXXXXXXXXXXXXXX:john", "userArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "lastAccess": 1562846115.0 } ] }
memberships
が配列になっていることからもわかるが membership には owner
以外に read-only
や read-write
といった権限で複数のユーザを登録することができるようになっている。
Cloud9 environment と membership の関連性
Cloud9 environment の権限には owner
、read-only
、read-write
の 3 種類がある。AWS マネジメントコンソールから Cloud9 environment を作成した場合、owner
には現在の IAM ユーザやロールが設定される。owner
は当然のことながら変更や削除などすべての操作を行うことができる。
Cloud9 environment には membership というものがあり、この membership をカスタマイズすることで environment を他のユーザと共有できるようになっている。現状の AWS マネジメントコンソールには membership を操作する画面は無く、Cloud9 IDE 内の共有設定で行うようになっている。
Cloud9 environment を AWS マネジメントコンソール、Terraform、AWS CLI からownerArn を指定せずにそれぞれ作成すると以下のようになる。なお、AWS マネジメントコンソールでは ownerArn の指定方法が無いため Owner には自動的に現在のユーザに設定される。
{ "environments": [ { "id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "name": "AWS Management Console", "description": "", "type": "ec2", "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john" }, { "id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "name": "Terraform", "description": "", "type": "ec2", "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/1562929437597457834" }, { "id": "cccccccccccccccccccccccccccccccc", "name": "AWS CLI", "description": "", "type": "ec2", "arn": "arn:aws:cloud9:ap-northeast-1:000000000000:environment:cccccccccccccccccccccccccccccccc", "ownerArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/botocore-session-1562929475" } ] }
それぞれの ownerArn
を見てみると Terraform ではナノ秒タイムスタンプ、AWS CLI では botocore-session-
接頭辞にタイムスタンプが自動的に設定されている。これらの違いを AWS マネジメントコンソールで確認してみる。
まずは「Your environments」を見てみると AWS マネジメントコンソールで作成した Cloud9 environment が表示されている。自分で作成したので Permissions は Owner となっている。作成したはずの「Terraform」や「AWS CLI」は無い。
「Shared with you」を飛ばして「Account environments」を見てみる。先程は無かった「Terraform」と「AWS CLI」も表示されている。
ではこの「Terraform」や「AWS CLI」が使えるのかというのを「Open IDE」を押して試してみるがアクセス権が無いと言われる。
IDE を開くことができないので「Terraform」と「AWS CLI」は AWS マネジメントコンソールから共有設定をすることができず、AWS CLI から設定することになる。しかし、AWS CLI のロールはオーナーではない membership への DeleteEnvironmentMembership
権限が無いので後述する「membership の削除」で問題が発生することがある。
フローにしてみると下記のようになる。
自身が Cloud9 environment の Owner であれば AWS CLI から追加した membership を Cloud9 IDE から削除することができるが、最も困るのが Terraform で owner_arn
を未指定で作成してしまった場合。Owner が Terraform で作成した ARN になっているので自身を read-write
で membership を追加したとしても Cloud9 IDE に共有設定が表示されないので membership 操作が一切できない。
membership の追加、更新、削除
Cloud9 environment の ID を調べ方
作成した Cloud9 environment の ID を控え忘れてしまった場合は AWS マネジメントコンソールで「Open IDE」や「View Details」、「Edit」を開けば URL に ID が含まれているのでそれをコピーしてもいい。
AWS CLI では list-environments
サブコマンドで ID の一覧を表示することができるが、この結果を describe-environments
に渡さないと名前がわからない。
aws cloud9 list-environments
{ "environmentIds": [ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "cccccccccccccccccccccccccccccccc" ] }
一つずつ調べるのは面倒だが list-environments
の結果を --query
オプションで整形して describe-environments --environment-ids
の引数に渡せば名前付きで一覧を取り出すことができる。
aws cloud9 describe-environments \ --environment-ids $(aws cloud9 list-environments --query "environmentIds" --output text) \ --query "environments[].[id, name]" \ --output text
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa AWS Management Console bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb Terraform cccccccccccccccccccccccccccccccc AWS CLI
membership を追加する
membership の追加には create-environment-membership
サブコマンドを使う。ここでは例として IAM ユーザである alice
を read-only
で追加する。
aws cloud9 create-environment-membership \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ --user-arn arn:aws:iam::000000000000:user/alice \ --permissions read-only
{ "membership": { "permissions": "read-only", "userId": "YYYYYYYYYYYYYYYYYYYYY", "userArn": "arn:aws:iam::000000000000:user/alice", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } }
describe-environment-memberships
で確認すると alice
が read-only
で追加されていることがわかる。
aws cloud9 describe-environment-memberships \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
{ "memberships": [ { "permissions": "read-only", "userId": "YYYYYYYYYYYYYYYYYYYYY", "userArn": "arn:aws:iam::000000000000:user/alice", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, { "permissions": "owner", "userId": "XXXXXXXXXXXXXXXXXXXXX:john", "userArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "lastAccess": 1562920538.0 } ] }
membership を更新する
membership の更新には update-environment-membership
サブコマンドを使う。
ここでは上で追加した alice
の権限を read-only
から read-write
に変更する。
aws cloud9 update-environment-membership \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ --user-arn arn:aws:iam::000000000000:user/alice \ --permissions read-write
{ "membership": { "permissions": "read-write", "userId": "YYYYYYYYYYYYYYYYYYYYY", "userArn": "arn:aws:iam::000000000000:user/alice", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } }
再び describe-environment-memberships
で確認すると alice
の権限が read-write
に変更されていることがわかる。
aws cloud9 describe-environment-memberships \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
{ "memberships": [ { "permissions": "read-write", "userId": "YYYYYYYYYYYYYYYYYYYYY", "userArn": "arn:aws:iam::000000000000:user/alice", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, { "permissions": "owner", "userId": "XXXXXXXXXXXXXXXXXXXXX:john", "userArn": "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john", "environmentId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "lastAccess": 1562920538.0 } ] }
membership を削除する
membership の削除には delete-environment-membership
サブコマンドを使う。
成功した場合は何も表示されない。
aws cloud9 delete-environment-membership \ --environment-id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ --user-arn arn:aws:iam::000000000000:user/alice
AccessDeniedException の例
先に説明したように AWS マネジメントコンソールで Cloud9 environment を作成した場合、AWS CLI からの操作には DeleteEnvironmentMembership
権限が無いためエラーになる。
An error occurred (AccessDeniedException) when calling the DeleteEnvironmentMembership operation: User arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/botocore-session-1562923366 is not authorized to perform: cloud9:DeleteEnvironmentMembership on resource: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Cloud9 と Terraform
Terraform v0.12.1
Terraform で Cloud9 を扱う場合は aws_cloud9_environment_ec2
リソースを使用する。現状では Cloud9 に関連するリソースはこれしか無い。
aws_cloud9_environment_ec2 を使うときの注意点
Cloud9 envionment の membership の説明に書いたとおり、owner_arn
の指定を忘れると membership の操作ができないばかりか Cloud9 IDE に入ることすらできないので(使い方によるが)指定しておいた方が無難。
aws_cloud9_environment_ec2
に変更を加えた場合、大抵は destroy される。automatic_stop_time_minutes
は modify になりそうだが、これも destroy の対象なので自動シャットダウンの時間を変更したければ Cloud9 IDE から変更する必要がある。
modify になるもの
name
を変更するdescription
を変更する
destroy になるもの
instance_type
を変更するautomatic_stop_time_minutes
を設定(never から 30 など)または変更(30 から 60 など)するowner_arn
を設定または変更する
Terraform による Cloud9 environment の作成
name
と instance_type
だけ指定すれば Cloud9 environment を作成することができる。
resource "aws_cloud9_environment_ec2" "foo" { // STEP 1 name = "Terraform" instance_type = "t2.micro" }
terraform apply --target aws_cloud9_environment_ec2.foo
terraform state show aws_cloud9_environment_ec2.foo
owner_arn
にはユーザ名としてタイムスタンプが設定される。
# aws_cloud9_environment_ec2.foo: resource "aws_cloud9_environment_ec2" "foo" { arn = "arn:aws:cloud9:ap-northeast-1:000000000000:environment:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" instance_type = "t2.micro" name = "foo" owner_arn = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/1562844915108062971" type = "ec2" }
STEP 2: owner_arn の指定
先程の TF ファイルを変更して owner_arn
を追記する。Terraform に限らずだが owner の変更は出来ないため同一名の Cloud9 environment を作成する場合は再構築する必要がある。
resource "aws_cloud9_environment_ec2" "foo" { // STEP 1 name = "foo" instance_type = "t2.micro" // STEP 2 owner_arn = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john" }
terraform apply --target aws_cloud9_environment_ec2.foo
terraform state show aws_cloud9_environment_ec2.foo
terraform state show
で確認すると owner_arn
が指定したとおりに設定されているのがわかる。
# aws_cloud9_environment_ec2.foo: resource "aws_cloud9_environment_ec2" "foo" { arn = "arn:aws:cloud9:ap-northeast-1:000000000000:environment:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" id = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" instance_type = "t2.micro" name = "foo" owner_arn = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john" type = "ec2" }
STEP 3: automatic_stop_time_minutes を指定する
Cloud9 ではインアクティブな状態が続いたときに自動的に EC2 インスタンスを停止させることができる機能が用意されている。aws_cloud9_environment_ec2
リソースでは automatic_stop_time_minutes
という引数(数値型)で指定する。未指定の場合は AWS CLI 同様に never
(停止しない)となる。destroy が発生するので「時間を変更したいけれど Cloud9 environment は壊したくない」という場合は IDE を開いて「AWS Cloud9 > Pereferences > PROJECT SETTINGS > EC2 Instance」で設定を変更する。なお、AWS マネジメントコンソールからは変更できない。
resource "aws_cloud9_environment_ec2" "foo" { // STEP 1 name = "foo" instance_type = "t2.micro" // STEP 2 owner_arn = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john" // STEP 3 automatic_stop_time_minutes = 30 }
terraform apply --target aws_cloud9_environment_ec2.foo
terraform state show aws_cloud9_environment_ec2.foo
指定した場合は下記のように属性と値が表示される。Q & A に書いておくが設定によっては IDE 上で確認できる値と一致しなくなる。指定しなかった場合は自動的に never
となり automatic_stop_time_minutes
属性は出てこない。
# aws_cloud9_environment_ec2.foo: resource "aws_cloud9_environment_ec2" "foo" { arn = "arn:aws:cloud9:ap-northeast-1:000000000000:environment:cccccccccccccccccccccccccccccccc" automatic_stop_time_minutes = 30 id = "cccccccccccccccccccccccccccccccc" instance_type = "t2.nano" name = "bar" owner_arn = "arn:aws:sts::000000000000:assumed-role/OrganizationAccountAccessRole/john" type = "ec2" }
EIP の設定やセキュリティグループのカスタマイズの自動化はできるのか
Cloud9 envionment には EIP の設定が無いので自動シャットダウンが行われればパブリック IP アドレスは変わってしまうし、セキュリティグループは自動作成以外の選択肢が無いのでインバウンドルールを追加しないと本来の「踏み台」としては使えない。
しかし、Cloud9 で EC2 インスタンスを作成してもステータスには EC2 インスタンスの詳細情報は含まれていない。これは AWS CLI を使っても同じである。EC2 インスタンスやセキュリティグループは CloudFormation によって作成されているので Name
タグには aws-cloud9-環境名-環境ID
という書式で名前が設定されている。CloudFormation の aws:cloud9:environment
タグにも Cloud9 environment の ID が入っているのでここからリソースを検索することもできるだろう。しかし、CloudFormation のスタックが Terraform の管理外で作成されているので変数呼び出しができない。
EC2 インスタンスの作成を通常通り行い、そちらで Cloud9 IDE のインストールと EIP やセキュリティグループを設定したあとに Cloud9 に SSH タイプで追加すれば…という方法も思いつくが残念ながらいまのところ Terraform には aws_cloud9_environment_ssh
というリソースは存在しない。
従って、Cloud9 で EC2 インスタンスを新規に作成する場合は「踏み台」として使うための設定の自動化は簡単ではないと思われる。
Try & Result
Try: owner_arn を指定し忘れたのだが Cloud9 IDE を使いたい
AWS CLI で cloud9 create-environment-membership
で使用するユーザを membership に追加すればいい。ただし、Cloud9 IDE に membership の設定が表示されず GUI から membership の編集や削除ができないなどの制限があるので潔く作り直したほうがよい。
Try: automatic_stop_time_minutes に適当な値(たとえば 45 など)を設定するとどうなるか
terraform show
では設定した通りの値が入っているが Cloud9 IDE で確認してみると切り上げられてプリセット値に設定されていた。(45 の場合であれば 1 時間)
Try: automatic_stop_time_minutes に明示的に never
を指定するにはどうするのか
数値型なので automatic_stop_time_minutes = 0
と指定すれば never
になる。
まとめ
一般的な「踏み台」として使わずに Cloud9 IDE を SSH ログイン用のスポットとして使う分にはいいと思うが、リモートサーバにログインするための秘密鍵を配置する必要があるなどいくつかの手間は残る。
個人的にはシェルのキーバインドを多用するので Ctrl + W(1単語前を消す)なんて押そうものならタブが落ちてしまうため Cloud9 IDE 上のターミナルは少々使いにくかったりする。
フロントの開発をやっているエンジニアさん相手には Proxy Jump の説明をするのも一苦労だったりするので AWS アカウントだけあれば使えるというのは便利なのかもしれない。しかし、プロジェクトで複数の人間が同じ Cloud9 IDE を使う場合はあまり向かないと思う。というのも、Cloud9 IDE を共有したとして現状ではそのログイン履歴を記録する方法が無いように思えるし、Linux システムのユーザ切り替えも行われない。これは例えば VNC などで画面共有をしているのとあまり変わらない。便利ではあるが、誰が SSH のコマンドを発行したのかわからないのは内部セキュリティ的にあまりよろしく無いように思える。
他のプロジェクトが Cloud9 をどのように SSH の踏み台として使っているのかは知らないがベストプラクティス的なものが教えてもらいたいものである。