Amazon FSx for NetApp ONTAP を Terraformでデプロイしてみる 

# Amazon FSx for NetApp ONTAP を Terraformで試してみる
NetAppが提供するOntapの技術を採用したフルマネージドな ストレージサービスが発表されました。
NetAppのONTAPという高機能なストレージOSを採用し、NFS/CIFS/iSCSIプロトコルに対応しデータ重複排除・圧縮などの効率化機能やデータ転送機能なども利用可能なサービスです。Amazon FSx for NetApp ONTAPの紹介は[こちらのブログ](https://www.storage-channel.jp/blog/amazon-fsx-for-netapp-ontap.html)が参考になるかと思いますので興味がありましたらどうぞ。

Amazon FSx for NetApp ONTAPは、AWSコンソールから簡単に利用開始が可能ですが、NetAppのCloudManagerからデプロイしたり、Terraformからデプロイして 利用開始することも可能です。

実際に試してみたので、Terraformを利用してデプロイする手順をご紹介します。


# Terraform処理概要
Terraformで実行する処理の概要は以下の図の通りです。
Amazon FSx for NetApp ONTAPのデプロイとあわせて、そのデプロイに必要なAWSリソース、NFSマウントを試すためのLinxuサーバも作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/981926/e80f6df4-0288-1aef-3b58-93bf08b9cc11.png)



TerraformプロバイダーはAWS , NetApp Cloud Managerの２つを利用します。
AWSプロバイダを利用してVPC、Subnet、Internet GWを作成し、さらにNFSマウントを試すためのLinuxサーバ、セキュリティグループ設定、ルーティング設定、ssh接続用Keyペア登録を行います。

