Google Forms からネットワークを制御してニンテンドースイッチをオンラインに接続できなくする

# はじめに

最近は、ネットワーク機器が多くの API を備えているので、自作スクリプトで機器を管理することが簡単です。しかし、自分以外の第三者がそのスクリプトを利用することを想定すると...マニュアルの整備や、CLI を使えない人の為に GUI を用意...など、なかなか万人が使える形で 自作スクリプトを提供するのは、大変だったりします。

そんな時、Google Form をGUIのフロントエンドとして使うことができれば、物理的なサーバーも不要で、カスタマイズも容易で、フロントエンド開発とか苦手なネットワークエンジニアには、とっても良いんじゃないでしょうか。

ここでは、Google Form で 簡単に作成した GUI(フォーム)に入力した値を Google Apps Script を使って API にアクセスし、ネットワーク機器（Meraki）の設定を変更します。

せっかくなので、Mearki の Group ポリシー機能と組み合わせて、ニンテンドースイッチをオンラインに接続できなくしてみたいと思います。

> 先に言っておくと、Nintendo から提供される[みまもり機能](https://www.nintendo.co.jp/hardware/switch/parentalcontrols/)を使えば、オンラインの制御だけじゃなく、よい子の 一日のゲーム時間の管理もできちゃうので、完全にこれは デモ用です

# イメージ
<img width="1920" alt="Screen Shot 2017-12-16 at 0.52.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/134942/8893865c-2aaa-0e28-344f-1a12fe9b0e0d.png">


# デモ
画像をクリックすると動画が開きます(Vimeo) 
[![Vimeoに飛びます](https://i.vimeocdn.com/video/672816042_1280.jpg)](https://vimeo.com/247536357)

デモの Meraki Dashboard では、ニンテンドースイッチ端末のOS が Nokia となってますが、この端末が間違いなくスイッチです。

ポリシーが適用されている間はニンテンドースイッチで、オンラインのロビーへ接続しようとすると、下記のエラーが出ます。

![connectfail1.jpg](https://qiita-image-store.s3.amazonaws.com/0/134942/8eef47d3-159a-5fcb-8746-4836ec8d9c6a.jpeg)

![connectfail2.jpg](https://qiita-image-store.s3.amazonaws.com/0/134942/31741957-153b-6e13-5367-1094037e621b.jpeg)



# Meraki とは

Meraki とは、クラウド管理型のネットワーク機器を提供する企業で、シスコが2012年に買収しましたが、ブランドはそのままに、シスコのノウハウを追加しながら製品展開を広げています。クラウド管理型なので、ユーザーはネットワーク機器(UTM/Switch/AP/カメラ)に個別にログインして設定する必要はないですし、CLIでコマンドを打つ必要は全くありません。Meraki の提供する Webサイトにログインすれば自身の機器についてはすべて管理できるというソリューションです。地理的に離れた場所の機器などもすべて一元的に、管理できるソリューションです。

Merakiについての詳細については meraki.com もしくは、定期的にウェビナーが開催されています(日本語のものもあります)また、最近は Youtube にも[ウェビナーの録画](https://www.youtube.com/playlist?list=PLTVCI_tE32rSaMZiHZ9kCzKma5K32vAk1) が上がっていますので、それらをご活用いただくと良いと思います。

## Meraki API

Meraki は、豊富な API を備えているので、自作のスクリプトからインベントリ情報を取得したり、設定を変更したり、はたまた ワイヤレスAPに接続するクライアントの位置に関する情報などについてもAPI経由で取得することができます。
Meraki には大きく分けると 3つの API があります

* Meraki Dashboard API
* Meraki Scanning API
* Meraki Captive Portal API

ここでは、Meraki Dashboard API を利用して、プロビジョニング操作を API 経由で行います。これら API については、さまざまなドキュメント、開発者向けのコミュニティや、トレーニングなどコンテンツも充実しています。

# Google Apps Script

JavaScriptをベースに作られた Google の Application を操作することのできるスクリプト言語です。Google 各種サービスをつかって、ちょっとした自動化するのに使えます。

https://developers.google.com/apps-script/


# サンプルスクリプト を使ってイメージを掴む

Meraki のエンジニアの作成したサンプルスクリプトが Github に公開されています。
https://github.com/meraki/google-form

ここでは、Google Form から、emailアドレス・名前を入力すると、Meraki の管理者アカウントが、API経由で追加されるというサンプルスクリプト(*example-registerMerakiAdmin-form.gs*)が公開されています。　

これを使ってみると Google Apps Script を使って Meraki を操作する雰囲気がつかめます。

## 利用手順 サマリ

詳細手順は、上記 Github のサイトに記載してあるので、サマリだけ。

### 1. Google Form の作成

スクリプトで必要な変数を入力するためのフォームを https://forms.google.com にて作成します。
このサンプルでは、管理者を追加するスクリプトなので、emailアドレスと 名前 を入力するためのフォームを作成します。

### 2. 作成した Form と Google Script を連携

作成した　Google Form と連携して動く Script を作成します。
作成したフォームの右上の More から Script Editor を開きます。

### 3. Meraki API-KEY などユーザー固有の情報を書き換え

サンプルスクリプト(*example-registerMerakiAdmin-form.gs*)を、Script 入力の画面にコピーし、API-KEY など、ユーザー固有の情報を書き換えます。

``` JavaScript
/* #####################################################
  Enter Your Environment Variables
/ ##################################################### */
var API_KEY = 'YourAPIKey';
var ORG_ID = 'YourOrgID';
var SHARD = 'nXXX';
var PERMISSIONS = 'full';
```

### 4. トリガの指定

フォームを送信したタイミングで、スクリプト中の関数を実行するようにトリガを指定します。

### 5. 確認



## サンプルスクリプト解説

### Meraki API 操作

``` JavaScript
// *************
// API CALLS TO MERAKI
// *************

// Add Meraki Admin
function addMerakiAdmin(apiKey, orgId, shard, payload){
  var headers = {
    "x-cisco-meraki-api-key": apiKey
  };
  var options =
      {
        "method" : "post",  // POST メソッドで管理者作成している
        "payload": payload,
        "headers": headers,
        "content-type": "application/json",
        "contentLength": payload.length,
        "followRedirects": true,
        "muteHttpExceptions":true
      };
  response = UrlFetchApp.fetch('https://'+shard+'.meraki.com/api/v0/organizations/'+orgId+'/admins', options);  // https:からはじまる URI が 管理者操作の REST API
  var result = JSON.parse(response.getContentText());
  Logger.log(result);
  Logger.log(response.getResponseCode());
}
```
この *addMerakiAdmin* が 管理者を追加する関数の本体です。
Meraki のユーザー固有値である *API KEY, ORG ID, Shard* と、Google フォームで入力した変数 *payload* を使って、Google Apps Script の関数である *UrlFetchApp* で、Meraki Dashboard API を叩いています。

Meraki Dashboard API は REST API として提供されており非常に カンタンです。例えば、ここでの管理者追加のAPIは下記のとおりですが、これを Google Apps script で実行するのに *UrlFetchApp* 関数を使っています。*payload* が *BODY* で記載すべき内容というわけです。

``` REST
POST https://api.meraki.com/api/v0/organizations/<org_id>/admins/
Request body: { "name": "John Doe", "email": "john@doe.com", "orgAccess": "none", "networks": [{ "id": <network_id>, "access": "full" }
```

[Meraki Dashboard API](https://dashboard.meraki.com/api_docs) では、上記管理者アカウント追加だけでなくネットワーク変更など様々なプロビジョニング機能を REST APIとして提供しています。


### Google Form 操作

``` JavaScript
// *************
// Form Handler
// *************

// Collects input from form submition
// (must run this via form or an error will occur)

function onFormSubmit(e) {
  var form = FormApp.getActiveForm();
  var formResponse = e.response;
  var itemResponses = formResponse.getItemResponses();

  var admin = {};
  admin.name = itemResponses[0].getResponse();　// 名前を変数に格納
  admin.email = formResponse.getRespondentEmail();  // emailアドレスを変数に格納

  Logger.log("onFormSubmit - name: "+admin.name);
  Logger.log("onFormSubmit - email: "+admin.email);

  // Create admin account
  var payload = {
    "name":admin.name,
    "email":admin.email,
    "orgAccess":PERMISSIONS
  };
  addMerakiAdmin(API_KEY, ORG_ID, SHARD, payload);
}
```

ここでは、変数 *payload* の中に、Googleフォーム で入力した email アドレスと Name を格納して、先ほどの関数 *addMerakiAdmin* を実行しています。

ここまでが、サンプルスクリプトの解説です。

# ニンテンドースイッチオンライン禁止ボタンを作る

## やりたいこと
Merakiの Group Policy の機能を使って、ニンテンドースイッチのオンライン接続を禁止

### Meraki Group ポリシー

Meraki には Group ポリシーという機能があり、Meraki 管理下の クライアント毎に、L3/L7ポリシーを適用できます。
詳細はこちらご確認ください。
[Group Policies and Blacklisting](https://documentation.meraki.com/MR/Group_Policies_and_Blacklisting)

### ニンテンドースイッチのオンライン通信の止め方

どのように止めるかの方法はいくつか考えられますが、せっかくなんで Meraki の グループポリシー機能を最大限活かすためにレイヤ高めのところで止めたいと思います。ニンテンドースイッチ のオンライン通信方式はゲームごとに色々な方式で接続しているらしく、例えば スプラトゥーン2 では、P2Pで各対戦ユーザー各々が通信しているらしいです。ただ、オンラインへの接続確認などは、Nintendo.net のドメインのサーバーに接続しているようなので、このドメインを止めれば、良さそうです。

<img width="1080" alt="Screen Shot 2017-12-16 at 0.25.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/134942/4009e622-e782-5ba1-51cb-aa7dbdce693d.png">


上記の形で作ったポリシーのポリシーIDを、下記で取得して覚えておきます

```
curl --request GET \
  --url 'https://dashboard.meraki.com/api/v0/networks/{{networkId}}/groupPolicies' \
  --header 'x-cisco-meraki-api-key: {{X-Cisco-Meraki-API-Key}}'
```

### Google フォーム

以下の感じで、フォームを作成します。ドロップダウンリストにしました。

<img width="743" alt="Screen Shot 2017-12-16 at 0.45.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/134942/80e46ce8-c127-aeec-f2ed-f6ab76062350.png">


### 実際のソースコード

``` JavaScript
/* #####################################################
  Enter Your Environment Variables
/ ##################################################### */

var API_KEY = 'YourAPIKey';
var ORG_ID = 'YourOrgID';
var SHARD = 'nXXX';
var NETWORK_ID = 'YourNetwrokID'
var CLIENTMAC = 'NintendoSwitch MAC Address'
var GROUP_POLICY_ID = 'YourGroupPolicyID'
var PERMISSIONS = 'full';

// *************
// API CALLS TO MERAKI
// *************

// apply IKAdeny
function applyIKAdeny(apiKey, shard, networkid, clientmac, payload){
  var headers = {
    "x-cisco-meraki-api-key": apiKey
  };
  var options =
      {
        "method" : "put",
        "payload": payload,
        "headers": headers
        };
  response = UrlFetchApp.fetch('https://'+shard+'.meraki.com/api/v0/networks/'+networkid+'/clients/'+clientmac+'/policy', options);
  Logger.log(result);
  Logger.log(response.getResponseCode());
}

// *************
// Form Handler
// *************

function onFormSubmit(e) {
  var form = FormApp.getActiveForm();
  var formResponse = e.response;
  var itemResponses = formResponse.getItemResponses();

  var policy = {};
  policy.selectOpt = itemResponses[0].getResponse();　

  if (policy.selectOpt == "イカっていいよ！"){
  policy.groupID = 'normal';
  }
  if (policy.selectOpt == "ダメです！"){
  policy.groupID = GROUP_POLICY_ID;
  }

  Logger.log("onFormSubmit - groupPolicyId: "+policy.groupID);


  // Apply Policy
  var payload = {
    "devicePolicy": "group",
    "groupPolicyId": policy.groupID
  };
  applyIKAdeny(API_KEY, SHARD, NETWORK_ID, CLIENTMAC, payload);
}
```

### いくつかの気づき

* スクリプトがうまく動かないときの debug のために、テスト出力用関数をこまめに書いたほうが良いです
* Shard の値は、Merakiダッシュボードにログインしたときの meraki.com の前に出る値を、正確に入力したほうが良いです (nXXX の代わりに、dashboard とかにすると微妙に動かないケースもあり)


# まとめ

Google Apps Script により、Google フォーム に入力した情報を、変数として取り出し、その値を Meraki dashboard API に渡しているだけなので、非常にシンプルです。他にも、例えば、

* コンテンツフィルタリング機能を使って、ペアレントコントロールのオン/オフボタンを実装
* トラフィックシェーピングの機能を使って、クライアントごとの ブーストボタンを実装
* Google スプレッドシートに定期的に取得した値を格納してグラフ作成

など、応用範囲は広いと思います。

あとは、GUI としての Goole Form も素晴らしいですが、実行環境としての Google Apps Scripts も自作スクリプト実行するサーバーの管理コストなど考えると、お財布にも優しいソリューションなのかも。


# 参考情報

[Meraki Developer Community](https://communities.cisco.com/community/developer/meraki)

[The Cisco Meraki Dashboard API](https://documentation.meraki.com/zGeneral_Administration/Other_Topics/The_Cisco_Meraki_Dashboard_API)

[Dashboard API Postman Collection](http://developers.meraki.com/post/157014824756/dashboard-api-postman-collection)