Tutorial de autenticação do usuário final para o Cloud Run

Neste tutorial, mostramos como criar um serviço de votação com o seguinte:

  • Um cliente baseado em navegador que:

    1. usa o Identity Platform para buscar um token de ID;
    2. permite que os usuários votem no animal de estimação favorito;
    3. adiciona esse token de ID para uma solicitação ao servidor do Cloud Run que processa o voto.
  • Um servidor do Cloud Run que:

    1. verifica se o usuário final foi autenticado corretamente fornecendo um token de ID válido;
    2. processa o voto do usuário final;
    3. usa as próprias credenciais para enviar o voto ao Cloud SQL para armazenamento.
  • Um banco de dados PostgresSQL que armazena os votos.

Para simplificar, este tutorial usa o Google como provedor: os usuários precisam fazer a autenticação usando uma Conta do Google para receber o token de ID. No entanto, é possível usar outros provedores ou métodos de autenticação para fazer login de usuários.

Esse serviço reduz riscos de segurança usando o Secret Manager para proteger dados confidenciais usados para se conectar à instância do Cloud SQL. Ele também usa uma identidade de serviço com privilégio mínimo para proteger o acesso ao banco de dados.

Objetivos

Grave, crie e implante um serviço no Cloud Run que mostra como:

  • Use o Identity Platform para autenticar um usuário final no back-end do serviço do Cloud Run.

  • Crie uma identidade com privilégio mínimo para que o serviço conceda acesso mínimo aos recursos do Google Cloud.

  • Use o Secret Manager para processar dados confidenciais ao conectar o serviço Cloud Run a um banco de dados PostgreSQL.

Custos

Neste tutorial, usamos os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

  1. Faça login na sua conta do Google Cloud. Se você começou a usar o Google Cloud agora, crie uma conta para avaliar o desempenho de nossos produtos em situações reais. Clientes novos também recebem US$ 300 em créditos para executar, testar e implantar cargas de trabalho.
  2. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar o seletor de projetos

  3. Verifique se o faturamento está ativado para seu projeto na nuvem. Saiba como verificar se o faturamento está ativado em um projeto.

  4. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar o seletor de projetos

  5. Verifique se o faturamento está ativado para seu projeto na nuvem. Saiba como verificar se o faturamento está ativado em um projeto.

  6. Ative as APIs Cloud Run, Secret Manager, Cloud SQL, Container Registry, and Cloud Build .

    Ative as APIs

Configuração dos padrões gcloud

Para configurar a gcloud com os padrões do serviço do Cloud Run, realize as etapas a seguir:

  1. Defina seu projeto padrão:

    gcloud config set project PROJECT_ID

    Substitua PROJECT_ID pelo nome do projeto que você criou para este tutorial.

  2. Configure a gcloud para a região escolhida:

    gcloud config set run/region REGION

    Substitua REGION pela região compatível do Cloud Run.

Locais do Cloud Run

O Cloud Run é regional, o que significa que a infraestrutura que executa seus serviços do Cloud Run está localizada em uma região específica e é gerenciada pelo Google para estar disponível de maneira redundante em todas as zonas da região.

Atender aos seus requisitos de latência, disponibilidade ou durabilidade são os principais fatores para selecionar a região em que seus serviços do Cloud Run são executados. Geralmente, é possível selecionar a região mais próxima de seus usuários, mas considere a localização dos outros produtos do Google Cloud usados pelo serviço do Cloud Run. O uso de produtos do Google Cloud em vários locais pode afetar a latência e o custo do serviço.

O Cloud Run está disponível nas regiões a seguir:

Sujeitas aos preços do nível 1

  • asia-east1 (Taiwan)
  • asia-northeast1 (Tóquio)
  • asia-northeast2 (Osaka)
  • europe-north1 (Finlândia) Ícone de folha Baixo CO2
  • europe-southwest1 (Madri) Ícone de folha Baixo CO2
  • europe-west1 (Bélgica) Ícone de folha Baixo CO
  • europe-west4 (Países Baixos)
  • europe-west8 (Milão)
  • europe-west9 (Paris) Ícone de folha Baixo CO2
  • us-central1 (Iowa) Ícone de folha Baixo CO2
  • us-east1 (Carolina do Sul)
  • us-east4 (Norte da Virgínia)
  • us-east5 (Columbus)
  • us-south1 (Dallas)
  • us-west1 (Oregon) Ícone de folha Baixo CO2

Sujeitas aos preços do nível 2

  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seul, Coreia do Sul)
  • asia-southeast1 (Singapura)
  • asia-southeast2 (Jacarta)
  • asia-south1 (Mumbai, Índia)
  • asia-south2 (Déli, Índia)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Varsóvia, Polônia)
  • europe-west2 (Londres, Reino Unido)
  • europe-west3 (Frankfurt, Alemanha)
  • europe-west6 (Zurique, Suíça) Ícone de folha Baixo CO
  • northamerica-northeast1 (Montreal) Ícone de folha Baixo nível de CO2
  • northamerica-northeast2 (Toronto) Ícone de folha Baixo CO2
  • southamerica-east1 (São Paulo, Brasil) Ícone de folha Baixo CO2
  • southamerica-west1 (Santiago, Chile)
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Se você já criou um serviço do Cloud Run, é possível visualizar a região no painel do Cloud Run no console.

