Cloud Run でのエンドユーザー認証のチュートリアル

このチュートリアルでは、以下で構成される投票サービスの作成方法について説明します。

  • ブラウザベースのクライアント。次の処理を行います。

    1. Identity Platform を使用して ID トークンを取得する。
    2. ユーザーがお気に入りの動物に投票する。
    3. 取得した ID トークンをリクエストに付加して、投票を処理する Cloud Run サーバーに送信する。
  • Cloud Run サーバー。次の処理を行います。

    1. 有効な ID トークンが提供され、エンドユーザーが正しく認証されていることを確認する。
    2. エンドユーザーの投票を処理する。
    3. 独自の認証情報を使用して、投票結果を Cloud SQL に送信して保管する。
  • 投票結果を保存する PostgreSQL データベース。

説明をわかりやすくするため、このチュートリアルでは Google をプロバイダとして使用します。ID トークンを取得するには、ユーザーは Google アカウントを使用して認証を行う必要があります。ただし、ユーザーのログインに他のプロバイダや認証方法を使用することもできます。

このサービスは、Secret Manager を使用して Cloud SQL インスタンスへの接続で使用されるセンシティブ データを保護し、セキュリティ リスクを最小限に抑えます。また、最小権限のサービス ID を使用して、データベースへのアクセスを保護します。

目標

次のことを行うサービスを作成、ビルドし、Cloud Run にデプロイします。

  • Identity Platform を使用し、Cloud Run サービスのバックエンドに対してエンドユーザーを認証します。

  • サービス用に最小権限の ID を作成して、Google Cloud リソースに対する最小のアクセス権を付与します。

  • Cloud Run サービスを PostgreSQL データベースに接続するときに Secret Manager を使用してセンシティブ データを扱います。

費用

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

始める前に

  1. Google Cloud アカウントにログインします。Google Cloud を初めて使用する場合は、アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  4. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  5. Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  6. Cloud Run, Secret Manager, Cloud SQL, Container Registry, and Cloud Build API を有効にします。

    API を有効にする

  7. Cloud SDK をインストールして初期化します。

gcloud のデフォルトを設定する

Cloud Run サービスを gcloud のデフォルトに構成するには:

  1. デフォルト プロジェクトを設定します。

    gcloud config set project PROJECT_ID

    PROJECT_ID は、このチュートリアルで作成したプロジェクトの名前に置き換えます。

  2. 選択したリージョン向けに gcloud を構成します。

    gcloud config set run/region REGION

    REGION は、任意のサポートされている Cloud Run のリージョンに置き換えます。

Cloud Run のロケーション

Cloud Run はリージョナルです。つまり、Cloud Run サービスを実行するインフラストラクチャは特定のリージョンに配置され、そのリージョン内のすべてのゾーンで冗長的に利用できるように Google によって管理されます。

レイテンシ、可用性、耐久性の要件を満たしていることが、Cloud Run サービスを実行するリージョンを選択する際の主な判断材料になります。一般的には、ユーザーに最も近いリージョンを選択できますが、Cloud Run サービスで使用されている他の Google Cloud サービスのロケーションも考慮する必要があります。使用する Google Cloud サービスが複数のロケーションにまたがっていると、サービスの料金だけでなくレイテンシにも影響します。

Cloud Run は、次のリージョンで利用できます。

ティア 1 料金を適用

  • asia-east1(台湾)
  • asia-northeast1(東京)
  • asia-northeast2(大阪)
  • europe-north1(フィンランド) リーフアイコン 低 CO2
  • europe-west1(ベルギー) リーフアイコン 低 CO2
  • europe-west4(オランダ)
  • us-central1(アイオワ) リーフアイコン 低 CO2
  • us-east1(サウスカロライナ)
  • us-east4(北バージニア)
  • us-west1(オレゴン) リーフアイコン 低 CO2

