Akamai AMD の設定を Terraform で作成する

### はじめに
以前投稿した記事内では、API を利用して Akamai の設定を作成しました。
しかし、API での配信設定作成は多くの手順があり、正直なところ非常に手間がかかります。
今回はライブストリーミングに特化した CDN である AMD (Adaptive Media Delivery) の配信設定を Terraform で作成する手順をご紹介します。
設定内容は以前の内容と全く同じです。
ライブオリジンサービスである MSL4 (Media Services Live 4) は Terraform に対応していないため、今回は対象外です。

以前投稿した記事。

https://qiita.com/tarooishi/items/d735f20b146abbf8e0f6

## Terraform で設定するところ

```mermaid
flowchart LR;
    エンコーダー(FFmpeg)-- HLS -->MSL4-- HLS -->AMD-- HLS -->エンドユーザー
    subgraph ここ
    AMD
    end
```

## 事前に必要な設定

Terraform を Akamai で使う際の初期設定については以下の記事を参考にしてみてください。
API Key の取得と配置が必要です。

https://qiita.com/kabe-akam/items/499507d19220b44b4901


## Terraform で利用する情報を取得する

以下のエンドポイントから GET で情報を取得します。
`https://{hostname}/papi/v1/groups`

:::note info
このリクエストには以下のヘッダを付与してください。
PAPI-Use-Prefixes: true
:::

```JSON
{
    "accountId": "act_Account-123",
    "accountName": "Example Contract",
    "groups": {
        "items": [
            {
                "groupName": "Example Contract - Contract-123",
                "groupId": "grp_Group-123",
                "contractIds": [
                    "ctr_Contract-123"
                 ]
            }
        ]
    }
}
```

この中で必要な情報は以下です。
| キー | 値 (例) | 説明 |
|:-:|:-:|:-:|
| groupName | Example Contract - Contract-123 | terraform の`group_name` に代入します |
| contractIds 内 | ctr_Contract-123 |　terraform の`contract_id` に代入します　|


## Terraform で使うコードを用意する
こちらで公開しているコードをもとに設定を進めます。

https://github.com/tarooishi/terraform-amd-live


### 編集が必要な部分

上記のコードの中で変更が必要なのは `variable.tf` です。
編集が必要な部分にコメントをしています。
それぞれ `default` の項目を変更してください。

```terraform
#########################
# Akamai Property
#########################

variable "edgerc_path" {
  type    = string
  default = "~/.edgerc"
}

variable "config_section" {
  type    = string
  default = "default"
}

variable "env" {
  type    = string
  default = "production"
}

# 先ほど取得した "groupName" と "contractId" をそれぞれ設定する

variable "akamai_group" {
  default = {
    group_name = "Example Contract"
    contract_id = "ctr_Contract-123"
  }
}

# CP Code Nameに任意の名前を設定する（Akamai に割り当てる Hostname と同じ値にするとわかりやすい）

variable "cpcode_name" {
  default = "livestreaming-example.akamaized.net"
}

# Akamaiに割り当てる Hostname を指定する

variable "cname_from" {
  default = "livestreaming-example.akamaized.net"
}

# Emailを指定する

variable "email" {
  default = "admin@example.com"
}

# EdgeHostnameを指定する

variable "edge_hostname" {
  default = "livestreaming-example.akamaized.net"
}

# MSL4 のオリジンを指定する

variable "mslorigin" {
  default = "014-dn001-mslexampleorigin.akamaiorigin.net"
}
```

:::note warn
本稿執筆時の2022年12月14日において Terraform 経由では Akamai の共有証明書を割り当てられません。
:::

## Terraform を実行する

以下のコマンドを順に実行します。
エラーが出たらメッセージに従って修正してください。

`terraform init`
`terraform plan`
`terraform apply`
<details><summary> terraform init の実行サンプル </summary><div>

```bash
$  terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of akamai/akamai...
- Installing akamai/akamai v3.0.0...
- Installed akamai/akamai v3.0.0 (signed by a HashiCorp partner, key ID A26ECDD8F0BCBA73)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```

</div></details>


