Python初心者が、ISE ERS API を利用してスクリプトを書いてみた



#はじめに
Cisco製品には様々な製品でAPIが用意されていますが、今回はISEのERS(External RESTful Services) APIを利用してPython(ver3.6.3)スクリプトを作成してみました。

#ISE ERS(External RESTful Services) API について
[ガイドはこちら](https://www.cisco.com/c/en/us/td/docs/security/ise/2-3/api_ref_guide/api_ref_book/ise_api_ref_ers1.html)
###事前準備
ISE ERS(External RESTful Services) APIを利用するためには、まず初めにISEの管理画面から、Administration->System->SettingsのERS Settingsで、ERSを有効にする必要があります。
<img width="617" alt="スクリーンショット 2017-12-04 午後2.09.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/217351/8955918f-ac03-f363-0a84-5e28fc7d9ea9.png">

さらに、Administration->Sytem->Admin AccessのAdmin Uers設定で、ESR APIへアクセスするための管理者IDとパスワードを作成します。
<img width="830" alt="スクリーンショット 2017-12-04 午後2.21.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/217351/d39dee2a-67a5-5e88-957b-f5158875a466.png">
Admin Groupsは、ERS Adminを選択します。
<img width="766" alt="スクリーンショット 2017-12-04 午後2.21.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/217351/e493b049-baec-4a82-c0cb-a1eb11524035.png">

APIについては、上記のCCOガイドに詳細な記載がないので、ERSを有効にした後、https://(ise ip address):9060/ers/sdkに作成した管理者IDとパスワードでアクセスするとAPIの詳細やスクリプト例を確認することができます。

#想定するユースケース
ユーザ様から、ISEが高機能なことは理解しているのだけど、管理者用のUIが（英語だし）複雑で使いにくい、もっとシンプルにUIをカスタマイズしたい、あるいは既存のシステムと連携させたい、という声をよくお聞きします。そういった場合に活用できるのがAPIです。
今回は、大学様や病院様での使用が多い有線のMacアドレス認証でのISEの利用を想定し、ISEの管理者UIにアクセスすることなくAPI経由で、ISEが管理している端末情報を取得するスクリプトを作成します。取得結果は、txtファイルに保存すると同時に、SparkのbotからSparkのRoomに結果をPostすることとします。

#利用するAPI

**Endpoint API (GET-All)**

*Request:*

```
Method: GET
URI: https://(ise ip address):9060/ers/config/endpoint
HTTP 'Content-Type' Header:application/json
```

*Response:*

```
JSON
{
  "SearchResult" : {
    "total" : 2,
    "resources" : [ {
      "id" : "id1",
      "name" : "name1",
      "description" : "description1"
    }, {
      "id" : "id2",
      "name" : "name2",
      "description" : "description2"
    } ],
    "nextPage" : {
      "rel" : "next",
      "href" : "link-to-next-page",
      "type" : "application/xml"
    },
    "previousPage" : {
      "rel" : "previous",
      "href" : "link-to-previous-page",
      "type" : "application/xml"
    }
  }
}

```

**EndPoints Identity Group (GET-By-Id)**
*Request:*

```
Method: GET
URI: https://(ise ip address):9060/ers/config/endpointgroup/{id}
HTTP 'Content-Type' Header:application/json
```

*Response:*

```
JSON
{
  "EndPointGroup" : {
    "id" : "id",
    "name" : "name",
    "description" : "description",
    "systemDefined" : true
  }
}

```

#スクリプト解説

###STEP0: Basic認証ヘッダを作成する

```python
import base64
user='ersadmin'　　## ISEのERS Adminのユーザ名
password='Password123'　## ISEのERS Adminのパスワード

creds = str.encode(':'.join((user, password)))
auth_header = bytes.decode(base64.b64encode(creds))
```

###STEP1: Pythonのrequestモジュールを使用して端末情報を取得する
```python
 import requests
 import json

 iseip='X.X.X.X'  ## ISEのIPアドレス
 url = "https://"+iseip+":9060/ers/config/"
 url_endpoint = url+"endpoint"
 headers = {
            'accept': "application/json",
            'authorization': 'Basic ' + header, ## STEP0で生成したbasic認証ヘッダー
            'cache-control': "no-cache",
            }
 
 ##全ての端末情報をjson形式で取得
 response = requests.get( url_endpoint, verify=False,headers=headers).json()

 ##ディクショナリのキーを指定し、必要な情報を取り出す
 o_json = json.dumps(response["SearchResult"]["resources"])
```

####*注意点*
```python
response = requests.get( uri,verify=False,headers)
```
SSL証明書が正しくない場合にはデフォルトでSSL認証エラーを出すため、正しい証明書を入れるか、verify=Falseでverifyを無効にしない場合、[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failedのエラーが返されます。verify=Falseを書いた場合も、下記のWarningが出ます。

```
/usr/lib/python3.6/site-packages/urllib3/connectionpool.py:858: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
```

Warningは、下記で無効にし出力させないようにすることができます。

```python
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
```

###STEP2: 結果をファイルに保存
```python
import datetime
 now = datetime.datetime.now()
 file_name = "{0:%Y%m%d.%H%M}".format(now)　##ファイル名に作成した時間を記録
 f = open(file_name + "_endpoints_list.txt","w")
 for x in message: ##STEP1でリスト化した要素を改行を入れながらファイルに記述
  x2 = x.replace('\\n','\n')
  f.write(str(x2))
 f.close()
```
###STEP3: SparkのbotからRoomにAPI経由で取得結果をpost
SPARK APIについては、[developerサイト](https://developer.ciscospark.com/)で詳細な情報を得られます。今回はMessages APIを利用して取得した情報をpostします。
<img width="1058" alt="スクリーンショット 2017-12-04 午後3.31.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/217351/be5cdf16-0a17-ed86-7560-fcbfdc9ac319.png">



#スクリプト全体

```python:get-all-internal-endpoint.py
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
import base64
import json
import sys
import datetime

iseip='X.X.X.X'  ## ISEのIPアドレス

ACCESS_TOKEN="***********" ## SPARK botのアクセストークン
ROOM_ID="*********" ## SPARKのROOM ID


def get_authheader():
 user='ersadmin'　　## ISEのERS Adminのユーザ名
 password='Password123'　## ISEのERS Adminのパスワード

 creds = str.encode(':'.join((user, password)))
 auth_header = bytes.decode(base64.b64encode(creds))
 return auth_header

APIAuth = get_authheader()

def get_endpoint_info(header):
 url = "https://"+iseip+":9060/ers/config/"
 url_endpoint = url+"endpoint"
 url_endpointgroup = url+"endpointgroup"

 ##ヘッダー情報
 headers = {
            'accept': "application/json",
            'authorization': 'Basic ' + header,
            'cache-control': "no-cache",
            }
 
 ##全ての端末情報を取得
 response = requests.get( url_endpoint, verify=False,headers=headers).json()
 o_json = json.dumps(response["SearchResult"]["resources"])

 msg=[]
 ## 取得した端末情報から、macアドレス情報と端末が所属するグループIDを取得
 for item in response["SearchResult"]["resources"]:
     href = item["link"]["href"]
     response_href = requests.get(href, verify=False,headers=headers).json()
     r = json.dumps(response_href["ERSEndPoint"]["groupId"])
     gid = r.split('"')
　　　## 所属するグループIDからグループ名を取得
     gname_url = url_endpointgroup+"/"+gid[1]
     gname_r = requests.get( gname_url, verify=False,headers=headers).json()  
     gname = json.dumps(gname_r["EndPointGroup"]["name"])
     gname2 = gname.replace('"','')
　　　
　　 ##端末のmacアドレス情報と端末が所属するグループ名をリスト化
     msg.append('mac address:'+ item["name"]+', identity group name:'+gname2+'\\n')
 return msg

## Sparkへ結果をpost
def postMsg(roomId, markdown):　
 accessToken_hdr= 'Bearer '+ACCESS_TOKEN
 spark_header={'Authorization':accessToken_hdr,'Content-Type': 'application/json;charset=utf-8'} ##ヘッダーの作成
 the_data = '{"roomId":"' + roomId + '","markdown":"' + markdown +'"}' ## 使用するbotのroom IDと、postする情報を指定
 uri = 'https://api.ciscospark.com/v1/messages' ## Messgae API URI
 
 resp=requests.post(uri,data=the_data,headers=spark_header)
 print (resp)

if __name__ == '__main__':
 APIAuth = get_authheader()
 message=get_endpoint_info(APIAuth) 
 num=len(message)

 ## Sparkへリストのままpostできないため、要素を連結
 m=''
 for x in message:
  m += x+'\\n'
 
 ## 取得結果をテキストファイルに保存
 now = datetime.datetime.now()
 file_name = "{0:%Y%m%d.%H%M}".format(now)
 f = open(file_name + "_endpoints_list.txt","w")
 for x in message:
  x2 = x.replace('\\n','\n')
  f.write(str(x2))
 f.close()
 
 data ='Total number of endpoints managed by ISE is :'+'  '+ str(num)+'\\n\\n'+ m

 postMsg(ROOM_ID,data)
```

#実行結果
```
[root@localhost naogawa]# python3 ./get-all-internal-endpoint.py 
<Response [200]>
[root@localhost naogawa]# ls
20171204.0652_endpoints_list.txt  get-all-internal-endpoint.py
[root@localhost naogawa]# cat 20171204.0652_endpoints_list.txt 
mac address:00:00:5E:00:01:01, identity group name:Profiled
mac address:00:0C:29:73:CB:DA, identity group name:Profiled
mac address:00:0C:29:8F:93:85, identity group name:Profiled
mac address:00:0C:29:94:E1:39, identity group name:Profiled
mac address:00:0C:29:BB:CE:BD, identity group name:Profiled
mac address:00:0C:29:C9:B1:CA, identity group name:Profiled
mac address:00:0C:29:D0:AC:AC, identity group name:Profiled
mac address:00:0C:29:F3:30:B6, identity group name:Profiled
mac address:00:24:97:CE:DF:A0, identity group name:Profiled
mac address:00:38:DF:20:49:48, identity group name:Profiled
mac address:00:38:DF:3E:3C:F0, identity group name:Profiled
mac address:00:A3:D1:88:B9:00, identity group name:Profiled
mac address:00:B0:64:FC:D6:65, identity group name:Profiled
mac address:00:B0:64:FC:D6:99, identity group name:Profiled
mac address:00:B0:64:FD:05:84, identity group name:Profiled
mac address:1C:DF:0F:1D:7F:D6, identity group name:Profiled
mac address:2C:86:D2:8B:B7:C0, identity group name:Profiled
mac address:2C:86:D2:FE:C8:C0, identity group name:Profiled
mac address:50:7A:55:DD:C1:EF, identity group name:Profiled
mac address:58:8D:09:08:98:68, identity group name:Profiled
[root@localhost naogawa]# 

```
<img width="752" alt="スクリーンショット 2017-12-04 午後2.59.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/217351/8606710f-2a0b-7510-68ba-24b24812ad5e.png">



#最後に
APIを活用すれば、ユーザ様の様々な要件に柔軟に対応することができます。ぜひ、APIの活用をご検討ください。