ティア 2 料金を適用

  • asia-east2(香港)
  • asia-northeast3(韓国、ソウル)
  • asia-southeast1(シンガポール)
  • asia-southeast2 (ジャカルタ)
  • asia-south1(インド、ムンバイ)
  • asia-south2(インド、デリー)
  • australia-southeast1(シドニー)
  • australia-southeast2(メルボルン)
  • europe-central2(ポーランド、ワルシャワ)
  • europe-west2(イギリス、ロンドン)
  • europe-west3(ドイツ、フランクフルト)
  • europe-west6(スイス、チューリッヒ) リーフアイコン 低 CO2
  • northamerica-northeast1(モントリオール) リーフアイコン 低 CO2
  • northamerica-northeast2(トロント) リーフアイコン 低 CO2
  • southamerica-east1(ブラジル、サンパウロ) リーフアイコン 低 CO2
  • southamerica-west1(チリ、サンティアゴ)
  • us-west2(ロサンゼルス)
  • us-west3(ソルトレイクシティ)
  • us-west4(ラスベガス)

Cloud Run サービスをすでに作成している場合は、Cloud Console の Cloud Run ダッシュボードにリージョンが表示されます。

サンプルコードを取得する

使用するサンプルコードを取得するには:

  1. ローカルマシンにサンプルアプリのリポジトリのクローンを作成します。

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

  2. Cloud Run のサンプルコードが含まれているディレクトリに移動します。

    Node.js

    cd nodejs-docs-samples/run/idp-sql/

    Python

    cd python-docs-samples/run/idp-sql/

    Java

    cd java-docs-samples/run/idp-sql/

アーキテクチャを可視化する

アーキテクチャ図
上の図は、エンドユーザーが IdP によって表示された Google ログイン ポップアップを使用してログインした後、ユーザーの ID とともに再度 Cloud Run へリダイレクトされる様子を示しています。
  1. エンドユーザーが、Cloud Run サーバーに最初のリクエストを行います。

  2. クライアントがブラウザに読み込まれます。

  3. ユーザーが、Identity Platform による Google ログイン ポップアップ ウィンドウを使用してログイン認証情報を提供します。ログインしたユーザーにアラートが表示されます。

  4. 制御がサーバーにリダイレクトされます。エンドユーザーがクライアントを使用して投票します。クライアントは、Identity Platform から ID トークンを取得して、投票リクエスト ヘッダーに追加します。

  5. サーバーは、リクエストを受信すると、Identity Platform ID トークンを検証してエンドユーザーが適切に認証されていることを確認します。次に、サーバーは、独自の認証情報を使用して Cloud SQL に投票を送信します。

コアコードについて

このサンプルは、以下のようにクライアントとサーバーとして実装されます。

Identity Platform との統合: クライアント側のコード

このサンプルでは、Firebase SDK を使用して Identity Platform と統合し、ユーザーのログインと管理を行います。Identity Platform に接続するため、クライアント側の JavaScript はプロジェクトの認証情報への参照を構成オブジェクトとして保持し、必要な Firebase JavaScript SDK をインポートします。

const config = {
  apiKey: 'API_KEY',
  authDomain: 'PROJECT_ID.firebaseapp.com',
};
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first-->
<script src="https://www.gstatic.com/firebasejs/7.18/firebase-app.js"></script>
<!-- Add Firebase Auth service-->
<script src="https://www.gstatic.com/firebasejs/7.18/firebase-auth.js"></script>

Firebase JavaScript SDK は、ポップアップ ウィンドウを表示してエンドユーザーに Google アカウントへのログインを求めることにより、ログインフローを処理します。次に、サービスにリダイレクトします。

function signIn() {
  const provider = new firebase.auth.GoogleAuthProvider();
  provider.addScope('https://www.googleapis.com/auth/userinfo.email');
  firebase
    .auth()
    .signInWithPopup(provider)
    .then(result => {
      // Returns the signed in user along with the provider's credential
      console.log(`${result.user.displayName} logged in.`);
      window.alert(`Welcome ${result.user.displayName}!`);
    })
    .catch(err => {
      console.log(`Error during sign in: ${err.message}`);
      window.alert('Sign in failed. Retry or check your browser logs.');
    });
}

