EMM 統合ガイド

このガイドは、企業向けモバイル管理(EMM)プロバイダがゼロタッチ登録をコンソールに統合する際に活用できます。この続きをお読みになって、登録の詳細と、DPC(デバイス ポリシー コントローラ)によるデバイスのプロビジョニングに役立つベスト プラクティスをご確認ください。DPC をお持ちの場合は、デバイスのプロビジョニングに関するベスト プラクティスを学び、開発とテストに役立つアドバイスを得ることができます。

IT 管理者向けの機能

お客様向け API を使用して、IT 管理者がコンソールから直接ゼロタッチ登録を設定できるようにします。IT 管理者がコンソールで実行するタスクの例を次に示します。

  • モバイル ポリシーに基づいて、ゼロタッチ登録の設定を作成、編集、削除します。
  • デフォルト設定を設定して、組織が今後購入するデバイスを DPC がプロビジョニングできるようにします。
  • デバイスに個別の設定を適用したり、ゼロタッチ登録からデバイスを削除したりします。

ゼロタッチ登録の詳細については、概要をご覧ください。

前提条件

ゼロタッチ登録を EMM コンソールに追加する前に、ソリューションが以下をサポートしていることを確認してください。

  • EMM ソリューションは、会社所有の Android 8.0 以降(Google Pixel 7.1 以降)のデバイスをフル マネージド モードでプロビジョニングする必要があります。会社所有の Android 10 以降のデバイスは、完全管理対象または仕事用プロファイル付きとしてプロビジョニングできます。
  • ゼロタッチ登録では DPC が自動的にダウンロードされてインストールされるため、DPC は Google Play から入手できる必要があります。互換性のある DPC のリストは、IT 管理者が顧客 API またはポータルを使用して構成できるように維持されています。EMM プロバイダ コミュニティからプロダクト変更リクエストを送信して、リストに DPC を追加します。
  • お客様が Customer API を呼び出すには、ゼロタッチ登録アカウントが必要です。組織がデバイスを購入すると、パートナー販売パートナーが IT 管理者の組織のアカウントを設定します。
  • ゼロタッチ登録を正しく機能させるには、Google モバイル サービス(GMS)に対応するデバイスで、Google Play 開発者サービスが常に有効である必要があります。

API を呼び出す

コンソールのユーザー(Google アカウントを使用)が、顧客 API への API リクエストを承認します。このフローは、他の EMM API で行う認証とは異なります。アプリでこれを行う方法については、認証をご覧ください。

利用規約を処理する

ユーザーは、API を呼び出す前に最新の利用規約に同意する必要があります。API 呼び出しが HTTP 403 Forbidden ステータス コードを返し、レスポンスの本文に TosError が含まれている場合は、ゼロタッチ登録ポータルにログインして利用規約に同意するようユーザーに促します。次の例は、この処理を行う方法の 1 つを示しています。

Java

// Authorize this method call as a user that hasn't yet accepted the ToS.
final String googleApiFormatHttpHeader = "X-GOOG-API-FORMAT-VERSION";
final String googleApiFormatVersion = "2";
final String tosErrorType =
      "type.googleapis.com/google.android.device.provisioning.v1.TosError";

try {
  // Send an API request to list all the DPCs available including the HTTP header
  // X-GOOG-API-FORMAT-VERSION with the value 2. Import the  exception:
  // from googleapiclient.errors import HttpError
  AndroidProvisioningPartner.Customers.Dpcs.List request =
        service.customers().dpcs().list(customerAccount);
  request.getRequestHeaders().put(googleApiFormatHttpHeader, googleApiFormatVersion);
  CustomerListDpcsResponse response = request.execute();
  return response.getDpcs();

} catch (GoogleJsonResponseException e) {
  // Get the error details. In your app, check details exists first.
  ArrayList<Map> details = (ArrayList<Map>) e.getDetails().get("details");
  for (Map detail : details) {
    if (detail.get("@type").equals(tosErrorType)
          && (boolean) detail.get("latestTosAccepted") != true) {
      // Ask the user to accept the ToS. If they agree, open the portal in a browser.
      // ...
    }
  }
  return null;
}

