この記事は DENSOアドベントカレンダー2024 の1日目の記事です。
目的
本記事では、Jamf Proを利用して、ゼロタッチデプロイメント前の在庫Macの物理的な場所や状態を効率的に管理する方法を紹介します。この手法により、在庫管理が簡素化され、手動での追跡作業を削減できます。
背景
Jamf Proは、利用者にデバイスを割り当てる機能が豊富ですが、物理的な在庫の追跡や状態の管理には特化していません。例えば、複数のオフィスや拠点にまたがる在庫を管理する場合、外部ツールを併用する運用となることもあります。しかし、Jamf Proのインベントリプリロード機能を活用すれば、CSVファイルを用いて物理的な場所や状態を直接登録し、Jamf Pro内で一元管理することが可能です。
ソリューション概要
インベントリプリロード機能は、CSVファイルを使用してデバイス情報を一括登録・更新できるJamf Proの機能です。この機能を利用すると、以下の情報をJamf Proに登録して管理できます:
- 物理的な場所(例:本社、倉庫Aなど)
- デバイスの状態(例:新品、使用中)
- 固定資産番号などのカスタム情報
公式ドキュメント:Jamf Pro Inventory Preload 機能
手順
ステップ1: CSVファイルの準備
手順
- Jamf Proにログインします。
- 左側メニューから「Settings」を選択します。
- 「Global Management」セクションの「Inventory Preload」をクリックします。
- 「Template」をクリックしてCSVテンプレートをダウンロードします。
CSVファイルの項目
CSVファイルには以下の情報を記載できます。必須項目に加え、カスタム項目(Extension Attributes)を追加して組織特有の情報を登録できます。
-
必須項目:
- シリアルナンバー(Serial Number)
- デバイスタイプ(Device Type):"Computer"または"Mobile Device"
-
推奨項目:
- IT資産番号(Asset Tag)
- 物理的な場所(EA Location)
- デバイスの状態(EA Condition)
CSVファイルの例
以下の例では、デバイスの物理的な場所と状態を登録しています。
Serial Number,Device Type,EA Location,EA Condition,EA Asset Tag
DUMMY12345,Computer,Main Office,New,12345
DUMMY67890,Computer,Warehouse A,Used,67890
ステップ2: CSVファイルのアップロード
-
Jamf Proにアクセス
- Jamf Proの「Settings」→「Global Management」→「Inventory Preload」に進みます。
-
CSVファイルをアップロード
- 「Upload CSV」をクリックし、準備したCSVファイルをアップロードします。
ステップ3: データの確認と適用
-
アップロード内容の確認
- CSVの内容が正しく反映されているかJamf Proの管理画面で確認します。
-
デバイス情報の適用
- デバイスがインベントリ収集時に自動的に情報を反映します。
ステップ4: 情報の更新
在庫情報を更新する場合、新しいCSVファイルを作成して再アップロードします。Jamf Pro上で直接編集するのではなく、常にCSVを基に更新を行うことで、ミスを防ぎ管理の一貫性を保てます。
カスタム属性の活用
Jamf Proのカスタム属性(Extension Attributes)を使用することで、組織固有の情報を管理に追加できます。以下のような項目を拡張可能です:
- IT資産番号(Fixed Asset Tag)
- 利用用途(Usage)
- 保管エリア(Location)
CSVファイルのカスタム属性例:
Serial Number,Device Type,EA Location,EA Condition,EA Fixed Asset Tag
DUMMY12345,Computer,Main Office,New,FA12345
DUMMY67890,Computer,Warehouse A,Used,FA67890
注意点
- 在庫データの更新フローを構築することで、常に最新の情報を反映可能にします。
例えば、GitHub Actionsを使って自動化する場合、以下のようなスクリプトを用意します:
"""
このスクリプトはJamfのインベントリプリロードを自動的に更新します。
OAuthトークンを取得し、既存のインベントリプリロードレコードを削除して、
在庫データを含む新しいCSVファイルをアップロードします。
"""
import logging
import os
import sys
import requests
from dotenv import load_dotenv
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
if os.path.exists(".env"):
load_dotenv()
def validate_env_vars(env_vars):
"""
必要な環境変数を検証します。
"""
missing_vars = [var for var in env_vars if not os.getenv(var)]
if missing_vars:
logging.error(
"必要な環境変数が不足しています: %s", ", ".join(missing_vars)
)
sys.exit(1)
class JamfRepository:
"""
Jamf APIと対話するためのリポジトリクラス。
"""
def __init__(self, base_url):
self.base_url = base_url
def get_oauth_token(self, client_id, client_secret):
"""
Jamf APIからOAuthトークンを取得します。
"""
token_url = f"{self.base_url}/api/oauth/token"
token_data = {
"client_id": client_id,
"grant_type": "client_credentials",
"client_secret": client_secret,
}
token_headers = {"Content-Type": "application/x-www-form-urlencoded"}
try:
response = requests.post(
token_url, headers=token_headers, data=token_data, timeout=10
)
response.raise_for_status()
logging.info("OAuthトークンを正常に取得しました。")
return response.json().get("access_token")
except requests.exceptions.HTTPError as http_err:
logging.error(
"HTTPエラーが発生しました: %s - レスポンス: %s", http_err, response.text
)
sys.exit(1)
except requests.exceptions.RequestException as err:
logging.error(
"リクエストエラーが発生しました: %s - レスポンス: %s",
err,
err.response.text if err.response else "レスポンスなし",
)
sys.exit(1)
def delete_all_inventory_records(self, access_token):
"""
すべてのインベントリプリロードレコードを削除します。
"""
delete_url = f"{self.base_url}/api/v2/inventory-preload/records/delete-all"
delete_headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
try:
response = requests.post(delete_url, headers=delete_headers, timeout=10)
response.raise_for_status()
logging.info("すべてのインベントリプリロードレコードを正常に削除しました。")
except requests.exceptions.HTTPError as http_err:
logging.error(
"HTTPエラーが発生しました: %s - レスポンス: %s", http_err, response.text
)
sys.exit(1)
except requests.exceptions.RequestException as err:
logging.error(
"リクエストエラーが発生しました: %s - レスポンス: %s",
err,
err.response.text if err.response else "レスポンスなし",
)
sys.exit(1)
def upload_inventory_csv(self, access_token, csv_file_path):
"""
インベントリプリロード用のCSVファイルをアップロードします。
"""
upload_url = f"{self.base_url}/api/v2/inventory-preload/csv"
upload_headers = {"Authorization": f"Bearer {access_token}"}
try:
with open(csv_file_path, "rb") as f:
files = {"file": f}
response = requests.post(
upload_url, headers=upload_headers, files=files, timeout=10
)
response.raise_for_status()
logging.info("インベントリプリロード用CSVを正常にアップロードしました。")
except requests.exceptions.HTTPError as http_err:
logging.error(
"HTTPエラーが発生しました: %s - レスポンス: %s", http_err, response.text
)
sys.exit(1)
except requests.exceptions.RequestException as err:
logging.error(
"リクエストエラーが発生しました: %s - レスポンス: %s",
err,
err.response.text if err.response else "レスポンスなし",
)
sys.exit(1)
def main():
"""
スクリプトを実行するメイン関数。
"""
required_env_vars = ["JAMF_CLIENT_ID", "JAMF_CLIENT_SECRET", "CSV_FILE_PATH", "JAMF_BASE_URL"]
validate_env_vars(required_env_vars)
client_id = os.getenv("JAMF_CLIENT_ID")
client_secret = os.getenv("JAMF_CLIENT_SECRET")
csv_file_path = os.getenv("CSV_FILE_PATH")
base_url = os.getenv("JAMF_BASE_URL")
repository = JamfRepository(base_url=base_url)
access_token = repository.get_oauth_token(client_id, client_secret)
repository.delete_all_inventory_records(access_token)
repository.upload_inventory_csv(access_token, csv_file_path)
logging.info("Jamfのインベントリプリロードを正常に更新しました。")
if __name__ == "__main__":
main()
このスクリプトを使って、自動化を実現します。
また、GitHub Actionsのワークフロー例は以下の通りです:
name: Jamf Inventory Update
on:
push:
branches:
- main
paths:
- '**/*.csv'
jobs:
update-inventory:
runs-on: ubuntu-latest
steps:
- name: リポジトリをチェックアウト
uses: actions/checkout@v4
- name: Pythonをセットアップ
uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: 依存関係をインストール
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Jamf Inventory Updateを実行
run: python scripts/jamf_inventory_update.py
env:
JAMF_CLIENT_ID: ${{ secrets.JAMF_CLIENT_ID }}
JAMF_CLIENT_SECRET: ${{ secrets.JAMF_CLIENT_SECRET }}
CSV_FILE_PATH: ${{ github.workspace }}/inventory.csv
JAMF_BASE_URL: ${{ secrets.JAMF_BASE_URL }}
追加情報
詳細な使い方はJamf Pro公式ドキュメントをご参照ください:
まとめ
Jamf Proのインベントリプリロード機能を活用することで、ゼロタッチデプロイメント前の在庫Macの管理が効率化されます。物理的な場所や状態を一括登録・更新することで、管理の手間を大幅に削減し、情報の一元化が実現できます。これを機に、よりスマートな在庫管理に取り組んでみてはいかがでしょうか?