2022年6月22日水曜日

Terraform で AWS の管理コンソールにログインできる IAM ユーザーを作成する

前提

  • Docker: Docker version 20.10.17, build 100c70180f
  • Terraform: Terraform v1.2.3
    • 使用する Docker イメージ: hashicorp/terraform:1.2.3
  • Terraform の tfstate を記録しておくための S3 バケットを作成済み
    • 今回は mikoto2000-terraform-user-admin という名前で作成した

今回作るユーザーたち

今回 Terraform で作成するユーザーは以下の通り。

ユーザー ロール
rookie0001 新人。EC2 利用者
rookie0002 新人。EC2 利用者
rookie0003 新人。EC2 利用者

AWS 側の設定

Terraform が使用するユーザーの追加

  1. 管理用ユーザーを追加
    • ユーザー名 : user-admin
    • AWS 認証情報タイプを選択 : アクセスキー - プログラムによるアクセス
  2. アクセス許可の設定
    1. 既存のポリシーを直接アタッチ を選択
    2. IAMFullAccessAmazonS3FullAccess をチェック

ユーザー追加完了画面の アクセスキー IDシークレットアクセスキー をメモ。

Terraform を使用するための準備

コンテナ起動

docker run -it --rm --name terraform -v "$(pwd):/work" --workdir /work --entrypoint sh hashicorp/terraform

必須パッケージインストール

IAM ユーザー作成で使用するツールをインストールする。

apk add gnupg jq
  • gnupg: 暗号化された初期パスワードを受け取るため、公開鍵を利用する。そのために必要なもの。
  • jq: Terraform からの出力をパースするために利用。

公開鍵作成

gpg2 コマンドで、公開鍵ペアを作成。

認証情報の暗号化と復号に使用する。

gpg2 --gen-key

作成した公開鍵を base64 エンコードして TF_VAR_user_admin_gpg_key の環境変数へ記録しておく。

Terraform がパスワードを暗号化するために利用する。

export TF_VAR_user_admin_gpg_key=$(gpg2 --export terraform-user-admin | base64 | tr -d '\n')

AWS 管理用の tf ファイルのたたき台作成

main.tf という名前で、 Terraform 用のファイルを作成。

terraform {
  # AWS を使いますよという定義
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }

  backend "s3" {
    bucket = "mikoto2000-terraform-user-admin"
    region = "ap-northeast-1"
    key = "terraform.tfstate"
  }
}

# AWS の設定
# アクセストークンとアスセスシークレットは、環境変数から取得する
provider "aws" {
  # リージョン
  region = "ap-northeast-1"
}

Terraform プロジェクトの初期化

export AWS_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxxxx"
export AWS_SECRET_ACCESS_KEY="yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"

terraform init

接続確認

アクセスキー ID とシークレットアクセスキーが正しく設定されていれば、 terraform plan すると No changes. Your infrastructure matches the configuration. という情報が表示される。

terraform plan

ユーザー定義の作成

概要

「ユーザー定義をしてはい終了」というわけには行かず、以下のような順番でリソース定義をしていく。

  1. ユーザー定義(aws_iam_user)
    • 今回新規追加する 3 人のユーザーを定義
  2. ユーザーのログインプロファイル定義(aws_iam_user_login_profile)
    • 今回新規追加する 3 人のユーザーの初期パスワードを作成
  3. グループ定義(aws_iam_group)
    • 「新人グループ」を作成
  4. グループポリシー定義(aws_iam_group_policy_attachment)
    • 「新人グループ」に「EC2 を自由に使える権限」を付与
  5. ユーザーをグループに所属させるための定義(aws_iam_group_membership)
    • 今回新規追加する 3 人のユーザーを「新人グループ」に所属させる
  6. 初期パスワードを表示するためのアウトプット定義

gpg 公開鍵を渡すための変数を定義

ユーザーのログインプロファイルを作成するときに公開鍵を設定する必要がある。 その鍵を渡すための変数を定義。

# gpg 公開鍵を渡すための変数を定義
variable "user_admin_gpg_key" {}

Terraform 定義

