CDK for Terraform入門

# 概要
* [2022年8月1日](https://www.hashicorp.com/blog/cdk-for-terraform-now-generally-available)にインフラ状態管理ツールTerraformのCDK(Cloud Development Kit)のプレビュー版が公開された。

* Terraformはマルチプロバイダーとして、AWSやVMware、OpenStackのインフラ状態をJSON互換のHCL(HashiCorp Configuration Language)という独自の記法によって管理する事ができる。HCLのテストツールには、Go言語のライブラリである[Terratest](https://terratest.gruntwork.io/)がある。

* CDKを用いることで、PythonやGo、TypeScriptといった一般的なプログラミング言語でTerraformを操作できる。

* Terraformコードに対して、条件分岐処理や繰り返し処理などの複雑な処理による操作、既存のテストツールの利用ができる。

* 現在のCDKはクラウドサービスごとに該当サービスのモジュールを適宜追加する必要がある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227705/1a071cdb-c5ca-be44-56cf-9e6e280fd40a.png)

# 準備
### 1. 開発用プログラミング言語のコンパイラのインストール
* Python: https://www.python.org/downloads/
   * pipenv: https://pipenv-ja.readthedocs.io/ja/translate-ja/

### 2. npm(Node.js)のインストール
* https://nodejs.org/ja/download/

### 3. yarnのインストール。
  * https://chore-update--yarnpkg.netlify.app/ja/docs/install

### 4. 対象クラウドのクライアントCLIのインストール

| クラウド | URL |
| --- | --- |
| AWS | https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html |
| Azure | https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli |
| GCP | https://cloud.google.com/sdk/docs/install?hl=ja |
| VMware | https://docs.vmware.com/jp/VMware-vSphere/7.0/com.vmware.esxi.install.doc/GUID-F02D0C2D-B226-4908-9E5C-2E783D41FE2D.html |
| OpenStack | https://docs.openstack.org/ja/user-guide/common/cli-install-openstack-command-line-clients.html |

### 5. Terraformのインストール
* https://www.terraform.io/downloads

### 6. CDK for Terraformのインストール
* https://www.terraform.io/cdktf

# 動作確認バージョン
| ソフトウェア名 | バージョン |
| ---- | ---- |
| python | 3.10.6 |
| pipenv | 2022.9.4 |
| npm | 8.19.1 |
| yarn | 1.22.19 |
| aws(aws cli) | 2.7.29 |
| terraform | 1.2.8 |
| terraform provider aws | 4.29.0 |
| cdktf(CDK for Terraform) | 0.13.0-pre.34 |
| cdk provider aws | 4.0 |

# ハンズオン
## Terraform
* AWS: https://learn.hashicorp.com/tutorials/terraform/aws-build?in=terraform/aws-get-started
  * 参考: https://colabmix.co.jp/tech-blog/terraform-ec2/

### 1. AWSアカウントの登録
### 2. AWSログインユーザ(IAM)の作成
### 3. 作業PCに各種ソフトウェアのインストール
### 4. 作業PCにインスタンスマシンにアクセスするための鍵の生成
```
$ ~/.ssh/ssh-keygen
  ※ 鍵名やパスフレームは無記名でも問題ありません。
```

### 5 .  Terraformプロジェクトの作成
```
$ mkdir -p ~/workspace/learn-terraform-aws-instance
$ cd ~/workspace/learn-terraform-aws-instance

$ terraform init
  ※ Cloud版Terraformの利用はNo、その他は無記名で問題ありません。
```

### 6 . Terraformコードの作成
```
$ touch ~/workspace/learn-terraform-aws-instance/terraform.tfvars
$ touch ~/workspace/learn-terraform-aws-instance/main.tf
```

* terraform.tfvars
具体的な鍵情報は省略する。ご自身の情報を記載する。
  * クラウド情報の変数定義
  * インスタンスマシンの鍵情報の変数定義
```
# Provider
aws_access_key = "XXXXXXXXXXXXXXXXXXXX"
aws_secret_key = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"

# EC2 Key
key_name = "ZZZZZZ"
public_key_path = "~/.ssh/ZZZZZZ.pub"
```

* main.tf
  * ネットワークの作成
  * セキュリティグループの作成
  * インスタンスマシンの作成
```
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {
    default = "ap-northeast-1"
}

provider "aws" {
    access_key = "${var.aws_access_key}"
    secret_key = "${var.aws_secret_key}"
    region = "${var.region}"
}

# VPC作成
resource "aws_vpc" "aws-tf-vpc" {
  cidr_block           = "10.1.0.0/16"
  instance_tenancy     = "default"
  enable_dns_support   = "true"
  enable_dns_hostnames = "true"
  tags = {
    Name = "tf-example-vpc"
  }
}

# サブネット２つ作成(publicとprivate)
resource "aws_subnet" "aws-tf-public-subnet-1a" {
  vpc_id            = aws_vpc.aws-tf-vpc.id
  cidr_block        = "10.1.1.0/24"
  availability_zone = "ap-northeast-1a"
  tags = {
    Name = "aws-tf-public-subnet-1a"
  }
}

resource "aws_subnet" "aws-tf-private-subnet-1a" {
  vpc_id            = aws_vpc.aws-tf-vpc.id
  cidr_block        = "10.1.20.0/24"
  availability_zone = "ap-northeast-1a"
  tags = {
    Name = "aws-tf-private-subnet-1a"
  }
}

# インターネットゲートウェイの作成
resource "aws_internet_gateway" "aws-tf-igw" {
  vpc_id = aws_vpc.aws-tf-vpc.id
  tags = {
    Name = "aws-tf-igw"
  }
}

# ルートテーブルの作成
resource "aws_route_table" "aws-tf-public-route" {
  vpc_id = aws_vpc.aws-tf-vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.aws-tf-igw.id
  }
  tags = {
    Name = "aws-tf-public-route"
  }
}

# サブネットの関連付けでルートテーブルをパブリックサブネットに紐付け
resource "aws_route_table_association" "aws-tf-public-subnet-association" {
  subnet_id      = aws_subnet.aws-tf-public-subnet-1a.id
  route_table_id = aws_route_table.aws-tf-public-route.id
}

# EC2作成(public側)
resource "aws_instance" "aws-tf-web" {
  ami                     = "ami-011facbea5ec0363b"
  instance_type           = "t2.micro"
  disable_api_termination = false
  key_name = aws_key_pair.auth.key_name
  vpc_security_group_ids  = [aws_security_group.aws-tf-web.id]
  subnet_id               = aws_subnet.aws-tf-public-subnet-1a.id

  tags = {
    Name = "aws-tf-web"
  }
}

# Security Group
resource "aws_security_group" "aws-tf-web" {
  name        = "aws-tf-web"
  description = "aws-tf-web_sg"
  vpc_id      = aws_vpc.aws-tf-vpc.id
  tags = {
    Name = "aws-tf-web"
  }
}

# 80番ポート許可のインバウンドルール
resource "aws_security_group_rule" "inbound_http" {
  type      = "ingress"
  from_port = 80
  to_port   = 80
  protocol  = "tcp"
  cidr_blocks = [
    "0.0.0.0/0",
  ]

  # ここでweb_serverセキュリティグループに紐付け
  security_group_id = aws_security_group.aws-tf-web.id
}

# 22番ポート許可のインバウンドルール
resource "aws_security_group_rule" "inbound_ssh" {
  type      = "ingress"
  from_port = 22
  to_port   = 22
  protocol  = "tcp"
  cidr_blocks = [
    "0.0.0.0/0",
  ]

  # ここでweb_serverセキュリティグループに紐付け
  security_group_id = aws_security_group.aws-tf-web.id
}

# アウトバウンドルール
resource "aws_security_group_rule" "outbound_all" {
  type      = "egress"
  from_port = 0
  to_port   = 0
  protocol  = -1
  cidr_blocks = [
    "0.0.0.0/0",
  ]

  # ここでweb_serverセキュリティグループに紐付け
  security_group_id = aws_security_group.aws-tf-web.id
}

# ElasticIP
resource "aws_eip" "aws-tf-eip" {
  instance = aws_instance.aws-tf-web.id
  vpc      = true
}

# EC2 Key
variable "key_name" {}
variable "public_key_path" {}
resource "aws_key_pair" "auth" {
    key_name = var.key_name
    public_key = file(var.public_key_path)
}
```

### 7. Terraformコードの文法確認
```
$ terraform plan
```

### 8. Terraformによるインフラの構築(状態管理)
```
$ terraform apply
  ※ 内容に問題がなければ、「yes」と記入し、エンターキーを押す。
```

### 9. 構築したインスタンスマシンにSSHアクセス
```
$ ssh -i ~/.ssh/ZZZZZZ.pub ec2-user@<ElasticIP>
```

### 10. 環境削除の確認
```
$ terraform plan -destroy
```

### 11. 環境削除
```
$ tarraform destroy
  ※ 内容に問題がなければ、「yes」と記入し、エンターキーを押す。
```

## CDK for Terraform(CDKTF)
### Python
#### 1. CDKTFプロジェクトの作成
```
$ mkdir -p ~/workspace/learn-cdktf-aws-instance
$ cd ~/workspace/learn-cdktf-aws-instance
$ cdktf init --template="python"
  ※ Cloud版Terraformの利用はNo、その他は無記名で問題ありません。
```

#### 2. 対象クラウドのプラグインの追加
```
$ pipenv install cdktf-cdktf-provider-aws
```

#### 3. CDKTFコードの作成
```
$ vi ~/workspace/learn-cdktf-aws-instance/main.py
```

* main.py
  * インスタンスマシンの作成
```
#!/usr/bin/env python
from constructs import Construct
from cdktf import App, NamedRemoteWorkspace, TerraformStack, TerraformOutput
from cdktf_cdktf_provider_aws import AwsProvider, ec2

class MyStack(TerraformStack):
  def __init__(self, scope: Construct, ns: str):
    super().__init__(scope, ns)
    AwsProvider(self, "AWS", region="us-west-1")
    instance = ec2.Instance(
      self, "compute",
      ami="ami-01456a894f71116f2",
      instance_type="t2.micro",
      tags={"Name": "aws-cdktf-web"},
    )
    TerraformOutput(
      self, "public_ip",
      value=instance.public_ip,
    )

app = App()
stack = MyStack(app, "aws_instance")

app.synth()
```

#### 4. CDKTFによるインフラの構築(状態管理)
```
$ cdktf deploy
  ※ 内容に問題がなければ、「Approve」と選択し、エンターキーを押す。
```

#### 5. 構築確認

#### 6. 環境削除
```
$ cdktf destroy
  ※ 内容に問題がなければ、「Approve」と記入し、エンターキーを押す。
```

# 所感
* ふんわりとTerraformやCDKの良さが見えてきた。
* CDKの本格的な活用はまだプレビュー版であり、対応機能が少なさや、記事外の機能は別途モジュール導入が必要などがあり先になりそう。
* 今回の記事は備忘録相当のコマンドメモになっているので、もう少しTerraformの理解が深まってから、追加投稿したい。