.NET

// Authorize this method call as a user that hasn't yet accepted the ToS.
try
{
    var request = service.Customers.Dpcs.List(customerAccount);
    CustomerListDpcsResponse response = request.Execute();
    return response.Dpcs;
}
catch (GoogleApiException e)
{
    foreach (SingleError error in e.Error?.Errors)
    {
        if (error.Message.StartsWith("The user must agree the terms of service"))
        {
            // Ask the user to accept the ToS. If they agree, open the portal in a browser.
            // ...
        }
    }
}

Python

# Authorize this method call as a user that hasn't yet accepted the ToS.
tos_error_type = ('type.googleapis.com/'
                  'google.android.device.provisioning.v1.TosError')
portal_url = 'https://enterprise.google.com/android/zero-touch/customers'

# Send an API request to list all the DPCs available including the HTTP
# header X-GOOG-API-FORMAT-VERSION with the value 2. Import the exception:
# from googleapiclient.errors import HttpError
try:
  request = service.customers().dpcs().list(parent=customer_account)
  request.headers['X-GOOG-API-FORMAT-VERSION'] = '2'
  response = request.execute()
  return response['dpcs']

except HttpError as err:
  # Parse the JSON content of the error. In your app, check ToS exists first.
  error = json.loads(err.content)
  tos_error = error['error']['details'][0]

  # Ask the user to accept the ToS (not shown here). If they agree, then open
  # the portal in a browser.
  if (tos_error['@type'] == tos_error_type
      and tos_error['latestTosAccepted'] is not True):
    if raw_input('Accept the ToS in the zero-touch portal? y|n ') == 'y':
      webbrowser.open(portal_url)

Google API クライアントが詳細なエラー(Java、Python、HTTP リクエスト)をサポートしている場合は、リクエストに値 2 を含む HTTP ヘッダー X-GOOG-API-FORMAT-VERSION を含めます。クライアントが詳細なエラー(.NET など)をサポートしていない場合は、エラー メッセージを照合します。

今後利用規約が更新された場合、この方法に沿ってアプリを実装していれば、ユーザーに新しい利用規約への再同意を求めることができます。

IT 管理者は、ゼロタッチ登録ポータルを使用して組織のユーザーを管理します。これは、お客様向け API では提供できません。IT 管理者は、ポータルを使用してデバイスと構成を管理することもできます。コンソールまたはドキュメントからポータルにリンクする必要がある場合は、次の URL を使用します。

https://enterprise.google.com/android/zero-touch/customers

IT 管理者に、Google アカウントでのログインを求められることを通知することをおすすめします。

デバイスの登録

ゼロタッチ登録は、デバイスを登録するメカニズムであり、NFC 登録や QR コード登録に似ています。コンソールが管理対象デバイスをサポートし、DPC が完全管理対象デバイス モードで実行できる必要があります。

ゼロタッチ登録は、Android 8.0 以降を搭載した対応デバイスで利用できます。IT 管理者は、パートナーの販売パートナーからサポート対象デバイスを購入する必要があります。コンソールは、customers.devices.list を呼び出すことで、IT 管理者のデバイスのうちゼロタッチ登録に利用できるものを追跡できます。

登録の仕組みの概要は次のとおりです。

  1. デバイスは、ゼロタッチ登録のために、初回起動時(または出荷時の設定にリセットした後)に Google サーバーとチェックインします。
  2. IT 管理者がデバイスに構成を適用している場合、ゼロタッチ登録ではフルマネージド デバイスの Android 設定ウィザードが実行され、構成のメタデータで画面がカスタマイズされます。
  3. ゼロタッチ登録では、Google Play から DPC がダウンロードされてインストールされます。
  4. DPC は ACTION_PROVISION_MANAGED_DEVICE インテントを受け取り、デバイスをプロビジョニングします。

