はじめに
こんにちは。スタートアップ企業で情シスをやってるヤスムラです。
今回は、Kandji MDMのAPIを使ってデバイス情報を取得するスクリプト(GAS)を作成したのでご紹介したいと思います。
GASを利用することによる属人化の問題はここでは触れませんが、Kandjiをこれから導入しようとしている方、すでに導入している方の参考になれば幸いです。
これを利用すれば、資産管理台帳の作成や最新化に役に立つと思います。
またSaaS管理サービスを利用して資産管理を行っている方は、現状Kandjiとのデバイス連携機能はないと思うので、手動同期させたい場合も有効な手段になると思います。
手順
手順は以下の通りです。
1.Kandji APIを作成
1. Kandjiにログイン
2. 左メニューにて[SETTINGS]>[Access]タブを開く
3. API Tokenにある[+Add API Token]をクリック
4. [Name]にわかりやすい名前を入力して[Create]をクリック
5. Tokenをコピーしてチェックをつけたら[Next]をクリック
6. ポップアップが表示されるので[Configure]をクリック
7. Permissionの設定画面が表示されるので以下にチェックを付けて[Save]をクリック
- Device details
- Device list
- Device ID
- Device notes
2.スプレッドシート(GAS)作成
- スプレッドシートを新規作成して適当な名前をつける
- メニューから[拡張機能] > [Apps Script] をクリック
- プロジェクト名を入力(名前は任意)
- 以下スクリプトをコピーして貼り付ける [コピペでOKです]
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('実行')
.addItem('デバイス一覧取得', 'ExportDevicesList')
.addToUi();
}
function ExportDevicesList() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const apiKey = PropertiesService.getScriptProperties().getProperty("apikey");
const domain = PropertiesService.getScriptProperties().getProperty("domain");
const devicesUrl = 'https://' + domain + '.api.kandji.io/api/v1/devices/';
const options = {
'method': 'get',
'headers': {
'Authorization': 'Bearer ' + apiKey
},
'muteHttpExceptions': true
};
const response = UrlFetchApp.fetch(devicesUrl, options);
const devices = JSON.parse(response.getContentText());
// シートクリアして項目を追加
sheet.clear();
sheet.appendRow([
'Device ID',
'Device UUID',
'Device Model',
'Model Identifier',
'CPU',
'RAM',
'Disk Capacity',
'Disk Available',
'Serial Number',
'Host Name',
'OS Version',
'Activation Lock',
'FileVault',
'Last Login User',
'User Name',
'Email',
'Last Check-In',
'Blueprint Name',
'Asset Tag',
'Tag',
'Notes'
]);
// APIからの応答が配列であることを確認
if (!Array.isArray(devices) || devices.length === 0) {
Logger.log('No devices found or wrong data structure');
return;
}
devices.forEach((device) => {
const deviceDetailsUrl = devicesUrl + device.device_id + '/details';
const deviceDetailsResponse = UrlFetchApp.fetch(deviceDetailsUrl, options);
const deviceDetails = JSON.parse(deviceDetailsResponse.getContentText());
const deviceNotesUrl = devicesUrl + device.device_id + '/notes';
const deviceNotesResponse = UrlFetchApp.fetch(deviceNotesUrl, options);
const deviceNotes = JSON.parse(deviceNotesResponse.getContentText());
// (RAM)GB以降のテキストを削除
let ram = deviceDetails.hardware_overview.memory || 'N/A';
if (ram && ram.includes('GB')) {
ram = ram.substring(0, ram.indexOf('GB') + 2);
}
//(ボリューム)encryptedがYesのDISK情報のみ抽出
let diskCapacity = 'N/A';
let diskAvailable = 'N/A';
if (Array.isArray(deviceDetails.volumes)) {
for (let i = 0; i < deviceDetails.volumes.length; i++) {
const volume = deviceDetails.volumes[i];
if (volume.encrypted === 'Yes') {
diskCapacity = volume.capacity || 'N/A';
diskAvailable = volume.available || 'N/A';
break;
}
}
}
// (Tags)有無をチェック。複数ある場合はカンマ区切りで取得
let tagResult = '';
if (Array.isArray(deviceDetails.tags)) {
if (deviceDetails.tags.length !== 0) {
tagResult = deviceDetails.tags.join(', '); // カンマ区切りで全ての要素を連結
}
}
// (Notes)有無をチェック。ある場合はHTMLタグを除去、複数ある場合はカンマ区切りで結合
let notesresult = '';
if (Array.isArray(deviceNotes.notes)) {
if (deviceNotes.notes.length !== 0) {
notesresult = deviceNotes.notes
.map(note => note.content ? note.content.replace(/<[^>]+>/g, '') : '')
.filter(content => content)
.join(', ');
}
}
const loggedInUser = deviceDetails.general.last_user || 'N/A'; // ログインユーザー情報
const processorName = deviceDetails.hardware_overview.processor_name || 'N/A'; // Processor Nameを取得
const activationLock = deviceDetails.activation_lock.user_activation_lock_enabled ? 'Enabled' : 'Disabled'; // アクティベーションロックの有無
const fileVaultEnabled = deviceDetails.filevault.filevault_enabled ? 'Enabled' : 'Disabled'; // FileVaultの有効/無効を取得
const txt_os_version = `'${device.os_version}`;
// シートに追加
sheet.appendRow([
device.device_id,
deviceDetails.hardware_overview.udid,
device.model,
deviceDetails.hardware_overview.model_identifier,
processorName,
ram,
diskCapacity,
diskAvailable,
device.serial_number,
device.device_name,
txt_os_version,
activationLock,
fileVaultEnabled,
loggedInUser,
device.user.name,
device.user.email,
device.last_check_in,
device.blueprint_name,
device.asset_tag,
tagResult,
notesresult
]);
});
}
5. 左メニューの歯車(プロジェクトの設定)を開く
6. スクリプト プロパティに以下登録して保存
プロパティ | 値 | 補足 |
---|---|---|
apikey | Kandjiで作成したAPIキー | |
domain | サブドメイン | https://xxx.kandji.io/ URLのxxxの部分 |
こちらを実行すればスプレッドシートに一覧が出力されるはずです
GASの補足説明
自分が処理内容をすぐ忘れるのでたくさんコメント記載していますが、ポイントのみ補足します。
RAM情報
- そのまま取得すると[16 GB LPDDR4]のようなフォーマットになってしまうのでGBより右のテキストを削除してます。
// (RAM)GB以降のテキストを削除
let ram = deviceDetails.hardware_overview.memory || 'N/A';
if (ram && ram.includes('GB')) {
ram = ram.substring(0, ram.indexOf('GB') + 2);
}
DISK情報
- KandjiだとDISKのハードウェア情報が取得出来ないので、パーティションの中からAESが有効なDISKの情報情報を取得しています。(もっとうまく出来る方法あったら教えて下さいっ)
//(ボリューム)encryptedがYesのDISK情報のみ抽出
let diskCapacity = 'N/A';
let diskAvailable = 'N/A';
if (Array.isArray(deviceDetails.volumes)) {
for (let i = 0; i < deviceDetails.volumes.length; i++) {
const volume = deviceDetails.volumes[i];
if (volume.encrypted === 'Yes') {
diskCapacity = volume.capacity || 'N/A';
diskAvailable = volume.available || 'N/A';
break;
}
}
}
タグ、ノートの情報
- 複数登録出来るので複数存在する場合はカンマ区切りで取得するようにしてます。
- またノートはHTMLとして記録されているのでタグも外すようにしました。
// (Tags)有無をチェック。複数ある場合はカンマ区切りで取得
let tagResult = '';
if (Array.isArray(deviceDetails.tags)) {
if (deviceDetails.tags.length !== 0) {
tagResult = deviceDetails.tags.join(', '); // カンマ区切りで全ての要素を連結
}
}
// (Notes)有無をチェック。ある場合はHTMLタグを除去、複数ある場合はカンマ区切りで結合
let notesresult = '';
if (Array.isArray(deviceNotes.notes)) {
if (deviceNotes.notes.length !== 0) {
notesresult = deviceNotes.notes
.map(note => note.content ? note.content.replace(/<[^>]+>/g, '') : '')
.filter(content => content)
.join(', ');
}
}
まとめ
Kandjiには標準でデバイス一覧をCSVエクスポートする機能がありますが、取得できる情報が限られているため、今回このスクリプトを作成しました。
このGASをさらに改良すれば、取得したデータを使ってOS未更新のユーザーや長期間未接続のデバイスを検知してSlackとかで通知を送るツールを作ったり出来ると思うのでぜひ色々試してみて下さい!
最後に
当方は副業情シスとして活動もしています。
こちらのサイトに詳細記載しておりますので、もしご興味がある方いればお気軽にお問い合わせ(またはSNSでDM等)をお待ちしております。
コメント