Codelab:预配 Google Cloud 使用权

准备工作

概览

为客户预配 Google Cloud 使用权涉及多个 API 调用。下图简要展示了为客户预配服务的步骤。

通过 Cloud Channel API 预配 Google Cloud 使用权的步骤

第 1 步:使用经过身份验证的凭据创建服务对象

使用客户端库

本 Codelab 假定您使用的是 Cloud Channel API 的某个客户端库。

我们建议您使用 Google 客户端库进行集成。这些库提供符合语言习惯且特定于语言的接口,通过使用 RPC 而非 HTTP 提供更出色的性能,并为字段设置默认值。

如需安装该库,请执行以下操作:

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:您的账号 ID,可在 Partner Sales Console 的“设置”页面中找到。
  • 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()