SAM - Adobe ユーザーサブスクリプションのインポート:エンドツーエンドのテクニカルガイドSummary<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } この記事では、SAM - Adobe ユーザーサブスクリプションのインポートスケジュール済みジョブの実行時に発生するすべてのことについて、技術的および機能的に説明します。仕事が何をするのかだけでなく、その仕事がどのように行われるのか、なぜそれぞれの決定が下されたのか、何か問題が起きたときに何を見るべきかを理解する必要がある人のために書かれています。 このジョブの概要と存在理由 毎週金曜日の朝、スケジュール済みジョブが ServiceNow 内で静かに起動し、アドビとの会話を開始します。その唯一の目的は、組織内の誰が現在アドビのライセンスを持っていて、ServiceNow の記録はそれを正確に反映しているかという 1 つの質問に答えることです。 このジョブは「SAM - Adobe ユーザーサブスクリプションのインポート」と呼ばれます。これは sysauto_script テーブル内に存在し、SaaS ライセンス管理 Adobe 統合パッケージに属し、ソフトウェア資産管理 - SaaS ライセンス管理アプリケーションスコープ内で実行されます。これを毎週金曜日に誰も考えずに行い、アドビのライセンスデータを同期させて、SAM管理者がコンプライアンス、コスト追跡、再利用を常に正確に把握できるようにしています。 ストーリーの始まり - スケジュール済みジョブスクリプト 何か意味のあることが起こる前に、このジョブは他のすべての準備を整える 2 つのことを行います。 まず、samp_job_logテーブルにレコードを書き込みます。これは、処理が開始される前に発生します。その理由は単純で、ジョブが途中でクラッシュしたとしても、ジョブが開始された証拠はまだあります。ログエントリは最後に完了または失敗のいずれかで更新され、失敗した場合は、その原因となった正確なエラーメッセージが表示されます。 次に、プラグインsn_sam_saas_intがインスタンスでアクティブかどうかを確認します。これはSaaSライセンス管理エンジンです。これがなければ、同期を強化するスクリプトインクルード、テーブル、またはロジックは存在しません。プラグインがオフの場合、ジョブはログに失敗とマークされ、すぐに終了します。これ以上やることはない。 両方のチェックに合格すると、実際のエンジンである SamImportUserSubscriptionsAdobe に制御が渡されます。 エンジンとそれが何を処理するかを決定する方法 SamImportUserSubscriptionsAdobe は単独では機能しません。AssetManagementPerGlideRecordBaseJob という基底クラスを拡張します。基底クラスは、ファクトリのフロアマネージャーと考えてください。レコードリストを 1 つずつ確認し、それぞれに対して特定の関数を呼び出す方法を知っています。子クラスは、どのレコードをフェッチし、それぞれのレコードをどう処理するかを指示するだけで済みます。 フェッチするレコードは、samp_sw_subscription_profile (Adobe 統合プロファイルが保存されているテーブル) から取得されます。ただし、すべての Adobe プロファイルがフェッチされるわけではありません。2 つのフィルターが適用されます。profile_typeは adobe_subscription と等しくなければならず、custom_propertiesフィールドには「成功validate_connection」というテキストを含める必要があります。この 2 番目の条件は重要です。 アドミンは、事前にプロファイルを開き、[接続を検証] ボタンをクリックし、ServiceNow がそのプロファイルの認証情報を使用して実際にアドビに連絡できることを確認しておく必要があります。その検証が一度も行われていないか失敗した場合、プロファイルはサイレントに除外されます。ジョブは、機能していることがわかっている接続のみを処理します。 このフィルターに合格するプロファイルごとに、操作全体の中心である importSubscriptionForProfile を呼び出します。 アドビのフロントドアを乗り越える — 認証 ServiceNow はアドビに何かを尋ねる前に、本人であることを証明する必要があります。プロファイルには認証に必要なものがすべて保持され、プロファイルのauthentication_typeフィールドは、使用するメソッドをジョブに指示します。 認証タイプが jwt の場合、このジョブは、発行者としてのアドビ組織 ID、件名としてのテクニカルアカウント ID、クライアント ID からビルドされた特別にフォーマットされたオーディエンス URL、24 時間後に設定された有効期限タイムスタンプ、およびアドビのユーザー管理 SDK へのアクセスを許可するメタスコープクレームを含む構造化ペイロードを構築します。 このペイロードは、ServiceNow の証明書ストアに安全に保存されている証明書を使用して RS256 アルゴリズムを使用して署名されます。証明書のsys_id、エイリアス、および暗号化されたパスワードはすべてプロファイルから読み取られます。署名された JWT は、フォームでエンコードされた POST リクエストとして、client_idと client_secret とともにアドビの IMS トークン交換エンドポイントに送信されます。 アドビは署名を検証し、有効期間の短いアクセストークンを返します。 認証タイプが OAuth の場合、ジャーニーは異なります。このジョブは、プロファイルから接続エイリアスを読み取り、関連するhttp_connectionレコードを検索し、リンクされたoauth_entity_profileに移動して、そこからclient_id、client_secret、およびapp_registry名を抽出します。クライアントシークレットは、プラットフォームに組み込まれている復号化を使用してその場で復号化され、どこにも記録されたり、プレーンテキストで保存されたりすることはありません。次に、ジョブは、スコープ openid、AdobeID、および user_management_sdk の client_credentials 権限許可タイプを使用して、GlideOAuthClient.requestToken() を呼び出します。アドビはアクセストークンを直接返します。 どちらの方法を使用しても、アクセストークンは REST クライアントで設定され、アドビがすべてのユーザー管理 API 要求に必要とする x-api-key ヘッダーとともに、認証ヘッダーのベアラートークンとして、後続のすべての API 呼び出しに挿入されます。 REST クライアントと Adobe との通信方法 アドビとの実際の HTTP 通信はすべて、SampAdobeRestClient を介して行われます。その最も重要な動作の 1 つは、失敗の処理方法です。すべての API 呼び出しは、断念するまでに最大 3 回試行される再試行メカニズムを経ます。特に、Adobe のレート制限応答である HTTP ステータス コード 429 と、Adobe のサーバーが一時的に動作していることを示す 502、503、および 504 を監視します。 これらのいずれかが見つかると、アドビが Retry-After ヘッダーを返送したかどうかを確認します。「はい」の場合は、その秒数に 1 秒のバッファーを加えて待機します。ヘッダーが指定されていない場合は、60 秒の待機から始まり、その後再試行するたびに 2 倍になります。試行が 3 回失敗した後にのみ、エラーがスローされ、呼び出し元コードに処理されます。この動作は、ジョブがアドビのシステムを尊重し、同じ API にヒットする可能性のある組織内の他のものとクリーンに共存することを意味します。 このジョブは、アドビに対して 3 つのカテゴリの呼び出しを行います。組織の製品の完全なリストを一度フェッチします。これにより、組織がライセンスを取得したすべての製品コードと名前が提供されます。次に、製品グループをページネーションし、アドビが最後のページに到達したことを通知するまでページカウンターをインクリメントします。最後に、製品グループごとにグループのメンバーをページネーションし、ユーザーがなくなるまでページごとにユーザーを取得します。 データに触れる前に除外ルールをロードしています インポートを開始する前に、ジョブは、誰と何をスキップするかを制御する 2 セットのルールをメモリにロードします。 最初のセットはsamp_sw_subscription_id_excl_ruleから来ています。これらは製品コードの除外です。アドミンから、特定のアドビ製品識別子は ServiceNow にインポートすべきではないと言われているかもしれません。これは、SAM レポートに表示すべきではないテストライセンスや内部エンタイトルメントかもしれません。 2番目のセットはsamp_sw_subscription_user_excl_ruleから来ています。これらはユーザーの除外です。特定のメールアドレスまたは UPN は、グローバルに、または特定のプロファイルタイプにスコープを設定して除外できます。除外レコードにprofile_typeがないユーザーは、すべてのアドビプロファイルから除外されます。 適用可能なすべてのルールの合計が 1 万未満の場合、ジョブはそれらすべてをルックアップが瞬時に行われる単純なメモリ内ディクショナリにロードします。ルール数が 1 万を超える場合は、メモリの問題を回避するためにキャッシュをスキップし、検出したユーザーと製品ごとにデータベースを個別に照会するようにフォールバックします。いずれにせよ、ロジックは機能します — キャッシュされたパスは劇的に高速になります。 インポート自体 — すべてを同期させる 10 のステップ 最初にすべてを非アクティブ化する :ジョブは、このプロファイルの現在アクティブなすべてのサブスクリプションレコードを samp_sw_subscription で非アクティブとしてマークすることで、実際の同期を開始します。setWorkflow を無効にして updateMultiple を一括更新し、ビジネスルールを完全にバイパスします。これは意図的なものです。数千のレコードがある可能性があるため、各レコードでビジネスルールチェーンをトリガーすると、ジョブが何時間も実行されることになります。事前にすべての非アクティブとしてマークすることで、ジョブによって白紙の状態が作成され、論理的な削除パターンが入力されます。アドビから再確認されたものはすべて、再びアクティブとしてマークされます。確認されないものはすべて最後に削除されます。 選択リストエントリの作成:サブスクリプションデータを書き込む前に、ジョブは、samp_sw_subscriptionの [instance_name] フィールドの選択レコードがsys_choiceに存在することを確認します。プロファイルの名前はサブスクリプションレコードのドロップダウンフィールドにこのように表示されます。選択肢がすでに存在する場合は、何も起こりません。そうでない場合は、最新の GlideQuery API を使用して作成されます。プロファイルのdisplay_nameがラベルとして使用され、100 文字にトリミングされます。 アドビからすべてを取得する:ジョブは getProducts() を 1 回呼び出して、組織にあるすべての製品コードを取得します。次に、すべての製品グループをループします。タイプフィールドが PRODUCT_PROFILE と等しいグループのみが関連します。アドビは、ライセンスに関係のない他のグループタイプを返します。同時に、external_idが ADOBESINGLEAPP_ で始まるレコードをローカルディクショナリにsamp_sw_subscription_product_definitionからプリロードします。これは、しばらくしてから、単一のアプリライセンスを識別するために使用されます。 各グループが何を表しているかを理解する:Adobeには、根本的に異なる2つのタイプの製品グループがあります。Creative Cloud のすべてのアプリのようなスイートライセンスには、多くの製品がバンドルされています。Photoshop や Illustrator などの単一アプリ ライセンスは、特定のツールの 1 つです。このジョブは、アドビがグループに名前を付ける方法に基づいて、どちらがどちらであるかを検出します。 スイートの場合、グループの productName を製品リストと照合し、比較前に Adobe が括弧内に追加したキーワード注釈を削除します。単一のアプリの場合、アドビでは、名前を Photoshop のように書式設定し、その後に「単一アプリ - エンタープライズ」というテキストなどのキーワードを含む括弧を続けます。このジョブは、名前内のフレーズ Single App の位置を検索し、そのポイントから次のカンマまたは閉じ括弧までのすべてを抽出して、それを製品コードとして使用します。また、プリロードされたディクショナリにADOBESINGLEAPP_Photoshopエントリが存在するかどうかも確認して、assigned_software_identifierを判断します。これは、後でコスト割り当てと再利用に関係します。 サブスクリプションレコードの作成または更新: 各製品グループのアクティブなユーザーごとに、ジョブは最初に除外ルールを確認します。ユーザーのメールまたは製品コードがいずれかの除外に一致する場合、レコードは完全にスキップされます。それ以外の場合は、subscription_profile、external_user_id、およびsubscription_identifierで一致する既存のレコードを探しsamp_sw_subscriptionクエリを実行します。見つかった場合は、レコードが更新され、再度アクティブとしてマークされます。そうでない場合は、insertDisableBR を使用して新しいものを作成し、パフォーマンスのビジネスルールをスキップします。 書き込まれるすべてのレコードには、そのアクティブフラグが true に設定され、Adobe からuser_principal_name external_user_id入力され、subscription_identifier製品コードに設定され、プロファイルにリンクされ、subscription_profile sourced_from_integration yes に設定され、instance_name前に作成された選択リストエントリに対応するプロファイルsys_idに設定されます。 ソフトウェアモデルの解決: SAM が役に立つこと (コストの追跡、コンプライアンス、再利用など) を行うには、すべてのサブスクリプションレコードを cmdb_software_product_model のソフトウェアモデルにリンクする必要があります。製品コードをソフトウェアモデルに解決するには、ますますコストのかかる 4 つのルックアップのウォーターフォールに従います。 最初のレベルは、ジョブの実行全体にわたって保持される skuSoftwareModelMap と呼ばれるメモリ内ディクショナリです。同じ製品コードが既に 1 回解決されている場合、キャッシュされたsys_idはデータベースクエリーなしで返されます。何千人ものユーザーが少数の製品コードを共有する大規模なアドビ組織では、これにより解決作業の大部分が不要になります。 第 2 レベルでは、同じ製品コードとプロファイル タイプの既存のサブスクリプション レコードにソフトウェア モデルが既に設定されているかどうかを確認します。「はい」の場合、そのモデルは再利用されます。 第 3 レベルでは、製品コードとプロファイルタイプに一致するアクティブなレコードをsamp_sw_subscription_product_definitionクエリし、リンクされたsamp_sw_entitlement_definitionに移動してから、製品、バージョン、エディション、プラットフォーム、言語、およびそれらに対応する演算子間で照合cmdb_software_product_modelクエリを実行します。1 つのモデルのみが一致すると、それが使用されます。複数が一致した場合は、作成日順で最も古いものが返されます。一致するものがない場合、ジョブはエンタイトルメント定義から新しいソフトウェアモデルを自動的に作成します。 第 4 レベルでは、完全な解決エラーが処理されます。製品コードは samp_sw_unrecognized_subscription_identifier で記録されるため、アドミンが確認して手動で正しいモデルにマッピングできます。アドミニストレーターが前回の実行からそのマッピングを既に行っている場合は、マッピングされたモデルが返され、サブスクリプションが正しく設定されます。 Adobe ユーザーと ServiceNow ユーザーの照合: 各サブスクリプションは、どのsys_userレコードに属しているかを知る必要があります。ジョブは、メールが UPN に一致するレコード、または user_name が UPN の記号の前の部分に一致するレコードを探しsys_userクエリを実行します。アクティブなユーザーが非アクティブなユーザーよりも優先されます。sn_itam_samp.user_resolution_exclude_table と呼ばれるシステムプロパティは、この検索から除外するテーブル名を保持できるため、拡張ユーザーテーブルを考慮しない場合に便利です。 sys_user一致と並行して、ジョブはsamp_discovered_userレコードを保持します。これらは、アドビユーザーがまだsys_userに存在するかどうかとは無関係に、アドビユーザーを追跡します。合体キーはexternal_user_idです。このメールとプロファイルタイプの組み合わせに対して検出されたユーザーレコードが既に存在する場合、そのユーザー参照が更新されます。そうでない場合は、UPN の at 記号の前の部分に display_name を設定して、新しいレコードが作成されます。その後、サブスクリプションレコードは検出されたユーザーにリンクされます。 スタンプの最後のアクティビティ: 誰かがライセンスを持っていることを知るだけでは十分ではありません — SAM は、その人が実際にライセンスを使用しているかどうかを知る必要があります。このジョブは、プロファイルのすべてのアクティブなサブスクリプションをループし、ソフトウェア使用テレメトリを保持する samp_sw_usage のレコードを検索します。Creative Cloud のすべてのアプリサブスクリプションの場合、最初に子製品別にグループ化された GlideAggregate クエリを使用してすべての子コンポーネント製品throughsamp_m2m_suite_entitlement_def解決し、次にすべての子コンポーネント製品の使用状況を検索します。単一のアプリサブスクリプションの場合、その特定の製品の使用状況のみをクエリします。見つかった最大last_used_timeは、last_activityとしてサブスクリプションに書き戻されます。この単一の日付フィールドは、再利用エンジンが後で長期間使用されていないライセンスを特定するために使用するものです。 再利用ルールの作成:サブスクリプションデータの形が整った後、ジョブは、samp_m2m_rule_productを介してリンクされた Creative Cloud の samp_sw_reclamation_rule に再利用ルールが存在するかどうかを確認します。ルールが既に存在する場合は、何も変更されません。そうでない場合は、60 日間の非アクティブしきい値、再利用タイプが last_used_date に設定され、ユーザー通知が有効になっている、再利用前の 3 日前の警告、および Creative Cloud のスイートの性質に固有の最低 3 つの登録済み製品のしきい値を持つものが作成されます。次に、ルールを Creative Cloud にリンクする親 M2M レコードと、samp_m2m_suite_entitlement_def から解決された各 Creative Cloud コンポーネント製品の子 M2M レコードを作成します。この操作全体はべき等であり、挿入前にチェックされ、重複するルールは決して作成されません。 残り物のクリーンアップ:この時点でまだ非アクティブとマークされているサブスクリプションレコードは、前回の同期から ServiceNow にありましたが、今回は Adobe のデータには表示されませんでした。これらのユーザーは、アドビのライセンスを紛失したり、組織を離れたり、アクセス権が削除されたりしています。これらのレコードは、ワークフローなしで deleteMultiple を使用して完全に削除され、サブスクリプションテーブルがクリーンで正確な状態に保たれます。 セーフティネット — 物事がうまくいかなかったときに何が起こるか 各プロファイルのインポートプロセス全体は、try-catch ブロックにラップされます。Adobe API のタイムアウト、予期しない null 値、データベースエラーなど、何かが例外をスローすると、catch ブロックは直ちに markSubscriptionActive() を呼び出し、そのプロファイルのすべてのサブスクリプションレコードをアクティブに戻す一括更新を実行します。データは失われません。 インスタンスは、この実行の前に開始した場所とまったく同じ位置で終了します。エラーメッセージとプロファイルsys_idは上方にスローされ、スケジュール済みジョブスクリプトによって検出され、失敗の理由としてsamp_job_logに書き込まれます。次の金曜日に、ジョブは最初から再試行します。 2 つのコピーが同時に実行されないようにする 処理を開始する前に、ジョブは、ステータスが 1 に等しい (現在実行中であることを意味します) 独自のsys_idに関連するエントリがないかsys_triggerチェックします。そのようなエントリが 2 つ以上見つかった場合は、同じジョブの別のインスタンスが既に実行されていることを意味します。前の実行がまだ実行されている可能性があります。その場合は、エラーをログに記録し、すぐに終了します。このガードが存在するのは、同時に実行されている 2 つのインスタンスが破壊的に競合するためです。一方がレコードを非アクティブ化し、もう一方がレコードを再アクティブ化しようとするためです。さらに、進行中のままになっている以前の実行のジョブログレコードは、実行開始前に失敗に一括更新され、古いログエントリによってレポートが汚染されるのを防ぎます。 すべてのプロファイルが完了した後 すべての Adobe プロファイルが処理されると、コントロールは SampSubscriptionUtil.countAllSubscriptionsInstalls() を呼び出すスケジュール済みジョブスクリプトに戻ります。これにより、インスタンス全体のサブスクリプションインストールの合計数が再カウントされ、SAM ダッシュボードとコンプライアンスレポートで使用される集計数が更新されます。その後、ジョブログが [完了] に更新され、ジョブは来週の金曜日までスリープ状態に戻ります。 うまくいかなかったときの見方 ジョブが失敗した場合、最初に行く場所はsamp_job_logです。このジョブの最新のレコードを検索し、メッセージフィールドを読み取ります。どのプロファイルが失敗し、何が例外であったかが正確にわかります。 正常に実行された後にサブスクリプションがない場合は、samp_sw_subscription_profileで統合プロファイルを開き、custom_propertiesを確認します。[validate_connection] フィールドには「成功」と表示されます。処理されていない場合、プロファイルは処理されていません。 サブスクリプションレコードは存在してもソフトウェアモデルがない場合、アドビの製品コードは認識されません。samp_sw_unrecognized_subscription_identifierを確認し、製品コードを見つけて、正しいcmdb_software_product_modelレコードに手動でリンクします。 サブスクリプションがsys_userにリンクされていない場合は、Adobe UPN がsys_user内のアクティブなユーザーのメールまたはuser_nameと一致するかどうかを確認します。また、両方の除外ルールテーブルをチェックして、ユーザーがスキップされていないことを確認します。 ジョブがまったく実行されなかった場合は、このジョブのsys_id sys_triggerを確認してください。クラッシュした前回の実行でレコードがステータス 1 でスタックすると、将来の実行がブロックされる可能性があります。 Adobe 認証に失敗した場合は、プロファイルのauthentication_typeを確認してください。JWT の場合は、証明書の有効期限が切れていないことを確認します。OAuth の場合は、http_connectionレコードをチェックし、リンクされた認証情報がまだ有効であり、OAuth トークンが取り消されていないことを確認します。