Alibaba Cloud 日本リージョンがTerraformに対応したので試した

TerraformでAlibaba Cloudの日本リージョンが扱えるようになったのでさっそく試してみました。
これでAlibaba Cloudのインフラもばっちりコードで管理ができるようになりますよ。

## Alibaba Cloudとは
そもそも「Alibaba Cloud」とは、中国の巨大ECサイト「淘宝網」を支える、アリババ社が開発したパブリッククラウドサービスです。
2016年12月には日本リージョンを開設しました。
SBクラウド株式会社が、そのAlibaba Cloudの日本リージョンの運営およびサポートなどを行っています。

https://jp.aliyun.com/

## Terraformとは
TerraformはHashiCorp製のインフラの構築、管理ツールです。
Terraformでは、まずはじめに管理したいインフラ構成をテンプレートファイルに定義します。
その後に、Terraformを実行すると定義した構成がクラウド上に構築されるというものです。
対応するクラウドサービスは様々です。

- AWS
- Azure
- GCP
- Heroku
- OpenStack
- [Packet](https://www.packet.co.jp/)(ベアメタルクラウド)

もともと、TerraformではAlibabaCloudのサービス自体には対応していましたが、日本リージョンは未対応でした。
この記事では日本リージョンに対応したということで、さっそくTerraformを使ってAlibabaCloud日本リージョンを操ってみよう！ということを目的としています。

### Dry-Runができる
Terraformの特徴のひとつとして、実際にクラウドインフラ側へ設定を反映する前にDry-Runができることがあります。
`terraform plan`コマンドを利用することで実際に反映する前に誤りがないか、期待通りの結果になるかどうか確認することができます。
クラウドサービスによってはTerraformに似た、インフラ管理サービスを用意していますが、このDry-Run機能がない場合が多いです。ここに大きなTerraformの価値を感じている人も多いはずです。

## 目標とする構成
本記事では、Terrafromを使って下記の環境を作ってみます。
※先に断っておくが、Alibaba CloudでECSはAWSのEC2に該当するサービス。AWSのECSとは別物ですので注意。

![terraform-infra.png](https://qiita-image-store.s3.amazonaws.com/0/259/bb69fec8-99e1-4f69-dbc0-dacc7286f698.png)

### テンプレートファイルの内容
##### sample.tf（ハマりそうなポイントはコメントアウトで記述）

```
/*
 * API KEYはとても重要なデータです。
 * terraform本体のファイルには記述せず、
 * 変数ファイル(sample.tfvars)に記述しています。
 */
variable "access_key" {}
variable "secret_key" {}
variable "region" {}
variable "zone" {}

# Alicloud Providerの設定
provider "alicloud" {
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region = "${var.region}"
}

# セキュリティグループの作成
resource "alicloud_security_group" "sg" {
  name   = "terraform-sg"
  vpc_id = "${alicloud_vpc.vpc.id}" # セキュリティグループはVPCにひも付きます
}

# セキュリティグループのルール設定
# 今回はWebサーバということで80番ポートのみ空けます
resource "alicloud_security_group_rule" "allow_http" {
  type              = "ingress"
  ip_protocol       = "tcp"
  /*
   * Webサーバインターネットからの通信ですが、実際にインターネットと通信するのはEIPのため
   * ECSのセキュリティグループのルール設定は"intranet"で問題ないです。
   */ 
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "80/80"
  priority          = 1
  security_group_id = "${alicloud_security_group.sg.id}"
  cidr_ip           = "0.0.0.0/0"
}

# VPCの作成
resource "alicloud_vpc" "vpc" {
  name = "terraform-vpc"
  cidr_block = "10.1.0.0/21"
}

# vswitchの作成。VPCの中に作ります。
resource "alicloud_vswitch" "vsw" {
  vpc_id            = "${alicloud_vpc.vpc.id}"
  cidr_block        = "10.1.1.0/24"
  availability_zone = "${var.zone}"
}

# ECSに紐付けるEIP(グローバルIP)の作成
resource "alicloud_eip" "eip" {
  internet_charge_type = "PayByTraffic"
}

# 作成したEIPをECSと紐付けします
resource "alicloud_eip_association" "eip_asso" {
  allocation_id = "${alicloud_eip.eip.id}"
  instance_id   = "${alicloud_instance.web.id}"
}

# ECSの作成
resource "alicloud_instance" "web" {
  instance_name = "terraform-ecs"
  availability_zone = "${var.zone}"
  image_id = "m-6wec065ood8fic52mh6v" # image_idはaliyuncli使って調べるといいです
  instance_type = "ecs.n4.small"
  io_optimized = "optimized"
  system_disk_category = "cloud_efficiency"
  security_groups = ["${alicloud_security_group.sg.id}"] # セキュリティグループは複数設定できるのでListになってます
  vswitch_id = "${alicloud_vswitch.vsw.id}"
}
```

##### sample.tfvars

```
# API KEYはとても大事な情報です。くれぐれもgithubとかにあげないように。
access_key = "xxxxxxxxxxxxxxxxxxxx"
secret_key = "xxxxxxxxxxxxxxxxxxxx"
region = "ap-northeast-1"
zone = "ap-northeast-1a"
```

### Dry-Runをしてみる
実行結果は長いですが、最後の１行(`Plan: 7 to add, 0 to change, 0 to destroy.`)で7つのリソースの作成、0個の変更と削除がされるとわかります。

```
$ terraform plan -var-file="sample.tfvars"
+ alicloud_eip.eip
    bandwidth:            "5"
    instance:             "<computed>"
    internet_charge_type: "PayByTraffic"
    ip_address:           "<computed>"
    status:               "<computed>"

+ alicloud_eip_association.eip_asso
    allocation_id: "${alicloud_eip.eip.id}"
    instance_id:   "${alicloud_instance.web.id}"

+ alicloud_instance.web
    allocate_public_ip:   "false"
    availability_zone:    "ap-northeast-1a"
    host_name:            "<computed>"
    image_id:             "m-6wec065ood8fic52mh6v"
    instance_name:        "terraform-ecs"
    instance_type:        "ecs.n4.small"
    io_optimized:         "optimized"
    private_ip:           "<computed>"
    public_ip:            "<computed>"
    security_groups.#:    "<computed>"
    status:               "<computed>"
    subnet_id:            "<computed>"
    system_disk_category: "cloud_efficiency"
    system_disk_size:     "<computed>"
    vswitch_id:           "${alicloud_vswitch.vsw.id}"

+ alicloud_security_group.sg
    name:   "terraform-sg"
    vpc_id: "${alicloud_vpc.vpc.id}"

+ alicloud_security_group_rule.allow_http
    cidr_ip:           "0.0.0.0/0"
    ip_protocol:       "tcp"
    nic_type:          "intranet"
    policy:            "accept"
    port_range:        "80/80"
    priority:          "1"
    security_group_id: "${alicloud_security_group.sg.id}"
    type:              "ingress"

+ alicloud_vpc.vpc
    cidr_block:      "10.1.0.0/21"
    name:            "terraform-vpc"
    router_id:       "<computed>"
    router_table_id: "<computed>"

+ alicloud_vswitch.vsw
    availability_zone: "ap-northeast-1a"
    cidr_block:        "10.1.1.0/24"
    vpc_id:            "${alicloud_vpc.vpc.id}"


Plan: 7 to add, 0 to change, 0 to destroy.
```

### クラウドへの反映
それでは、実際にクラウドインフラ側に反映してみます。
実際にサーバを構築したりするので、しばらく待ちましょう。

```
$ terraform apply -var-file="sample.tfvars"
alicloud_eip.eip: Creating...
  bandwidth:            "" => "5"
  instance:             "" => "<computed>"
  internet_charge_type: "" => "PayByTraffic"
  ip_address:           "" => "<computed>"
  status:               "" => "<computed>"
alicloud_vpc.vpc: Creating...
  cidr_block:      "" => "10.1.0.0/21"
  name:            "" => "terraform-vpc"
  router_id:       "" => "<computed>"
  router_table_id: "" => "<computed>"

（中略）

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
```

### コンソール画面で確認してみる
実際に意図したとおりに構築しているか、実際にWebのコンソール画面で確認してみよう。
代表として以下２つがきちんと設定されていることを確認します。

##### ECS管理コンソール。意図したサーバが作成
![terraform-ecs-console.png](https://qiita-image-store.s3.amazonaws.com/0/259/ecb7779f-4fa7-f50c-afc5-38ad06e85e41.png)

##### セキュリティグループのルール設定
![terraform-sg.png](https://qiita-image-store.s3.amazonaws.com/0/259/03925092-9e93-1235-1287-8cd76d5941c0.png)

## インフラ要件を変更してみる
WebサーバでHTTPSの対応が必要になったと仮定します。
セキュリティグループで80番ポートの他に443のポートをあけるように変更しよう。
`sample.tf`に下記リソースを追加します。

```
resource "alicloud_security_group_rule" "allow_https" {
  type              = "ingress"
  ip_protocol       = "tcp"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "443/443"
  priority          = 2
  security_group_id = "${alicloud_security_group.sg.id}"
  cidr_ip           = "0.0.0.0/0"
}
```

前回と同じようにまずはDry-Runで変更を確認します。

```
$ terraform plan -var-file="sample.tfvars"
+ alicloud_security_group_rule.allow_https
    cidr_ip:           "0.0.0.0/0"
    ip_protocol:       "tcp"
    nic_type:          "intranet"
    policy:            "accept"
    port_range:        "443/443"
    priority:          "2"
    security_group_id: "sg-xxxxxxxxxxxxxxxx"
    type:              "ingress"


Plan: 1 to add, 0 to change, 0 to destroy.
```

セキュリティグループへのルールの追加が確認できたので、インフラへ反映させていきます。

```
$ terraform apply -var-file="sample.tfvars"
alicloud_security_group_rule.allow_https: Creating...
  cidr_ip:           "" => "0.0.0.0/0"
  ip_protocol:       "" => "tcp"
  nic_type:          "" => "intranet"
  policy:            "" => "accept"
  port_range:        "" => "443/443"
  priority:          "" => "2"
  security_group_id: "" => "sg-xxxxxxxxxxxxxxxx"
  type:              "" => "ingress"
alicloud_security_group_rule.allow_https: Creation complete (ID: sg-xxxxxxxxxxxxxxxx:ingress:tcp:443/443:intranet)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

## 構築した環境を削除してみる
最後に、リソースの削除もやってみます。これは危険な作業なため、実際に行う場合には注意してくださいね。
本当に実行するかどうか聞かれるので`yes`と入力します。

```
$ terraform destroy -var-file="sample.tfvars"
Do you really want to destroy?
  Terraform will delete all your managed infrastructure.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

（中略）

Destroy complete! Resources: 8 destroyed.
```

## 最後に
実はTerraformの利用自体もはじめてでしたが、無事にAlibaba Cloudの日本リージョンも操作できました。
Terraformですが、はじめてAnsibleを触ったときと同じような感覚を感じました。
いちどこの便利さに慣れてしまうとおそらく離れられない気がします。

Alibaba CloudのTerraform対応ですが、日々進化しています。
もっといろんなプロダクトへの対応、およびその精度があがっていくと思います。
期待しましょう。