GASでJamf Pro API

考え方とか書き方とか、自分用にメモするぞ。
答えだけ欲しい人はついてこれる内容なのかわからないぞ。

謝辞

@iamfreeelover

本当にありがとうございます!
めっちゃ勉強になった!!
頑張ってnote書いてみるけど、まとまるか?

今回やりたいこと

GASでJamf Pro APIを叩きたい。
Classic APIじゃない方使いたい。
最終的にはPC稼働ログを取ってきたい、と思ったけど、そこは別のサービス使えば良いかもしんない。まあ、とにかくAPIを叩くぞ。
参考記事がfor文で処理時間掛かるから改変したい気もする。

まずはAPI叩けるところまで。

参考

今回のコード(GitHub)

https://github.com/ymgcmnk/JamfProAPI_FromGAS

まずBasic認証を通して、トークンを取得し、毎回リフレッシュめんどくさいからエンドポイントとなるAPI呼び出しの関数を実行した時にリフレッシュトークンを取得する仕組み。

考え方・作成手順


最終的なことはコード見ていただければ、多分だいたい分かる、はず、伝わって、お願い。しかし、そこに至るまでは、あれこれ苦戦があったので記してみます。

1. リファレンス読む 

で、上記のコードに至るまでには、いろいろなリファレンスを読む必要があった。
手始めに、2018年のものでちょっと古いが、こちらの日本語のJamf Blogに目を通してみた。

Classic API 使用時の7つのポイント

