EMM 통합 가이드

이 가이드는 엔터프라이즈 모바일 관리 (EMM) 제공업체가 제로터치 등록을 콘솔에 통합하는 데 도움이 됩니다. 계속해서 등록에 관해 자세히 알아보고 DPC (기기 정책 컨트롤러)에서 기기를 프로비저닝하는 데 도움이 되는 권장사항을 확인하세요. DPC를 통해 기기를 프로비저닝할 때의 권장사항을 알아보고 개발 및 테스트에 관한 조언도 얻을 수 있습니다.

IT 관리자를 위한 기능

고객 API를 사용하면 IT 관리자가 콘솔에서 직접 제로터치 등록을 설정할 수 있습니다. 다음은 IT 관리자가 콘솔에서 완료할 수 있는 몇 가지 작업입니다.

  • 모바일 정책에 따라 제로터치 등록 구성을 생성, 수정, 삭제합니다.
  • DPC가 조직에서 구매할 향후 기기를 프로비저닝하도록 기본 구성을 설정합니다.
  • 개별 구성을 기기에 적용하거나 제로터치 등록에서 기기를 삭제합니다.

제로터치 등록에 관한 자세한 내용은 개요를 읽어보세요.

기본 요건

EMM 콘솔에 제로터치 등록을 추가하기 전에 솔루션이 다음을 지원하는지 확인하세요.

  • EMM 솔루션은 회사 소유 Android 8.0 이상 (Pixel 7.1 이상) 기기를 완전 관리형 모드로 프로비저닝해야 합니다. 회사 소유 Android 10 이상 기기는 완전 관리형 또는 직장 프로필로 프로비저닝할 수 있습니다.
  • 제로터치 등록은 DPC를 자동으로 다운로드하고 설치하므로 Google Play에서 DPC를 사용할 수 있어야 합니다. Google은 IT 관리자가 고객 API 또는 포털을 사용하여 구성할 수 있는 호환되는 DPC 목록을 유지합니다. DPC를 목록에 추가하려면 EMM 제공업체 커뮤니티를 통해 제품 수정 요청을 제출합니다.
  • 고객이 고객 API를 호출하려면 제로터치 등록 계정이 필요합니다. 파트너 리셀러는 조직이 기기를 구매할 때 IT 관리자 조직의 계정을 설정합니다.
  • 제로터치 등록이 올바르게 작동하려면 기기가 Google 모바일 서비스 (GMS)와 호환되고 Google Play 서비스가 항상 사용 설정되어 있어야 합니다.

API 호출

콘솔 사용자는 Google 계정을 사용하여 고객 API에 대한 API 요청을 승인합니다. 이 흐름은 다른 EMM API에 실행하는 승인과 다릅니다. 앱에서 이 작업을 실행하는 방법은 승인을 참고하세요.

서비스 약관 처리

사용자는 API를 호출하기 전에 최신 서비스 약관 (ToS)에 동의해야 합니다. API 호출이 HTTP 403 Forbidden 상태 코드를 반환하고 응답 본문에 TosError가 포함된 경우 제로터치 등록 포털에 로그인하여 서비스 약관에 동의하라는 메시지를 사용자에게 표시합니다. 아래 예는 이를 실행할 수 있는 방법 중 하나를 보여줍니다.

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://partner.android.com/zerotouch'

# 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 클라이언트에서 자세한 오류 (자바, Python 또는 HTTP 요청)를 지원하는 경우 요청에 2 값과 함께 HTTP 헤더 X-GOOG-API-FORMAT-VERSION를 포함합니다. 클라이언트가 자세한 오류 (.NET 및 기타)를 지원하지 않는 경우 오류 메시지와 일치시킵니다.

향후 서비스 약관이 업데이트될 때 이 접근 방식을 따르면 앱에서 사용자에게 새 서비스 약관에 다시 동의하도록 안내합니다.

IT 관리자는 제로터치 등록 포털을 사용하여 조직의 사용자를 관리하므로 고객 API를 통해 제로터치 등록 포털을 제공할 수 없습니다. IT 관리자는 포털을 사용하여 기기 및 구성을 관리할 수도 있습니다. 콘솔이나 문서에서 포털에 연결해야 한다면 다음 URL을 사용하세요.

https://partner.android.com/zerotouch

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 인텐트를 수신하고 기기를 프로비저닝합니다.

인터넷에 연결되어 있지 않은 경우 인터넷 연결을 사용할 수 있게 되면 확인이 진행됩니다. 제로터치 등록을 통한 기기 프로비저닝에 관한 자세한 내용은 아래의 프로비저닝을 참고하세요.

기본 구성

제로터치 등록은 IT 관리자가 조직에서 구매한 새 기기에 적용되는 기본 구성을 설정할 때 가장 큰 도움을 줍니다. 기본 구성이 설정되지 않은 경우 콘솔에서 기본 구성 설정을 승격합니다. 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의 고유하고 변경되지 않는 식별자가 포함됩니다. customers.dpcs.list를 호출하여 지원되는 모든 DPC 목록을 가져옵니다. 리소스 이름에는 고객 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 인텐트를 사용하여 DPC를 설치하고 실행합니다. 시스템은 android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE JSON 속성의 값을 인텐트의 추가 항목으로 DPC에 전송합니다. DPC는 동일한 키를 사용하여 PersistableBundle에서 프로비저닝 설정을 읽을 수 있습니다.

권장됨 - 다음 인텐트 추가 항목을 사용하여 DPC를 설정합니다.

권장되지 않음 - 다른 등록 방법에 사용할 수 있는 다음 추가 항목은 포함하지 마세요.

DPC에서 이러한 설정을 추출하고 사용하는 방법을 알아보려면 고객 기기 프로비저닝을 참고하세요.

개발 및 테스트

콘솔의 제로터치 등록 기능을 개발하고 테스트하려면 다음이 필요합니다.

  • 지원되는 기기
  • 고객 제로터치 등록 계정

Google Pixel과 같이 제로터치 등록을 지원하는 기기로 개발하고 테스트합니다. 리셀러 파트너로부터 개발 기기를 구매할 필요가 없습니다.

테스트 고객 계정을 확보하고 제로터치 등록 포털에 액세스하려면 Google에 문의하세요. Google 계정과 연결된 회사 이메일 주소에서 Google에 이메일을 보냅니다. 한 대 또는 두 대의 기기의 제조업체와 IMEI 번호를 알려주시면 개발 계정에 추가해 드리겠습니다.

제로터치 등록은 DPC를 자동으로 다운로드하고 설치하므로 Google Play에서 DPC를 사용할 수 있어야 프로비저닝을 테스트할 수 있습니다. DPC의 개발 버전으로는 테스트할 수 없습니다.

IT 관리자 지원

콘솔 인터페이스 또는 문서에서 IT 관리자를 지원해야 하는 경우 IT 관리자를 위한 제로터치 등록에서 안내를 확인하세요. 콘솔 사용자를 해당 고객센터 도움말로 안내할 수도 있습니다.

추가 자료

콘솔에서 제로터치 등록을 통합하는 데 도움이 되는 다음 문서를 읽어보세요.