<details><summary> terraform plan の実行サンプル </summary><div>

```bash
$ terraform plan
data.akamai_group.group: Reading...
data.akamai_group.group: Read complete after 2s [id=grp_1234567]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

Terraform will perform the following actions:

  # data.akamai_property_rules_template.rules will be read during apply
  # (config refers to values not yet known)
 <= data "akamai_property_rules_template" "rules" {
      + id            = (known after apply)
      + json          = (known after apply)
      + template_file = "/home/user/terraform-amd-live/property-snippets/main.json"

      + variables {
          + name  = "cpcode"
          + type  = "number"
          + value = (known after apply)
        }
      + variables {
          + name  = "cpcode_name"
          + type  = "string"
          + value = "livestreaming-example.akamaized.net"
        }
      + variables {
          + name  = "mslorigin"
          + type  = "string"
          + value = "014-dn001-mslexampleorigin.akamaiorigin.net"
        }
    }

  # akamai_cp_code.amd will be created
  + resource "akamai_cp_code" "amd" {
      + contract    = (known after apply)
      + contract_id = "ctr_Contract-123"
      + group       = (known after apply)
      + group_id    = "grp_1234567"
      + id          = (known after apply)
      + name        = "livestreaming-example.akamaized.net"
      + product     = (known after apply)
      + product_id  = "prd_Adaptive_Media_Delivery"
    }

  # akamai_edge_hostname.amd will be created
  + resource "akamai_edge_hostname" "amd" {
      + contract      = (known after apply)
      + contract_id   = "ctr_Contract-123"
      + edge_hostname = "livestreaming-example.akamaized.net"
      + group         = (known after apply)
      + group_id      = "grp_1234567"
      + id            = (known after apply)
      + ip_behavior   = "IPV6_COMPLIANCE"
      + product       = (known after apply)
      + product_id    = "prd_Adaptive_Media_Delivery"
      + use_cases     = jsonencode(
            [
              + {
                  + option  = "LIVE"
                  + type    = "GLOBAL"
                  + useCase = "Segmented_Media_Mode"
                },
            ]
        )
    }

  # akamai_property.amd will be created
  + resource "akamai_property" "amd" {
      + contract           = (known after apply)
      + contract_id        = "ctr_Contract-123"
      + group              = (known after apply)
      + group_id           = "grp_1234567"
      + id                 = (known after apply)
      + latest_version     = (known after apply)
      + name               = "livestreaming-example.akamaized.net"
      + product            = (known after apply)
      + product_id         = "prd_Adaptive_Media_Delivery"
      + production_version = (known after apply)
      + read_version       = (known after apply)
      + rule_errors        = (known after apply)
      + rule_format        = "latest"
      + rules              = (known after apply)
      + staging_version    = (known after apply)

      + hostnames {
          + cert_provisioning_type = "CPS_MANAGED"
          + cname_from             = "livestreaming-example.akamaized.net"
          + cname_to               = "livestreaming-example.akamaized.net"
          + cname_type             = (known after apply)
          + edge_hostname_id       = (known after apply)

          + cert_status {
              + hostname          = (known after apply)
              + production_status = (known after apply)
              + staging_status    = (known after apply)
              + target            = (known after apply)
            }
        }

      + rule_warnings {
          + behavior_name  = (known after apply)
          + detail         = (known after apply)
          + error_location = (known after apply)
          + instance       = (known after apply)
          + status_code    = (known after apply)
          + title          = (known after apply)
          + type           = (known after apply)
        }
    }

  # akamai_property_activation.amd will be created
  + resource "akamai_property_activation" "amd" {
      + activation_id                  = (known after apply)
      + auto_acknowledge_rule_warnings = true
      + contact                        = [
          + "admin@example.com",
        ]
      + errors                         = (known after apply)
      + id                             = (known after apply)
      + network                        = "PRODUCTION"
      + property                       = (known after apply)
      + property_id                    = (known after apply)
      + status                         = (known after apply)
      + version                        = (known after apply)
      + warnings                       = (known after apply)

      + rule_errors {
          + behavior_name  = (known after apply)
          + detail         = (known after apply)
          + error_location = (known after apply)
          + instance       = (known after apply)
          + status_code    = (known after apply)
          + title          = (known after apply)
          + type           = (known after apply)
        }

      + rule_warnings {
          + behavior_name  = (known after apply)
          + detail         = (known after apply)
          + error_location = (known after apply)
          + instance       = (known after apply)
          + status_code    = (known after apply)
          + title          = (known after apply)
          + type           = (known after apply)
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
```