また、NetApp Cloud Managerプロバイダを利用して、Amazon FSx for NetApp ONTAPをデプロイします。
**※この記事を書いている最中に AWSプロバイダでも Amazon FSx for NetApp ONTAPの機能が実装されたようなので、そちらでのデプロイは、[別の記事](https://qiita.com/yoihoka/items/c90549158305eebc919c)で紹介しています**

Amazon FSx for NetApp ONTAPは AWSコンソールから導入可能ですが、他のNetAppのクラウドサービスと同様にCloud Managerからも導入可能です。
[こちら](https://cloud.netapp.com/blog/aws-fsxo-blg-how-to-set-up-amazon-fsx-for-netapp-ontap-with-cloud-manager)のページで操作画面付きで導入手順が紹介されていますが、みていただくとわかる通り GUIに従って情報を入力していくだけで簡単に導入が可能になっています。



#前提
以下の環境で試しています。AWSアカウントは作成済みの前提としています。

- Terrafrom 1.0.3
 - hashicorp/aws 3.59.0
 - netapp-cloudmanager 21.9.1
- Cloud Manager 3.9.10 
- CentOS7.7
- aws-cli/2.2.41

なお、この記事で書かれている 手順やファイルは動作を保証されたものではありません。
各環境に合わせてテスト・確認をしたうえで自己責任にて利用していただければと思います。

# 作業の流れ
以下の順序で作業を実施してAmazon FSx for NetApp ONTAPをデプロイします。

1. AWSコンソールでの準備
2. NetApp Cloud Centralでの準備
3. NetApp Cloud Managerでの準備
4. 作業用Linuxサーバでの準備
5. Terraform実行

AWSアカウントは作成済みの前提としております。

## 1. AWSコンソールでの準備
terraform用ユーザを作成し、アクセスキー・シークレットアクセスキー情報を取得します。

AWSコンソールにログインして、以下の操作を実施します。

***
 **[ AWSコンソール ]**
　 1.　画面左上の "サービス"から "セキュリティ、ID、およびコンプライアンス"欄の"IAM"を選択
　 2.　画面左側の"アクセス管理"→"ユーザー"を選択
　 3.　"ユーザーを追加"をクリック
　 4.　ユーザー名に"terauser11"を入力して、"アクセスキー - プログラムによるアクセス"にチェックをいれ、"次のステップ: アクセス権限"をクリック
　 5.　"ユーザーをグループに追加" 欄の"Administrators"にチェックをいれて、"次のステップ：タグ"をクリック
　 6.　"次のステップ: 確認"をクリック
　 7.　"ユーザーの作成"をクリック
　 8.　表示された アクセスキーID、シークレットアクセスキーの情報を確認*します。  
　 *　  ※".csvのダウンロード" をクリックしてファイルをダウンロードしておくと安心

***

上記で、ユーザーに適用したグループは権限が高いため、別途最低限の権限を付与したポリシーを作成して適用することをお勧めします。
詳細な情報は[NetAppのドキュメントページ](https://docs.netapp.com/ja-jp/occm/concept_fsx_aws.html)を参照してください。

##2. NetApp Cloud Centralでの準備事項 : 
NetApp Cloud Centralのアカウントを作成してTokenを取得します。

 1. アカウント作成 
 2. Token取得
 3. Cloud Manager アカウントのID確認

詳細な手順は[別の記事](https://qiita.com/yoihoka/items/9d04ad2fdfc662667873)の"1. NetApp Cloud Centralでの準備事項"で紹介していますので、その内容をもとに上記３つの作業を実施します。


## 3. NetApp Cloud Centralでの準備事項
NetApp Cloud Managerに、AWSのアクセスキー等の登録したAWS FSx Credentialを作成します。
あわせて、Wowkspace IDを確認します。

[Cloud Manager](https://cloudmanager.netapp.com/)にアクセスして以下の操作を行います。

***
**Cloud Managerでの操作**
　 1.　Canvasの画面で、“Add Working Environment” をクリック
　 2.　"Amazon Web Services"をクリックして、"Amazon FSx for ONTAP "をクリックし、Nextをクリック
　 3.　"Create New"を選択して、以下情報を入力します。

　 　　Credentials Name　： cm-terauser11
　 　　AWS Access Key： "AWS terauser11のアクセスキー"
　 　　AWS Secret Key： "AWS terauser11のシークレットアクセスキー"

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/981926/0be07203-bfd3-e24c-9495-ff2b7d8cf2cf.png)

　 4.　"I have Verified ~ "のチェックボックスにチェックをいれて、Nextをクリック
　 5.　画面右上の×ボタンをクリックし
　 6.　画面上部の"All Services(+8)"をクリックして、"Timeline"をクリックします
　 7.　Action:"Add AWS Credentials"の"Status"列が"Success"となっていることを確認します
　 8.　画面最上部のWorkspaceをクリックして、Workspace名※の横にある"・・・"ボタンをクリックすると表示される "Workspace ID"を確認します。

 * 複数のWorkspaceがある場合は 任意のWorkspace名を選択（デプロイ先となります）

***

"Credentials Name　"は任意に指定可能ですが、変更した場合はterraformの.tfファイルの記述をあわせて変更ください。

## 4. 作業用Linuxサーバでの準備
Linuxサーバにterraformを実行する環境を準備します。
CentOS 7を利用しています。

各ディレクトリ・ファイルの構成・配置は以下の前提となります。

```shell:ディレクトリ・ファイル構成
~/terraform
   └─ fsxn
      ├─ aws-resource.tf
      ├─ fsxn.tf  
      ├─ provider.tf  
      ├─ variable.tf  
      └─ ssh_key
         ├─ tera_ssh_key
         └─ tera_ssh_key.pub
```

上記構成から 配置ディレクトリ・ファイル名を変更する場合は、".tfファイル"の対応するパラメータを変更して利用してください。

### 4-1. AWS CLIを導入
作業用Linuxサーバに AWS CLIを導入します。
AWSのサイトに[導入方法のガイド](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-linux.html#cliv2-linux-install)があります。最新の情報はそちらを参照ください。

```shell
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
$ aws --version
```


### 4-2. aws credentialを設定
作業用Linuxサーバで、AWS認証用クレデンシャルを設定します。

```shell
$ aws configure --profile terauser11
AWS Access Key ID [None]: "terauser11のアクセスキーID"
AWS Secret Access Key [None]: "terauser11のシークレットアクセスキー"
Default region name [None]: ap-northeast-1
Default output format [None]: json
$  aws configure list-profiles
terauser11
$
```

なお、他の認証用クレデンシャル設定方法については[AWS Providerのページ](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)に情報がありますので、必要に応じて参照してください。


### 4-3. terraform初期設定
作業用LinuxサーバでTerraformコマンドを実行可能にするための初期設定を実施します。

なお、terraformの最新バイナリについては[Terraformのサイト](https://www.terraform.io/downloads.html)で確認してください。

```shell:
$ mkdir ~/terraform
$ cd ~/terraform
$ curl -sL https://releases.hashicorp.com/terraform/1.0.3/terraform_1.0.3_linux_amd64.zip > terraform.zip
$ unzip terraform.zip
$ export PATH=$PATH:~/terraform/
```

上記でterraformコマンドを実行可能となります。
以下のようにバージョンの確認コマンドを実行できることを確認してください。

```shell:
$ terraform --version
Terraform v1.0.3
on linux_amd64
```

### 4-4. .tfファイルを作成
terraform用のファイルを配置するディレクトリを作成し、ファイルを配置します。

最初に、.tfファイル及び SSHの秘密鍵,公開鍵のキーペアファイル を 配置するためのディレクトリを作成します。

```shell:
$ mkdir -p ~/terraform/fsxn ~/terraform/fsxn/ssh_key
$ cd ~/terraform/fsxn
```
~/terraform/fsxnディレクトリ配下に、以下の.tfファイルを作成します。

※長いので折り畳んでます。
<details><summary>①aws-resource.tf</summary><div>

```plaintext:①aws-resource.tf
resource "aws_instance" "terra-lin01" {
  ami                    = var.tera-linux.ami
  instance_type          = var.tera-linux.instance_type
  key_name               = aws_key_pair.ec2-ssh_key01.id
  vpc_security_group_ids = [aws_security_group.terra-lin01-sg1.id]

  subnet_id = element(aws_subnet.tera-subnet01.*.id, 0)

  tags = {
    Name = var.tera-linux.tagname
  }
}

resource "aws_eip" "terra-lin01-eip01" {
  instance = aws_instance.terra-lin01.id
  vpc      = true
}

output "public_ip01" {
  value = aws_eip.terra-lin01-eip01.public_ip
}


resource "aws_key_pair" "ec2-ssh_key01" {
  key_name   = "ec2-ssh_key01"
  public_key = file("./ssh_key/tera_ssh_key.pub")
}

resource "aws_security_group" "terra-lin01-sg1" {
  name        = "terra-lin01-sg1"
  description = "terraform"
  vpc_id      = aws_vpc.tera-vpc01.id

  # SSH access from anywhere
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 2049
    to_port     = 2049
    protocol    = "tcp"
    cidr_blocks = [var.tera-network.cidr]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "allow_tls"
  }
}
resource "aws_vpc" "tera-vpc01" {
  cidr_block = var.tera-network.cidr

  tags = {
    Name = var.tera-network.name
  }
}


resource "aws_subnet" "tera-subnet01" {
  count             = length(var.tera-network.subnets)
  cidr_block        = values(var.tera-network.subnets)[count.index].cidr
  availability_zone = values(var.tera-network.subnets)[count.index].availability_zone
  tags = {
    Name = keys(var.tera-network.subnets)[count.index]
  }
  vpc_id = aws_vpc.tera-vpc01.id
}

resource "aws_internet_gateway" "tera-vpc01-gw01" {
  vpc_id = aws_vpc.tera-vpc01.id

  tags = {
    Name = "teraform"
  }
}

resource "aws_route_table" "tera-vpc01-route-table01" {
  vpc_id = aws_vpc.tera-vpc01.id

  tags = {
    Name = "teraform"
  }
}

resource "aws_route" "tera-vpc01-route-table01-route01" {
  gateway_id             = aws_internet_gateway.tera-vpc01-gw01.id
  route_table_id         = aws_route_table.tera-vpc01-route-table01.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "tera-vpc01-route-table01-subnet01" {
  count          = length(var.tera-network.subnets)
  subnet_id      = element(aws_subnet.tera-subnet01.*.id, count.index)
  route_table_id = aws_route_table.tera-vpc01-route-table01.id
}
```

</div></details>

<details><summary>②fsxn.tf</summary><div>


```plaintext:②fsxn.tf
resource "netapp-cloudmanager_aws_fsx" "aws-fsx01" {
  provider                   = netapp-cloudmanager
  name                       = var.tera-fsxn.name
  region                     = var.aws.region
  primary_subnet_id          = element(aws_subnet.tera-subnet01.*.id, 1)
  secondary_subnet_id        = element(aws_subnet.tera-subnet01.*.id, 2)
  tenant_id                  = var.tera-fsxn.tenant_id
  workspace_id               = var.tera-fsxn.workspace_id
  fsx_admin_password         = var.tera-fsxn.fsx_admin_password
  throughput_capacity        = var.tera-fsxn.throughput_capacity
  storage_capacity_size      = var.tera-fsxn.storage_capacity_size
  storage_capacity_size_unit = var.tera-fsxn.storage_capacity_size_unit
  aws_credentials_name       = var.tera-fsxn.aws_credentials_name
  route_table_ids            = [aws_route_table.tera-vpc01-route-table01.id]
  security_group_ids         = [aws_security_group.terra-lin01-sg1.id]
}
```

</div></details>

<details><summary>③provider.tf</summary><div>

```plaintext:③provider.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "3.59.0"
    }
    netapp-cloudmanager = {
      source  = "NetApp/netapp-cloudmanager"
      version = "21.9.1"
    }
  }
}

provider "aws" {
  region                  = var.aws.region
  profile                 = var.aws.profile
  shared_credentials_file = var.aws.shared_credentials_file
}

provider "netapp-cloudmanager" {
  refresh_token = var.r-token
}
```


</div></details>

<details><summary>④variable.tf</summary><div>


```plaintext:④variable.tf
variable "aws" {
  default = {
    region                  = "ap-northeast-1"
    profile                 = "terauser11"
    shared_credentials_file = "~/.aws/credentials"
  }
}

variable "r-token" {
  default = "＜NetApp Cloud Centralで確認した Token＞"
}

variable "tera-fsxn" {
  default = {
    name                       = "TerraformAWSFSX"
    tenant_id                  = "＜アカウントIDを記載＞"
    workspace_id               = "＜Workspace IDを記載＞"
    fsx_admin_password         = "＜fsxadmin用 パスワード＞"
    throughput_capacity        = 512
    storage_capacity_size      = 1024
    storage_capacity_size_unit = "GiB"
    aws_credentials_name       = "cm-terauser11"
  }
}

variable "tera-network" {
  default = {
    name = "tera-vpc"
    cidr = "172.19.0.0/16"
    subnets = {
      tera-subnet1 = {
        availability_zone = "ap-northeast-1a"
        cidr              = "172.19.1.0/24"
      }
      tera-subnet2 = {
        availability_zone = "ap-northeast-1c"
        cidr              = "172.19.2.0/24"
      }
      tera-subnet3 = {
        availability_zone = "ap-northeast-1d"
        cidr              = "172.19.3.0/24"
      }
    }
  }
}

variable "tera-linux" {
  default = {
    ami           = "ami-02892a4ea9bfa2192"
    instance_type = "t2.micro"
    tagname       = "terra-lin01"
  }
}

```

</div></details>

④variable.tfファイルに①〜③のtfファイルで利用する変数を宣言しています。
以下の項目を環境にあわせて更新してください。

- tenant_id　　         : "2.NetApp Cloud Centralでの確認"で確認したCloud Manager アカウントのIDを記載
- workspace_id       : "3.NetApp Cloud Managerでの確認"で確認した"Workspace ID"を記載
- r-token - default  : "3.NetApp Cloud Managerでの確認"で確認した"Token"を記載
- fsx_admin_password : fsxadmin用の任意のパスワードを記載(例: Passwd$51)



### 4-5. keyペアファイルを作成
SSHの秘密鍵,公開鍵のキーペアを作成します。
（ec2インスタンスへのssh接続用keyペア）

```shell:
$ cd ~/terraform/fsxn/ssh_key
$ ssh-keygen -t rsa -f tera_ssh_key -N ''
$ ls -l
-rw------- 1 terauser11 terauser11  xxxx mm月 dd hh:mm tera_ssh_key
-rw-r--r-- 1 terauser11 terauser11  xxx  mm月 dd hh:mm tera_ssh_key.pub
```


## 5. Terraformの実行
terraformを実行してAmazon FSx for NetApp ONTAP環境を構成します。
最初にiniコマンドでを初期化処理を行い、その後に planコマンドで 実行内容を確認します。
（terraform planでは、確認のみで処理は実行されません。）


```shell:実行例
$ cd ~/terraform/fsxn
$ terraform init

Initializing the backend...

Initializing provider plugins...
〜〜出力省略〜〜
Terraform has been successfully initialized!
〜〜出力省略〜〜

$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
〜〜出力省略〜〜
Plan: 15 to add, 0 to change, 0 to destroy.
〜〜出力省略〜〜

```
planでの内容確認後、applyコマンドを実行し、"Enter a value:"でyesを入力してEnterを押下すると、実際に terraformが実行されます。

Linuxサーバなどはすぐに作成が完了するかと思いますが、**Amazon FSx for NetApp ONTAPは20-30分程度**でデプロイに時間がかかります。

完了を確認するには、AWS コンソールでボリュームが作成されていることを確認するか、
NetApp Cloud Managerで、画面上部の"All Services(+8)"をクリックして、"Timeline"をクリックし、Action:"Create FSx Ontap
"の"Status"列が"Success"となっていることを確認します。(Pendingの場合は作成中)


Terraform

```shell:実行例
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create
〜〜出力省略〜〜
Plan: 15 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を入力してEnter

〜〜出力省略〜〜
Apply complete! Resources: 15 added, 0 changed, 0 destroyed.

Outputs:

public_ip01 = "＜＜ Linuxサーバの EIP ＞＞"
$ terraform state list
aws_eip.terra-lin01-eip01
aws_instance.terra-lin01
aws_internet_gateway.tera-vpc01-gw01
aws_key_pair.ec2-ssh_key01
aws_route.tera-vpc01-route-table01-route01
aws_route_table.tera-vpc01-route-table01
aws_route_table_association.tera-vpc01-route-table01-subnet01[0]
aws_route_table_association.tera-vpc01-route-table01-subnet01[1]
aws_route_table_association.tera-vpc01-route-table01-subnet01[2]
aws_security_group.terra-lin01-sg1
aws_subnet.tera-subnet01[0]
aws_subnet.tera-subnet01[1]
aws_subnet.tera-subnet01[2]
aws_vpc.tera-vpc01
netapp-cloudmanager_aws_fsx.aws-fsx01
```
"Outputs:"行で、LinuxサーバのIPアドレスを確認します。このIPアドレスを利用してssh接続します。
terraform state listコマンドでリソースが表示されることを確認します。

AWSコンソールでも確認できます。



# NFSボリュームをマウントしてみる
AWSコンソールで以下操作を行い、NFS接続用のIPアドレスを確認します。

***
1. 画面左上の"サービス"→"ストレージ" - "FSx"を選択
2. ONTAP - ストレージ仮想マシンをクリック
3. 対象の ストレージ仮想マシン にチェックをいれて、アクション→詳細を表示
4. エンドポイント欄の"NFS IP アドレス"で、NFS接続用のIPアドレスを確認します。
***

Linuxサーバへsshで接続し FSx for NetApp OntapのボリュームをNFSマウントします。

```shell
$ ssh -i ssh_key/tera_ssh_key ec2-user@"＜＜ Linuxサーバの EIP ＞＞"
[ec2-user@ip-xxx-xx-x-xxx ~]$ sudo mkdir /fsx
[ec2-user@ip-xxx-xx-x-xxx ~]$ sudo mount "NFS IPアドレス":/ /fsx
[ec2-user@ip-xxx-xx-x-xxx ~]$ df 
```

アンマウントしてLinuxサーバからログアウトします。
```shell
[ec2-user@ip-xxx-xx-x-xxx ~]$ sudo umount /fsx
$ exit
```


# 環境削除
terraform destroyコマンドで デプロイした環境の削除を行います。
必ず**Amazon FSx for NetApp ONTAP削除を確認後**に、他のリソースを削除してください。

Amazon FSx for NetApp ONTAP削除 → その他リソース の順番に 削除します。

## 1.Amazon FSx for NetApp ONTAP削除 及び 削除完了の確認
環境の削除を以下の順序で実施します。
なお、本記事に記載の手順以外で、手動でFSx for NetApp Ontapの設定変更等を実施した場合は、先にそれらの設定削除等を実施してから 以下の処理を実施してください。

①-1 削除処理の開始

```shell:実行例
$ terraform destroy -target=netapp-cloudmanager_aws_fsx.aws-fsx01
〜〜出力省略〜〜
Enter a value:
 ↑ yesを入力してEnter
〜〜出力省略〜〜
Destroy complete! Resources: 1 destroyed.
$ 
```

Terraform上の出力はすぐに返ってきますが、これは**削除リクエスト発行**をしただけで、削除処理は 15分-20分程度かかります。

①-2 削除完了の確認

AWSコンソール、NetApp Cloud Managerの画面で、削除完了の確認を行います。

- AWS コンソールで画面左上の"サービス"→"ストレージ" - "FSx"を選択し、該当のファイルシステムが表示されなくなったことを確認
- NetApp Cloud Managerで、画面上部の"All Services(+8)"をクリックして、"Timeline"をクリックし、Action:"Delete FSx Ontap"の"Status"列が"Success"となっていることを確認。(Pendingの場合は削除中)


## 2.その他リソース削除
AWSプロバイダーのリソースを削除します。
この操作は**Amazon FSx for NetApp ONTAPの削除完了確認後に実施**してください。

```shell:実行例
$ terraform destroy 
〜〜出力省略〜〜
Enter a value: 
 ↑ yesを入力してEnter
〜〜出力省略〜〜
Destroy complete! Resources: 14 destroyed.

$ terraform state list
```

terraform state listコマンドで出力がなくなっていることを確認します。


# まとめ
Terraformを利用したAmazon FSx for NetApp ONTAPデプロイ手順を紹介させていただきました。

Amazon FSx for NetApp ONTAPは、AWSコンソールから簡単にデプロイできますが、 付随するリソース作成とあわせて自動化しておくことで、より簡単に環境を用意して利用開始が可能になります。
一度自動化の仕組みをつくっておくと、手順・構成の再利用がしやすくなりますし、環境構築作業に不慣れな人に 試してもらうとかいったケースにも適用できるかと思います。

NetApp製品には他にも自動化ツールが色々提供されているので、また面白そうなケースがあったら紹介したいと思います。