🙂

TerraformでIAM Policiesを作ってみた

2023/02/05に公開

今回はTerraformでAWS IAM Policiesを作ってみたので、作り方について書きます。
Terraformの公式サイトを参考にやってみました。

前提条件

サンプルのリポジトリーをクローンする

下記のコマンドで今回使用するリポジトリをクローンする。

% % git clone https://github.com/hashicorp/learn-terraform-iam-policy.git
Cloning into 'learn-terraform-iam-policy'...
remote: Enumerating objects: 82, done.
remote: Counting objects: 100% (31/31), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 82 (delta 19), reused 21 (delta 17), pack-reused 51
Receiving objects: 100% (82/82), 25.42 KiB | 6.35 MiB/s, done.
Resolving deltas: 100% (34/34), done.

クローンしたらそのディレクトリーに移動する。

% cd learn-terraform-iam-policy

中身の確認

ディレクトリーに移動したらファイルを参照してみる。

% ls -g
total 72
-rw-r--r--  1 staff  16761 Feb  5 17:51 LICENSE
-rw-r--r--  1 staff    294 Feb  5 17:51 README.md
-rw-r--r--  1 staff    952 Feb  5 17:51 main.tf
-rw-r--r--  1 staff      0 Feb  5 17:51 outputs.tf
-rw-r--r--  1 staff    232 Feb  5 17:51 terraform.tf
-rw-r--r--  1 staff     46 Feb  5 17:51 variables.tf

main.tfやterraform.tfなどのファイルがあった。
main.tfの中身を確認してみます。
policyのnameがrandom_petになっているのは名前の重複を防ぐためとのことです。ランダムにペットの名前で作成されるみたい。

% cat main.tf 
provider "aws" {
  region = var.region

  default_tags {
    tags = {
      Hashicorp-Learn = "aws-iam-policy"
    }
  }

}

resource "random_pet" "pet_name" {
  length    = 3
  separator = "-"
}

resource "aws_iam_user" "new_user" {
  name = "new_user"
}

resource "aws_s3_bucket" "bucket" {
  bucket = "${random_pet.pet_name.id}-bucket"

  tags = {
    Name        = "My bucket"
    Environment = "Dev"
  }
}

resource "aws_s3_bucket_acl" "bucket" {
  bucket = aws_s3_bucket.bucket.id

  acl = "private"
}

resource "aws_iam_policy" "policy" {
  name        = "${random_pet.pet_name.id}-policy"
  description = "My test policy"

  policy = <<EOT
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:ListAllMyBuckets"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": "${aws_s3_bucket.bucket.arn}"
    }

  ]
}
EOT
}

また、このpolicyの書き方はヒア文字列を使用しています。policyがシンプルな場合は大丈夫ですが、もっと複雑になった時はヒア文字列はお勧めしないと公式サイトには記載があります。パースするのが難しくなるとの事でした。

Policyの更新

編集モードでmain.tfを開きます。

% vi main.tf

下記のpolicyをmain.tfにコピーして貼り付けます。

data "aws_iam_policy_document" "example" {
  statement {
    actions   = ["s3:ListAllMyBuckets"]
    resources = ["arn:aws:s3:::*"]
    effect = "Allow"
  }
  statement {
    actions   = ["s3:*"]
    resources = [aws_s3_bucket.bucket.arn]
    effect = "Allow"
  }
}

下記のコマンドでファイルを保存して閉じます。

:wq!

このpolicyの意味としては最初のstatementでs3にあるバケットリストを表示させます。二つ目はこのmain.tfで作成されたs3のバケットに対してはどんなアクションでも許可する設定です。他のバケットに対しては何も出来ません。
次に下記の更新を行います。
main.tfファイルにpolicy = data.aws_iam_policy_document.example.jsonを追記して、それ以降の行を削除します。

% vi main.tf
provider "aws" {
  region = var.region

  default_tags {
    tags = {
      Hashicorp-Learn = "aws-iam-policy"
    }
  }

}

resource "random_pet" "pet_name" {
  length    = 3
  separator = "-"
}

resource "aws_iam_user" "new_user" {
  name = "new_user"
}

resource "aws_s3_bucket" "bucket" {
  bucket = "${random_pet.pet_name.id}-bucket"

  tags = {
    Name        = "My bucket"
    Environment = "Dev"
  }
}

resource "aws_s3_bucket_acl" "bucket" {
  bucket = aws_s3_bucket.bucket.id

  acl = "private"
}