</div></details>

<details><summary> terraform apply の実行サンプル </summary><div>

```bash
$ terraform apply
data.akamai_group.group: Reading...
data.akamai_group.group: Read complete after 1s [id=grp_1234567]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

Terraform will perform the following actions:

  # data.akamai_property_rules_template.rules will be read during apply
  # (config refers to values not yet known)
 <= data "akamai_property_rules_template" "rules" {
      + id            = (known after apply)
      + json          = (known after apply)
      + template_file = "/home/user/terraform-amd-live/property-snippets/main.json"

      + variables {
          + name  = "cpcode"
          + type  = "number"
          + value = (known after apply)
        }
      + variables {
          + name  = "cpcode_name"
          + type  = "string"
          + value = "livestreaming-example.akamaized.net"
        }
      + variables {
          + name  = "mslorigin"
          + type  = "string"
          + value = "014-dn001-mslexampleorigin.akamaiorigin.net"
        }
    }

  # akamai_cp_code.amd will be created
  + resource "akamai_cp_code" "amd" {
      + contract    = (known after apply)
      + contract_id = "ctr_Contract-123"
      + group       = (known after apply)
      + group_id    = "grp_1234567"
      + id          = (known after apply)
      + name        = "livestreaming-example.akamaized.net"
      + product     = (known after apply)
      + product_id  = "prd_Adaptive_Media_Delivery"
    }

  # akamai_edge_hostname.amd will be created
  + resource "akamai_edge_hostname" "amd" {
      + contract      = (known after apply)
      + contract_id   = "ctr_Contract-123"
      + edge_hostname = "livestreaming-example.akamaized.net"
      + group         = (known after apply)
      + group_id      = "grp_1234567"
      + id            = (known after apply)
      + ip_behavior   = "IPV6_COMPLIANCE"
      + product       = (known after apply)
      + product_id    = "prd_Adaptive_Media_Delivery"
      + use_cases     = jsonencode(
            [
              + {
                  + option  = "LIVE"
                  + type    = "GLOBAL"
                  + useCase = "Segmented_Media_Mode"
                },
            ]
        )
    }

  # akamai_property.amd will be created
  + resource "akamai_property" "amd" {
      + contract           = (known after apply)
      + contract_id        = "ctr_Contract-123"
      + group              = (known after apply)
      + group_id           = "grp_1234567"
      + id                 = (known after apply)
      + latest_version     = (known after apply)
      + name               = "livestreaming-example.akamaized.net"
      + product            = (known after apply)
      + product_id         = "prd_Adaptive_Media_Delivery"
      + production_version = (known after apply)
      + read_version       = (known after apply)
      + rule_errors        = (known after apply)
      + rule_format        = "latest"
      + rules              = (known after apply)
      + staging_version    = (known after apply)

      + hostnames {
          + cert_provisioning_type = "CPS_MANAGED"
          + cname_from             = "livestreaming-example.akamaized.net"
          + cname_to               = "livestreaming-example.akamaized.net"
          + cname_type             = (known after apply)
          + edge_hostname_id       = (known after apply)

          + cert_status {
              + hostname          = (known after apply)
              + production_status = (known after apply)
              + staging_status    = (known after apply)
              + target            = (known after apply)
            }
        }

      + rule_warnings {
          + behavior_name  = (known after apply)
          + detail         = (known after apply)
          + error_location = (known after apply)
          + instance       = (known after apply)
          + status_code    = (known after apply)
          + title          = (known after apply)
          + type           = (known after apply)
        }
    }

  # akamai_property_activation.amd will be created
  + resource "akamai_property_activation" "amd" {
      + activation_id                  = (known after apply)
      + auto_acknowledge_rule_warnings = true
      + contact                        = [
          + "admin@example.com",
        ]
      + errors                         = (known after apply)
      + id                             = (known after apply)
      + network                        = "PRODUCTION"
      + property                       = (known after apply)
      + property_id                    = (known after apply)
      + status                         = (known after apply)
      + version                        = (known after apply)
      + warnings                       = (known after apply)

      + rule_errors {
          + behavior_name  = (known after apply)
          + detail         = (known after apply)
          + error_location = (known after apply)
          + instance       = (known after apply)
          + status_code    = (known after apply)
          + title          = (known after apply)
          + type           = (known after apply)
        }

      + rule_warnings {
          + behavior_name  = (known after apply)
          + detail         = (known after apply)
          + error_location = (known after apply)
          + instance       = (known after apply)
          + status_code    = (known after apply)
          + title          = (known after apply)
          + type           = (known after apply)
        }
    }

Plan: 4 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

akamai_edge_hostname.amd: Creating...
akamai_cp_code.amd: Creating...
akamai_cp_code.amd: Creation complete after 7s [id=cpc_1234567]
data.akamai_property_rules_template.rules: Reading...
data.akamai_property_rules_template.rules: Read complete after 0s [id=da39a3ee5e6bddb0d3255bfef95601890afd80709]
akamai_edge_hostname.amd: Creation complete after 7s [id=ehn_1234567]
akamai_property.amd: Creating...
akamai_property.amd: Still creating... [10s elapsed]
akamai_property.amd: Still creating... [20s elapsed]
akamai_property.amd: Creation complete after 25s [id=prp_1234567]
akamai_property_activation.amd: Creating...
akamai_property_activation.amd: Still creating... [10s elapsed]
akamai_property_activation.amd: Still creating... [20s elapsed]
akamai_property_activation.amd: Still creating... [30s elapsed]
akamai_property_activation.amd: Still creating... [40s elapsed]
akamai_property_activation.amd: Still creating... [50s elapsed]
akamai_property_activation.amd: Still creating... [1m0s elapsed]
akamai_property_activation.amd: Still creating... [1m10s elapsed]
akamai_property_activation.amd: Still creating... [1m20s elapsed]
akamai_property_activation.amd: Still creating... [1m30s elapsed]
akamai_property_activation.amd: Still creating... [1m40s elapsed]
akamai_property_activation.amd: Still creating... [1m50s elapsed]
akamai_property_activation.amd: Still creating... [2m0s elapsed]
akamai_property_activation.amd: Still creating... [2m10s elapsed]
akamai_property_activation.amd: Still creating... [2m20s elapsed]
akamai_property_activation.amd: Creation complete after 2m23s [id=prp_1234567:PRODUCTION]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

```

