AWS SSO を活用しているなら aws-sso-util を使おう
tl; dr
aws-sso-util
を使うとコマンド一発で ~/.aws/config
が生成できたりして便利なので使うべし。
AWS SSO とは
皆さん、AWS Single Sign-On (AWS SSO) というサービスを利用されていますか。
AWS SSO は 公式ページ によると下記のような記載がありますが、もっぱら前者の複数の AWS アカウントのアクセスに利用している方が多いでしょう。
複数の AWS アカウントとビジネスアプリケーションへのアクセスの一元的な管理を容易にし、割り当てられたアカウントとアプリケーションのすべてに対する 1 か所からのシングルサインオンアクセスをユーザーに提供できるようにする AWS のサービスです。
また、AWS CLI v2 からは ~/.aws/config
に下記のような設定することで、CLI 作業でも AWS SSO による認証・認可が可能になりました[1]。ちょっと手で設定するのは面倒な記述量なのですが、一度設定するだけで安全に必要な環境に適切な権限でアクセスが出来るようになり、想像している以上に便利です。
[profile my-dev-profile]
sso_start_url = https://my-sso-portal.awsapps.com/start
sso_region = us-east-1
sso_account_id = 123456789012
sso_role_name = readOnly
region = us-west-2
output = json
問題点
実際に AWS SSO を運用し始めると様々な事情で AWS SSO の AWS アカウントとアクセス権限セットが増えていくことになります。もちろん、権限管理において適切な棚卸しが必要なのですが、複数プロジェクトを横断するようなロールやポジションにいる方は、どうしても AWS アカウントやアクセス権限セットが増えがちです。
- 関わっているプロジェクトの開発フェーズ (開発・本番 etc.) 毎の AWS アカウント
- 職務分掌による統制を効かせるための AWS アカウントやアクセス権限セット
- 過去に関わったプロジェクトにおける有識者枠のアクセス権限セット (ReadOnlyAccess)
どうでしょう。AWS SSO を活用されている方は、1つくらいは心当たりがあるのではないでしょうか。また、前述の通り、ちょっと手で設定するのは面倒な記述量です。
そして、各プログラミング言語の SDK での AWS SSO への対応状況はまちまちです。例えば、AWS CLI 自体が依存している AWS SDK for Python (boto3) が AWS SSO による認証・認可に対応したのは、2020 年 6 月の1.14.0です。私は主に Python 3 で開発することが多いので他言語の SDK の対応状況を把握していません。しかし、SDK レベルで AWS SSO による認証・認可に対応していないというのは、マルチアカウント運用下でちょっとしたツールを開発するなどといったニーズにおいて、なかなかしんどいものがありました。
解決策
- 大量の AWS アカウントやアクセス権限セット
- プロファイル設定が面倒
- SDK やツールが AWS SSO による認証・認可に対応していない場合がある
こういった問題点を解決してくれるのが、aws-sso-util
です。
インストール方法
README.md の Quickstart
に書いてある通り、pipx
でインストールするだけです。pipx
を使ってない方は普通に pip
で入れるとよいでしょう。
$ pipx install aws-sso-util
大量の AWS アカウントやアクセス権限セット & プロファイル設定が面倒
一行で解決できます。
$ aws-sso-util configure populate -u https://<example>.awsapps.com/start --sso-region us-east-1 --region us-east-1
Logging in to https://<example>.awsapps.com/start
Gathering accounts and roles
Writing N profiles to /home/<username>/.aws/config
プロファイル名は、デフォルトの動作では AWS アカウント名の A-Za-z0-9-._
ではない文字列は -
に変更された上で、アクセス権限セット名と .
で連結したものになります[2]。実際に生成されたプロファイルを見てみましょう。アカウント名に含まれている
が -
に変更されていることが分かります。
[profile Organizations-Management.AdministratorAccess]
sso_start_url = https://<example>.awsapps.com/start
sso_region = us-east-1
sso_account_name = Organizations Management
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
credential_process = aws-sso-util credential-process --profile Organizations-Management.AdministratorAccess
sso_auto_populated = true
自動的に設定されるのは良いのですが、常用するにはプロファイル名が長すぎます。そこで、aws-sso-util
では AWS アカウント名・ID、アクセス権限セット、リージョンといった情報からプロファイル名を生成するスクリプトを指定できるようになっています[3]。下記にサンプルスクリプトを示します。
#!/usr/bin/env python3
import sys
import re
sep = "-"
(
account_name,
account_id,
role_name,
region_name,
short_region_name,
) = sys.argv[1:6]
# サンプルスクリプトということもあり、辞書によるマッピングで。
# 実際には法則性のあるアカウント名を付けて機械的に処理すると良いでしょう。
# 動作に問題がないことを確認するため、一度だけ下記のパラメーターで起動されるので、
# これを適切に処理できるようにしておく必要があります。
# sys.argv[1:6] = ['foo', 'bar', 'baz', 'us-east-1', 'usea1', 0, 1]
account_map = {
'foo': 'foo',
'Organizations Management': 'mgmt',
'Member Account 1': 'member1',
}
role_map = {
'baz': 'baz',
'AdministratorAccess': 'admin',
}
fields = [account_map[account_name], role_map[role_name]]
# aws-sso-util configure populate で --region, -r オプションを複数指定された場合、
# リージョン毎のプロファイル名に短縮形のリージョン名を追加する処理です。
# 短縮形のリージョン名は Availability Zone ID のプレフィックスと似ていますが、
# us-east-1 や us-west-1 では一致しないなど、aws-sso-util 独自形式なのが難点。
region_index, num_regions = (int(v) for v in sys.argv[6:8])
if region_index != 0:
fields.append(short_region_name)
# プロファイル名を標準出力に表示する。
print(sep.join(fields))
一度、~/.aws/config
を削除して、再生成してみます。私の環境は AWS SSO が東京リージョンでリリースされる前にバージニア北部でセットアップしたため、sso_region
は us-east-1
です。一方で普段使いのリージョンは ap-northeast-1
となっています。こういった場合、--region
オプションを付けると複数リージョンのプロファイルが生成されます。ではやってみましょう。
$ rm -f ~/.aws/config
$ chmod +x ./profile-process.py
$ aws-sso-util configure populate -u https://<example>.awsapps.com/start --sso-region us-east-1 \
--region us-east-1 --region ap-northeast-1 \
--raw-account-names --profile-name-process ./profile-process.py
[profile mgmt-admin]
sso_start_url = https://<example>.awsapps.com/start
sso_region = us-east-1
sso_account_name = Organizations Management
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
credential_process = aws-sso-util credential-process --profile mgmt-admin
sso_auto_populated = true
[profile mgmt-admin-apne1]
sso_start_url = https://<example>.awsapps.com/start
sso_region = us-east-1
sso_account_name = Organizations Management
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = ap-northeast-1
credential_process = aws-sso-util credential-process --profile mgmt-admin-apne1
sso_auto_populated = true
...(以下省略)
クラウド推進組織などが AWS アカウント名を一定のルールで付けているのであれば、人間が入力しやすいプロファイル名を自動生成できますね。更に、~/.aws/config
が機械的に生成できるので、CLI ベースの運用手順書を利用している現場では、各人が利用しているプロファイル名を意識する必要がなくなります。
SDK やツールが AWS SSO による認証・認可に対応していない場合がある
SDK やツールが AWS SSO による認証・認可に対応していない場合にも、aws-sso-util
自身が credential_process
としての機能を有しています。credential_process
は外部プロセスによって認証情報(アクセスキー ID、シークレットアクセスキー、セッショントークン)を取得する設定です。古くからある設定なので、多くの SDK が対応しているというメリットがあります[4]。もう一度、生成されたプロファイルを見てみましょう。credential_process
に aws-sso-util
自身が設定されていることが分かります。
[profile mgmt-admin]
sso_start_url = https://<example>.awsapps.com/start
sso_region = us-east-1
sso_account_name = Organizations Management
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
credential_process = aws-sso-util credential-process --profile mgmt-admin
sso_auto_populated = true
credential_proecss
に指定されたコマンドを実際に実行してみると、ドキュメント[4]に記載された通り JSON 形式で認証情報が取得できます。そして、その認証情報を利用すると、AWS SSO で認証・認可が出来ていることが確認できるます。
$ aws-sso-util credential-process --profile mgmt-admin > auth.json
$ export AWS_ACCESS_KEY_ID=$(jq -r .AccessKeyId auth.json)
$ export AWS_SECRET_ACCESS_KEY=$(jq -r .SecretAccessKey auth.json)
$ export AWS_SESSION_TOKEN=$(jq -r .SessionToken auth.json)
$ rm auth.json
$ aws sts get-caller-identity
{
"UserId": "AROASAMPLEIAMIDENTIFY:username@example.com",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_AdministratorAccess_maybefnv1a64hash/username@example.com"
}
その他の機能
AWS SSO サインイン時にプロファイル指定が不要に
AWS CLI v2 で AWS SSO にサインインする場合、下記のように何らかのプロファイルを指定する必要があります。
$ aws sso login --profile member1-admin
実際には AWS SSO のユーザーポータルにサインインするわけですが、オプションとして sso_start_url
を指定するのは長すぎる。そこで、なんらかのプロファイルを指定してサインインするという方式に至ったのでしょうが、若干の違和感があります。
そこで、aws-sso-util
はプロファイルを指定せずに AWS SSO のユーザーポータルにサインインする機能を提供しています。~/.aws/config
内で指定されている sso_start_url
を自動的に取得してサインインが行われます。
$ aws-sso-util login
レアなユースケースかと思われますが、複数の AWS SSO にまたがった ~/.aws/config
の場合でも、全てのユーザーポータルを開く --all
オプションが利用できます。その場合、既に認証済みのユーザーポータルは開かないという気が利いた仕様となっていました。
AWS CloudFormation の出力
AWS SSO は AWS CloudFormation による設定をサポートしていますが[5]、他の CloudFormation リソースと同様、なかなか冗長な記述が必要になっています[6]。結構な AWS アカウント数を抱えている環境でも、AWS SSO の設定は手順書ベースで行っているケースも多いのではないでしょうか。
aws-sso-util
では、CloudFormation マクロによりクライアントサイドでのテンプレート生成する機能を提供していています[7]。例えば、こんな感じのテンプレートファイルを用意します。
Transform: AWS-SSO-Util-2020-11-08
InstanceArn: arn:aws:sso:::instance/ssoins-samplessoinsid01
Groups:
- 1234567890-550e8400-e29b-41d4-a716-446655440000
PermissionSet:
- Name: ReadOnlyAccess
ManagedPolicies:
- ViewOnlyAccess
OUs:
- ou-ouid-sample01
RecursiveOUs:
- ou-ouid-sample02
Accounts:
- 123456789012
そして、下記のコマンドを実行すると、デフォルトでは ./templates
ディレクトリ配下にテンプレートが生成されます。
# 今のところ、aws-sso-util admin 系のコマンドに --region
オプションがないため、プロファイルの region
と sso_region
が異なると使い勝手が悪いです。
# それを避けるため、以降、mgmt-admin プロファイルを使用していきます。
$ export AWS_PROFILE=mgmt-admin
$ aws-sso-util admin cfn aws-sso-config.yaml
サンプルで上手く伝わらないのが残念ですが、多段の OU 配下にある AWS アカウントに対しても RecursiveOUs
によってアクセス権限セットを割り当てられています。
AWSTemplateFormatVersion: '2010-09-09'
Resources:
PermSetReadOnlyAccess:
Type: AWS::SSO::PermissionSet
Properties:
InstanceArn: arn:aws:sso:::instance/ssoins-samplessoinsid01
Name: ReadOnlyAccess
ManagedPolicies:
- arn:aws:iam::aws:policy/ViewOnlyAccess
Assignment0ED97B:
Type: AWS::SSO::Assignment
Metadata:
SSO:
AccountSourceOU: ou-ouid-sample01
TargetName: Main Account
Properties:
InstanceArn: arn:aws:sso:::instance/ssoins-samplessoinsid01
PrincipalType: GROUP
PrincipalId: 1234567890-550e8400-e29b-41d4-a716-446655440000
PermissionSetArn: !GetAtt 'PermSetReadOnlyAccess.PermissionSetArn'
TargetType: AWS_ACCOUNT
TargetId: '123456789012'
AssignmentD97B0E:
... (以下省略)
InstanceArn
は省略するとサインインしている AWS SSO ユーザーポータルから自動取得されます。コマンドで確認したい場合は下記を入力します。
$ aws sso-admin list-instances
OUs
や RecursiveOUs
で指定する Organization Unit の ID は下記のように取得します。
$ ROOT_ID=$(aws organizations list-roots --query 'Roots[].Id' --output text)
$ aws organizations list-children --parent-id $ROOT_ID --child-type ORGANIZATIONAL_UNIT
ネスト構造にある OU では、--parent-id
に指定する ID を親 OU のものにして、下位の OU を辿っていく必要があります。RecursiveOUs
がいかに便利かが分かるでしょう。
Groups
や Users
は aws identitystore
です。これは AWS CLI v2 側の問題ですが、aws identitystore
系のコマンドは使い勝手がイマイチでした。--filters
でユーザーやグループの出力する際にフィルターが指定できます。しかし、現時点 (aws-cli/2.2.1) では list-users
は AttributeName=UserName,AttributeValue=...
しか指定できません。--list-groups
も同様に、AttributeName=DisplayName,AttributeValue=...
しか指定できません。そのため、list-users
や list-groups
を使っても、フィルターの都合でユーザーやグループが1つしか取得できないのです。AWS SSO 側はアクセスコントロールの属性を有効にしていますが[9]、これらの属性は現時点では利用できないようです。AWS CLI v2 側の仕様ではありますが、残念ですね。
$ aws identitystore list-users --identity-store-id d-1234567890 --filters 'AttributePath=email,AttributeValue=username@example.com'
An error occurred (ValidationException) when calling the ListUsers operation: Only filter with value 'UserName' is allowed for this operation
また、aws-sso-util
で現在の割り当て状況を CSV 形式で出力できるようになっています。こちらは権限管理の棚卸しに役立つことでしょう。
$ aws-sso-util admin assignments
最後に
いかがだったでしょうか。
// 一度書いてみたかった。
AWS Control Tower が東京リージョンで利用できるようになったこともあり[8]、今後のマルチアカウント環境において AWS SSO は欠かせないサービスになっています。これを機会に aws-sso-util
を導入して、ズバッと ~/.aws/config
を生成しましょう。
Discussion