はじめに
- 業務でTerraformを使う機会があり、とことん躓いたので備忘録を残します
- 本稿ではTerraformでIAMユーザを一気に作る手順を記録します
- 誤りの訂正・より良い方法のご助言、どしどしコメント頂ければ幸いです
参考とさせていただいた記事様
Terraform 実行環境
- AWS Cloud9
- Amazon Linux2
- t2.micro
- Cloud9を使う理由
- 標準でTerraformが備わっているため
- Cloud9の設定
- AWS managed temporary credentialsをオフにする
- オンだとIAMの一部機能にアクセスできないため
- EC2インスタンスにAdministratorAccessポリシーを持つロールをアタッチしておく
- AWS managed temporary credentialsをオフにする
アウトライン
- GPGで鍵の生成
- TerraformでIAMユーザの一括作成
- ワンライナーでIAMユーザパスワードの一括復号
- おまけ:一括復号ワンライナー完全理解
1.GPGで鍵の生成
鍵の生成
gpg --gen-key
- 色々聞かれるが、基本デフォルト値(何も入力しないでエンター押すとそうなる)
- Real Nameは必須
- 今回は
taishi_o
とする
- 今回は
- パスワードは入力しなくてもいける
- 今回はなしで
鍵を保存
gpg -o ./taishi_o.public.gpg --export taishi_o
gpg -o ./taishi_o.private.gpg --export-secret-key taishi_o
公開鍵をBase64エンコードして保存
cat taishi_o.public.gpg | base64 | tr -d '\n' > taishi_o.public.gpg.base64
-
taishi_o.public.gpg.base64
にエンコード済かつ改行コードが取り除かれた公開鍵が記録される
2. IAMユーザの作成
- 下記の通りterraformのコードを書く
iam_user.tf
provider "aws" {
region = "ap-northeast-1"
}
variable "pgp_key" {
type = string
default = "mQ......Qz" # taishi_o.public.gpg.base64 の中身
}
variable "aws_iam_user" {
type = map(any)
default = {
member1 = {
name = "member1",
},
member2 = {
name = "member2",
},
member3 = {
name = "member3",
},
}
}
# IAM Userの作成
resource "aws_iam_user" "team_a" {
for_each = var.aws_iam_user
name = each.value.name
path = "/"
force_destroy = true
}
# Login Profileの作成
resource "aws_iam_user_login_profile" "team_a" {
for_each = aws_iam_user.team_a
user = each.value.name
pgp_key = var.pgp_key
password_reset_required = true
password_length = "20"
}
output "username" {
value = aws_iam_user_login_profile.team_a
}
- ターミナルで
terraform apply
出力イメージ
username = {
"member1" = {
"encrypted_password" = "wc......A="
"id" = "member1"
"key_fingerprint" = "01...70"
"password_length" = 20
"password_reset_required" = true
"pgp_key" = "mQ......Qz"
"user" = "member1"
}
"member2" = {
"encrypted_password" = "wc......A="
"id" = "member2"
"key_fingerprint" = "01...70"
"password_length" = 20
"password_reset_required" = true
"pgp_key" = "mQ......Qz"
"user" = "member2"
}
"member3" = {
"encrypted_password" = "wc......A="
"id" = "member3"
"key_fingerprint" = "01...70"
"password_length" = 20
"password_reset_required" = true
"pgp_key" = "mQ......Qz"
"user" = "member3"
}
}
3. IAMユーザパスワードの一括復号(ワンライナー)
- 下記をターミナルで実行することで、作成したIAMユーザのパスワードを一括で標準出力できる
- ご自身で作成されたgpgキーを使うところのみご変更ください(下記ではtaishi.o
の部分)
パスワード一括復号ワンライナー
terraform output -json | ruby -rjson -e 'json = JSON.load(ARGF); values = json["username"]["value"]; keys = %w(id encrypted_password); puts [keys, *keys.map{|key| values.map{|value| v = value[1][key].split; key == "encrypted_password" ? v.map{|s| `echo #{s} | base64 -di | gpg -r taishi_o`.chomp} : v}}.transpose].map{|a| a.join(",")}'
出力イメージ
gpg: encrypted with 2048-bit RSA key, ID 2E35ECDD, created 2021-08-06
"taishi_o"
gpg: encrypted with 2048-bit RSA key, ID 2E35ECDD, created 2021-08-06
"taishi_o"
gpg: encrypted with 2048-bit RSA key, ID 2E35ECDD, created 2021-08-06
"taishi_o"
id,encrypted_password
member1,+B......Bk
member2,a#......MS
member3,Xz......i{
- これら
id
とencrypted_password
でサインインすることができる
4. おまけ:一括復号ワンライナー完全理解
- TerraformでIAMグループ・ユーザを作成する 様を参考に見様見真似で何も理解できてないまま作ってしまった
- 分割して理解していく
terraform output -json
- terraformのアウトプットコマンド(json形式)
|
(パイプ)
-
command a| command b
でcommand a
の結果をcommand b
に渡せる
ruby -rjson -e '...'
-
ruby
: Rubyコマンド -
-rjson
: rubyでjsonを扱えるJSONライブラリ
を読み込む -
-e
:...
に記述されたスクリプトを実行する- スクリプト内の
;
は改行を表す
- スクリプト内の
json = JSON.load(ARGF);
-
パイプされた
terraform output -json
の出力をjson形式で読み込んで、json変数に入れる- ARGFは渡されたものを1つの仮想ファイルにするオブジェクト
-
備考:渡されたjsonは下記で見れる
terraform output -json | ruby -rjson -e 'json = JSON.load(ARGF); puts [JSON.pretty_generate(json)];'
-
puts
は標準出力 -
JSON.pretty_generate(json)
はjsonを可読性出力するものっぽい(Pythonで言うならpprint?)
-
terraform output -json | ruby -rjson -e 'json = JSON.load(ARGF); puts [json["username"]["value"]];'
values = json["username"]["value"];
- データ部を抽出し、values変数に格納
keys = %w(username encrypted_password);
-
username
とencrypted_password
という要素をもつ1次元配列keys
を宣言- Rubyでは
%w(a b c)
で["a","b","c"]
が作れる
- Rubyでは
puts["char1", "char2"]
- 出力
char1
char2
- 要素を改行しながら表示してくれる
*keys.map{|key| values.map{|value| v = value[1][key].split;
- Rubyでは
list.map
で要素ごとに処理を追加できる-
keys
の要素(username, encrypted_password)をjson
のキーに指定し、値を変数v
に格納している -
split
は文字列を空白区切りで配列に変換している
-
- 備考:
*
は配列展開-
*keys
はkeys[0], keys[1]と要素ごとに出力してくれる -
*
があるおかげで最終結果を見やすく改行できている
-
key == "encrypted_password" ? v.map{|s| `echo #{s} | base64 -di | gpg -r taishi_o`.chomp} : v}
- 三項演算子
- 条件:
key
が"encrypted_password"
か否か- True:
v.map{|s|
echo #{s} | base64 -di | gpg -r taishi_o.chomp}
の結果を出力 - False:
v
をそのまま出力
- True:
v.map{|s| `echo #{s} | base64 -di | gpg -r taishi_o`.chomp}
-
encrypted_password
のリストv
の各要素s
に対して秘密鍵(?)taishi_o
で復号している - chompは文字列から改行コードを除いて返すメソッド
transpose
- 配列の転置
-
transpose
の実行でmemberとそのパスワードをセットで表示できる
member1 password1
member2 password2
list.map{|a| a.join","}
- listの要素を
,
区切りの文字列として連結 - これにより下記のようなcsv形式になる
member1,password1
member2,password2
おわりに
- 無事IAMユーザを一気に作り、ログインパスワードを一括で表示できました
- 余談:
count
を使ってresourceを一括作成する方法もありますが、こちらは変更に弱くなってしまいます(詳しくは下記リンク様参照) - 苦しめられますが非常に便利なので、ネタがあれば引き続きTerraformの検証記事を書いていく予定です