【後編】AWSをゼロから作ってPlatform Engineering活動を始めるまで


こんにちは、博報堂テクノロジーズ・インフラ開発一部の近藤です。
前半に続き、AWS環境をゼロから作りPlatform Engineering活動を始めるまでの変遷を記載します。

# アカウント数の増加(N+12か月)

## ビジネス側の状況
複数プロダクトが稼働する可能性と顧客数が増加する状況になりました。

## インフラの対応
以下状況となり、精神的負荷が高まり払い出しに時間が掛かる状況となりました。
- AWSアカウントやOU、ユーザが2桁を超えてきた
- 紐づけるOUによっては適用するSCPが変わるため、気を張った作業が必要
- 基本手作業で実施し同期的再鑑が必要だが、担当者繁忙により日程調整が難しい

これらの状況から、現状を継続すると破綻する可能性があると考えました。

改めて振り返った時に、**我々インフラチームの直接の顧客はアプリ開発者であり、インフラの提供スピードは開発、ひいてはリリースのリードタイムにも影響すると考えました**。
ここから、開発者体験の向上が目的でもあるPlatform Engineeringに基づいた対応を取る方針としました。

我々の守備範囲で出来ることを考え、まずは大きな効果が見込めるAWSアカウントの払い出し※自動化を検討・実装しました。下図に、アーキテクチャイメージ図を記載します。
※：ざっくり、AWSアカウントの払い出しでは以下を実施しています。
　　1.管理アカウントで、テナントアカウントを払い出し
　　2.テナントアカウントで、初期キッティング
　　3.グローバルアカウントで、テナントアカウントに適用するIPAM作成・共有

アーキテクチャイメージ図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3672465/010b49de-03a5-9727-ea58-f6a27cd7d5e9.png)

```mermaid
graph LR
	subgraph diff-check
		checkout-diff(checkout) --> GetDiff
	end
	subgraph 管理アカウント&テナントアカウントジョブ
	  GetDiff --> checkout(checkout)
	  checkout(checkout) --> FMT(fmtcheck<br>terraform fmt)
	  FMT(fmtcheck<br>terraformfmt) -->|OK| SAST(SAST<br>trivyスキャン)
	  FMT(fmtcheck<br>terraformfmt) -->|Error| Exit
	  SAST(SAST<br>trivyスキャン) --> |OK| SWITCH(管理アカウントへAssumeRole)
	  SAST(SAST<br>trivyスキャン) --> |Error| Exit
	  SWITCH(管理アカウント<br>AssumeRole)　--> init(TF初期化<br>terraform init) 
		init(TF初期化<br>terraform init) --> vali(静的解析<br>terraform Validate)
	  vali(静的解析<br>terraform Validate) --> |Error| Exit
	  vali(静的解析<br>terraform Validate) --> |OK| plan(DryRun<br>terraform plan) --> com(PRコメント<br>DryRun結果)
		-->|PRMerge<br>Approval| state(状態出力<br>terraform state) --> apply(Deploy<br>terraform apply)
	  apply --> |既存アカウント<br>の場合| 処理終了
      apply(Deploy<br>terraform apply) --> |新規アカウント<br>作成の場合| VPCdelete(テナントアカウント初期キッティング_手動可)
    end
    subgraph グローバルアカウントジョブ
      GetDiff --> checkout-glbcmn(checkout)
	  checkout-glbcmn(checkout) --> FMT-glbcmn(fmtcheck<br>terraform fmt)
	  FMT-glbcmn(fmtcheck<br>terraform fmt) -->|OK| SAST-glbcmn(SAST<br>trivyスキャン)
	  FMT-glbcmn(fmtcheck<br>terraform fmt) -->|Error| Exit-glbcmn(Exit)
	  SAST-glbcmn(SAST<br>trivyスキャン) --> |OK| SWITCH-glbcmn(管理アカウント<br>AssumeRole)
	  SAST-glbcmn(SAST<br>trivyスキャン) --> |Error| Exit-glbcmn(Exit)
	  SWITCH-glbcmn(管理アカウント<br>AssumeRole) --> SWITCH2-glbcmn(グローバルアカウント<br>AssumeRole) 
	  SWITCH2-glbcmn(グローバルアカウント<br>AssumeRole)　--> init-glbcmn(TF初期化<br>terraform init) 		
	  init-glbcmn(TF初期化<br>terraform init) --> vali-glbcmn(静的解析<br>terraform Validate)
	  vali-glbcmn(静的解析<br>terraform Validate) --> |Error| Exit-glbcmn(Exit)
	  vali-glbcmn(静的解析<br>terraform Validate) --> |OK| plan-glbcmn(DryRun<br>terraform plan) --> com-glbcmn(PRコメント<br>DryRun結果)
		-->|PRMerge<br>Approval| state-glbcmn(状態出力<br>terraform state) --> apply-glbcmn(Deploy-glbcmn<br>terraform apply)
	end
```

## 期間

4か月ほどで実装しました。

## 効果

### 定量評価
実装前後で、1AWSアカウント払い出しに掛かる時間は以下の通りです。

