terraform による Akamai DSA構成管理

# terraform による Akamai DSA構成管理
## 始めに
Akamai DSAのproperty設定のコード化に関して概要を解説する
DSAは設定項目が多岐に渡り複雑性が高く、手動での運用コストが非常に高い状態であった
それを解消するために、terraform akamai providerを利用してIaC化を行なった
その設計概要である

## ディレクトリ設計
弊社では開発用のdev,動作検証環境のstg、本番環境のproが存在する
そのため以下のようなディレクトリ構成をとった
アプリケーション用のDSAではstg/pro共通のresourceディレクトリを参照し、Dryなコード環境を実現する

```
■ ディレクトリ構成イメージ
└── app_property_group
    └── app_property_1
        ├── environments
        │   ├── pro
        │   │   ├── akamai.tf
        │   └── stg
        │       ├── akamai.tf
        └── resources
            ├── property-snippets
            │   └── main.json
            ├── property.tf
            ├── variables.tf
            └── versions.tf
── dev_property_group
    └── dev_property_1
        ├── environments
        │   └── dev
        │       ├── akamai.tf
        └── resources
            ├── property-snippets
            │   └── main.json
            ├── property.tf
            ├── variables.tf
            └── versions.tf
```

### ディレクトリ構成概要

|name| 用途|
| ---- | ---- |
|app_property_group|プロパティグループの名前、アプリ名やドメインにする|
|app_property_1|アプリケーション毎のdsa property用ディレクトリ|
| environments|各環境用のtfファイルを格納する,この配下の環境ディレクトリでapplyを行う|
|resources|akamai を構成するための設定ファイル群|
| property-snippets|akamaiで指定されるjson形式の設定ファイル群|



## ファイル構成
### akamai.tf
- 記述例

プロパティ固有の設定値を全て記述する

```
module "stg" {
  source       = "../../resources"
  env = "stg"
  property_name = "stg-akamai-IaC-test.xxxx.xx.jp"
  origin_hostname = "stg-shiina-test.xxxx.xx.jp"
  edge_hostname = "stg-akamai-iac-test.xxxx.xx.jp-v1.edgesuite.net"
  property_hostname = "stg-akamai-IaC-test.xxxx.xx.jp"
  akamai_group_name = "akamai_IaC"
  notify_email = "t_shiina@xxxx.xx.jp"
  deny_by_ip_enabled = true
}
```

```
module "pro" {
  source       = "../../resources"
  env = "pro"
  property_name = "akamai-IaC-test.xxxx.xx.jp"
  origin_hostname = "shiina-test.xxxx.xx.jp"
  edge_hostname = "akamai-iac-test.xxxx.xx.jp-v1.edgesuite.net"
  property_hostname = "akamai-IaC-test.xxxx.xx.jp"
  group_name = "akamai_IaC_dev"
  notify_email = "t_shiina@xxxx.xx.jp"
  deny_by_ip_enabled = false

}
```


### property.tf
dsa propertyを構成する要素を全て記述する
`data "akamai_property_rules_template" "default"` で可変値を指定して読み込ませる

