Codelab: Google Cloud 사용 권한 프로비저닝

시작하기 전에

개요

Google Cloud 사용 권한이 있는 고객을 프로비저닝하려면 API를 여러 번 호출해야 합니다. 다음 다이어그램은 고객을 프로비저닝하는 대략적인 단계를 보여줍니다.

Cloud Channel API를 통해 Google Cloud 사용 권한을 프로비저닝하는 단계

1단계: 인증된 사용자 인증 정보를 사용하여 서비스 객체 만들기

클라이언트 라이브러리 사용

이 Codelab에서는 리셀러가 Cloud Channel API용 클라이언트 라이브러리 중 하나를 사용한다고 가정합니다.

통합을 위해 Google 클라이언트 라이브러리를 사용하는 것이 좋습니다. 이러한 라이브러리는 언어별로 자연어 인터페이스를 제공하고, HTTP 대신 RPC를 사용하여 성능을 향상하며, 필드의 기본값을 설정합니다.

라이브러리를 설치하려면 다음 안내를 따르세요.

C++

C++ 클라이언트 라이브러리를 설치하려면 C++ 개발 환경 설정을 참조하세요.

C#

Visual Studio 2017 이상을 사용하는 경우 Nuget 패키지 관리자 창을 열고 다음을 입력합니다.

Install-Package Google.Cloud.Channel.V1

.NET Core 명령줄 인터페이스 도구를 사용하여 종속 항목을 설치하려면 다음 명령어를 실행합니다.

dotnet add package Google.Cloud.Channel.V1

Go

go mod init YOUR_MODULE_NAME
go get cloud.google.com/go/channel/apiv1

Java

Maven을 사용하는 경우 pom.xml 파일에 다음을 추가합니다. BOM에 대한 자세한 내용은 Google Cloud Platform 라이브러리 BOM을 참조하세요.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>libraries-bom</artifactId>
      <version>20.9.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-channel</artifactId>
    <version>2.3.0</version>
  </dependency>

Gradle을 사용하는 경우 종속 항목에 다음을 추가합니다.

implementation group: 'com.google.cloud', name: 'google-cloud-channel', version: '2.3.0'

VS Code, IntelliJ, Eclipse를 사용하는 경우 다음과 같은 IDE 플러그인을 사용하여 클라이언트 라이브러리를 프로젝트에 추가할 수 있습니다.

이 플러그인은 서비스 계정의 키 관리와 같은 추가 기능을 제공합니다. 자세한 내용은 각 플러그인의 문서를 참조하세요.

Node.js

npm install --save @google-cloud/channel

PHP

composer require google/cloud-channel

Python

pip install google-cloud-channel

Ruby

gem install google-cloud-channel

클라이언트 라이브러리를 사용하지 않도록 선택한 경우에는 인증을 처리할 더 작은 클라이언트 라이브러리가 있는지 확인하는 것이 좋습니다. 인증 레이어를 처음부터 다시 작성하는 것은 권장되지 않습니다.

인증용 사용자 인증 정보 설정

Cloud Channel API는 다음을 요구하는 인증 유형을 사용합니다.

  1. 서비스 계정 및 해당 JSON 키 파일.
  2. 리셀러 도메인 최고 관리자가 서비스 계정의 클라이언트에서 도메인 전체 위임을 사용하여 가장해야 합니다.

기본 요건이 누락된 경우 API 설정 Codelab을 따르세요. 자세한 내용은 서비스 계정용 OAuth 2.0을 참조하세요.

아래 코드에서 내 정보를 사용하여 다음 변수를 입력합니다.

  • jsonKeyFile: 서비스 계정을 만들 때 생성된 JSON 키 파일의 경로입니다.
  • resellerAdminUser: 리셀러 도메인 최고 관리자의 이메일 주소입니다.
  • accountId: Partner Sales Console의 설정 페이지에 있는 계정 ID입니다.
  • customerDomain: 최종 고객의 도메인입니다.

C#

using Google.Apis.Auth.OAuth2;
using Google.Api.Gax;
using Google.Cloud.Billing.V1; // optional
using Google.Cloud.Channel.V1;
using Google.Cloud.Iam.V1; // optional
using Google.Type;
using Newtonsoft.Json;
using System;
using System.Linq;

namespace Codelab {

  class Program {

    /***************** REPLACE WITH YOUR OWN VALUES ********************************/
    private static readonly string jsonKeyFile = "path/to/json_key_file.json";
    private static readonly string resellerAdminUser = "admin@yourresellerdomain.com";
    private static readonly string accountId = "C012345";
    private static readonly string customerDomain = "example.com";
    /*******************************************************************************/

    private static readonly string accountName = "accounts/" + accountId;

    private static CloudChannelServiceClient client;

