TerraformとAPIでCML環境をラクラク作成

# はじめに
CMLを使ったチーム対抗トラシュートレーニングを開催する機会があったため、プロビジョニングを自動化し、トレーニング用のサーバーを削減し、問題更新を容易にできないかと考えました。

社内でCMLを使ったトレーニングを企画されている方などにおすすめです。
※CMLとは?→[こちらのスライド](https://www.cisco.com/c/dam/global/ja_jp/training-events/training-certifications/cln/seminar/jp-webinar-cml01.pdf)を御覧ください

# 環境
```
ProductName:		macOS
ProductVersion:		13.3.1
BuildVersion:		22E261
```
```
Terraform v1.4.4　on darwin_arm64
+ provider registry.terraform.io/ciscodevnet/cml2 v0.5.1
```
```
CML Version: 2.4.1+build.34
```
# 概要
トレーニングの開催には、

1. チームアカウントの作成
1. チームごとのラボのデプロイ
1. ラボへのアクセス権限設定

の3つのプロセスが必要でした。
問題を複数出題する際、②③が繰り返しのオペレーションとなるため、これらの自動化に取り組みました。
![Screenshot 2023-04-27 at 15.25.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3346921/657455af-9af0-a8b2-6c97-97df5b7413f2.png)



# TerraformによるCML環境プロビジョニング
```main.tf
variable "parameter_csv" {
    type = string
    default = "parameter.csv"
    }

locals {
  namelist = csvdecode(file(var.parameter_csv))
}

terraform {
  required_providers {
    cml2 = {
      source = "registry.terraform.io/ciscodevnet/cml2"
    }
  }
}

provider "cml2" {
  address = "https://x.x.x.x"
  username = "user"
  password = "pw"
}

resource "cml2_lifecycle" "this" { 
  for_each = { for inst in local.namelist : inst.id => inst }
  topology = templatefile("question1.yaml", { toponame = "${each.value.name}_lab_Q1" })
  provisioner "local-exec" {
    command = "python3 group_assign.py"
  }
} 
```
question1.yamlはCMLのラボのエクスポートファイルです。
```parameter.csv
id,name
1,team1
2,team2
3,team3
```

# APIでのラボ操作権限設定
```group_assign.py
import requests
import json

#token generation
url = "https://x.x.x.x/api/v0/authenticate"
payload = json.dumps({
  "username": "user",
  "password": "pw"
})
headers = {
  'Accept': 'application/json',
  'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload, verify=False)
token=response.text.strip('"')

#lab list
url = "https://x.x.x.x/api/v0/labs"
headers = {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'Authorization': 'Bearer {}'.format(token)
}
response = requests.request("GET", url, headers=headers, data=payload,verify=False)
lab_list=eval(response.text)
print("lab list: {0}".format(lab_list))

#group list
url = "https://x.x.x.x/api/v0/groups"
response = requests.request("GET", url, headers=headers, data=payload,verify=False)
group_list=eval(response.text)
print("group list: {0}".format(group_list))

#group configuration
for count, lab in enumerate(lab_list):
    url = "https://x.x.x.x/api/v0/labs/"+lab.strip("")
    response = requests.request("GET", url, headers=headers, data=payload,verify=False)
    lab_info=eval(response.text)
    lab_name=lab_info["lab_title"]
    for count, group in enumerate(group_list):
        if group["name"] in lab_name:
            url = "https://x.x.x.x/api/v0/labs/"+lab.strip("")+"/groups"
            payload = json.dumps([
            {
                "id": group['id'],
                "permission": "read_only"
            }
            ])
            response = requests.request("PUT", url, headers=headers, data=payload,verify=False)

```


# まとめ
今回CML環境のプロビジョニングに取り組んだことにより、以下のメリットが得られました
* サーバー台数の削減

問題の入れ替えをスムーズにできるようになり、複数の問題を常時デプロイしなくてよくなりました



　
* 更新・修正の簡易化

ミスがあってもプロビジョニングが自動化できているため、気軽に修正やアップデートをできるようになりました

# 参考情報
[CML Terraform Provider docs](https://registry.terraform.io/providers/CiscoDevNet/cml2/latest/docs)
[Get Started With Terraform and Cisco Modeling Labs-Ralph Schmieder](https://blogs.cisco.com/learning/get-started-with-terraform-and-cisco-modeling-labs)