</div></details>

最終的に配信設定が展開されたら完了です。

`terraform apply` が完了すると、実行したディレクトリに `terraform.state` という名称のファイルが作成されます。
こちらには作成したリソースの情報が JSON 形式で保存されています。
例えば、配信設定の展開状況を確認する場合はこんな感じです。


```bash
$ terraform state show akamai_property_activation.amd
# akamai_property_activation.amd:
resource "akamai_property_activation" "amd" {
    activation_id                  = "atv_1234567"
    auto_acknowledge_rule_warnings = true
    contact                        = [
        "admin@example.com",
    ]
    id                             = "prp_1234567:PRODUCTION"
    network                        = "PRODUCTION"
    property_id                    = "prp_1234567"
    status                         = "ACTIVE"
    version                        = 1
}
```

`status` が `ACTIVE` になっていればOKです。


## まとめ
Terraform での配信設定の作成は、API で実行するよりもわかりやすく簡単であるとおわかりいただけたと思います。
MSL4 が Terraform に対応していないのが惜しいところですが、配信設定の管理だけでも徐々に Terraform 化することで、運用の負荷は削減していけると考えています。


## 関連記事
アカマイ・テクノロジーズ合同会社の Qiita では Linode 関連など開発者向けの記事を投稿しています。

https://qiita.com/isss802/items/8201595651f285061e0f

https://qiita.com/organizations/Akamai