CloudFormationを扱う際のIAMの考え方

# あるスタックを作成するケースを考える
SQSのキューを1つだけ作るテンプレートを用意する。

```yaml:sample.cf.yml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  SampleQueue:
    Type: AWS::SQS::Queue
```

とあるIAMユーザーのプロファイルを指定して`create-stack`を実行。  
このIAMユーザーは`cloudformation:*`のみを許可されているものとする。  

```sh
$ aws cloudformation create-stack --stack-name sample --template-body file://./sample.cf.yml --profile sample-user
{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxx:stack/sample/b579cc80-aaf1-11ea-bafc-0a62df4444de"
}
```

マネコンからステータスを確認すると、キューの作成に失敗している。

<img width="707" alt=" 2020-06-10 19.08.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83183/01deefbc-f85d-387d-b003-96b95eb3a9c0.png">


これはIAMポリシーの不足が原因。  

基本的に`create-stack`を実行するユーザーは、スタックに含まれるリソースそれぞれに対して作成・取得・削除等の権限を有している必要がある。

※`*:create*`だけを与えればいいかというとそうでもない。例のIAMユーザーに`sqs:CreateQueue`のポリシーを付与して再度実行すると今度は以下のエラーが発生する。

>API: sqs:GetQueueAttributes Access to the resource https://sqs.ap-northeast-1.amazonaws.com/xxxx/sample-SampleQueue-G5X7EGA2HGMA is denied.

ステータスの取得やロールバック時のリソースの削除などにも対応するために、結局はフルアクセスを与えておくのが早かったりする。

ただここで問題となるのがメンバーのIAM管理の話。

スタックに併せてリソースの作成・削除権限をやみくもに与えていくのはセキュリティ上好ましくない。

リソースで制限をかけるとしても、まだ作成される前のスタックに関してはリソース名が不定なことが多いのでそもそも不可能。

# じゃあどうするのか
**結論**： サービスロールを使いましょう

# What is *サービスロール*
>AWS の多くのサービスでは、ロールを使用して、ユーザーに代わって該当サービスが他のサービスのリソースにアクセスすることを許可する必要があります。サービスがお客様に代わってアクションを実行するために引き受けるロールは、サービスロールと呼ばれます。

IAMユーザーがユーザーに対してAWS APIのアクセスを許可するように、サービスロールは**AWSのサービス**に対して**別のサービスへの操作権限**を許可します。

[AWS サービスにアクセス許可を委任するロールの作成 - AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_create_for-service.html)

例えば Lambda なんかは CloudWatch Logs にログを送信するために`log:PutLogEvents`を持っている必要があったりしますが、この権限をLambdaサービスに対して許可するのがサービスロールです。

Lambdaの場合関数作成時にデフォルトの`LambdaBasicExecutionRole`で諸々付与してくれるのでマネコンで作ってるとあまり意識しないかもしれません。

<img width="1067" alt="2020-06-10_19_31_46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83183/9436d37e-7b91-8736-56eb-7d626b85dd2a.png">


ロールの引受先としてLambdaサービス（`lambda.amazonaws.com`）が設定されていることがわかります。

<img width="1075" alt=" 2020-06-10 19.31.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83183/6c87eac0-f621-8d61-113b-85e862c27bcc.png">


CloudFormationで作る場合はこんな感じでロールを自分で作ります。

```yaml
IAMRoleForLambda:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
```

そんな感じで、これをCloudFormationのスタック作成時に応用するのがベストプラクティスになります。

つまり、CloudFormationサービスにサービスロールを引き受けさせて、そのロールの権限のもとスタックに含まれるリソースの作成を行うというわけです。

# 閑話休題：*Service  Linked Role*

てなわけでサービスロールの作成に移りたいんですが、ここで`Service Linked Role`についても少し触れておきます。

日本語にすると`サービスにリンクされたロール`ですが、

>サービスにリンクされたロールは、AWS サービスに直接リンクされた一意のタイプの IAM ロールです。サービスにリンクされたロールは、サービスによって事前定義されており、お客様の代わりにサービスから他の AWS サービスを呼び出す必要のあるアクセス権限がすべて含まれています。

[サービスにリンクされたロールの使用 - AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/using-service-linked-roles.html)

AWS側で事前に定義してくれているもの、という理解でよさそうです。  

例えばECSの場合、`AWSServiceRoleForECS`というものが定義されています。

<img width="1068" alt="2020-06-10_19_47_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83183/7f0f095c-e1a7-cebc-89e1-62b280a9626f.png">


ECSが必要とするロードバランサー、Route53、オートスケーリングなどの各種操作権限が含まれています。

<img width="1026" alt=" 2020-06-10 19.47.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83183/2f86705f-64e2-2de2-7d3c-8d02e9bf0a2e.png">


こういうテンプレートがあると `aws iam create-service-linked-role
--aws-service-name <value>` で簡単にロールが作成できるのですが、この`Service Linked Role`、全てのサービスに定義されているわけではありません。

こちらに一覧がありますが、 CloudFormation は `No` となっていますね。

[AWS Services That Work with IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html)

閑話休題でした。

# サービスロールの作成

CloudFormationサービスを引き受け先として、SQSのフルアクセスを許可したサービスロールを作成します。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  CloudFormationServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - cloudformation.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: create-sqs-resource
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action: 
                  - sqs:*
                Resource: 
                  - "*"
```

# サービスロールの使用

`create-stack`のオプションとして`--role-arn`があります。  
これを指定すると、CloudFormationがそのロールを引き受けてスタックの操作を行います。ちなみに、指定していない場合はAPIを実行したユーザーの一時的なセッションが使われます。

IAMユーザーには`iam:PassRole`権限が必要になります。サービスに対してロールを渡す操作の許可です。

```sh
$ aws cloudformation create-stack --stack-name sample --template-body file://./sample.cf.yml --role-arn arn:aws:iam::xxxxxxx:role/xxxxxx
```

これで、IAMユーザーにSQSリソースに対する権限がなくてもスタックの作成を実行することができるようになりました。


直接ユーザーに権限を付与していくのではなく、サービスロールを使おうねという話でした。