アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

AWS CDK で IAM ユーザを作ってみる

morishitaです。

AWSのリソースを作るときにはコンソールをポチポチやるよりは Cloud Formation を使ったほうが楽に感じるようになってきました。
そんなところに aws-cdk が GA になりました。Typescript で Cloud Formation スタックが書けるということで気になっていました。
ちょっとググると DyanamoDB とか Lambda を使ってサーバレスアプリケーションを作りました的な記事が出て来るのですが、今回はちょっと地味にIAMユーザを作ってみました。

背景

当社ではほとんどのプロダクトで Docker Compose による開発環境を用意しています。
更に、いくつかのプロダクトでは本番DBのデータ1を含むDockerイメージを毎日ビルドしてECRにプッシュしています。それを Pull すれば、ほぼ最新の本番DB同様のデータをローカルの開発環境で利用できるようになっています。
アクトインディではデザイナが Slim のテンプレートを書くので、デザイナーも開発環境を作ります。DBのイメージを Pull するにはECRにアクセスできる必要があるので、そのためのデザイナにもIAMユーザを振りだそうということになりました。

そのときは次の様な要件で YAMLでテンプレートを作って Cloud Formation スタックを作りました。

  • デザイナ用グループを作る
  • グループに ECR から Pull できるだけのポリシーをつける
  • そのグループに各デザイナ用の個別のIAMユーザを作成する
  • IAMユーザには AccessKeySecretAccessKey も発行する

やってみて思ったのは、同じ様なIAMユーザを作るのに繰り返しのシンタックスがないので不便だということでした。
コピペしてちょっと変えていくだけで増やせるのですが、そのコピペがイマイチだなぁと。

でも、Typescript で書ける CDK ならもっとスッキリ書けるのでは、ということ入門からやってみました。

AWS CDK のインストールとスタックの初期化

CDKのCLIはnpmパッケージです。
それをインストールして、iam-users という名前で CDK プロジェクトを初期化するには次のコマンドを実行します。
言語は、 Python、Java、.NET、TypeScript から選択できます。
私は TypeScript を指定します。

$ npm install -g aws-cdk             # CDKのインストール
$ mkdir iam-users
$ cd iam-users
$ cdk init app --language=typescript # CDKプロジェクトの初期化

ちなみに、このときインストールされたCDKは 1.3.0 でした。

cdk init appの結果、次のファイル群が生成されます。

iam-users/
├── README.md
├── bin/
│   └── iam-users.ts        # CDKコマンドが実行する
├── cdk.json
├── cdk.out
├── lib/
│   └── iam-users-stack.ts  # 実装対象
├── node_modules/
├── package-lock.json
├── package.json
└── tsconfig.json

実装

さて、IAMユーザを作るための実装に入りますが、その前にIAMのリソースを扱うには更に追加のモジュールが必要です。
次のコマンドで @aws-cdk/aws-iam を追加できます。

$ npm install -S @aws-cdk/aws-iam

CDKが実行するのは bin/iam-users.ts です(実際にはトランスパイルされたJS)。
bin/iam-users.ts を見てみると、lib/iam-users-stack.ts をインポートしてインスタンス生成をしているだけです。
ということで、lib/iam-users-stack.ts が実装対象となるファイルということです。

次の様に実装しました。
AWSの提供するサンプルを参考に実装して見たので、コンストラクタに全コードを書いてしいましたが、適宜メソッドに切り出しても構わないだろうと思います。

import { Construct, Stack, StackProps, CfnOutput }  from '@aws-cdk/core';
import { Group, Policy, PolicyStatement, User, CfnAccessKey } from '@aws-cdk/aws-iam';

const account = 'your-account-id';
const repositoryName = 'your-repository-name';

const groupName = 'Group01';
const userNames = ['User01', 'User02', 'User03'];

export class CdkLessonStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // ポリシーステートメントの定義
    const policyStatement = new PolicyStatement({
      resources: [`arn:aws:ecr:ap-northeast-1:${account}:repository/${repositoryName}`],
      actions: [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:DescribeImages",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetRepositoryPolicy",
      ],
    })
    // ポリシーの定義
    const policyName = 'AllowGetImagesFromECR';
    const policy = new Policy(this, policyName, { 
      policyName,
      statements: [policyStatement],
    });
    // グループの定義
    const group = new Group(this, groupName, { groupName });
    group.attachInlinePolicy(policy);

    // ユーザとアクセスキーの定義
    userNames.forEach((userName) => {
      const user = new User(this, userName, { userName, groups: [group] });
      const key = new CfnAccessKey(this, `${userName}Key`, { userName: user.userName });
      new CfnOutput(this, `${userName}AccessKey`, { value: key.ref });
      new CfnOutput(this, `${userName}SecretAccessKey`, { value: key.attrSecretAccessKey });
    })
  }
}

変数userNamesforEachで回して複数のユーザを作っています。
このuserNamesを変更するだけでユーザの作成、削除ができるはずです。
YAMLに比べるととてもスッキリ書けたのではないかと思います。