ユーザーがログインに成功すると、クライアントは Firebase メソッドを使用して ID トークンを作成します。クライアントが、この ID トークンをサーバーに対するリクエストの Authorization ヘッダーに追加します。

async function vote(team) {
  if (firebase.auth().currentUser) {
    // Retrieve JWT to identify the user to the Identity Platform service.
    // Returns the current token if it has not expired. Otherwise, this will
    // refresh the token and return a new one.
    try {
      const token = await firebase.auth().currentUser.getIdToken();
      const response = await fetch('/', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          Authorization: `Bearer ${token}`,
        },
        body: 'team=' + team, // send application data (vote)
      });
      if (response.ok) {
        const text = await response.text();
        window.alert(text);
        window.location.reload();
      }
    } catch (err) {
      console.log(`Error when submitting vote: ${err}`);
      window.alert('Something went wrong... Please try again!');
    }
  } else {
    window.alert('User not signed in.');
  }
}

Identity Platform との統合: サーバー側のコード

サーバーでは、Firebase Admin SDK を使用して、クライアントから送信されたユーザー ID トークンを確認します。提供された ID トークンが正しい形式で、期限切れではなく、適切に署名されていれば、メソッドはデコードされた ID トークンを返します。そのユーザーの Identity Platform uid が、サーバーにより抽出されます。

Node.js

const firebase = require('firebase-admin');
// Initialize Firebase Admin SDK
firebase.initializeApp();

// Extract and verify Id Token from header
const authenticateJWT = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (authHeader) {
    const token = authHeader.split(' ')[1];
    // If the provided ID token has the correct format, is not expired, and is
    // properly signed, the method returns the decoded ID token
    firebase
      .auth()
      .verifyIdToken(token)
      .then(decodedToken => {
        const uid = decodedToken.uid;
        req.uid = uid;
        next();
      })
      .catch(err => {
        req.logger.error(`Error with authentication: ${err}`);
        return res.sendStatus(403);
      });
  } else {
    return res.sendStatus(401);
  }
};

Python

def jwt_authenticated(func: Callable[..., int]) -> Callable[..., int]:
    @wraps(func)
    def decorated_function(*args: Any, **kwargs: Any) -> Any:
        header = request.headers.get("Authorization", None)
        if header:
            token = header.split(" ")[1]
            try:
                decoded_token = firebase_admin.auth.verify_id_token(token)
            except Exception as e:
                logger.exception(e)
                return Response(status=403, response=f"Error with authentication: {e}")
        else:
            return Response(status=401)

        request.uid = decoded_token["uid"]
        return func(*args, **kwargs)

    return decorated_function

Java

/** Extract and verify Id Token from header */
private String authenticateJwt(Map<String, String> headers) {
  String authHeader =
      (headers.get("authorization") != null)
          ? headers.get("authorization")
          : headers.get("Authorization");
  if (authHeader != null) {
    String idToken = authHeader.split(" ")[1];
    // If the provided ID token has the correct format, is not expired, and is
    // properly signed, the method returns the decoded ID token
    try {
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
      String uid = decodedToken.getUid();
      return uid;
    } catch (FirebaseAuthException e) {
      logger.error("Error with authentication: " + e.toString());
      throw new ResponseStatusException(HttpStatus.FORBIDDEN, "", e);
    }
  } else {
    logger.error("Error no authorization header");
    throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
  }
}

サーバーを Cloud SQL に接続する

サービスは、/cloudsql/CLOUD_SQL_CONNECTION_NAME の形式を使用して、Cloud SQL インスタンスの Unix ドメイン ソケットに接続します。

Node.js

/**
 * Connect to the Cloud SQL instance through UNIX Sockets
 *
 * @param {object} credConfig The Cloud SQL connection configuration from Secret Manager
 * @returns {object} Knex's PostgreSQL client
 */
const connectWithUnixSockets = async credConfig => {
  const dbSocketPath = process.env.DB_SOCKET_PATH || '/cloudsql';
  // Establish a connection to the database
  return Knex({
    client: 'pg',
    connection: {
      user: credConfig.DB_USER, // e.g. 'my-user'
      password: credConfig.DB_PASSWORD, // e.g. 'my-user-password'
      database: credConfig.DB_NAME, // e.g. 'my-database'
      host: `${dbSocketPath}/${credConfig.CLOUD_SQL_CONNECTION_NAME}`,
    },
    ...config,
  });
};