インターネットに接続されていない場合は、接続が利用可能になったときにチェックが行われます。ゼロタッチ登録によるデバイスのプロビジョニングについて詳しくは、下記のプロビジョニングをご覧ください。

デフォルト構成。

ゼロタッチ登録では、デフォルトの設定を作成し、その設定を組織が新規購入するすべてのデバイスに適用することをおすすめします。デフォルトの構成が設定されていない場合は、コンソールからデフォルトの構成を設定するように促します。customers.configurations.isDefault の値を確認すると、組織がデフォルト設定を設定しているかどうかを確認できます。

次の例は、既存の構成をデフォルトにする方法を示しています。

Java

// Send minimal data with the request. Just the 2 required fields.
// targetConfiguration is an existing configuration that we want to make the default.
Configuration configuration = new Configuration();
configuration.setIsDefault(true);
configuration.setConfigurationId(targetConfiguration.getConfigurationId());

// Call the API, including the FieldMask to avoid setting other fields to null.
AndroidProvisioningPartner.Customers.Configurations.Patch request = service
      .customers()
      .configurations()
      .patch(targetConfiguration.getName(), configuration);
request.setUpdateMask("isDefault");
Configuration results = request.execute();

.NET

// Send minimal data with the request. Just the 2 required fields.
// targetConfiguration is an existing configuration that we want to make the default.
Configuration configuration = new Configuration
{
    IsDefault = true,
    ConfigurationId = targetConfiguration.ConfigurationId,
};

// Call the API, including the FieldMask to avoid setting other fields to null.
var request = service.Customers.Configurations.Patch(configuration,
                                                     targetConfiguration.Name);
request.UpdateMask = "IsDefault";
Configuration results = request.Execute();

Python

# Send minimal data with the request. Just the 2 required fields.
# target_configuration is an existing configuration we'll make the default.
configuration = {
    'isDefault': True,
    'configurationId': target_configuration['configurationId']}

# Call the API, including the FieldMask to avoid setting other fields to null.
response = service.customers().configurations().patch(
    name=target_configuration['name'],
    body=configuration, updateMask='isDefault').execute()

DPC を参照する

API リソース名 customers.dpcs.name を使用して DPC を識別し、構成で使用することをおすすめします。リソース名には、DPC の一意で不変の識別子が含まれます。サポートされているすべての DPC のリストを取得するには、customers.dpcs.list を呼び出します。リソース名には顧客 ID も含まれているため、最後のパス コンポーネントを使用してリストをフィルタし、一致する Dpc インスタンスを見つけます。次の例は、DPC を照合して、後で構成で使用できるように永続化する方法を示しています。

Java

// Return a customer Dpc instance for the specified DPC ID.
String myDpcIdentifier = "AH6Gbe4aiS459wlz58L30cqbbXbUa_JR9...xMSWCiYiuHRWeBbu86Yjq";
final int dpcIdIndex = 3;
final String dpcComponentSeparator = "/";
// ...
for (Dpc dpcApp : dpcs) {
    // Because the DPC name is in the format customers/{CUST_ID}/dpcs/{DPC_ID}, check the
    // fourth component matches the DPC ID.
    String dpcId = dpcApp.getName().split(dpcComponentSeparator)[dpcIdIndex];
    if (dpcId.equals(myDpcIdentifier)) {
        System.out.format("My DPC is: %s\n", dpcApp.getDpcName());
        return dpcApp;
    }
}
// Handle the case when the DPC isn't found...

.NET

// Return a customer Dpc instance for the specified DPC ID.
var myDpcIdentifer = "AH6Gbe4aiS459wlz58L30cqbbXbUa_JR9...fE9WdHcxMSWCiYiuHRWeBbu86Yjq";
const int dpcIdIndex = 3;
const String dpcComponentSeparator = "/";
// ...
foreach (Dpc dpcApp in dpcs)
{
    // Because the DPC name is in the format customers/{CUST_ID}/dpcs/{DPC_ID}, check the
    // fourth component matches the DPC ID.
    String dpcId = dpcApp.Name.Split(dpcComponentSeparator)[dpcIdIndex];
    if (dpcId.Equals(myDpcIdentifer))
    {
        Console.WriteLine("Matched DPC is: {0}", dpcApp.DpcName);
        return dpcApp;
    }
}
// Handle the case when the DPC isn't found...