ビルド、デプロイ

さて、実装できたところで、これを実行してリソースを生成するのですが、 デプロイ前にはビルドが必要なので次のコマンドを実行します。

$ npm run build

ビルドは単に tsc が実行されるだけです。これで Typescript のコードがトランスパイルされ、Javascriptのコードが出力されます。

ビルドが済んだら次のコマンドでデプロイできます。

$ cdk deploy

実行結果は次の様に出力されます。

f:id:HeRo:20190816082305p:plain
cdk deployの出力

Cloud Formationのチェンジセットが作られてリソースが生成される様子が出力されています。
お、狙い通りuserNamesの要素数分、IAMユーザが作成されたようです。

すでに、Cloud Formationに慣れていてYAMLでも確認したいという場合にはcdk synthを実行すれば出力されます。

生成されたリソースの確認

AWSコンソールで生成されたものを確認してみます。

Cloud Formation スタック

AWSコンソールを確認するとCloud Formation スタックが作成され、コード通りのリソースが作成されています。

f:id:HeRo:20190816082346p:plain
Cloud Formation リソース

狙い通り、AWS::IAM::USER が3件作成されています。

また、Cloud Formation で AWS::IAM::AccessKey を作った場合、Outputで出力しないとsecretAccessKeyの値が見れないですが、それもちゃんと出力されています。

f:id:HeRo:20190816082418p:plain
Cloud Formation 出力

Outputに出力してしまうとsecretAccessKeyがいつでも見れてしまうので、スタックを参照できる人の管理が重要です。更にCFnで作るユーザの権限を大きくしすぎないようにするといいと思います。

余談

本当はCloud FormationのOutputにせず、console.logで出力するだけにしたかったのですが、無理そうです。
ユーザを作っているforEachの中で次のコードを追加してみました。

console.log({ name: user.userName, key: key.ref, secret: key.attrSecretAccessKey});

しかし、出力されたのは値ではなく、次の様にCloud Formationスタック内での参照を表しているであろう文字列のみでした。

  {
    name: '${Token[TOKEN.24]}',
    key: '${Token[TOKEN.28]}',
    secret: '${Token[TOKEN.27]}'
  }
  {
    name: '${Token[TOKEN.36]}',
    key: '${Token[TOKEN.40]}',
    secret: '${Token[TOKEN.39]}'
  }
  {
    name: '${Token[TOKEN.48]}',
    key: '${Token[TOKEN.52]}',
    secret: '${Token[TOKEN.51]}'
  }

これを見ても、CDKがやってくれるのは Cloud Formationのテンプレートを作って、スタックに反映するところだけで、リソース自体はCloud Formationが作っているのだなと思います。

IAMリソース

次に作られたIAMリソースをそれぞれ確認してみます。

まずはグループ。
インラインポリシーも指定した名前でちゃんとできています。

f:id:HeRo:20190816082449p:plain
IAM グループ

ポリシーの中身もこの通り。

f:id:HeRo:20190816082531p:plain
IAM インラインポリシー

ユーザも次の通り作られています。もちろん3件作られています。

f:id:HeRo:20190816082559p:plain
IAMユーザ

グループに所属し、ポリシーも割り当てられています。

削除

要らなくなったら次のコマンドで削除できます。

$ cdk destroy

f:id:HeRo:20190816082635p:plain
cdk destroyの出力

IAMのリソースの場合、残るものもなく綺麗サッパリなくなります。

メリット・デメリット

メリット

  • Typescriptは書きやすい
  • エディタによるサジェストも便利すぎる
  • 環境変数による条件分岐や繰り返しなどの制御構文を活用できる

デメリット

  • npm パッケージのアップデートが面倒そう

所感

AWSの各リソースの知識はやはり必要なものの、慣れないJSONやYAMLの仕様と格闘するより、使い慣れたTypescriptで書けるのがやりやすいです。 私はVSCodeを使っているのですが、マウスオーバしたメソッドのコメントも参照できます。 各メソッドの引数はインタフェースが定義されているでどんな値を設定すればいいかもわかりやすいです。

f:id:HeRo:20190816082732p:plain

文字列の連結もCloud FormationのDSLとして用意された使いづらい関数を使わなくてもよく、普通に文字列中の変数展開ができ楽々です。

デメリットはこれといったものが思いつかなかったのですが、強いて言うならCDK自身、そして依存しているnpmのアップデートに追従するのがちょっと面倒そうかなということくらいですかね。

とか思っていたら早速CDKの更新が!

*************************************************
*** Newer version of CDK is available [1.4.0] ***
*** Upgrade recommended                       ***
*************************************************

まあ、この程度の面倒は無視できるくらいCDKを使うとCloud Formationの作成、運用が楽になるのではないでしょうか。

もう私はYAMLに戻れないかも。

参考

最後に

アクトインディではエンジニアを募集しています。 actindi.net


  1. もちろん、開発環境用にはメアドやユーザ名、パスワードなどの情報は書き換えてマスクしています。