```
# acitivation時の連絡先
locals {
     email = var.notify_email
     rule_format = "v2020-03-04"
     is_activate = false
}

# propertyを作成するグループ名
data "akamai_contract" "default" {
     group_name = var.akamai_group_name
}

# contract_idを取得
data "akamai_group" "default" {
    name = var.akamai_group_name
    contract_id = data.akamai_contract.default.id
}

# akamai 形式のjsonファイルをdataとして読み込む
data "akamai_property_rules_template" "default" {
  template_file = abspath("../../resources/property-snippets/main.json")
 # json に引き渡す可変値を指定
 variables {
    name = "origin_hostname"
    value = var.origin_hostname
    type = "string"
  }
  variables {
    name = "cp_code_id"
    value = split("_", akamai_cp_code.default.id)[1]
    type = "number"
  }
    variables {
    name = "cp_code_name"
    value = akamai_cp_code.default.name
    type = "string"
  }
}

# propertyの作成
resource "akamai_property" "default" {
    name = "${var.property_name}_dsa"
    group_id = data.akamai_contract.default.group_id
    contract_id  = data.akamai_contract.default.id
    product_id = "prd_Site_Accel"
    hostnames {
      cname_from = "${var.property_hostname}"
      cname_to = akamai_edge_hostname.default.edge_hostname
      cname_type             = "CUSTOM"
      cert_provisioning_type = "CPS_MANAGED"
    }
    rules       = data.akamai_property_rules_template.default.json
 } 

# edge hostnameも作成する
resource "akamai_edge_hostname" "default" {
  product_id    = "prd_Site_Accel"
  group_id = data.akamai_contract.default.group_id
  contract_id  = data.akamai_contract.default.id
  edge_hostname = "${var.edge_hostname}"
  ip_behavior   = "IPV4"
}

# CP CODEの作成
resource "akamai_cp_code" "default" {
  name = var.property_hostname
  contract_id = data.akamai_contract.default.id
  group_id = data.akamai_group.default.id
  product_id = "prd_Site_Accel"
}

# stating activation
resource "akamai_property_activation" "staging" {
    property_id = akamai_property.default.id
    contact  = [local.email]
    version  = akamai_property.default.latest_version
    note = "Sample activation"
 }

# prodution activation
resource "akamai_property_activation" "production" {
  property_id = akamai_property.default.id
  network     = "PRODUCTION"
　# PRODUCTIONの適用をフラグを立てて管
  version     = var.deploy_production_network == true ? akamai_property.default.latest_version : akamai_property.default.production_version
  depends_on = [
    akamai_property_activation.staging
  ]
  contact = [local.email]
  note    = var.acitivate_note
}
}
```

## property-snippets以下
各種テンプレートのjsonファイルが用意されており、それをmain.jsonから読み込み指定する
可変値は`${env.変数名}`として指定し、property.tfに記述する

- main以外の各種Jsonファイルについて
akamaiのデフォルトファイル構成に倣って下記の様にしている。

|name| 用途|
| ---- | ---- |
|Augment_insights.json|モニタリング用の設定|
|Accelerate_Delivery.json|配信高速化の設定|
|Offload_origin.json|キャッシュ制御設定|
|Increase_availability.json|可用性向上のための設定|
|Minimize_payload.json|圧縮に関する設定等|

### main.json
```
{
  "rules": {
    "name": "default",
    "behaviors": [
      {
        "name": "origin",
        "options": {
          "cacheKeyHostname": "ORIGIN_HOSTNAME",
          "compress": true,
          "customValidCnValues": [
            "{{Origin Hostname}}",
            "{{Forward Host Header}}"
          ],
          "enableTrueClientIp": true,
          "forwardHostHeader": "ORIGIN_HOSTNAME",
          "hostname": "${env.origin_hostname}",　# ⬅︎可変値指定をしている
          "httpPort": 80,
          "httpsPort": 443,
```

テンプレート呼び出し部抜粋
```
    "children": [
      "#include:Augment_insights.json",
      "#include:Accelerate_Delivery.json",
      "#include:Offload_origin.json",
      "#include:Strengthen_security.json",
      "#include:Increase_availability.json",
      "#include:Minimize_payload.json"
    ],
```
## コード作成方針
akamai 設定値は多岐にわたりakamai様式のjsonファイル、tfコード相互で連携する必要がある
設定値全てをtfコード化すると管理対象が膨大になり可読性、運用性ともに悪化するため以下の方針を取る

- 設定値は環境毎にユニークなもののみを原則としてakamai.tfに記載し、property.tfは初期値のまま運用する
- 環境差異がある値のみterraform変数としてproperty-snippetsを読み込ませる
- 共通の設定内容はproperty-snippets配下のjsonを直接変更する

### 運用イメージ

1. `dev_property_1` で コードの作成及び動作検証
2. `app_property_1` のresourcesに設定ファイルをコピー
3. `app_property_1` のakamai.tf等を必要に応じて編集
4. `app_property_1` の`stg`をapply
5. `app_property_1`で動作検証
6. `app_property_1` の`pro`を staging network にapply
7. `app_property_1` の staging networkで動作検証
8. `app_property_1` をakamai コントロールパネルから手動でactivate

## 今後の課題
terraformコード化は完了したもののAkamai API側で散発的にエラーが発生するためCI駆動にはできておらず、手動でのterraform実行運用にとどまっている
CI化に向けて問題解決をしていきたい


## 参考文献
- https://registry.terraform.io/providers/akamai/akamai/latest/docs
- https://techdocs.akamai.com/start/docs/setup-dynamic-site-accelerator