Atelier de programmation: Provisionner les droits d'accès Google Cloud

Avant de commencer

Présentation

Le provisionnement d'un client disposant de droits d'accès Google Cloud implique plusieurs appels d'API. Le schéma suivant présente les grandes étapes à suivre pour provisionner votre client.

Procédure pour provisionner les droits d'accès Google Cloud via l'API Cloud Channel

Étape 1: Créez des objets de service avec des identifiants authentifiés

Utiliser une bibliothèque cliente

Cet atelier de programmation suppose que vous utilisez l'une des bibliothèques clientes de l'API Cloud Channel.

Nous vous recommandons d'utiliser une bibliothèque cliente Google pour votre intégration. Ces bibliothèques fournissent une interface propre au langage naturel, offrent de meilleures performances en utilisant RPC au lieu de HTTP, et définissent des valeurs par défaut pour les champs.

Pour installer la bibliothèque:

C++

Pour installer la bibliothèque cliente C++, consultez la section Configurer un environnement de développement C++.

C#

Si vous utilisez Visual Studio 2017 ou une version ultérieure, ouvrez la fenêtre du gestionnaire de packages nuget et saisissez la commande suivante:

Install-Package Google.Cloud.Channel.V1

Si vous utilisez les outils d'interface de ligne de commande .NET Core pour installer vos dépendances, exécutez la commande suivante:

dotnet add package Google.Cloud.Channel.V1

Go

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

Java

Si vous utilisez Maven, ajoutez ce qui suit à votre fichier pom.xml. Pour en savoir plus sur la nomenclature, consultez la nomenclature des bibliothèques Google Cloud Platform.

<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>

Si vous utilisez Gradle, ajoutez les éléments suivants à vos dépendances:

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

Si vous utilisez VS Code, IntelliJ ou Eclipse, vous pouvez ajouter des bibliothèques clientes à votre projet à l'aide des plug-ins IDE suivants:

Les plug-ins offrent des fonctionnalités supplémentaires, telles que la gestion des clés pour les comptes de service. Reportez-vous à la documentation de chaque plug-in pour plus de détails.

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

Si vous choisissez de ne pas utiliser de bibliothèque cliente, nous vous recommandons de rechercher une bibliothèque cliente plus petite pour gérer l'authentification. Nous vous déconseillons de réécrire la couche d'authentification à partir de zéro.

Configurer des identifiants pour l'authentification

L'API Cloud Channel utilise un type d'authentification qui requiert:

  1. Un compte de service et son fichier de clé JSON correspondant
  2. Un super-administrateur de domaine de revendeur doit emprunter l'identité de la délégation au niveau du domaine sur le client du compte de service.

Suivez l'atelier de programmation de configuration de l'API si vous ne remplissez pas les conditions préalables. Pour en savoir plus, consultez la page OAuth 2.0 pour les comptes de service.

Dans le code ci-dessous, renseignez ces variables à l'aide de vos informations:

  • jsonKeyFile : chemin d'accès au fichier de clé JSON généré lors de la création d'un compte de service.
  • resellerAdminUser : adresse e-mail d'un super-administrateur de domaine de revendeur.
  • accountId : Votre ID de compte, que vous trouverez sur la page "Paramètres" de la Partner Sales Console.
  • customerDomain : domaine du client final.

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

Étape 2: Sélectionnez une offre dans votre liste d'offres

L'exemple de code ci-dessous sélectionne la première offre pour Google Cloud Platform. Si vous souhaitez sélectionner l'offre pour un ID de compte de facturation Cloud spécifique (sous la forme billingAccounts/...), vous pouvez filtrer les offres avec leur champ 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)

Étape 3: Créer un client

Avant de pouvoir créer des droits d'accès, vous devez créer un client Channel Services pour votre client indirect.

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)

Étape 4: Créez le droit d'accès

Après avoir créé le client, vous pouvez provisionner le droit d'accès.

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)

Facultatif: Mettre à jour la stratégie IAM sur le compte de facturation du client

Créer le droit d'accès provisionne un compte de facturation client. Vous pouvez accorder à votre client un accès explicite à ce compte.

Cette opération peut être effectuée de manière automatisée via l'API Cloud Billing. Pour ce faire :

  • Accédez à la bibliothèque d'API dans la console Google Cloud et activez l'API Cloud Billing.
  • Attribuez à votre compte de service le rôle IAM Cloud Billing sur votre compte de facturation parent. L'attribution d'un administrateur de compte de facturation (roles/billing.admin) est une bonne option pour cet atelier de programmation, mais vous pouvez utiliser des droits plus précis dans votre intégration de production. Pour en savoir plus sur les autorisations d'accès, consultez la page Contrôle des accès à l'API Cloud Billing.

Commencez par installer la bibliothèque:

C#

Si vous utilisez Visual Studio 2017 ou une version ultérieure, ouvrez la fenêtre du gestionnaire de packages nuget et saisissez la commande suivante:

Install-Package Google.Cloud.Billing.V1

Si vous utilisez les outils d'interface de ligne de commande .NET Core pour installer vos dépendances, exécutez la commande suivante:

dotnet add package Google.Cloud.Billing.v1

Go

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

Java

Reportez-vous au dépôt Maven pour obtenir des instructions d'installation. Le code suivant a été testé avec la version 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

Vous pouvez ensuite instancier le client et mettre à jour la stratégie 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)

Synthèse

Exemple de code complet permettant de provisionner un client disposant d'un droit d'accès Google Cloud (anciennement 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()