|#  |項目  |実装前(分)  |実装後(分)|
|---|---   |:-:    |:-:   |
|1  |要件解釈|5  |5|
|2  |パラメタ整理|5  |5|
|3  |手順書準備  |5  |0|
|4  |作業実施(管理アカウント)  |10(5分×2人)  |-|
|5  |作業実施(テナントアカウント)  |10(5分×2人)  |-|
|6  |作業実施(グローバルアカウント)  |10(5分×2人)  |-|
|7  |コード修正  |-  |5|
|8  |PRレビュー  |-  |5|
|9  |マージ・適用  |-  |1|
|10 |**合計**  |**45分**  |**21分**|

おおよそ半分の時間まで短縮できました。

### 定性評価
以下2点あります。
- IaC・CICD化によってDryRunが可能になり、精神的負荷が大幅に軽減されたこと
- 同期的再鑑が不要となり、不毛なスケジュール調整が不要となったこと

## 悩んだポイント
悩んだ事とどう対応したかを記載します。

### 何を自動化するか
私たちの守備範囲では、AWSアカウント払い出しの他、複数の作業を実施していました。
ただ、すべてを自動化することはコストに見合わないことや、自動化は新規参入者の認知負荷を上げるリスクもあることから、以下の観点で対象を選定しました。
- 誤操作リスク
- 作業頻度
- 精神的負荷含む作業負荷

これらを評価し、優先度をつけた結果、AWSアカウント払い出しを自動化対象としました。

### アカウント払い出しの中でもどこまでを自動化するか
自動化といっても、依頼を受けるところからかなど、**どこからを自動化するかが悩みのポイント**でした。依頼内容のバリデーションチェックは実施すべきと考えたため、依頼は従来通り受領し、以降の部分を自動化することとしました。

### IaCツール・CICDツール選定のポイント
個人的には社内で利用頻度・練度が高いものを利用すべきと考えています。IaC化したコードは、モジュール化し別PJなどで利用する可能性があるためです。
今回はTerraformを採用しています。なお、CICDも同様の考えでGitHubActionsを採用しています。

IaCツールの選定は、以下私が執筆したブログもご参照ください！
https://qiita.com/tkkon/items/fb39e39f9b8d6c043f15

### IaC化・CICD化するに当たり工夫した点
アカウント払い出しの実装コードは様々なサイトで紹介されていますので、ここでは運用面に配慮した内容を紹介します。

**可読性への配慮**
個人的に、Organizations系のTerraformリソース名は横に長い印象です。エディタ内の横スクロールは、コードの可読性を低下させるため、対処すべきと考えました。具体的には、以下のようにリソース名自体を変数化し省略するよう心掛けました。

```terraform
locals{
    # ou_infraとして、Organizationsのリソース名を短縮化
	ou_infra = aws_organizations_organizational_unit.root_under["OU名"].id	
    org_acct = [
      { "name" = "<アカウント名>", "email" = "メールアドレス", parent_id = local.ou_infra },
      ・・・
    ]
}

resource "aws_organizations_account" "org_acct" {
  for_each = { for i in local.org_acct : i.name => i }

  name      = each.value.name
  email     = each.value.email
  parent_id = each.value.parent_id
}

```

localsで変数定義することにより、aws_organizations_accountリソースのparent_idを以下まで省略可能です。
　「aws_organizations_organizational_unit.root_under["OU名"].id」→「local.ou_infra」

**手間でも安全性重視**
GitHubActionsではマーケットプレイスより3rdParty製のアクションが利用可能です。今回は、検証済バッジがついたアクションのみ利用するようにしました。
検証済バッジのみ利用する場合は多少不便ですが、実現を考えている実装は比較的自前で実装が可能と考え、検証済バッジのみ利用する方針としました。

### テナントアカウントを含めたCICD一本化
AWSアカウントの払い出しでは、管理アカウント・テナントアカウント・グローバルアカウントの3つが登場します。これらは環境が異なるため、CICDパイプラインを分けるかを検討しましたが、**連動するところは一連のCICDで実装することにより自動化の効率を上げること出来ると考え**、実装しました。
- 管理アカウント→テナントアカウント：1本目
- グローバルアカウント：2本目

パイプラインを繋げるデメリットとして、処理中断時のロールバックがポイントと考え、実行アカウントの切り替え前後で処理が分断するところをケアしました。具体的には、テナントアカウントの処理を手動実行出来る口を設け、前半の管理アカウントで処理が中断したとしても、後続を切り離しで稼働することが出来るよう工夫しました。

### 既存リソースのインポート要否

既に一通りの環境が出来上がっている状態だったため、**既存リソースをIaCに取り込むべきかを悩みました**。**将来的に既存のリソースと新規のリソースの区分けが分からなくなってしまうことを踏まえ、既存リソースの取り込みは実施する方針**としました。

# これから
これまでのビジネス側の状況に応じた段階的なアプローチを通してて、インフラは必要なタイミングにて最適な形態で提供することが大事だと考えています。
今後も、ビジネス側の状況に応じてPlatformEngineeringの取り組みを発展させ、開発者体験の向上やセキュリティ強化の両立を目指していきます。

最後に、**インフラをどう作るかは、目的ではなく手段であることを忘れず、アプリケーション開発チームの生産性向上に貢献できるよう、今後も継続的な改善を進めていきたい**と考えています。