    static void Main(string[] args) {
      // Set up credentials with user impersonation
      ICredential credential = GoogleCredential.FromFile(jsonKeyFile)
                                   .CreateScoped(CloudChannelServiceClient.DefaultScopes)
                                   .CreateWithUser(resellerAdminUser);

      // Create the API client
      client = new CloudChannelServiceClientBuilder {
        TokenAccessMethod = credential.GetAccessTokenForRequestAsync
      }.Build();

Go

package main

import (
	"context"
	"fmt"
	"io/ioutil"

	billing "cloud.google.com/go/billing/apiv1" // optional
	channel "cloud.google.com/go/channel/apiv1"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
	channelpb "google.golang.org/genproto/googleapis/cloud/channel/v1"
	iampb "google.golang.org/genproto/googleapis/iam/v1" // optional
	"google.golang.org/genproto/googleapis/type/postaladdress"
	"google.golang.org/protobuf/encoding/protojson"
)

// ############## REPLACE WITH YOUR OWN VALUES ####################
const jsonKeyFile = "path/to/json_key_file.json"
const resellerAdminUser = "admin@yourresellerdomain.com"
const accountID = "C012345"
const customerDomain = "example.com"

// ################################################################

const accountName = "accounts/" + accountID

func main() {
	ctx := context.Background()

	// set up credentials with user impersonation
	jsonKey, _ := ioutil.ReadFile(jsonKeyFile)
	jwt, _ := google.JWTConfigFromJSON(jsonKey, "https://www.googleapis.com/auth/apps.order")
	jwt.Subject = resellerAdminUser
	tokenSource := jwt.TokenSource(ctx)
	// create the API client
	client, _ := channel.NewCloudChannelClient(ctx, option.WithTokenSource(tokenSource))

Java

import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.channel.v1.CloudChannelServiceClient;
import com.google.cloud.channel.v1.CloudChannelServiceSettings;
import com.google.cloud.channel.v1.CreateCustomerRequest;
import com.google.cloud.channel.v1.CreateEntitlementRequest;
import com.google.cloud.channel.v1.Customer;
import com.google.cloud.channel.v1.Entitlement;
import com.google.cloud.channel.v1.ListOffersRequest;
import com.google.cloud.channel.v1.Offer;
import com.google.cloud.channel.v1.OperationMetadata;
import com.google.cloud.channel.v1.Parameter;
import com.google.cloud.channel.v1.Value;
import com.google.gson.Gson;
import com.google.type.PostalAddress;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;

// optional
import java.util.ArrayList;
import java.util.List;
import com.google.cloud.billing.v1.CloudBillingClient;
import com.google.cloud.billing.v1.CloudBillingSettings;
import com.google.iam.v1.Binding;
import com.google.iam.v1.Policy;

// ...

public class Codelab {

  /***************** REPLACE WITH YOUR OWN VALUES ********************************/
  public static final String JSON_KEY_FILE = "path/to/json_key_file.json";
  public static final String RESELLER_ADMIN_USER = "admin@yourresellerdomain.com";
  public static final String ACCOUNT_ID = "C012345";
  public static final String CUSTOMER_DOMAIN = "example.com";
  /*******************************************************************************/

  public static final String ACCOUNT_NAME = "accounts/" + ACCOUNT_ID;

  private static CloudChannelServiceClient client;
  private static final Gson gson = new Gson();

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException {

    // Set up credentials with user impersonation
    FileInputStream jsonKeyFileSteam = new FileInputStream(JSON_KEY_FILE);
    GoogleCredentials credentials = ServiceAccountCredentials.fromStream(jsonKeyFileSteam)
                                        .createScoped("https://www.googleapis.com/auth/apps.order")
                                        .createDelegated(RESELLER_ADMIN_USER);

    CloudChannelServiceSettings clientSettings =
        CloudChannelServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
            .build();

    // Create the API client
    client = CloudChannelServiceClient.create(clientSettings);

Node.js

const {JWT} = require('google-auth-library');
const {grpc} = require('google-gax');
const {CloudChannelServiceClient} = require('@google-cloud/channel');

// ############## REPLACE WITH YOUR OWN VALUES ####################
const jsonKeyFile = 'path/to/json_key_file.json';
const resellerAdminUser = 'admin@yourresellerdomain.com';
const accountId = 'C012345';
const customerDomain = 'example.com';
// ################################################################
const accountName = `accounts/${accountId}`;

// Set up credentials with user impersonation
const authClient = new JWT({
  keyFile: jsonKeyFile,
  scopes: ['https://www.googleapis.com/auth/apps.order'],
  subject: resellerAdminUser,
});
const sslCreds = grpc.credentials.combineChannelCredentials(
  grpc.credentials.createSsl(),
  grpc.credentials.createFromGoogleCredential(authClient)
);
// Create the API client
const client = new CloudChannelServiceClient({sslCreds});

PHP

require 'vendor/autoload.php';

use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Cloud\Channel;

// ############## REPLACE WITH YOUR OWN VALUES ####################
$JSON_KEY_FILE = 'path/to/json_key_file.json';
$RESELLER_ADMIN_USER = 'admin@yourresellerdomain.com';
$ACCOUNT_ID = 'C012345';
$CUSTOMER_DOMAIN = 'example.com';
// ################################################################

$ACCOUNT_NAME = 'accounts/' . $ACCOUNT_ID;

// Set up credentials with user impersonation
$credentials = new ServiceAccountCredentials(
  'https://www.googleapis.com/auth/apps.order', /* $scope */
  $JSON_KEY_FILE, /* $keyFile */
  $RESELLER_ADMIN_USER /* $sub */
);

// Create the API client
$client = new Channel\V1\CloudChannelServiceClient([
  'credentials' => $credentials
]);

Python

from google.cloud import channel
from google.oauth2 import service_account
# optional
from google.cloud import billing
from google.iam.v1 import policy_pb2

############## REPLACE WITH YOUR OWN VALUES ####################
JSON_KEY_FILE = "path/to/json_key_file.json"
RESELLER_ADMIN_USER = "admin@yourresellerdomain.com"
ACCOUNT_ID = "C012345"
CUSTOMER_DOMAIN = "example.com"
################################################################

ACCOUNT_NAME = "accounts/" + ACCOUNT_ID

# Set up credentials with user impersonation
credentials = service_account.Credentials.from_service_account_file(
    JSON_KEY_FILE, scopes=["https://www.googleapis.com/auth/apps.order"])
credentials_delegated = credentials.with_subject(RESELLER_ADMIN_USER)

# Create the API client
client = channel.CloudChannelServiceClient(credentials=credentials_delegated)

Ruby

require 'google-cloud-channel'
require 'google-cloud-billing' # Optional
require 'google/iam/v1/policy_pb' # Optional

################## REPLACE WITH YOUR OWN VALUES ################################
JSON_PRIVATE_KEY_FILE = 'path/to/json_key_file.json'
RESELLER_ADMIN_USER = 'admin@yourresellerdomain.com'
ACCOUNT_ID = 'C012345'
CUSTOMER_DOMAIN = 'example.com'
################################################################################

ACCOUNT_NAME = "accounts/#{ACCOUNT_ID}"

# Set up credentials with user impersonation
credentials = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: File.open(JSON_PRIVATE_KEY_FILE),
  scope: 'https://www.googleapis.com/auth/apps.order')
credentials.sub = RESELLER_ADMIN_USER

# Create the API client
CLIENT = Google::Cloud::Channel::cloud_channel_service do |config|
  config.credentials = credentials
end

2단계: 재판매 견적서(오퍼) 목록에서 재판매 견적서(오퍼) 선택하기

아래 샘플 코드는 Google Cloud Platform에 대한 첫 번째 재판매 견적서(오퍼)를 선택합니다. 특정 Cloud Billing 계정 ID(billingAccounts/... 형식)에 대한 재판매 견적서(오퍼)를 선택하려면 plan.billingAccount 필드가 포함된 재판매 견적서(오퍼)를 필터링하면 됩니다.

C#

PagedEnumerable<ListOffersResponse, Offer> offers =
    client.ListOffers(new ListOffersRequest { Parent = accountName });

// For the purpose of this codelab, the code lists all offers and selects
// the first offer for Google Cloud Platform.
// This is needed because offerIds vary from one account to another,
// but this is not a recommended model for your production integration
Offer selectedOffer = offers.FirstOrDefault(
  o => o.Sku.MarketingInfo.DisplayName == "Google Cloud Platform");

Console.WriteLine("=== Selected offer");
Console.WriteLine(JsonConvert.SerializeObject(selectedOffer));

Go

var selectedOffer *channelpb.Offer
req := &channelpb.ListOffersRequest{
	Parent: accountName,
}
it := client.ListOffers(ctx, req)

// For the purpose of this codelab, the code lists all offers and selects
// the first offer for Google Cloud Platform.
// This is needed because offerIds vary from one account to another and
// is not a recommended model for your production integration.
for {
	offer, err := it.Next()
	if err == iterator.Done {
		break
	}
	if offer.Sku.MarketingInfo.DisplayName == "Google Cloud Platform" {
		selectedOffer = offer
		break
	}
}

fmt.Println("=== Selected offer")
fmt.Println(protojson.Format(selectedOffer))

Java

ListOffersRequest request =
    ListOffersRequest.newBuilder().setParent(ACCOUNT_NAME).build();

// For the purpose of this codelab, the code lists all offers and selects
// the first offer for Google Cloud Platform.
// This is needed because offerIds vary from one account to another,
// but this is not a recommended model for your production integration
CloudChannelServiceClient.ListOffersPagedResponse response = client.listOffers(request);
Offer selectedOffer = Offer.newBuilder().build();
Iterator<Offer> iterator = response.iterateAll().iterator();
while (iterator.hasNext()) {
  Offer offer = iterator.next();
  String skuName = offer.getSku().getMarketingInfo().getDisplayName();
  if (skuName.equals("Google Cloud Platform")) {
    selectedOffer = offer;
    break;
  }
}

System.out.println("=== Selected offer");
System.out.println(gson.toJson(selectedOffer));

Node.js

const [offers] = await client.listOffers({
  parent: accountName,
});

// For the purpose of this codelab, the code lists all offers and selects
// the first offer for the Google Cloud Platform.
// This is needed because offerIds vary from one account to another,
// but this is not a recommended model for your production integration
const selectedOffer = offers.find(o => {
  return o.sku.marketingInfo.displayName === 'Google Cloud Platform';
});

console.log('=== Selected offer');
console.info(selectedOffer);

PHP

$offers = $client->listOffers($ACCOUNT_NAME /* parent */);

// For the purpose of this codelab, the code lists all offers and selects
// the first offer for the Google Cloud Platform.
// This is needed because offerIds vary from one account to another,
// but this is not a recommended model for your production integration
$sampleSku = 'Google Cloud Platform';
foreach ($offers as $offer) {
  if ($offer->getSku()->getMarketingInfo()->getDisplayName() == $sampleSku) {
    $selectedOffer = $offer;
    break;
  }
}

print '=== Selected offer' . PHP_EOL;
print $selectedOffer->serializeToJsonString() . PHP_EOL;

Python

request = channel.ListOffersRequest(parent=ACCOUNT_NAME)
offers = client.list_offers(request)

# For the purpose of this codelab, the code lists all offers and selects
# the first offer for Google Cloud Platform.
# This is needed because offerIds vary from one account to another,
# but this is not a recommended model for your production integration
selected_offer = None
for offer in offers:
  if offer.sku.marketing_info.display_name == "Google Cloud Platform":
    selected_offer = offer
    break

print("=== Selected offer")
print(selected_offer)

Ruby

# For the purpose of this codelab, the code lists all offers and selects
# the first offer for Google Cloud Platform.
# This is needed because offerIds vary from one account to another,
# but this is not a recommended model for your production integration

request = Google::Cloud::Channel::V1::ListOffersRequest.new({ parent: ACCOUNT_NAME })
offers = CLIENT.list_offers(request)
sample_offer = 'Google Cloud Platform'

offer = offers.detect { |offer| offer.sku.marketing_info.display_name == sample_offer }
puts('=== Selected offer')
puts(offer.inspect)

3단계: 고객 만들기

사용 권한을 만들려면 먼저 리셀러가 재판매한 고객을 위해 채널 서비스 고객을 만들어야 합니다.

C#

// Create the Customer resource
CreateCustomerRequest request = new CreateCustomerRequest {
  Parent = accountName,
  Customer = new Customer { OrgDisplayName = "Acme Corp",
                            OrgPostalAddress = new PostalAddress {
                                  AddressLines = { "1800 Amphibious Blvd" },
                                  PostalCode = "94045",
                                  RegionCode = "US" },
                            Domain = customerDomain,
                            // Optional. Add the CRM ID for this customer.
                            CorrelationId = "CRMID012345" }
};

Customer customer = client.CreateCustomer(request);
Console.WriteLine("=== Created customer with id " + customer.Name);
Console.WriteLine(JsonConvert.SerializeObject(customer));

Go

// Create the Customer resource
req := &channelpb.CreateCustomerRequest{
	Parent: accountName,
	Customer: &channelpb.Customer{
		OrgDisplayName: "Acme Corp",
		OrgPostalAddress: &postaladdress.PostalAddress{
			AddressLines: []string{"1800 Amphibious Blvd"},
			PostalCode:   "94045",
			RegionCode:   "US",
		},
		Domain: customerDomain,
		// Optional. Add the CRM ID for this customer.
		CorrelationId: "CRMID012345",
		// Distributors need to pass the following value
		// ChannelPartnerId: channelPartnerLinkId
	},
}
customer, _ := client.CreateCustomer(ctx, req)
fmt.Println("=== Created customer with id " + customer.Name)
fmt.Println(protojson.Format(customer))

Java

// Create the Customer resource
PostalAddress postalAddress =
    PostalAddress.newBuilder()
        .addAddressLines("1800 Amphibious Blvd")
        .setPostalCode("94045")
        .setRegionCode("US")
        .build();

CreateCustomerRequest request =
    CreateCustomerRequest.newBuilder()
        .setParent(ACCOUNT_NAME)
        .setCustomer(
            Customer.newBuilder()
                .setOrgDisplayName("Acme Corp")
                .setOrgPostalAddress(postalAddress)
                .setDomain(CUSTOMER_DOMAIN)
                // Optional. Add the CRM ID for this customer.
                .setCorrelationId("CRMID012345")
                // Distributors need to pass the following field
                // .setChannelPartnerId(channelPartnerLinkId)
                .build())
        .build();

Customer customer = client.createCustomer(request);

System.out.println("=== Created customer with id " + customer.getName());
System.out.println(gson.toJson(customer));

Node.js

// Create the Customer resource
const [customer] = await client.createCustomer({
  parent: accountName,
  customer: {
    orgDisplayName: 'Acme Corp',
    orgPostalAddress: {
      addressLines: ['1800 Amphibious Blvd'],
      postalCode: '94045',
      regionCode: 'US',
    },
    domain: customerDomain,
    // Optional. Add the CRM ID for this customer.
    correlationId: "CRMID012345",
    // Distributors need to pass the following field
    // channelPartnerId: channelPartnerLinkId
  },
});
console.log(`=== Created customer with id ${customer.name}`);
console.info(customer);

PHP

// Create the Customer resource
$customer = $client->createCustomer(
  $ACCOUNT_NAME  /* parent */,
  new Channel\V1\Customer([
    'org_display_name' => 'Acme Corp',
    'org_postal_address' => new Google\Type\PostalAddress([
      'address_lines' => ['1800 Amphibious Blvd'],
      'postal_code' => '94045',
      'region_code' => 'US',
    ]),
    'domain' => $CUSTOMER_DOMAIN,
    // Optional. Add the CRM ID for this customer.
    'correlation_id' => 'CRMID012345',
    // Distributors need to pass the following field
    // 'channel_partner_id' => $channelPartnerLinkId
  ])
);

print '=== Created customer with id ' . $customer->getName() . PHP_EOL;
print $customer->serializeToJsonString() . PHP_EOL;

Python

# Create the Customer resource
request = channel.CreateCustomerRequest(
    parent=ACCOUNT_NAME,
    customer={
        "org_display_name": "Acme Corp",
        "domain": CUSTOMER_DOMAIN,
        "org_postal_address": {
            "address_lines": ["1800 Amphibious Blvd"],
            "postal_code": "94045",
            "region_code": "US"
        },
        # Optional. Add the CRM ID for this customer.
        "correlation_id": "CRMID012345"
    })
# Distributors need to also pass the following field for the `customer`
# "channel_partner_id": channel_partner_link_id

customer = client.create_customer(request)

print("=== Created customer with id ", customer.name)
print(customer)

Ruby

# Create the Customer resource
request = Google::Cloud::Channel::V1::CreateCustomerRequest.new(
  parent: ACCOUNT_NAME,
  customer: {
    'org_display_name': 'Acme Corp',
    'domain': CUSTOMER_DOMAIN,
    'org_postal_address': {
      'address_lines': ['1800 Amphibious Blvd'],
      'postal_code': '94045',
      'region_code': 'US'
    },
    # Optional. Add the CRM ID for this customer.
    'correlation_id': 'CRMID012345'
  })
  # Distributors need to also pass the following field for the `customer`
  # "channel_partner_id": channel_partner_link_id

customer = CLIENT.create_customer(request)

puts("=== Created customer with id " + customer.name)
puts(customer.inspect)

4단계: 사용 권한 만들기

고객을 만들었으면 이제 사용 권한을 프로비저닝할 수 있습니다.

C#

// This display name shows on the Google Cloud console when a customer
// links the account to their project.
// Recommended format: "[Reseller name] - [Customer name]"
string displayName = "Reseller XYZ - Acme corp";

CreateEntitlementRequest request = new CreateEntitlementRequest {
  Parent = customer.Name,
  Entitlement = new Entitlement {
      Offer = selectedOffer.Name,
      Parameters = { new Parameter { Name = "display_name",
                                     Value = new Value {
                                          StringValue = displayName } } },
      // A string of up to 80 characters.
      // We recommend using an internal transaction ID or
      // identifier for the customer in this field.
      PurchaseOrderId = "A codelab test"
    }
};

// This call returns a long-running operation.
var operation = client.CreateEntitlement(request);

// Wait for the long-running operation and get the result.
Entitlement entitlement = operation.PollUntilCompleted().Result;
Console.WriteLine("=== Created entitlement");
Console.WriteLine(JsonConvert.SerializeObject(entitlement));

Go

// This display name shows on the Google Cloud console when a customer
// links the account to their project.
// Recommended format: "[Reseller name] - [Customer name]"
const displayName = "Reseller XYZ - Acme corp"

// This endpoint returns a long-running operation.
req := &channelpb.CreateEntitlementRequest{
	Parent: customer.Name,
	Entitlement: &channelpb.Entitlement{
		Offer: selectedOffer.Name,
		Parameters: []*channelpb.Parameter{
			{
				Name: "display_name",
				Value: &channelpb.Value{
					Kind: &channelpb.Value_StringValue{StringValue: displayName},
				},
			},
		},
		// A string of up to 80 characters.
		// We recommend using an internal transaction ID or
		// identifier for the customer in this field.
		PurchaseOrderId: "A codelab test",
	},
}
// This endpoint returns a long-running operation.
op, _ := client.CreateEntitlement(ctx, req)

// Wait for the long-running operation and get the result.
entitlement, _ := op.Wait(ctx)

fmt.Println("=== Created entitlement")
fmt.Println(protojson.Format(entitlement))

Java

// This display name shows on the Google Cloud console when a customer
// links the account to their project.
// Recommended format: "[Reseller name] - [Customer name]"
String displayName = "Reseller XYZ - Acme corp";

Entitlement entitlement =
    Entitlement.newBuilder()
        .setOffer(selectedOffer.getName())
        .addParameters(
            Parameter.newBuilder()
                .setName("display_name")
                .setValue(Value.newBuilder().setStringValue(displayName).build())
                .build())
        // A string of up to 80 characters.
        // We recommend using an internal transaction ID or
        // identifier for the customer in this field.
        .setPurchaseOrderId("A codelab test")
        .build();

CreateEntitlementRequest request =
    CreateEntitlementRequest.newBuilder()
        .setParent(customer.getName())
        .setEntitlement(entitlement)
        .build();

// This call returns a long-running operation.
OperationFuture<Entitlement, OperationMetadata> operation =
    client.createEntitlementAsync(request);

// Wait for the long-running operation and get the result.
entitlement = operation.get();
System.out.println("=== Created entitlement");
System.out.println(gson.toJson(entitlement));

Node.js

// This display name shows on the Google Cloud console when a customer
// links the account to their project.
// Recommended format: "[Reseller name] - [Customer name]"
const displayName = 'Reseller XYZ - Acme corp';

// This call returns a long-running operation.
const [operation] = await client.createEntitlement({
  parent: customer.name,
  entitlement: {
    offer: selectedOffer.name,
    parameters: [
      {
        name: 'display_name',
        value: {
          stringValue: displayName,
        },
      },
    ],
    // A string of up to 80 characters.
    // We recommend using an internal transaction ID or
    // identifier for the customer in this field.
    purchaseOrderId: 'A codelab test',
  },
});

// Wait for the long-running operation and get the result.
const [entitlement] = await operation.promise();
console.log('=== Created entitlement');
console.info(entitlement);

PHP

// This display name shows on the Google Cloud console when a customer
// links the account to their project.
// Recommended format: "[Reseller name] - [Customer name]"
$displayName = 'Reseller XYZ - Acme corp';

// This call returns a long-running operation.
$operation = $client->createEntitlement(
  $customer->getName() /* parent */,
  new Channel\V1\Entitlement([
    'offer' => $selectedOffer->getName(),
    'parameters' => [
      new Channel\V1\Parameter([
        'name' => 'display_name',
        'value' => new Channel\V1\Value([
          'string_value' => $displayName,
        ])
      ]),
    ],
    // A string of up to 80 characters.
    // We recommend using an internal transaction ID or
    // identifier for the customer in this field.
    'purchase_order_id' => 'A codelab test'
  ])
);

// Wait for the long-running operation and get the result.
$operation->pollUntilComplete();
$entitlement = $operation->getResult();
print '=== Created entitlement' . PHP_EOL;
print $entitlement->serializeToJsonString() . PHP_EOL;

Python

# This display name shows on the Google Cloud console when a customer
# links the account to their project.
# Recommended format: "[Reseller name] - [Customer name]"
display_name = "Reseller XYZ - Acme corp"

request = channel.CreateEntitlementRequest(
    parent=customer.name,
    entitlement={
        "offer": selected_offer.name,
        "parameters": [{
            "name": "display_name",
            "value": {
                "string_value": display_name
            }
        }],
        # A string of up to 80 characters.
        # We recommend an internal transaction ID or
        # identifier for this customer in this field.
        "purchase_order_id": "A codelab test"
    })

# This call returns a long-running operation.
operation = client.create_entitlement(request)

# Wait for the long-running operation and get the result.
entitlement = operation.result()

print("=== Created entitlement")
print(entitlement)

Ruby

# This display name shows on the Google Cloud console when a customer
# links the account to their project.
# Recommended format: "[Reseller name] - [Customer name]"
display_name = 'Reseller XYZ - Acme Corp'

request = Google::Cloud::Channel::V1::CreateEntitlementRequest.new(
  parent: customer.name,
  entitlement: {
    offer: selected_offer.name,
    # Setting 5 seats for this Annual offer
    parameters: [{
                   name: 'display_name',
                   value: {
                     string_value: display_name
                   }
                 }],
    # A string of up to 80 characters.
    # We recommend an internal transaction ID or
    # identifier for this customer in this field.
    purchase_order_id: 'A codelab test'
  })

# This call returns a long-running operation.
operation = CLIENT.create_entitlement(request)

# Wait for the long-running operation and get the result.
CLIENT.operations_client.wait_operation(Google::Longrunning::WaitOperationRequest
                                        .new({ name: operation.name }))
operation = CLIENT.operations_client.get_operation(Google::Longrunning::GetOperationRequest
                                                   .new({ name: operation.name }))
entitlement = operation.response

puts("=== Created entitlement")
puts(entitlement)

선택사항: 고객 결제 계정의 IAM 정책 업데이트

사용 권한을 만들면 고객 결제 계정이 프로비저닝됩니다. 고객에게 이 계정에 대한 명시적인 액세스 권한을 부여해 줄 수 있습니다.

이 작업은 Cloud Billing API를 통해 프로그래매틱 방식으로 수행할 수 있습니다. 방법은 다음과 같습니다.

  • Google Cloud 콘솔에서 API 라이브러리로 이동하여 Cloud Billing API를 사용 설정합니다.
  • 서비스 계정에 상위 결제 계정에 대한 Cloud Billing IAM 역할을 부여합니다. 결제 계정 관리자(roles/billing.admin) 역할 부여는 이 Codelab의 목적에 부합하는 옵션이지만 프로덕션 통합에서 보다 세분화된 권한을 사용해 볼 수 있습니다. 액세스 권한 부여에 대한 자세한 내용은 Cloud Billing API 액세스 제어 페이지를 참조하세요.

먼저 다음과 같이 라이브러리를 설치합니다.

C#

Visual Studio 2017 이상을 사용하는 경우 Nuget 패키지 관리자 창을 열고 다음을 입력합니다.

Install-Package Google.Cloud.Billing.V1

.NET Core 명령줄 인터페이스 도구를 사용하여 종속 항목을 설치하려면 다음 명령어를 실행합니다.

dotnet add package Google.Cloud.Billing.v1

Go

go get cloud.google.com/go/billing/apiv1

Java

설치 방법에 대한 안내는 Maven 저장소를 참조하세요. 다음 코드는 1.1.12 버전으로 테스트되었습니다.

Node.js

npm install --save @google-cloud/nodejs-billing

PHP

composer require google/cloud-billing

Python

pip install google-cloud-billing

Ruby

gem install google-cloud-billing

그런 다음 클라이언트를 인스턴스화하고 IAM 정책을 업데이트할 수 있습니다.

C#

// Get the name of the customer's billing account from the entitlement
String billingAccount = entitlement.ProvisionedService.ProvisioningId;

// Create a Cloud Billing API client
CloudBillingClient billingClient = new CloudBillingClientBuilder {
  CredentialsPath = jsonKeyFile
}.Build();

// For the purpose of this codelab, we'll grant an IAM role to the reseller
// admin user, but this is not a requirement for a production integration.
Policy policy = billingClient.GetIamPolicy(billingAccount);

string role = "roles/billing.user";
string member = "user:" + resellerAdminUser;
var binding = policy.Bindings.FirstOrDefault(b => b.Role == role);

// If the binding already exists, add the user to it, else add a new binding
if (binding != null) {
  binding.Members.Add(member);
} else {
  binding = new Binding { Role = role };
  binding.Members.Add(member);
  policy.Bindings.Add(binding);
}

// Update the IAM policy
SetIamPolicyRequest request = new SetIamPolicyRequest {
  Resource = billingAccount,
  Policy = policy
};
billingClient.SetIamPolicy(request);

Console.WriteLine("=== Set IAM policy");
Console.WriteLine(JsonConvert.SerializeObject(policy));

Go

// Get the name of the customer's billing account from the entitlement
billingAccount := entitlement.ProvisionedService.ProvisioningId

// Create a Cloud Billing API client
client, _ := billing.NewCloudBillingClient(ctx, option.WithCredentialsFile(jsonKeyFile))

// For the purpose of this codelab, we'll grant an IAM role to the reseller
// admin user, but this is not a requirement for a production integration.
getPolicyReq := &iampb.GetIamPolicyRequest{Resource: billingAccount}
policy, _ := client.GetIamPolicy(ctx, getPolicyReq)

var binding *iampb.Binding
const role = "roles/billing.user"
const member = "user:" + resellerAdminUser
for _, b := range policy.Bindings {
	if b.Role == role {
		binding = b
		break
	}
}
// If the binding already exists, add the user to it, else add a new binding
if binding != nil {
	binding.Members = append(binding.Members, member)
} else {
	binding = &iampb.Binding{
		Role:    role,
		Members: []string{member},
	}
	policy.Bindings = append(policy.Bindings, binding)
}

// Update the IAM policy
setPolicyReq := &iampb.SetIamPolicyRequest{
	Resource: billingAccount,
	Policy:   policy,
}
client.SetIamPolicy(ctx, setPolicyReq)
fmt.Println("=== Set IAM policy")
fmt.Println(protojson.Format(policy))

Java

// Get the name of the customer's billing account from the entitlement
String billingAccount = entitlement.getProvisionedService().getProvisioningId();

// Create a Cloud Billing API client
FileInputStream jsonKeyFileSteam = new FileInputStream(JSON_KEY_FILE);
GoogleCredentials credentials = ServiceAccountCredentials.fromStream(jsonKeyFileSteam);
CloudBillingSettings clientSettings =
    CloudBillingSettings.newBuilder()
        .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
        .build();
CloudBillingClient client = CloudBillingClient.create(clientSettings);

// For the purpose of this codelab, we'll grant an IAM role to the reseller
// admin user, but this is not a requirement for a production integration.
Policy policy = client.getIamPolicy(billingAccount);

Binding binding = null;
int bindingIndex = 0;
String role = "roles/billing.user";
String member = "user:" + RESELLER_ADMIN_USER;
// getBindingsList() returns an ImmutableList and copying over to an ArrayList so it's mutable.
List<Binding> bindings = new ArrayList(policy.getBindingsList());
for (int index = 0; index < bindings.size(); index++) {
  Binding b = bindings.get(index);
  if (b.getRole().equals(role)) {
    binding = b;
    bindingIndex = index;
    break;
  }
}

// If the binding already exists, add the user to it, else add a new binding
Policy.Builder newPolicyBuilder = policy.toBuilder();
if (binding != null) {
  newPolicyBuilder.setBindings(bindingIndex, binding.toBuilder().addMembers(member).build());
} else {
  binding = Binding.newBuilder()
              .setRole(role)
              .addMembers(member)
              .build();
  newPolicyBuilder.addBindings(binding);
}

// Update the IAM policy
Policy newPolicy = newPolicyBuilder.build();
client.setIamPolicy(billingAccount, newPolicy);
System.out.println("=== Set IAM policy");
System.out.println(gson.toJson(newPolicy));

Node.js

// Get the name of the customer's billing account from the entitlement
const billingAccount = entitlement.provisionedService.provisioningId;

// Create a Cloud Billing API client
const {CloudBillingClient} = require('@google-cloud/billing');
const billingClient = new CloudBillingClient({keyFile: jsonKeyFile});

// For the purpose of this codelab, we'll grant an IAM role to the reseller
// admin user, but this is not a requirement for a production integration.
const [policy] = await billingClient.getIamPolicy({
  resource: billingAccount,
});

const role = 'roles/billing.user';
const member = `user:${resellerAdminUser}`;
const binding = policy.bindings.find(binding => {
  return binding.role === role;
});
// If the binding already exists, add the user to it, else add a new binding
if (binding) {
  binding.members.push(member);
} else {
  policy.bindings.push({
    role,
    members: [member],
  });
}

// Update the IAM policy
await billingClient.setIamPolicy({
  resource: billingAccount,
  policy,
});
console.log('=== Set IAM policy');
console.info(policy);

PHP

// Get the name of the customer's billing account from the entitlement
$billingAccount = $entitlement->getProvisionedService()->getProvisioningId();

// Create a Cloud Billing API client
$billingClient = new Google\Cloud\Billing\V1\CloudBillingClient([
  'credentials' => $JSON_KEY_FILE
]);

// For the purpose of this codelab, we'll grant an IAM role to the reseller
// admin user, but this is not a requirement for a production integration.
$policy = $billingClient->getIamPolicy($billingAccount /* $resource */);

$role = 'roles/billing.user';
$member = 'user:' . $RESELLER_ADMIN_USER;
$binding = null;
foreach($policy->getBindings() as $b) {
  if ($b->getRole() == $role) {
    $binding = $b;
    break;
  }
}
// If the binding already exists, add the user to it, else add a new binding
if ($binding) {
  $binding->getMembers()[] = $member;
} else {
  $binding = new \Google\Cloud\Iam\V1\Binding([
    'role' => $role,
    'members' => [ $member ],
  ]);
  $policy->getBindings()[] = $binding;
}

// Update the IAM policy
$billingClient->setIamPolicy(
  $billingAccount /* $resource */,
  $policy
);
print '=== Set IAM policy' . PHP_EOL;
print $policy->serializeToJsonString() . PHP_EOL;

Python

# Get the name of the customer's billing account from the entitlement
billing_account = entitlement.provisioned_service.provisioning_id

# Create a Cloud Billing API client
billing_credentials = service_account.Credentials.from_service_account_file(
    JSON_KEY_FILE)
billing_client = billing.CloudBillingClient(credentials=billing_credentials)

# For the purpose of this codelab, we'll grant an IAM role to the reseller
# admin user, but this is not a requirement for a production integration.
policy = billing_client.get_iam_policy(resource=billing_account)

role = "roles/billing.user"
member = "user:" + RESELLER_ADMIN_USER
binding = None
for b in policy.bindings:
  if b.role == role:
    binding = b
    break
# If the binding already exists, add the user to it, else add a new binding
if binding is not None:
  binding.members.append(member)
else:
  binding = policy_pb2.Binding(role=role, members=[member])
  policy.bindings.append(binding)

# Update the IAM policy
billing_client.set_iam_policy(request={
    "resource": billing_account,
    "policy": policy
})
print("=== Set IAM policy")
print(policy)

Ruby

# Get the name of the customer's billing account from the entitlement
billing_account = entitlement.provisioned_service.provisioning_id

# Create a Cloud Billing API client
billing_client = Google::Cloud::Billing::cloud_billing_service do |config|
  config.credentials = JSON_PRIVATE_KEY_FILE
end

# For the purpose of this codelab, we'll grant an IAM role to the reseller
# admin user, but this is not a requirement for a production integration.
iam_request = Google::Iam::V1::GetIamPolicyRequest.new do |config|
  config.billing_account = billing_account
end

policy = billing_client.get_iam_policy({ resource: billing_account })
role = 'roles/billing.user'
member = "user:#{RESELLER_ADMIN_USER}"
binding = policy.bindings.detect { |b| b.role == role }

# If the binding already exists, add the user to it, else add a new binding
if (binding != nil)
  binding.members.add(member)
else
  binding = Google::Iam::V1::Binding.new
  binding.role = role
  binding.members.push(member)
  policy.bindings.push(binding)
end

request = Google::Iam::V1::SetIamPolicyRequest.new do |config|
  config.policy = policy
end

# Update the IAM policy
billing_client.set_iam_policy({ resource: billing_account, policy: policy })

puts('=== Set IAM policy')
puts(policy.inspect)

요약 정리

다음은 Google Cloud(이전 Google Cloud Platform) 사용 권한이 있는 고객을 프로비저닝하는 전체 코드 예시입니다.

C#

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Instructions for this codelab can be found on this page
// https://cloud.google.com/channel/docs/codelabs/gcp/provisioning

using Google.Apis.Auth.OAuth2;
using Google.Api.Gax;
using Google.Cloud.Billing.V1; // optional
using Google.Cloud.Channel.V1;
using Google.Cloud.Iam.V1; // optional
using Google.Type;
using Newtonsoft.Json;
using System;
using System.Linq;

namespace Codelab {

  class Program {

    /***************** REPLACE WITH YOUR OWN VALUES ********************************/
    private static readonly string jsonKeyFile = "path/to/json_key_file.json";
    private static readonly string resellerAdminUser = "admin@yourresellerdomain.com";
    private static readonly string accountId = "C012345";
    private static readonly string customerDomain = "example.com";
    /*******************************************************************************/

    private static readonly string accountName = "accounts/" + accountId;

    private static CloudChannelServiceClient client;

    static void Main(string[] args) {
      // Set up credentials with user impersonation
      ICredential credential = GoogleCredential.FromFile(jsonKeyFile)
                                   .CreateScoped(CloudChannelServiceClient.DefaultScopes)
                                   .CreateWithUser(resellerAdminUser);

      // Create the API client
      client = new CloudChannelServiceClientBuilder {
        TokenAccessMethod = credential.GetAccessTokenForRequestAsync
      }.Build();

      Offer selectedOffer = SelectOffer();

      Customer customer = CreateCustomer();

      Entitlement entitlement = CreateEntitlement(customer, selectedOffer);

      SetIamPolicy(entitlement);
    }

    static Offer SelectOffer() {
      PagedEnumerable<ListOffersResponse, Offer> offers =
          client.ListOffers(new ListOffersRequest { Parent = accountName });

      // For the purpose of this codelab, the code lists all offers and selects
      // the first offer for Google Cloud Platform.
      // This is needed because offerIds vary from one account to another,
      // but this is not a recommended model for your production integration
      Offer selectedOffer = offers.FirstOrDefault(
        o => o.Sku.MarketingInfo.DisplayName == "Google Cloud Platform");

      Console.WriteLine("=== Selected offer");
      Console.WriteLine(JsonConvert.SerializeObject(selectedOffer));

      return selectedOffer;
    }

    static Customer CreateCustomer() {
      // Create the Customer resource
      CreateCustomerRequest request = new CreateCustomerRequest {
        Parent = accountName,
        Customer = new Customer { OrgDisplayName = "Acme Corp",
                                  OrgPostalAddress = new PostalAddress {
                                        AddressLines = { "1800 Amphibious Blvd" },
                                        PostalCode = "94045",
                                        RegionCode = "US" },
                                  Domain = customerDomain,
                                  // Optional. Add the CRM ID for this customer.
                                  CorrelationId = "CRMID012345" }
      };

      Customer customer = client.CreateCustomer(request);
      Console.WriteLine("=== Created customer with id " + customer.Name);
      Console.WriteLine(JsonConvert.SerializeObject(customer));

      return customer;
    }

    static Entitlement CreateEntitlement(Customer customer, Offer selectedOffer) {
      // This display name shows on the Google Cloud console when a customer
      // links the account to their project.
      // Recommended format: "[Reseller name] - [Customer name]"
      string displayName = "Reseller XYZ - Acme corp";

      CreateEntitlementRequest request = new CreateEntitlementRequest {
        Parent = customer.Name,
        Entitlement = new Entitlement {
            Offer = selectedOffer.Name,
            Parameters = { new Parameter { Name = "display_name",
                                           Value = new Value {
                                                StringValue = displayName } } },
            // A string of up to 80 characters.
            // We recommend using an internal transaction ID or
            // identifier for the customer in this field.
            PurchaseOrderId = "A codelab test"
          }
      };

      // This call returns a long-running operation.
      var operation = client.CreateEntitlement(request);

      // Wait for the long-running operation and get the result.
      Entitlement entitlement = operation.PollUntilCompleted().Result;
      Console.WriteLine("=== Created entitlement");
      Console.WriteLine(JsonConvert.SerializeObject(entitlement));

      return entitlement;
    }

    static void SetIamPolicy(Entitlement entitlement) {
      // Get the name of the customer's billing account from the entitlement
      String billingAccount = entitlement.ProvisionedService.ProvisioningId;

      // Create a Cloud Billing API client
      CloudBillingClient billingClient = new CloudBillingClientBuilder {
        CredentialsPath = jsonKeyFile
      }.Build();

      // For the purpose of this codelab, we'll grant an IAM role to the reseller
      // admin user, but this is not a requirement for a production integration.
      Policy policy = billingClient.GetIamPolicy(billingAccount);

      string role = "roles/billing.user";
      string member = "user:" + resellerAdminUser;
      var binding = policy.Bindings.FirstOrDefault(b => b.Role == role);

      // If the binding already exists, add the user to it, else add a new binding
      if (binding != null) {
        binding.Members.Add(member);
      } else {
        binding = new Binding { Role = role };
        binding.Members.Add(member);
        policy.Bindings.Add(binding);
      }

      // Update the IAM policy
      SetIamPolicyRequest request = new SetIamPolicyRequest {
        Resource = billingAccount,
        Policy = policy
      };
      billingClient.SetIamPolicy(request);

      Console.WriteLine("=== Set IAM policy");
      Console.WriteLine(JsonConvert.SerializeObject(policy));
    }
  }
}

Go

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Instructions for this codelab can be found on this page
// https://cloud.google.com/channel/docs/codelabs/gcp/provisioning

package main

import (
	"context"
	"fmt"
	"io/ioutil"

	billing "cloud.google.com/go/billing/apiv1" // optional
	channel "cloud.google.com/go/channel/apiv1"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
	channelpb "google.golang.org/genproto/googleapis/cloud/channel/v1"
	iampb "google.golang.org/genproto/googleapis/iam/v1" // optional
	"google.golang.org/genproto/googleapis/type/postaladdress"
	"google.golang.org/protobuf/encoding/protojson"
)

// ############## REPLACE WITH YOUR OWN VALUES ####################
const jsonKeyFile = "path/to/json_key_file.json"
const resellerAdminUser = "admin@yourresellerdomain.com"
const accountID = "C012345"
const customerDomain = "example.com"

// ################################################################

const accountName = "accounts/" + accountID

func main() {
	ctx := context.Background()

	// set up credentials with user impersonation
	jsonKey, _ := ioutil.ReadFile(jsonKeyFile)
	jwt, _ := google.JWTConfigFromJSON(jsonKey, "https://www.googleapis.com/auth/apps.order")
	jwt.Subject = resellerAdminUser
	tokenSource := jwt.TokenSource(ctx)
	// create the API client
	client, _ := channel.NewCloudChannelClient(ctx, option.WithTokenSource(tokenSource))

	selectedOffer := selectOffer(ctx, client)

	customer := createCustomer(ctx, client)

	entitlement := createEntitlement(ctx, client, customer, selectedOffer)

	setIamPolicy(ctx, entitlement)
}

func selectOffer(ctx context.Context, client *channel.CloudChannelClient) *channelpb.Offer {
	var selectedOffer *channelpb.Offer
	req := &channelpb.ListOffersRequest{
		Parent: accountName,
	}
	it := client.ListOffers(ctx, req)

	// For the purpose of this codelab, the code lists all offers and selects
	// the first offer for Google Cloud Platform.
	// This is needed because offerIds vary from one account to another and
	// is not a recommended model for your production integration.
	for {
		offer, err := it.Next()
		if err == iterator.Done {
			break
		}
		if offer.Sku.MarketingInfo.DisplayName == "Google Cloud Platform" {
			selectedOffer = offer
			break
		}
	}

	fmt.Println("=== Selected offer")
	fmt.Println(protojson.Format(selectedOffer))

	return selectedOffer
}

func createCustomer(ctx context.Context, client *channel.CloudChannelClient) *channelpb.Customer {
	// Create the Customer resource
	req := &channelpb.CreateCustomerRequest{
		Parent: accountName,
		Customer: &channelpb.Customer{
			OrgDisplayName: "Acme Corp",
			OrgPostalAddress: &postaladdress.PostalAddress{
				AddressLines: []string{"1800 Amphibious Blvd"},
				PostalCode:   "94045",
				RegionCode:   "US",
			},
			Domain: customerDomain,
			// Optional. Add the CRM ID for this customer.
			CorrelationId: "CRMID012345",
			// Distributors need to pass the following value
			// ChannelPartnerId: channelPartnerLinkId
		},
	}
	customer, _ := client.CreateCustomer(ctx, req)
	fmt.Println("=== Created customer with id " + customer.Name)
	fmt.Println(protojson.Format(customer))

	return customer
}

func createEntitlement(ctx context.Context, client *channel.CloudChannelClient,
	customer *channelpb.Customer, selectedOffer *channelpb.Offer) *channelpb.Entitlement {
	// This display name shows on the Google Cloud console when a customer
	// links the account to their project.
	// Recommended format: "[Reseller name] - [Customer name]"
	const displayName = "Reseller XYZ - Acme corp"

	// This endpoint returns a long-running operation.
	req := &channelpb.CreateEntitlementRequest{
		Parent: customer.Name,
		Entitlement: &channelpb.Entitlement{
			Offer: selectedOffer.Name,
			Parameters: []*channelpb.Parameter{
				{
					Name: "display_name",
					Value: &channelpb.Value{
						Kind: &channelpb.Value_StringValue{StringValue: displayName},
					},
				},
			},
			// A string of up to 80 characters.
			// We recommend using an internal transaction ID or
			// identifier for the customer in this field.
			PurchaseOrderId: "A codelab test",
		},
	}
	// This endpoint returns a long-running operation.
	op, _ := client.CreateEntitlement(ctx, req)

	// Wait for the long-running operation and get the result.
	entitlement, _ := op.Wait(ctx)

	fmt.Println("=== Created entitlement")
	fmt.Println(protojson.Format(entitlement))

	return entitlement
}

func setIamPolicy(ctx context.Context, entitlement *channelpb.Entitlement) {
	// Get the name of the customer's billing account from the entitlement
	billingAccount := entitlement.ProvisionedService.ProvisioningId

	// Create a Cloud Billing API client
	client, _ := billing.NewCloudBillingClient(ctx, option.WithCredentialsFile(jsonKeyFile))

	// For the purpose of this codelab, we'll grant an IAM role to the reseller
	// admin user, but this is not a requirement for a production integration.
	getPolicyReq := &iampb.GetIamPolicyRequest{Resource: billingAccount}
	policy, _ := client.GetIamPolicy(ctx, getPolicyReq)

	var binding *iampb.Binding
	const role = "roles/billing.user"
	const member = "user:" + resellerAdminUser
	for _, b := range policy.Bindings {
		if b.Role == role {
			binding = b
			break
		}
	}
	// If the binding already exists, add the user to it, else add a new binding
	if binding != nil {
		binding.Members = append(binding.Members, member)
	} else {
		binding = &iampb.Binding{
			Role:    role,
			Members: []string{member},
		}
		policy.Bindings = append(policy.Bindings, binding)
	}

	// Update the IAM policy
	setPolicyReq := &iampb.SetIamPolicyRequest{
		Resource: billingAccount,
		Policy:   policy,
	}
	client.SetIamPolicy(ctx, setPolicyReq)
	fmt.Println("=== Set IAM policy")
	fmt.Println(protojson.Format(policy))
}

Java

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Instructions for this codelab can be found on this page
// https://cloud.google.com/channel/docs/codelabs/gcp/provisioning

import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.channel.v1.CloudChannelServiceClient;
import com.google.cloud.channel.v1.CloudChannelServiceSettings;
import com.google.cloud.channel.v1.CreateCustomerRequest;
import com.google.cloud.channel.v1.CreateEntitlementRequest;
import com.google.cloud.channel.v1.Customer;
import com.google.cloud.channel.v1.Entitlement;
import com.google.cloud.channel.v1.ListOffersRequest;
import com.google.cloud.channel.v1.Offer;
import com.google.cloud.channel.v1.OperationMetadata;
import com.google.cloud.channel.v1.Parameter;
import com.google.cloud.channel.v1.Value;
import com.google.gson.Gson;
import com.google.type.PostalAddress;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;

// optional
import java.util.ArrayList;
import java.util.List;
import com.google.cloud.billing.v1.CloudBillingClient;
import com.google.cloud.billing.v1.CloudBillingSettings;
import com.google.iam.v1.Binding;
import com.google.iam.v1.Policy;

/**
* This is a basic example of provisioning a GCP customer.
*/

public class Codelab {

  /***************** REPLACE WITH YOUR OWN VALUES ********************************/
  public static final String JSON_KEY_FILE = "path/to/json_key_file.json";
  public static final String RESELLER_ADMIN_USER = "admin@yourresellerdomain.com";
  public static final String ACCOUNT_ID = "C012345";
  public static final String CUSTOMER_DOMAIN = "example.com";
  /*******************************************************************************/

  public static final String ACCOUNT_NAME = "accounts/" + ACCOUNT_ID;

  private static CloudChannelServiceClient client;
  private static final Gson gson = new Gson();

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException {

    // Set up credentials with user impersonation
    FileInputStream jsonKeyFileSteam = new FileInputStream(JSON_KEY_FILE);
    GoogleCredentials credentials = ServiceAccountCredentials.fromStream(jsonKeyFileSteam)
                                        .createScoped("https://www.googleapis.com/auth/apps.order")
                                        .createDelegated(RESELLER_ADMIN_USER);

    CloudChannelServiceSettings clientSettings =
        CloudChannelServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
            .build();

    // Create the API client
    client = CloudChannelServiceClient.create(clientSettings);

    Offer selectedOffer = selectOffer();

    Customer customer = createCustomer();

    Entitlement entitlement = createEntitlement(customer, selectedOffer);

    setIamPolicy(entitlement);
  }

  private static Offer selectOffer() {
    ListOffersRequest request =
        ListOffersRequest.newBuilder().setParent(ACCOUNT_NAME).build();

    // For the purpose of this codelab, the code lists all offers and selects
    // the first offer for Google Cloud Platform.
    // This is needed because offerIds vary from one account to another,
    // but this is not a recommended model for your production integration
    CloudChannelServiceClient.ListOffersPagedResponse response = client.listOffers(request);
    Offer selectedOffer = Offer.newBuilder().build();
    Iterator<Offer> iterator = response.iterateAll().iterator();
    while (iterator.hasNext()) {
      Offer offer = iterator.next();
      String skuName = offer.getSku().getMarketingInfo().getDisplayName();
      if (skuName.equals("Google Cloud Platform")) {
        selectedOffer = offer;
        break;
      }
    }

    System.out.println("=== Selected offer");
    System.out.println(gson.toJson(selectedOffer));

    return selectedOffer;
  }

  private static Customer createCustomer() throws InterruptedException, ExecutionException {
    // Create the Customer resource
    PostalAddress postalAddress =
        PostalAddress.newBuilder()
            .addAddressLines("1800 Amphibious Blvd")
            .setPostalCode("94045")
            .setRegionCode("US")
            .build();

    CreateCustomerRequest request =
        CreateCustomerRequest.newBuilder()
            .setParent(ACCOUNT_NAME)
            .setCustomer(
                Customer.newBuilder()
                    .setOrgDisplayName("Acme Corp")
                    .setOrgPostalAddress(postalAddress)
                    .setDomain(CUSTOMER_DOMAIN)
                    // Optional. Add the CRM ID for this customer.
                    .setCorrelationId("CRMID012345")
                    // Distributors need to pass the following field
                    // .setChannelPartnerId(channelPartnerLinkId)
                    .build())
            .build();

    Customer customer = client.createCustomer(request);

    System.out.println("=== Created customer with id " + customer.getName());
    System.out.println(gson.toJson(customer));

    return customer;
  }

  private static Entitlement createEntitlement(Customer customer, Offer selectedOffer)
      throws InterruptedException, ExecutionException {
    // This display name shows on the Google Cloud console when a customer
    // links the account to their project.
    // Recommended format: "[Reseller name] - [Customer name]"
    String displayName = "Reseller XYZ - Acme corp";

    Entitlement entitlement =
        Entitlement.newBuilder()
            .setOffer(selectedOffer.getName())
            .addParameters(
                Parameter.newBuilder()
                    .setName("display_name")
                    .setValue(Value.newBuilder().setStringValue(displayName).build())
                    .build())
            // A string of up to 80 characters.
            // We recommend using an internal transaction ID or
            // identifier for the customer in this field.
            .setPurchaseOrderId("A codelab test")
            .build();

    CreateEntitlementRequest request =
        CreateEntitlementRequest.newBuilder()
            .setParent(customer.getName())
            .setEntitlement(entitlement)
            .build();

    // This call returns a long-running operation.
    OperationFuture<Entitlement, OperationMetadata> operation =
        client.createEntitlementAsync(request);

    // Wait for the long-running operation and get the result.
    entitlement = operation.get();
    System.out.println("=== Created entitlement");
    System.out.println(gson.toJson(entitlement));

    return entitlement;
  }

  private static void setIamPolicy(Entitlement entitlement) throws IOException {
    // Get the name of the customer's billing account from the entitlement
    String billingAccount = entitlement.getProvisionedService().getProvisioningId();

    // Create a Cloud Billing API client
    FileInputStream jsonKeyFileSteam = new FileInputStream(JSON_KEY_FILE);
    GoogleCredentials credentials = ServiceAccountCredentials.fromStream(jsonKeyFileSteam);
    CloudBillingSettings clientSettings =
        CloudBillingSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
            .build();
    CloudBillingClient client = CloudBillingClient.create(clientSettings);

    // For the purpose of this codelab, we'll grant an IAM role to the reseller
    // admin user, but this is not a requirement for a production integration.
    Policy policy = client.getIamPolicy(billingAccount);

    Binding binding = null;
    int bindingIndex = 0;
    String role = "roles/billing.user";
    String member = "user:" + RESELLER_ADMIN_USER;
    // getBindingsList() returns an ImmutableList and copying over to an ArrayList so it's mutable.
    List<Binding> bindings = new ArrayList(policy.getBindingsList());
    for (int index = 0; index < bindings.size(); index++) {
      Binding b = bindings.get(index);
      if (b.getRole().equals(role)) {
        binding = b;
        bindingIndex = index;
        break;
      }
    }

    // If the binding already exists, add the user to it, else add a new binding
    Policy.Builder newPolicyBuilder = policy.toBuilder();
    if (binding != null) {
      newPolicyBuilder.setBindings(bindingIndex, binding.toBuilder().addMembers(member).build());
    } else {
      binding = Binding.newBuilder()
                  .setRole(role)
                  .addMembers(member)
                  .build();
      newPolicyBuilder.addBindings(binding);
    }

    // Update the IAM policy
    Policy newPolicy = newPolicyBuilder.build();
    client.setIamPolicy(billingAccount, newPolicy);
    System.out.println("=== Set IAM policy");
    System.out.println(gson.toJson(newPolicy));
  }
}

Node.js

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Instructions for this codelab can be found on this page
// https://cloud.google.com/channel/docs/codelabs/gcp/provisioning

const {JWT} = require('google-auth-library');
const {grpc} = require('google-gax');
const {CloudChannelServiceClient} = require('@google-cloud/channel');

// ############## REPLACE WITH YOUR OWN VALUES ####################
const jsonKeyFile = 'path/to/json_key_file.json';
const resellerAdminUser = 'admin@yourresellerdomain.com';
const accountId = 'C012345';
const customerDomain = 'example.com';
// ################################################################
const accountName = `accounts/${accountId}`;

// Set up credentials with user impersonation
const authClient = new JWT({
  keyFile: jsonKeyFile,
  scopes: ['https://www.googleapis.com/auth/apps.order'],
  subject: resellerAdminUser,
});
const sslCreds = grpc.credentials.combineChannelCredentials(
  grpc.credentials.createSsl(),
  grpc.credentials.createFromGoogleCredential(authClient)
);
// Create the API client
const client = new CloudChannelServiceClient({sslCreds});

async function main() {
  const selectedOffer = await selectOffer();

  const customer = await createCustomer();

  const entitlement = await createEntitlement(customer, selectedOffer);

  await setIamPolicy(entitlement);
}

async function selectOffer() {
  const [offers] = await client.listOffers({
    parent: accountName,
  });

  // For the purpose of this codelab, the code lists all offers and selects
  // the first offer for the Google Cloud Platform.
  // This is needed because offerIds vary from one account to another,
  // but this is not a recommended model for your production integration
  const selectedOffer = offers.find(o => {
    return o.sku.marketingInfo.displayName === 'Google Cloud Platform';
  });

  console.log('=== Selected offer');
  console.info(selectedOffer);

  return selectedOffer;
}

async function createCustomer() {
  // Create the Customer resource
  const [customer] = await client.createCustomer({
    parent: accountName,
    customer: {
      orgDisplayName: 'Acme Corp',
      orgPostalAddress: {
        addressLines: ['1800 Amphibious Blvd'],
        postalCode: '94045',
        regionCode: 'US',
      },
      domain: customerDomain,
      // Optional. Add the CRM ID for this customer.
      correlationId: "CRMID012345",
      // Distributors need to pass the following field
      // channelPartnerId: channelPartnerLinkId
    },
  });
  console.log(`=== Created customer with id ${customer.name}`);
  console.info(customer);

  return customer;
}

async function createEntitlement(customer, selectedOffer) {
  // This display name shows on the Google Cloud console when a customer
  // links the account to their project.
  // Recommended format: "[Reseller name] - [Customer name]"
  const displayName = 'Reseller XYZ - Acme corp';

  // This call returns a long-running operation.
  const [operation] = await client.createEntitlement({
    parent: customer.name,
    entitlement: {
      offer: selectedOffer.name,
      parameters: [
        {
          name: 'display_name',
          value: {
            stringValue: displayName,
          },
        },
      ],
      // A string of up to 80 characters.
      // We recommend using an internal transaction ID or
      // identifier for the customer in this field.
      purchaseOrderId: 'A codelab test',
    },
  });

  // Wait for the long-running operation and get the result.
  const [entitlement] = await operation.promise();
  console.log('=== Created entitlement');
  console.info(entitlement);

  return entitlement;
}

async function setIamPolicy(entitlement) {
  // Get the name of the customer's billing account from the entitlement
  const billingAccount = entitlement.provisionedService.provisioningId;

  // Create a Cloud Billing API client
  const {CloudBillingClient} = require('@google-cloud/billing');
  const billingClient = new CloudBillingClient({keyFile: jsonKeyFile});

  // For the purpose of this codelab, we'll grant an IAM role to the reseller
  // admin user, but this is not a requirement for a production integration.
  const [policy] = await billingClient.getIamPolicy({
    resource: billingAccount,
  });

  const role = 'roles/billing.user';
  const member = `user:${resellerAdminUser}`;
  const binding = policy.bindings.find(binding => {
    return binding.role === role;
  });
  // If the binding already exists, add the user to it, else add a new binding
  if (binding) {
    binding.members.push(member);
  } else {
    policy.bindings.push({
      role,
      members: [member],
    });
  }

  // Update the IAM policy
  await billingClient.setIamPolicy({
    resource: billingAccount,
    policy,
  });
  console.log('=== Set IAM policy');
  console.info(policy);
}

main().catch(err => {
  console.error(err.message);
  process.exitCode = 1;
});
process.on('unhandledRejection', err => {
  console.error(err.message);
  process.exitCode = 1;
});

PHP

<?php
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Instructions for this codelab can be found on this page
// https://cloud.google.com/channel/docs/codelabs/gcp/provisioning

require 'vendor/autoload.php';

use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Cloud\Channel;

// ############## REPLACE WITH YOUR OWN VALUES ####################
$JSON_KEY_FILE = 'path/to/json_key_file.json';
$RESELLER_ADMIN_USER = 'admin@yourresellerdomain.com';
$ACCOUNT_ID = 'C012345';
$CUSTOMER_DOMAIN = 'example.com';
// ################################################################

$ACCOUNT_NAME = 'accounts/' . $ACCOUNT_ID;

// Set up credentials with user impersonation
$credentials = new ServiceAccountCredentials(
  'https://www.googleapis.com/auth/apps.order', /* $scope */
  $JSON_KEY_FILE, /* $keyFile */
  $RESELLER_ADMIN_USER /* $sub */
);

// Create the API client
$client = new Channel\V1\CloudChannelServiceClient([
  'credentials' => $credentials
]);

$selectedOffer = selectOffer();

$customer = createCustomer();

$entitlement = createEntitlement($customer, $selectedOffer);

setIamPolicy($entitlement);

function selectOffer() {
  global $client, $ACCOUNT_NAME;

  $offers = $client->listOffers($ACCOUNT_NAME /* parent */);

  // For the purpose of this codelab, the code lists all offers and selects
  // the first offer for the Google Cloud Platform.
  // This is needed because offerIds vary from one account to another,
  // but this is not a recommended model for your production integration
  $sampleSku = 'Google Cloud Platform';
  foreach ($offers as $offer) {
    if ($offer->getSku()->getMarketingInfo()->getDisplayName() == $sampleSku) {
      $selectedOffer = $offer;
      break;
    }
  }

  print '=== Selected offer' . PHP_EOL;
  print $selectedOffer->serializeToJsonString() . PHP_EOL;

  return $selectedOffer;
}

function createCustomer() {
  global $client, $ACCOUNT_NAME, $CUSTOMER_DOMAIN;

  // Create the Customer resource
  $customer = $client->createCustomer(
    $ACCOUNT_NAME  /* parent */,
    new Channel\V1\Customer([
      'org_display_name' => 'Acme Corp',
      'org_postal_address' => new Google\Type\PostalAddress([
        'address_lines' => ['1800 Amphibious Blvd'],
        'postal_code' => '94045',
        'region_code' => 'US',
      ]),
      'domain' => $CUSTOMER_DOMAIN,
      // Optional. Add the CRM ID for this customer.
      'correlation_id' => 'CRMID012345',
      // Distributors need to pass the following field
      // 'channel_partner_id' => $channelPartnerLinkId
    ])
  );

  print '=== Created customer with id ' . $customer->getName() . PHP_EOL;
  print $customer->serializeToJsonString() . PHP_EOL;

  return $customer;
}

function createEntitlement($customer, $selectedOffer) {
  global $client;

  // This display name shows on the Google Cloud console when a customer
  // links the account to their project.
  // Recommended format: "[Reseller name] - [Customer name]"
  $displayName = 'Reseller XYZ - Acme corp';

  // This call returns a long-running operation.
  $operation = $client->createEntitlement(
    $customer->getName() /* parent */,
    new Channel\V1\Entitlement([
      'offer' => $selectedOffer->getName(),
      'parameters' => [
        new Channel\V1\Parameter([
          'name' => 'display_name',
          'value' => new Channel\V1\Value([
            'string_value' => $displayName,
          ])
        ]),
      ],
      // A string of up to 80 characters.
      // We recommend using an internal transaction ID or
      // identifier for the customer in this field.
      'purchase_order_id' => 'A codelab test'
    ])
  );

  // Wait for the long-running operation and get the result.
  $operation->pollUntilComplete();
  $entitlement = $operation->getResult();
  print '=== Created entitlement' . PHP_EOL;
  print $entitlement->serializeToJsonString() . PHP_EOL;

  return $entitlement;
}

function setIamPolicy($entitlement) {
  global $JSON_KEY_FILE, $RESELLER_ADMIN_USER;

  // Get the name of the customer's billing account from the entitlement
  $billingAccount = $entitlement->getProvisionedService()->getProvisioningId();

  // Create a Cloud Billing API client
  $billingClient = new Google\Cloud\Billing\V1\CloudBillingClient([
    'credentials' => $JSON_KEY_FILE
  ]);

  // For the purpose of this codelab, we'll grant an IAM role to the reseller
  // admin user, but this is not a requirement for a production integration.
  $policy = $billingClient->getIamPolicy($billingAccount /* $resource */);

  $role = 'roles/billing.user';
  $member = 'user:' . $RESELLER_ADMIN_USER;
  $binding = null;
  foreach($policy->getBindings() as $b) {
    if ($b->getRole() == $role) {
      $binding = $b;
      break;
    }
  }
  // If the binding already exists, add the user to it, else add a new binding
  if ($binding) {
    $binding->getMembers()[] = $member;
  } else {
    $binding = new \Google\Cloud\Iam\V1\Binding([
      'role' => $role,
      'members' => [ $member ],
    ]);
    $policy->getBindings()[] = $binding;
  }

  // Update the IAM policy
  $billingClient->setIamPolicy(
    $billingAccount /* $resource */,
    $policy
  );
  print '=== Set IAM policy' . PHP_EOL;
  print $policy->serializeToJsonString() . PHP_EOL;
}

Python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""GCP Provisioning codelab.

Instructions for this codelab can be found on this page:
https://cloud.google.com/channel/docs/codelabs/gcp/provisioning
"""

from google.cloud import channel
from google.oauth2 import service_account
# optional
from google.cloud import billing
from google.iam.v1 import policy_pb2

############## REPLACE WITH YOUR OWN VALUES ####################
JSON_KEY_FILE = "path/to/json_key_file.json"
RESELLER_ADMIN_USER = "admin@yourresellerdomain.com"
ACCOUNT_ID = "C012345"
CUSTOMER_DOMAIN = "example.com"
################################################################

ACCOUNT_NAME = "accounts/" + ACCOUNT_ID

# Set up credentials with user impersonation
credentials = service_account.Credentials.from_service_account_file(
    JSON_KEY_FILE, scopes=["https://www.googleapis.com/auth/apps.order"])
credentials_delegated = credentials.with_subject(RESELLER_ADMIN_USER)

# Create the API client
client = channel.CloudChannelServiceClient(credentials=credentials_delegated)

def main():
  offer = select_offer()

  customer = create_customer()

  entitlement = create_entitlement(customer, offer)

  set_iam_policy(entitlement)

def select_offer():
  """Selects a GCP offer.

  Returns:
    A Channel API Offer for GCP
  """
  request = channel.ListOffersRequest(parent=ACCOUNT_NAME)
  offers = client.list_offers(request)

  # For the purpose of this codelab, the code lists all offers and selects
  # the first offer for Google Cloud Platform.
  # This is needed because offerIds vary from one account to another,
  # but this is not a recommended model for your production integration
  selected_offer = None
  for offer in offers:
    if offer.sku.marketing_info.display_name == "Google Cloud Platform":
      selected_offer = offer
      break

  print("=== Selected offer")
  print(selected_offer)

  return selected_offer

def create_customer():
  """Create the Customer resource, with a cloud identity.

  Returns:
    The created Channel API Customer
  """
  # Create the Customer resource
  request = channel.CreateCustomerRequest(
      parent=ACCOUNT_NAME,
      customer={
          "org_display_name": "Acme Corp",
          "domain": CUSTOMER_DOMAIN,
          "org_postal_address": {
              "address_lines": ["1800 Amphibious Blvd"],
              "postal_code": "94045",
              "region_code": "US"
          },
          # Optional. Add the CRM ID for this customer.
          "correlation_id": "CRMID012345"
      })
  # Distributors need to also pass the following field for the `customer`
  # "channel_partner_id": channel_partner_link_id

  customer = client.create_customer(request)

  print("=== Created customer with id ", customer.name)
  print(customer)

  return customer

def create_entitlement(customer, selected_offer):
  """Create the Entitlement.

  Args:
    customer: a Customer resource
    selected_offer: an Offer

  Returns:
    The created Channel API Entitlement
  """
  # This display name shows on the Google Cloud console when a customer
  # links the account to their project.
  # Recommended format: "[Reseller name] - [Customer name]"
  display_name = "Reseller XYZ - Acme corp"

  request = channel.CreateEntitlementRequest(
      parent=customer.name,
      entitlement={
          "offer": selected_offer.name,
          "parameters": [{
              "name": "display_name",
              "value": {
                  "string_value": display_name
              }
          }],
          # A string of up to 80 characters.
          # We recommend an internal transaction ID or
          # identifier for this customer in this field.
          "purchase_order_id": "A codelab test"
      })

  # This call returns a long-running operation.
  operation = client.create_entitlement(request)

  # Wait for the long-running operation and get the result.
  entitlement = operation.result()

  print("=== Created entitlement")
  print(entitlement)

  return entitlement

def set_iam_policy(entitlement):
  """Sets IAM Policy on the created entitlement.

  Args:
    entitlement: a GCP Entitlement
  """
  # Get the name of the customer's billing account from the entitlement
  billing_account = entitlement.provisioned_service.provisioning_id

  # Create a Cloud Billing API client
  billing_credentials = service_account.Credentials.from_service_account_file(
      JSON_KEY_FILE)
  billing_client = billing.CloudBillingClient(credentials=billing_credentials)

  # For the purpose of this codelab, we'll grant an IAM role to the reseller
  # admin user, but this is not a requirement for a production integration.
  policy = billing_client.get_iam_policy(resource=billing_account)

  role = "roles/billing.user"
  member = "user:" + RESELLER_ADMIN_USER
  binding = None
  for b in policy.bindings:
    if b.role == role:
      binding = b
      break
  # If the binding already exists, add the user to it, else add a new binding
  if binding is not None:
    binding.members.append(member)
  else:
    binding = policy_pb2.Binding(role=role, members=[member])
    policy.bindings.append(binding)

  # Update the IAM policy
  billing_client.set_iam_policy(request={
      "resource": billing_account,
      "policy": policy
  })
  print("=== Set IAM policy")
  print(policy)

if __name__ == "__main__":
  main()

Ruby

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# GCP Provisioning codelab.

# Instructions for this codelab can be found on this page:
# https://cloud.google.com/channel/docs/codelabs/gcp/provisioning

require 'google-cloud-channel'
require 'google-cloud-billing' # Optional
require 'google/iam/v1/policy_pb' # Optional

################## REPLACE WITH YOUR OWN VALUES ################################
JSON_PRIVATE_KEY_FILE = 'path/to/json_key_file.json'
RESELLER_ADMIN_USER = 'admin@yourresellerdomain.com'
ACCOUNT_ID = 'C012345'
CUSTOMER_DOMAIN = 'example.com'
################################################################################

ACCOUNT_NAME = "accounts/#{ACCOUNT_ID}"

# Set up credentials with user impersonation
credentials = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: File.open(JSON_PRIVATE_KEY_FILE),
  scope: 'https://www.googleapis.com/auth/apps.order')
credentials.sub = RESELLER_ADMIN_USER

# Create the API client
CLIENT = Google::Cloud::Channel::cloud_channel_service do |config|
  config.credentials = credentials
end

def main
  offer = select_offer
  customer = create_customer
  entitlement = create_entitlement(customer, offer)
  set_iam_policy(entitlement)
end

def select_offer
  # For the purpose of this codelab, the code lists all offers and selects
  # the first offer for Google Cloud Platform.
  # This is needed because offerIds vary from one account to another,
  # but this is not a recommended model for your production integration

  request = Google::Cloud::Channel::V1::ListOffersRequest.new({ parent: ACCOUNT_NAME })
  offers = CLIENT.list_offers(request)
  sample_offer = 'Google Cloud Platform'

  offer = offers.detect { |offer| offer.sku.marketing_info.display_name == sample_offer }
  puts('=== Selected offer')
  puts(offer.inspect)
  offer
end

def create_customer
  # Create the Customer resource
  request = Google::Cloud::Channel::V1::CreateCustomerRequest.new(
    parent: ACCOUNT_NAME,
    customer: {
      'org_display_name': 'Acme Corp',
      'domain': CUSTOMER_DOMAIN,
      'org_postal_address': {
        'address_lines': ['1800 Amphibious Blvd'],
        'postal_code': '94045',
        'region_code': 'US'
      },
      # Optional. Add the CRM ID for this customer.
      'correlation_id': 'CRMID012345'
    })
    # Distributors need to also pass the following field for the `customer`
    # "channel_partner_id": channel_partner_link_id

  customer = CLIENT.create_customer(request)

  puts("=== Created customer with id " + customer.name)
  puts(customer.inspect)
  customer
end

def create_entitlement(customer, selected_offer)
  # This display name shows on the Google Cloud console when a customer
  # links the account to their project.
  # Recommended format: "[Reseller name] - [Customer name]"
  display_name = 'Reseller XYZ - Acme Corp'

  request = Google::Cloud::Channel::V1::CreateEntitlementRequest.new(
    parent: customer.name,
    entitlement: {
      offer: selected_offer.name,
      # Setting 5 seats for this Annual offer
      parameters: [{
                     name: 'display_name',
                     value: {
                       string_value: display_name
                     }
                   }],
      # A string of up to 80 characters.
      # We recommend an internal transaction ID or
      # identifier for this customer in this field.
      purchase_order_id: 'A codelab test'
    })

  # This call returns a long-running operation.
  operation = CLIENT.create_entitlement(request)

  # Wait for the long-running operation and get the result.
  CLIENT.operations_client.wait_operation(Google::Longrunning::WaitOperationRequest
                                          .new({ name: operation.name }))
  operation = CLIENT.operations_client.get_operation(Google::Longrunning::GetOperationRequest
                                                     .new({ name: operation.name }))
  entitlement = operation.response

  puts("=== Created entitlement")
  puts(entitlement)

  entitlement
end

def set_iam_policy(entitlement)
  # Get the name of the customer's billing account from the entitlement
  billing_account = entitlement.provisioned_service.provisioning_id

  # Create a Cloud Billing API client
  billing_client = Google::Cloud::Billing::cloud_billing_service do |config|
    config.credentials = JSON_PRIVATE_KEY_FILE
  end

  # For the purpose of this codelab, we'll grant an IAM role to the reseller
  # admin user, but this is not a requirement for a production integration.
  iam_request = Google::Iam::V1::GetIamPolicyRequest.new do |config|
    config.billing_account = billing_account
  end

  policy = billing_client.get_iam_policy({ resource: billing_account })
  role = 'roles/billing.user'
  member = "user:#{RESELLER_ADMIN_USER}"
  binding = policy.bindings.detect { |b| b.role == role }

  # If the binding already exists, add the user to it, else add a new binding
  if (binding != nil)
    binding.members.add(member)
  else
    binding = Google::Iam::V1::Binding.new
    binding.role = role
    binding.members.push(member)
    policy.bindings.push(binding)
  end

  request = Google::Iam::V1::SetIamPolicyRequest.new do |config|
    config.policy = policy
  end

  # Update the IAM policy
  billing_client.set_iam_policy({ resource: billing_account, policy: policy })

  puts('=== Set IAM policy')
  puts(policy.inspect)
end

main()