Como recuperar o exemplo de código

Para recuperar o exemplo de código para uso, siga estas etapas:

  1. Clone o repositório do aplicativo de amostra na máquina local:

    Node.js

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

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Python

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

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Java

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

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

  2. Mude para o diretório que contém o código de amostra do 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/

Como visualizar a arquitetura

Diagrama de arquitetura
O diagrama mostra um login de usuário final usando um pop-up do Login do Google fornecido pelo IdP e sendo redirecionado para Cloud Run com a identidade do usuário.
  1. Um usuário final faz a primeira solicitação ao servidor do Cloud Run.

  2. O cliente carrega um arquivo no navegador.

  3. O usuário fornece credenciais de login pela janela pop-up do Login do Google no Identity Platform. Um alerta aceita o usuário conectado.

  4. O controle é redirecionado de volta para o servidor. O usuário final vota usando o cliente, que busca um token de ID do Identity Platform e o adiciona ao cabeçalho da solicitação de voto.

  5. Ao receber a solicitação, o servidor verifica o token de ID do Identity Platform, confirmando se o usuário final está devidamente autenticado. Em seguida, o servidor envia o voto para o Cloud SQL, usando suas próprias credenciais.

Noções básicas sobre o código principal

A amostra é implementada como cliente e servidor, conforme descrito a seguir.

Integração com o Identity Platform: código do lado do cliente

Este exemplo usa SDKs do Firebase para integração com o Identity Platform a fim de fazer login e gerenciar usuários. Para se conectar ao Identity Platform, o JavaScript do lado do cliente mantém a referência às credenciais do projeto como um objeto de configuração e importa os SDKs do Firebase para JavaScript necessários:

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>

O SDK do Firebase para JavaScript processa o fluxo de login solicitando que o usuário final faça login na Conta do Google em uma janela pop-up. Em seguida, ele o redireciona de volta para o serviço.

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.');
    });
}

Quando um usuário faz login corretamente, o cliente usa os métodos do Firebase para produzir um token de ID. O cliente adiciona o token de ID ao cabeçalho Authorization da solicitação para o servidor.

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.');
  }
}

Integração com o Identity Platform: código do lado do servidor

O servidor usa o SDK Admin do Firebase para verificar o token do ID do usuário enviado pelo cliente. Se o token de ID fornecido tiver o formato correto, não tiver expirado e estiver devidamente assinado, o método retornará o token de ID decodificado. O servidor extrai o uid do Identity Platform para esse usuário.

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: a, **kwargs: a) -> a:
        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);
  }
}

Como conectar o servidor ao Cloud SQL

O servidor conecta-se ao soquete do domínio Unix da instância do Cloud SQL usando o formato: /cloudsql/CLOUD_SQL_CONNECTION_NAME.

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

Use a integração de inicialização do Spring Cloud Google Cloud PostgreSQL para interagir com os bancos de dados do PostgreSQL no Google Cloud SQL usando bibliotecas JDBC do Spring JDBC. Defina o Cloud SQL para MySQL para configurar automaticamente um bean DataSource, que, junto do Spring JDBC, fornece um objeto JdbcTemplate que permite operações como consulta e modificação de um banco de dados.

# 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;
}

Como lidar com configurações confidenciais com o Gerenciador de secrets

O Secret Manager permite o armazenamento centralizado e seguro de dados sensíveis, como a configuração do Cloud SQL. O servidor usa uma variável de ambiente para injetar no ambiente de execução as credenciais do Cloud SQL contidas no Secret Manager. Saiba mais sobre como usar secrets com o 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.");
  }
}

Como configurar o Identity Platform

O Identity Platform requer a configuração manual no console.

  1. No console, acesse a página do Identity Platform no Marketplace.

    Acessar a página do Identity Platform no Marketplace

  2. Clique em Ativar Identity Platform. Isso gera um ID do cliente OAuth2 com o nome: Web client (auto created by Google Service).

  3. Faça o download do ID OAuth2 gerado:

    1. Em uma nova janela, acesse a página APIs e serviços > Credenciais.

      Acessar a página APIs e serviços > Credenciais

    2. No registro de "Cliente da Web (criado automaticamente pelos Serviços do Google)", clique no ícone de download.

    3. Observe client_id e client_secret.

  4. Configure o Google como provedor:

    1. Acesse a página "Provedores de identidade" no Console do Cloud.

      Acessar a página "Provedores do Identity Platform"

    2. Clique em Adicionar um provedor.

    3. Selecione Google na lista.

    4. Na configuração do SDK da Web, insira os valores anteriores:

      1. ID do cliente da Web: client_id

      2. Chave secreta do cliente da Web: client_secret

    5. Clique em Configurar tela.

      1. Selecione Externo em "Tipo de usuário".

      2. Preencha os campos obrigatórios (e-mail de suporte, e-mail do desenvolvedor).

      3. Continue até chegar na página Resumo.

    6. Em "Configure sua aplicação", clique em Detalhes de configuração. Copie o snippet no static/config.js da amostra para inicializar o SDK do cliente do Identity Platform. Como alternativa, localize o snippet da página do Console do Google Cloud nos Detalhes de configuração do aplicativo e faça uma cópia dele no static/config.js do exemplo.

    7. Clique em Save.

