AWS
IAM
aws-cli
DynamoDB
Terraform

Terraform で DynamoDB の Table を作って IAM Policy でアクセス制限

やりたいこと


  • Terraform で DynamoDB の Table を作る。

  • 既存のテーブルに読み込みだけが出来る IAM Role を割り当てる。

  • 権限が設定されたかテスト。


Terraform で DynamoDB の Table を作る。

まず AWS ルートユーザや IAM ユーザで aws コマンドが動く事を確認します。

$ export AWS_PROFILE=hoge

$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile hoge manual --profile
access_key hogehoge shared-credentials-file
secret_key ********************* shared-credentials-file
region us-west-2 config-file ~/.aws/config
... 適切な access_key, secret_key, region が表示されると OK。

次のような Terraform file で aws_dynamodb_table を作ります。


dynamodb.tf

resource "aws_dynamodb_table" "music" {

name = "Music"
read_capacity = 1
write_capacity = 1
hash_key = "Artist"
range_key = "SongTitle"

attribute {
name = "Artist"
type = "S"
}

attribute {
name = "SongTitle"
type = "S"
}
}


初期化

terraform init

設定

terraform apply

これで、DynamoDB へのアクセス » CLI の使用 と同じテーブルが出来ます。

動作確認

$ aws dynamodb put-item --table-name Music --item '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}}'

$ aws dynamodb query --table-name Music --key-condition-expression 'Artist = :a' --expression-attribute-values '{ ":a": {"S": "No One You Know"}}'
{
"Count": 1,
"Items": [
{
"AlbumTitle": {
"S": "Somewhat Famous"
},
"SongTitle": {
"S": "Call Me Today"
},
"Artist": {
"S": "No One You Know"
}
}
],
"ScannedCount": 1,
"ConsumedCapacity": null
}

削除

terraform destroy

terraform だと削除が簡単なのが良いですね。


既存のテーブルに Query だけが出来る IAM Policy を作る。

次に、うっかりテーブルを変更してしまわないように、作ったテーブルの Query と GetItem だけが出来る IAM Policy を作ります。

AWS では、テーブル等すべてのリソースに ARN が付きます。DynamoDB のテーブルは以下の ARN で表します。

arn:aws:dynamodb:region:account-id:table/table-name

例えばもしも account-id が 123456789012 で region が us-west-2 なら、上の例は arn:aws:dynamodb:us-west-2:123456789012:table/Music です。terraform で作った ARN は terraform show で確認出来ます。

このテーブルのアクセス権を設定するには Policy 言語 を使います。DynamoDB の例が Amazon DynamoDB でアイデンティティベースのポリシー (IAM ポリシー) を使用する に色々あります。

Policy を Terraform で定義する場合は aws_iam_policy に埋め込む事が出来ます。Music の読み書きを行う Policy は以下のようになります。

resource "aws_iam_policy" "music" {

name = "music_policy"
description = "DynamoDB Music policy"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:Query",
"dynamodb:GetItem"
],
"Effect": "Allow",
"Resource": "${aws_dynamodb_table.music.arn}"
}
]
}
EOF
}


IAM Role を作る。

作った Policy で制限がかかっているか試すにはいくつか方法があります。ここでは IAM Role を作って自分の IAM User から切り替えて使ってみます。IAM Role を作るには aws_iam_role を使います。assume_role_policy で Role を与える対象 (Principal) を選びます。実用上では Principal に ec2.amazonaws.com 等を選んで特定の AWS サービスに IAM Role を与える事が多いかと思いますが、ここではテストが簡単なように Principal として今まで作業してきた自分の IAM User を設定し、aws cli で Role を切り替えて使うことします。

data "aws_caller_identity" "current" {}

resource "aws_iam_role" "music" {
name = "music_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"AWS": "${data.aws_caller_identity.current.arn}"
}
}
]
}
EOF
}

このように aws_iam_roleassume_role_policyaws_iam_policy と同様 Policy 言語で記述します。IAM User を Principal として指定するためには ARN を設定しますが、次のような種類があります。


  • アカウント 123456789012 の任意のユーザ: arn:aws:iam::123456789012:root

  • アカウント 123456789012 のユーザ hoge: arn:aws:iam::123456789012:user/hoge

上の例では Terraform の aws_caller_identity 機能を使って現在のユーザの ARN を取得しているため、実際の ARN をベタ書きする必要がありません。便利。


IAM Policy と IAM Role を関連付ける

これも Terraform だと参照を使えるので非常に簡単です。

resource "aws_iam_role_policy_attachment" "music" {

role = "${aws_iam_role.music.name}"
policy_arn = "${aws_iam_policy.music.arn}"
}

ここまでで Terraform の設定は終わりなので、terraform apply で再度反映させます。


テスト

IAM ロールへの切り替え を参考に作った IAM Role に切り替えてテストしてみます。

もしも現在 AWS_PROFILE=hoge を使っていて、デフォルト region が us-west-2 で、アカウント ID が 123456789012 の場合、~/.aws/config に以下を追記します。

role_arn は terraform show で aws_iam_role.music: の欄に出てくる arn です。


~/.aws/config

...

[profile music]
role_arn = arn:aws:iam::123456789012:role/music_role
source_profile = hoge
region = us-west-2

このように設定すると、music という aws profile で music_role が有効になります。例えば --profile music をつけて DynamoDB にアクセスしてみます。

$ aws dynamodb query --profile music --table-name Music --key-condition-expression 'Artist = :a' --expression-attribute-values '{ ":a": {"S": "No One You Know"}}'

{
"Count": 1,
"Items": [
{
"AlbumTitle": {
"S": "Somewhat Famous"
},
"SongTitle": {
"S": "Call Me Today"
},
"Artist": {
"S": "No One You Know"
}
}
],
"ScannedCount": 1,
"ConsumedCapacity": null
}

のように query は成功しますが、

$ aws dynamodb put-item --profile music --table-name Music --item '{"Artist": {"S": "No One You Know"}, "SongTitle": {"S": "Call Me Today"}, "AlbumTitle": {"S": "Somewhat Famous"}}'

An error occurred (AccessDeniedException) when calling the PutItem operation: User: arn:aws:sts::123456789012:assumed-role/music_role/botocore-session-1557893071 is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:us-west-2:123456789012:table/Music

のように put-item は目出度くエラーになります。

実際に業務で使う時はこの IAM Role を EC2 とか Kubernetes pod とかに割り当てると、ACCESS_KEY を書かなくても適切な権限を与えられるので便利という物です。