まずは Jamf Developer Portal (https://developer.jamf.com) をご覧くださいってことなのでご覧になりますわよ。

なんか色々書いてあるけど、JAmfProをポチー。

で、「Classic API」と「Jamf Pro API」の二種類があるわけですよ。なんとなく新しいの使った方がいいような気がしますが、ここでスクロールしてちょっと書いてあることを読んでみます。

Thats a complicated questionって言っちゃってるやん

JSONもサポートしてて、OAthなJamf Pro APIでやっぱりいいような気がします。この辺、教えて詳しい人!!

そんなわけで、Jamf Pro APIを見てみます。
Getting Staeted と Overviewを斜め読みにして、必要そうな情報を探します。APIを叩くにはとにかくにも認証だよねってことで、OAthとかAuthentication っぽいところを探します。

と、認証の前に、リファレンス見ると最終的なAPIとしては例えばこれが良さそうかなー。

クエリパラメータをセットすると、左側の黒いところ、CURLにクエリ入りのURLが出てくる。便利。

各種言語対応。つっても、GASはこれコピペしただけじゃ動かんからね、参考程度に。


話を認証の話に戻します。
Overview の Authentication and Authorization です。
https://developer.jamf.com/jamf-pro/docs/jamf-pro-api-overview

最初のパラグラフで、ユーザアカウントかグループ使うから必要なprivileges(権限、特権)付けといて的なこと言ってます(適当な大意)

Slack API使うときに、App作ってそれにスコープ付ける的な感じですね。
この件はこちらのブログでも言及されていました。
https://tech.visasq.com/getworktimefromjamf/
必要以上の権限をつけると危ないので、今回の目的であればcomputerのReadだけでいいですね。

ユーザアカウントはJamf Proの管理コンソールからどうにかしてください。


で、認証の流れです。

  1. Basic認証使って、リクエストを送ってね

  2. こんな感じのレスポンスを受け取るよ

  3. そのレスポンスのトークン使ってね

的なこと言ってます。

なので、まずはBasic認証を通し、トークンをゲットし、それを使ってAPIを通じてGETする、という流れ。

が、ここで注意。

そう、デフォルトでは、トークンは30分で有効期限が切れるんである。
で、keep-aliveを使って、前回有効だったトークンを使って、また新しいトークンを作る、ってことになる。

あと、Getting Started のここ。

Example Codeには認証ヘッダー情報ないから追加してね、と。
https://developer.jamf.com/jamf-pro/reference/post_v1-auth-token


2. Jamf Pro管理コンソールからユーザアカウント作成

こちらのブログの「補足:実行するJamfアカウントの権限について」にある通り。
https://tech.visasq.com/getworktimefromjamf/

3.Basic認証

認証の流れ、おさらいです。

  1. Basic認証使って、リクエストを送ってね

  2. こんな感じのレスポンスを受け取るよ

  3. そのレスポンスのトークン使ってね


まずはBasic認証でトークンを取得するところをやっていきます。

GASでさあどうするってことで考えます。
https://developer.jamf.com/jamf-pro/docs/jamf-pro-api-overview 
から、レシピを開いてみる。

ここをコピー


ここで curlconverterの出番です。コピーした部分をコンバータにかけます。
https://curlconverter.com/javascript

お、よく見る感じになってきた。
これをもとに整えていきます。

説明すっ飛ばしますが、コードはこうなりました。

function getTokenViaBasicAuth() {
  const url = PropertiesService.getScriptProperties().getProperty("url")

  const username = PropertiesService.getScriptProperties().getProperty("username")
  const password = PropertiesService.getScriptProperties().getProperty("password")

  // ClassicAPIはBasic認証なのでCredentialをBase64エンコードする
  // username:passwordはAPIアクセス用のJamfアカウントを作成し指定する
  const auth_data = Utilities.base64Encode(`${username}:${password}`)

  const response = UrlFetchApp.fetch(url + 'auth/token',
    {
      method: 'POST',
      headers: { 'Authorization': 'Basic ' + auth_data }  //Basicのあとは半角スペース必要
    });

  const obj = JSON.parse(response.getContentText())
  const token = obj.token

  PropertiesService.getScriptProperties().setProperty("token", token)

  const test = PropertiesService.getScriptProperties().getProperty("token")
  console.log(test)
}

先ほどのレシピを見てみます。

これと、コンバータを組み合わせて、GAS用に整えた、という感じです。
btoaは削除して良かったんですが、ここちょっとよくわかってない。

この初代トークンを、次のリフレッシュトークンに利用するため、最後にプロパティストアに格納しています。


3.Basic認証で取得したトークンを使ってAPI、でも30分でexpireするからkeep-alive を噛ます

書き捨てのコードだったら、例えば下記のコードの token_value に Basic認証で取得したコードをそのまま直書きすればよいわけです。

/**
 * https://developer.jamf.com/jamf-pro/reference/get_v1-computers-inventory
 */
function getComputersInventory() {
  const token_value = tokenRefresh()
  const url = PropertiesService.getScriptProperties().getProperty("url")
  const response = UrlFetchApp.fetch(url + '/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc',
    {
      method: 'GET',
      headers: {
        'Authorization': 'Bearer ' + token_value,       //Bearerのあとは半角スペース必要
        'Accept': 'application/json'
      }
    });
  const obj = JSON.parse(response.getContentText())
  console.log(obj)
}

しかし、まあ、あれよ、ね。いちいち、またBasic認証してコード書いてって面倒じゃん。
そういうわけで、トークンリフレッシュ用のコードを用意します。

/**
 * tokenRefresh
 * @return {string} newToken
 */
function tokenRefresh() {
  const token_value = PropertiesService.getScriptProperties().getProperty("token")

  const url = PropertiesService.getScriptProperties().getProperty("url")
  // console.log(url)
  // return
  const response = UrlFetchApp.fetch(url + 'auth/keep-alive',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer ' + token_value,
        'Accept': 'application/json'
      }
      //Bearerのあとは半角スペース必要
    });

  const obj = JSON.parse(response.getContentText())
  const newToken = obj.token

  PropertiesService.getScriptProperties().setProperty("token", newToken)
  console.log(newToken)
  return newToken
}

さっきのBasic認証とほぼ同じ構造です。
ベースURLの後ろが違うです。
新しいトークンをまたプロパティストアに上書き格納です。

4.エンドポイントAPI

先に紹介してしまいましたが、最終的にはこのコードでAPI経由して情報を取得します。