Python

def init_unix_connection_engine(
    db_config: Dict[str, str]
) -> sqlalchemy.engine.base.Engine:
    creds = credentials.get_cred_config()
    db_user = creds["DB_USER"]
    db_pass = creds["DB_PASSWORD"]
    db_name = creds["DB_NAME"]
    db_socket_dir = creds.get("DB_SOCKET_DIR", "/cloudsql")
    cloud_sql_connection_name = creds["CLOUD_SQL_CONNECTION_NAME"]

    pool = sqlalchemy.create_engine(
        # Equivalent URL:
        # postgres+pg8000://<db_user>:<db_pass>@/<db_name>
        #                         ?unix_sock=<socket_path>/<cloud_sql_instance_name>/.s.PGSQL.5432
        sqlalchemy.engine.url.URL.create(
            drivername="postgresql+pg8000",
            username=db_user,  # e.g. "my-database-user"
            password=db_pass,  # e.g. "my-database-password"
            database=db_name,  # e.g. "my-database-name"
            query={
                "unix_sock": "{}/{}/.s.PGSQL.5432".format(
                    db_socket_dir, cloud_sql_connection_name  # e.g. "/cloudsql"
                )  # i.e "<PROJECT-NAME>:<INSTANCE-REGION>:<INSTANCE-NAME>"
            },
        ),
        **db_config,
    )
    pool.dialect.description_encoding = None
    logger.info("Database engine initialised from unix conection")

    return pool

Java

Spring Cloud Google Cloud PostgreSQL スターター統合を使用して、Spring JDBC ライブラリを使用する Google Cloud SQL の PostgreSQL データベースを操作します。DataSource Bean を自動的に構成するように Cloud SQL for MySQL 構成を設定します。これは、Spring JDBC と一体となり、データベースのクエリや変更などのオペレーションを可能にする JdbcTemplate オブジェクト Bean を提供します。

# Uncomment and add env vars for local development
# spring.datasource.username=${DB_USER}
# spring.datasource.password=${DB_PASSWORD}
# spring.cloud.gcp.sql.database-name=${DB_NAME}
# spring.cloud.gcp.sql.instance-connection-name=${CLOUD_SQL_CONNECTION_NAME}  
private final JdbcTemplate jdbcTemplate;

public VoteController(JdbcTemplate jdbcTemplate) {
  this.jdbcTemplate = jdbcTemplate;
}

Secret Manager で機密性の高い構成を処理する

Cloud SQL 構成などのセンシティブ データは、Secret Manager によって一元管理され、安全に保管されます。このサービスは、Secret Manager から環境変数を介してランタイムに Cloud SQL 認証情報を挿入します。詳しくは、Cloud Run でのシークレットの使用をご覧ください。

Node.js

// CLOUD_SQL_CREDENTIALS_SECRET is the resource ID of the secret, passed in by environment variable.
// Format: projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
const {CLOUD_SQL_CREDENTIALS_SECRET} = process.env;
if (CLOUD_SQL_CREDENTIALS_SECRET) {
  try {
    // Parse the secret that has been added as a JSON string
    // to retrieve database credentials
    return JSON.parse(CLOUD_SQL_CREDENTIALS_SECRET.toString('utf8'));
  } catch (err) {
    throw Error(
      `Unable to parse secret from Secret Manager. Make sure that the secret is JSON formatted: ${err}`
    );
  }
}

Python

def get_cred_config() -> Dict[str, str]:
    secret = os.environ.get("CLOUD_SQL_CREDENTIALS_SECRET")
    if secret:
        return json.loads(secret)

Java