Python

# Return a customer Dpc instance for the specified DPC ID.
my_dpc_id = 'AH6Gbe4aiS459wlz58L30cqb...fE9WdHcxMSWCiYiuHRWeBbu86Yjq'
# ...
for dpc_app in dpcs:
  # Because the DPC name is in the format customers/{CUST_ID}/dpcs/{DPC_ID},
  # check the fourth component matches the DPC ID.
  dpc_id = dpc_app['name'].split('/')[3]
  if dpc_id == my_dpc_id:
    return dpc_app

# Handle the case when the DPC isn't found...

コンソールのユーザー インターフェースに DPC の名前を表示する必要がある場合は、customers.dpcs.dpcName から返された値を表示します。

プロビジョニング

デバイス プロビジョニングで優れたユーザー エクスペリエンスを提供してください。デバイスをプロビジョニングするには、ユーザー名とパスワードのみが必要です。販売パートナー様がデバイスをリモート ユーザーに直接配送する場合があることに注意してください。EMM サーバーや組織部門など、その他の設定はすべて customers.configuration.dpcExtras に含めます。

次の JSON スニペットは、構成例の一部を示しています。

{
  "android.app.extra.PROVISIONING_LOCALE": "en_GB",
  "android.app.extra.PROVISIONING_TIME_ZONE": "Europe/London",
  "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED": true,
  "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE": {
    "workflow_type": 3,
    "default_password_quality": 327680,
    "default_min_password_length": 6,
    "company_name": "XYZ Corp",
    "organizational_unit": "sales-uk",
    "management_server": "emm.example.com",
    "detail_tos_url": "https://www.example.com/policies/terms/",
    "allowed_user_domains": "[\"example.com\", \"example.org\", \"example.net\"]"
    }
}

ゼロタッチ登録では、Android Intent を使用して DPC をインストールして起動します。システムは、android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE JSON プロパティの値をインテントの追加情報として DPC に送信します。DPC は、同じキーを使用して PersistableBundle からプロビジョニング設定を読み取ることができます。

推奨 - 次のインテント エクストラを使用して DPC を設定します。

推奨されません - 他の登録方法で使用する可能性のある次の追加機能は含めないでください。

DPC でこれらの設定を抽出して使用する方法については、お客様のデバイスをプロビジョニングするをご覧ください。

開発とテスト

コンソールのゼロタッチ登録機能を開発してテストするには、次のものが必要です。

  • 対応しているデバイス
  • お客様のゼロタッチ登録アカウント

Google Pixel などのゼロタッチ登録に対応したデバイスで開発とテストを行います。開発用デバイスは販売パートナーから購入する必要はありません。

お問い合わせいただくと、テスト用のお客様アカウントとゼロタッチ登録ポータルへのアクセス権が付与されます。Google アカウントに関連付けられている会社のメールアドレスからメールをお送りください。1 台または 2 台のデバイスのメーカーと IMEI 番号をお知らせください。開発アカウントに追加いたします。

ゼロタッチ登録では DPC が自動的にダウンロードされてインストールされるため、プロビジョニングをテストするには、DPC が Google Play で利用可能になっている必要があります。DPC の開発バージョンでテストすることはできません。

IT 管理者向けのサポート

コンソールのインターフェースやドキュメントで IT 管理者をサポートする必要がある場合は、ゼロタッチ登録: IT 管理者向けをご覧ください。コンソールのユーザーにそのヘルプセンター記事をご案内することもできます。

関連情報

コンソールにゼロタッチ登録を統合するには、次のドキュメントをご覧ください。