/**
 * https://developer.jamf.com/jamf-pro/reference/get_v1-computers-inventory
 */
function getComputersInventory() {
  const token_value = tokenRefresh()
  const url = PropertiesService.getScriptProperties().getProperty("url")
  const response = UrlFetchApp.fetch(url + '/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc',
    {
      method: 'GET',
      headers: {
        'Authorization': 'Bearer ' + token_value,       //Bearerのあとは半角スペース必要
        'Accept': 'application/json'
      }
    });
  const obj = JSON.parse(response.getContentText())
  console.log(obj)
}

上記は https://developer.jamf.com/jamf-pro/reference/get_v1-computers-inventory を参考にしています。

const response = UrlFetchApp.fetch(url + '/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc'

この部分は、リファレンスのクエリパラメータでウニョウニョしてお好みのものにしてください。

はー、これで、とりあえず取れました。やったー!

コーヒーメソッドとハマリポイント

コーヒーメソッドとは

なんかうまくいかない時、再読み込みする時、再起動する時、そんな時にはコーヒーを淹れて人間をリフレッシュしよう。トークンのリフレッシュ(再取得)が必要なように、人間にもリフレッシュが必要なのだ。
By そーちゃん
 https://twitter.com/black777cat


ハマりポイント

今回コード書いてて、あーってなったところ。

・GASが重い、実行したあと、そのままになる。→たいていなんかコードが間違ってるのでコーヒーメソッドを投入。

・http / https
httpsが正解。

・プロパティストアに格納してる値が間違ってる
特にベースURL。/が余計だったり、足りなかったり。
あと、コピペ間違いで https://yourserver.jamfcloud.com とそのまま入れてたり。

yourserver ここのことですよ!!しっかりして、自分!!

うーん、あとは閉じカッコなかったり余計だったり、よくあるやつ…




curl

curlをうまく扱えるようになりたひ。

curlでBasic認証はこう

curl -s -u username:password https://yourserver.jamfcloud.com/api/v1/auth/token -X POST

curl -h でオプションの説明みるとこう。
-s, --silent        Silent mode
-u, --user user:password Server user and password

ここに書いてある通りですやね。
""が要るの?要らないの?って毎回思ってしまう。]


で、これを応用して、
こことか

ここ

をみながら、やってみたけど上手くいかなかった。

curl -s -H "Authorization: Bearer XXXXXXX" -H 'Accept: application/json' -u 'https://yourserver.jamfcloud.com/api/v1/computers-inventory?section=null&page=0&page-size=200&sort=id%3Aasc' -X GET

ではない?

もう寝ろってことだな。

今後の課題

認証よくわかってない。
Postmanよくわかってない。
ロジックの組み立て弱い。
curlわからん。


余談

海外のまとめサイト的なものに載った、のか?


参考URLまとめ

curlを各種言語に変換してくれる優れもの。
https://curlconverter.com/

GAS BASIC認証をパスしてJSONをPOSTする方法
https://admin-it.xyz/gas/gas-basicauth/

Basic認証、Digest認証、Bearer認証、OAuth認証方式について
https://architecting.hateblo.jp/entry/2020/03/27/130535

JamfでPC稼働時間を集計する

Jamfで勤怠の稼働時間を取得して労務課題を解決する!

Jamf Pro の API を調べてみました

配列の中にオブジェクトが入っている場合のvalue値の取得方法二次元配列 Javascript
https://qiita.com/shuichi0712/items/cf966ad8bae9e610ea32

curlコマンドのきほんと使用例
https://maasaablog.com/curl/2648/


Mac/Windowsで起動やシャットダウン、スリープオン/オフの時間を調べる方法
https://komoriss.com/how-to-check-reboot-shutdown-sleep-on-off-time/

https://freeelover.com/

#JamfProAPI
#JamfPro
#GAS
#ペアプロ
#ノンプロ研


いただいたサポートで、書籍代や勉強費用にしたり、美味しいもの食べたりします!