Como implantar o serviço

Siga as etapas abaixo para concluir o provisionamento e a implantação da infraestrutura ou automatizar o processo no Cloud Shell clicando em "Executar no Google Cloud":

Executar no Google Cloud

  1. Crie uma instância do Cloud SQL com um banco de dados PostgreSQL usando o console ou a CLI:

    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. Adicione seus valores da credencial do Cloud SQL a 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. Crie um secret com controle de versões usando o console ou a CLI:

    gcloud secrets create idp-sql-secrets \
        --replication-policy="automatic" \
        --data-file=postgres-secrets.json
  4. Crie uma conta de serviço para o servidor usando o console ou a CLI:

    gcloud iam service-accounts create idp-sql-identity
  5. Conceda papéis para acesso ao Secret Manager e ao Cloud SQL usando o console ou a CLI:

    1. Permita que a conta de serviço associada ao servidor acesse o secret criado:

      gcloud secrets add-iam-policy-binding idp-sql-secrets \
        --member serviceAccount:idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/secretmanager.secretAccessor
    2. Permita que a conta de serviço associada ao servidor acesse o 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. Crie a imagem do contêiner usando o 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

    Esta amostra usa o Jib (em inglês) para criar imagens do Docker usando ferramentas comuns do Java. O Jib otimiza builds de contêiner sem a necessidade de um Dockerfile ou de ter o Docker (em inglês) instalado. Saiba mais sobre como criar contêineres Java com o Jib.

    1. Use o auxiliar de credencial do gcloud para autorizar o Docker a enviar por push ao Container Registry.

      gcloud auth configure-docker

    2. Use o plug-in do Maven do Jib para criar e enviar por push o contêiner ao Container Registry.

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

  7. Implante a imagem do contêiner no Cloud Run usando o console ou a CLI. O servidor é implantado para permitir o acesso não autenticado. Dessa maneira, o usuário pode carregar o cliente e iniciar o processo. O servidor verifica manualmente o token de ID adicionado à solicitação de votação, autenticando o usuário final.

    gcloud 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

    Observe também as sinalizações, --service-account, --add-cloudsql-instances e --update-secrets, que especificam a identidade do serviço, a conexão da instância do Cloud SQL e o nome do secret com a versão como variável de ambiente, respectivamente.

Toques finais

O Identity Platform exige que você autorize o URL de serviço do Cloud Run como um redirecionamento permitido depois de fazer login do usuário:

  1. Para editar o provedor do Google, clique no ícone da caneta na página Provedores de identidade.

  2. Clique em Adicionar domínio em "Domínios autorizados" no painel à direita e insira o URL do serviço do Cloud Run.

    Localize o URL do serviço nos registros após a criação ou implantação ou encontre-o a qualquer momento usando:

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

Como testar

Para testar o serviço completo:

  1. Navegue até o URL fornecido pela etapa de implantação acima.

  2. Clique no botão Fazer login com o Google e siga o fluxo de autenticação.

  3. Dê seu voto!

    Você verá o seguinte:

    A captura de tela da interface do usuário mostra o número de votos de cada equipe e uma lista de votos.

Se você optar por continuar desenvolvendo esses serviços, lembre-se de que eles têm acesso restrito do gerenciamento de identidade e acesso (IAM, na sigla em inglês) ao restante do Google Cloud e precisarão receber mais papéis do IAM para acessar muitos outros serviços.

Limpeza

Se você criou um novo projeto para este tutorial, exclua o projeto. Se você usou um projeto atual e quer mantê-lo sem as alterações incluídas neste tutorial, exclua os recursos criados para o tutorial.

Como excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  1. No console, acesse a página Gerenciar recursos.

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

Como excluir recursos do tutorial

  1. Exclua o serviço do Cloud Run que você implantou neste tutorial:

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME é o nome escolhido do serviço.

    Também é possível excluir os serviços do Cloud Run no Console do Google Cloud.

  2. Remova a configuração da região padrão da gcloud que você adicionou durante a configuração do tutorial:

     gcloud config unset run/region
    
  3. Remova a configuração do projeto:

     gcloud config unset project
    
  4. Exclua outros recursos do Google Cloud criados neste tutorial:

A seguir