今回作成した tf ファイルの最終形
terraform {
  # AWS を使いますよという定義
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }

  backend "s3" {
    bucket = "mikoto2000-terraform-user-admin"
    region = "ap-northeast-1"
    key = "terraform.tfstate"
  }
}

# AWS の設定
# アクセストークンとアスセスシークレットは、環境変数から取得する
provider "aws" {
  # リージョン
  region = "ap-northeast-1"
}

# gpg 公開鍵を渡すための変数を定義
variable "user_admin_gpg_key" {}

locals {
  # 作成するユーザーのリスト(新人ユーザーリスト)
  rookies = [
    "rookie0001"
    , "rookie0002"
    , "rookie0003"
  ]
}

resource "aws_iam_user" "rookies" {
  # `rookies` の要素ごとに繰り返し定義するという設定
  for_each = toset(local.rookies)

  # ユーザー名。 rookies に定義した文字列が設定される
  name = "${each.value}"
}

resource "aws_iam_user_login_profile" "rookie" {
  # `aws_iam_user` で作成したユーザーごとに繰り返し定義するという設定
  # ここで `local.rookies` とかを指定してしまうと、
  # ユーザーがまだ作成されないうちにプロファイル定義を作ろうとして、
  # 「ユーザーがいません」って怒られることがある。
  for_each = aws_iam_user.rookies

  # 対象ユーザー
  user = "${each.value.name}"

  # 初期パスワードを暗号化するための公開鍵
  pgp_key = "${var.user_admin_gpg_key}"
}

resource "aws_iam_group" "rookies" {
  # グループ名
  name = "rookies"

  # 謎。 See: https://dev.classmethod.jp/articles/aws-iam-with-path/
  path = "/"
}

resource "aws_iam_group_policy_attachment" "AmazonEC2FullAccess-to-rookies" {
  # 付与対象のグループ
  group = aws_iam_group.rookies.name

  # 付与するポリシー
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
}

resource "aws_iam_group_policy_attachment" "IAMUserChangePassword-to-rookies" {
  # 付与対象のグループ
  group = aws_iam_group.rookies.name

  # 付与するポリシー
  policy_arn = "arn:aws:iam::aws:policy/IAMUserChangePassword"
}

resource "aws_iam_group_membership" "rookies" {
  # グループメンバーシップ名
  name = "rookies"

  # グループに所属させるユーザーの、名前のリストを指定
  users = [for v in aws_iam_user.rookies: v.name]

  # 対象のグループを指定
  group = aws_iam_group.rookies.name
}

output "rookies_encrypted_initia_password" {
  # aws_iam_user_login_profile.rookie の定義ごとに繰り返し、
  # `user` と `encrypted_password` をセットで表示するように指定
  value = "${[for v in aws_iam_user_login_profile.rookie: tomap({
    "user_name" = v.user
    "encrypted_password" = v.encrypted_password}
  )]}"
}

今回作成するユーザーのユーザー名が格納されたリストを作成

やはり同じ権限の人は for 文的なもので作成するほうが良いのでその方針で行くとする。

それに利用するため、ユーザー名が格納されたリストを作成する。

locals {
  # 作成するユーザーのリスト(新人ユーザーリスト)
  rookies = [
    "rookie0001"
    , "rookie0002"
    , "rookie0003"
  ]
}

ユーザー定義

for_each を使用して、今回作成する 3 名分の aws_iam_user を定義する。

resource "aws_iam_user" "rookies" {
  # `rookies` の要素ごとに繰り返し定義するという設定
  for_each = toset(local.rookies)

  # ユーザー名。 rookies に定義した文字列が設定される
  name = "${each.value}"
}

ユーザーログインプロファイル定義

今回作成する 3 名分の aws_iam_user_login_profile を定義する。

resource "aws_iam_user_login_profile" "rookie" {
  # `aws_iam_user` で作成したユーザーごとに繰り返し定義するという設定
  # ここで `local.rookies` とかを指定してしまうと、
  # ユーザーがまだ作成されないうちにプロファイル定義を作ろうとして、
  # 「ユーザーがいません」って怒られることがある。
  for_each = aws_iam_user.rookies

  # 対象ユーザー
  user = "${each.value.name}"

  # 初期パスワードを暗号化するための公開鍵
  pgp_key = "${var.user_admin_gpg_key}"
}