/** Retrieve config from Secret Manager */
public static HashMap<String, Object> getConfig() {
  String secret = System.getenv("CLOUD_SQL_CREDENTIALS_SECRET");
  if (secret == null) {
    throw new IllegalStateException("\"CLOUD_SQL_CREDENTIALS_SECRET\" is required.");
  }
  try {
    HashMap<String, Object> config = new Gson().fromJson(secret, HashMap.class);
    return config;
  } catch (JsonSyntaxException e) {
    logger.error(
        "Unable to parse secret from Secret Manager. Make sure that it is JSON formatted: "
            + e);
    throw new RuntimeException(
        "Unable to parse secret from Secret Manager. Make sure that it is JSON formatted.");
  }
}

Identity Platform の設定

Identity Platform は、Cloud Console で手動で設定する必要があります。

  1. Cloud Console で Identity Platform の Marketplace ページに移動します。

    Identity Platform の Marketplace ページに移動

  2. [Identity Platform を有効化] をクリックします。これにより、Web client (auto created by Google Service) の名前が付された OAuth2 クライアント ID が作成されます。

  3. 生成された OAuth2 ID をダウンロードします。

    1. 新しいウィンドウで、[API とサービス] > [認証情報] ページに移動します。

      [API とサービス] > [認証情報] ページに移動

    2. 「Web client (auto created by Google Service)」のレコードでダウンロード アイコンをクリックします。

    3. ダウンロードした JSON 出力で、client_idclient_secret をメモします。

  4. Google をプロバイダとして構成します。

    1. Cloud Console で [ID プロバイダ] ページに移動します。

      [ID プロバイダ] ページに移動

    2. [プロバイダを追加] をクリックします。

    3. リストから [Google] を選択します。

    4. [Web SDK Configuration] の設定で、先ほどダウンロードした JSON の値を入力します。

      1. ウェブ クライアント ID: client_id

      2. ウェブ クライアント シークレット: client_secret

    5. [構成画面] をクリックします。

      1. ユーザータイプに [外部] を選択します。

      2. 必須項目(サポートのメールアドレス、デベロッパーのメールアドレス)に入力します。

      3. [概要] ページが表示されるまで進みます。

    6. [アプリケーションの構成] で [詳細を設定] をクリックします。スニペットをサンプルの static/config.js にコピーし、Identity Platform Client SDK を初期化します。

    7. [保存] をクリックします。

サービスのデプロイ

次の手順に沿って、インフラストラクチャのプロビジョニングおよびデプロイを完了するか、Cloud Shell で [Run on Google Cloud] をクリックしてプロセスを自動化します。