resource "aws_iam_policy" "policy" {
  name        = "${random_pet.pet_name.id}-policy"
  description = "My test policy"
  policy = data.aws_iam_policy_document.example.json
}
data "aws_iam_policy_document" "example" {
  statement {
    actions   = ["s3:ListAllMyBuckets"]
    resources = ["arn:aws:s3:::*"]
    effect = "Allow"
  }
  statement {
    actions   = ["s3:*"]
    resources = [aws_s3_bucket.bucket.arn]
    effect = "Allow"
  }
}

このaws_iam_policy_documentはHCLというTerraform独自の言語で書かれています。先ほどの説明でヒア文字列はお勧めしないというので、今回HCLに書き換えています。

Policyアタッチメントを作成する

main.tfファイルに書かれているaws_iam_policyとaws_iam_policy_documentでpolicyを作成しますが、これだとどのロールやユーザーにも適用されないため、アタッチメントを作成します。
下記をmain.tfファイルの最後に追記します。

resource "aws_iam_user_policy_attachment" "attachment" {
  user       = aws_iam_user.new_user.name
  policy_arn = aws_iam_policy.policy.arn
}

この記述はuserとpolicy_arnを定義しています。arnはiam_policyで定義されたものを参照するようにします。
outputs.tfに下記を追加します。

% vi outputs.tf 
output "rendered_policy" {
  value = data.aws_iam_policy_document.example.json
}

これで準備完了です。

ユーザー、バケット、policyの作成

先ず、terraform.tf を開いてcloud部分をコメントアウトします。これはTerraform OSSを使っているためです。また、私のTerraformのバージョンは1.3のため、required_versionの所を1.3に変更しています。1.2の人はそのままで大丈夫です。

% vi terraform.tf  
terraform {
/*
  cloud {
    workspaces {
      name = "learn-terraform-aws-iam-policy"
    }
  }
*/
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.15.0"
    }
  }
  required_version = "~> 1.3.0"
}

初期化します。

% terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/random from the dependency lock file
- Installing hashicorp/aws v4.15.0...
- Installed hashicorp/aws v4.15.0 (signed by HashiCorp)
- Installing hashicorp/random v3.3.2...
- Installed hashicorp/random v3.3.2 (signed by HashiCorp)

Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

変更を適用します。

% terraform apply

Terraform used the selected providers to generate the following execution plan. Resource
actions are indicated with the following symbols:
  + create
 <= read (data resources)
...

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

Outputs:

rendered_policy = <<EOT
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:ListAllMyBuckets",
      "Resource": "arn:aws:s3:::*"
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::curiously-growing-shark-bucket"
    }
  ]
}
EOT

Outputs:の所には作成されたarnが含まれているはずです。それにしてもサメがペットとかどういう感覚なんですかね。。。

Policyシミュレーターでテストする

さて、完成したpolicyをテストしてみます。
AWS Policy Simulatorを使います。
作成したnew userを選択します。

Serviceの所でs3を選択し、actionで"DeleteObject" と "DeleteBucket"をチェックします。

右上のRun Simulationをクリックします。

deniedになったと思います。これは想定通りですね。今回作成したバケット以外は操作出来ないpolicyのため正しい動作です。
先ほど作成されたOutputにあったarnをコピーして対象のバケットにして再度実行します。

そうするとallowedになったと思います。
アクションを許可しているs3に対してなので、正しい動作ですね。

これでTerraformでのpolicy作成は完了です。
最後に作成したリソースを削除します。

% terraform destroy
random_pet.pet_name: Refreshing state... [id=curiously-growing-shark]
aws_iam_user.new_user: Refreshing state... [id=new_user]
aws_s3_bucket.bucket: Refreshing state... [id=curiously-growing-shark-bucket]
data.aws_iam_policy_document.example: Reading...
aws_s3_bucket_acl.bucket: Refreshing state... [id=curiously-growing-shark-bucket,private]
...

Destroy complete! Resources: 6 destroyed.

初めてTerraformでAWSリソースを作成した感想

私は今回のチュートリアルで初めてTerraformのリソースを作成してみましたが、意外に簡単に出来たと思います。ただ、HCLの書き方などまだ良くわからない箇所があります。今回は用意されていたtfファイルの更新だけだったので、出来ましたが一から作成するとなると大変だと思います。今後もTerraformを触って理解を深めて行きたいです。

Discussion