Terraform からは、ここで指定した pgp_key を使用して暗号化された初期パスワードが返ってくる(後述)

ユーザーグループ定義

rookies グループを作成し、必要なポリシーを割り当てる。

ユーザーグループ定義

aws_iam_group を定義する。

resource "aws_iam_group" "rookies" {
  # グループ名
  name = "rookies"

  # 謎。 See: https://dev.classmethod.jp/articles/aws-iam-with-path/
  path = "/"
}

ユーザーグループへグループポリシーを割り当てる

aws_iam_group_policy_attachment を使って、先程定義した rookies グループにポリシーを付与する。

EC2 への全権限を付与

resource "aws_iam_group_policy_attachment" "AmazonEC2FullAccess-to-rookies" {
  # 付与対象のグループ
  group = aws_iam_group.rookies.name

  # 付与するポリシー
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
}

自分のパスワードを変更するための権限を付与

resource "aws_iam_group_policy_attachment" "IAMUserChangePassword-to-rookies" {
  # 付与対象のグループ
  group = aws_iam_group.rookies.name

  # 付与するポリシー
  policy_arn = "arn:aws:iam::aws:policy/IAMUserChangePassword"
}

ユーザーをグループに所属させる

resource "aws_iam_group_membership" "rookies" {
  # グループメンバーシップ名
  name = "rookies"

  # グループに所属させるユーザーの、名前のリストを指定
  users = [for v in aws_iam_user.rookies: v.name]

  # 対象のグループを指定
  group = aws_iam_group.rookies.name
}

初期パスワードを表示するためのアウトプット定義

output "rookies_encrypted_initia_password" {
  # aws_iam_user_login_profile.rookie の定義ごとに繰り返し、
  # `user` と `encrypted_password` をセットで表示するように指定
  value = "${[for v in aws_iam_user_login_profile.rookie: tomap({
    "user_name" = v.user
    "encrypted_password" = v.encrypted_password}
  )]}"
}

実際に AWS にユーザーを作る

ドライラン

terraform plan で、どんなリソースが作成されるか確認。

terraform plan

リソース作成

terraform apply でリソースを作成する。

terraform apply --auto-approve

パスワードの復号

以下コマンドを組み合わせて、初期パスワードを取得する。

  1. terraform output で 「base64 済み・暗号化済みのパスワード」を取得
  2. base64 -d で base64 デコード
  3. gpg -r で復号

以下は、 rookie0001 の初期パスワード複合例。

# base64 エンコード済み、かつ、暗号化済みのパスワードが記載された json を取得
ENCRYPTED_PASSWORD=$(terraform output -json rookies_encrypted_initia_password | jq -r '.[0].encrypted_password')

# encrypted password を base64 デコードして復号
echo $ENCRYPTED_PASSWORD | base64 -d | gpg -r terraform-user-admin  2> /dev/null; echo

動作確認

rookie0001 でログインしてみる。

  1. AWS コンソールを開く -> IAM ユーザー 選択
    • アカウント ID: ルートユーザーのアカウント ID を入力
    • ユーザー名: rookie0001
    • パスワード: 前述の手順で取得したパスワードを入力
  2. パスワード変更画面が表示されるので、変更する
  3. 権限確認
    • EC2 のサービスを見てみる -> 普通に使用できる。無料枠の範囲内のインスタンスが無事たったことを確認
      • SSH 接続もできた
    • EC2 以外のサービスを見てみる -> アクセス許可がありません みたいな表示になって操作できない

今後のために

他の管理者が認証情報の復号をしたくなった際に復号できるように、(そんなことあるか???) 公開鍵ペアをエクスポートしてどこかに保存しておく。

gpg -o ./terraform-user-admin.public.gpg  --export terraform-user-admin
gpg -o ./terraform-user-admin.private.gpg --export-secret-key terraform-user-admin

後片付け

terraform destroy で作成したリソースを削除。

terraform destroy --auto-approve

以上。

これで「新人のスキルアップのためにアカウント欲しい」って言われたときにさくっとユーザー作れるかな?…どうだろう

参考資料

0 件のコメント:

コメントを投稿