Google Cloud での実行

  1. Console または CLI を使用して、PostgSQL データベースの Cloud SQL インスタンスを作成します。

    gcloud sql instances create CLOUD_SQL_INSTANCE_NAME \
        --database-version=POSTGRES_12 \
        --region=CLOUD_SQL_REGION \
        --cpu=2 \
        --memory=7680MB \
        --root-password=DB_PASSWORD
  2. Cloud SQL の認証情報の値を postgres-secrets.json に追加します。

    Node.js

    {
      "CLOUD_SQL_CONNECTION_NAME": "PROJECT_ID:REGION:INSTANCE",
      "DB_NAME": "postgres",
      "DB_USER": "postgres",
      "DB_PASSWORD": "PASSWORD_SECRET"
    }
    

    Python

    {
      "CLOUD_SQL_CONNECTION_NAME": "PROJECT_ID:REGION:INSTANCE",
      "DB_NAME": "postgres",
      "DB_USER": "postgres",
      "DB_PASSWORD": "PASSWORD_SECRET"
    }
    

    Java

    {
      "spring.cloud.gcp.sql.instance-connection-name": "PROJECT_ID:REGION:INSTANCE",
      "spring.cloud.gcp.sql.database-name": "postgres",
      "spring.datasource.username": "postgres",
      "spring.datasource.password": "PASSWORD_SECRET"
    }

  3. Console または CLI を使用して、バージョニングされたシークレットを作成します。

    gcloud secrets create idp-sql-secrets \
        --replication-policy="automatic" \
        --data-file=postgres-secrets.json
  4. Console または CLI を使用して、サーバーのサービス アカウントを作成します。

    gcloud iam service-accounts create idp-sql-identity
  5. Console または CLI を使用して、Secret Manager と Cloud SQL にアクセスするためのロールを付与します。

    1. サーバーに関連付けられたサービス アカウントに、作成したシークレットへのアクセスを許可します。

      gcloud secrets add-iam-policy-binding idp-sql-secrets \
        --member serviceAccount:idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/secretmanager.secretAccessor
    2. サーバーに関連付けられたサービス アカウントに Cloud SQL へのアクセスを許可します。

      gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/cloudsql.client
  6. Cloud Build を使用してコンテナ イメージをビルドします。

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/idp-sql

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/idp-sql

    Java

    このサンプルでは、Jib を使用して一般的な Java ツールにより Docker イメージをビルドします。Jib は、Dockerfile や Docker をインストールせずにコンテナのビルドを最適化します。Jib を使用して Java コンテナを構築する方法の詳細を確認します。

    1. Docker を承認して Container Registry に push するには、gcloud 認証ヘルパーを使用します。

      gcloud auth configure-docker

    2. Jib Maven プラグインを使用して、コンテナをビルドして Container Registry に push します。

      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/idp-sql

  7. Console または CLI を使用して、コンテナ イメージを Cloud Run にデプロイします。このサーバーは、未認証のアクセスを許可するようにデプロイされています。これは、ユーザーがクライアントを読み込んで処理を開始できるようにするためです。サーバーは、投票リクエストに追加された ID トークンを手動で検証し、エンドユーザーを認証します。

    gcloud beta run deploy idp-sql \
        --image gcr.io/PROJECT_ID/idp-sql \
        --allow-unauthenticated \
        --service-account idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --add-cloudsql-instances PROJECT_ID:REGION:CLOUD_SQL_INSTANCE_NAME \
        --update-secrets CLOUD_SQL_CREDENTIALS_SECRET=idp-sql-secrets:latest

    具体的には、--service-account--add-cloudsql-instances--update-secrets フラグを使用して、サービス ID、Cloud SQL インスタンス接続、シークレット名(環境変数としてバージョンを指定)をそれぞれ指定します。

最後の仕上げ

Identity Platform では、ユーザーがログインした後に、Cloud Run サービスの URL を許可されたリダイレクトとして承認する必要があります。

  1. [ID プロバイダ] ページでペンのアイコンをクリックして、Google プロバイダを編集します。

  2. [承認済みドメイン] で [ドメインを追加] をクリックし、Cloud Run サービスの URL を入力します。

    ビルドまたはデプロイの後、サービス URL はログで確認できます。また、次のコマンドを使用して確認することもできます。

    gcloud run services describe idp-sql --format 'value(status.url)'

試してみる

完成したサービスを試すには:

  1. ブラウザで、前述のデプロイの手順により提供された URL に移動します。

  2. [Google でログイン] ボタンをクリックして、認証フローを行います。

  3. 投票します。

    次のようになります。

    ユーザー インターフェースのスクリーンショットには、各チームへの投票数と投票の一覧が示されます。

これらのサービスを開発し続けることにした場合、Google Cloud の他のサービスへの Identity and Access Management(IAM)アクセスが制限されます。他の多くのサービスにアクセスするには、追加の IAM ロールをこれらのサービスに与える必要があることにご注意ください。

クリーンアップ

このチュートリアル用に新規プロジェクトを作成した場合は、そのプロジェクトを削除します。既存のプロジェクトを使用し、このチュートリアルで変更を加えずに残す場合は、チュートリアル用に作成したリソースを削除します。

プロジェクトを削除する

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

チュートリアル リソースを削除する

  1. このチュートリアルでデプロイした Cloud Run サービスを削除します。

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME は、選択したサービス名です。

    Cloud Run サービスは Google Cloud Console から削除することもできます。

  2. チュートリアルの設定時に追加した gcloud のデフォルト リージョン構成を削除します。

     gcloud config unset run/region
    
  3. プロジェクト構成を削除します。

     gcloud config unset project
    
  4. このチュートリアルで作成した他の Google Cloud リソースを削除します。

次のステップ