Instructivo de autenticación de usuarios finales para Cloud Run

En este instructivo, se muestra cómo crear una aplicación web en Cloud Run con acceso restringido a los usuarios registrados y datos almacenados en PostgreSQL. La integración en Identity Platform controla la autenticación del usuario final y proporciona tokens de ID del usuario para autorizar al servicio a que consulte una base de datos de Cloud SQL. Este servicio tiene un cliente público que permite que los usuarios externos se registren y accedan a una IU de votación para emitir votos.

Para que sea más simple, en este instructivo se usa Google como proveedor: los usuarios deben acceder con una Cuenta de Google. Sin embargo, puedes usar otros proveedores o métodos de autenticación para que los usuarios accedan.

Este servicio minimiza los riesgos de seguridad con Secret Manager para proteger los datos sensibles que se usan a fin de conectarse a la instancia de Cloud SQL y mediante una identidad de servicio con privilegios mínimos.

En esta muestra, durante una autenticación de usuario final, sucede lo siguiente:

  1. El cliente solicita un token de ID firmado para el usuario basado en Identity Platform.
  2. El token de ID firmado se envía con solicitudes a un servidor.
  3. El servidor verifica la identidad del usuario mediante ese token de ID firmado y otorga acceso a la base de datos de PostgreSQL de Cloud SQL.

En este instructivo, no se muestra el uso del siguiente método de autenticación:

  • Autenticación de IAM, que usa funciones de IAM para confirmar y verificar identidades de forma automática. Esto se recomienda en la autenticación de servicio a servicio, no para las identidades individuales. Si deseas obtener más información sobre la autenticación de servicio a servicio, consulta el Instructivo para proteger los servicios de Cloud Run. Por el momento, no se pueden combinar la autenticación basada en IAM con los métodos de token de ID.

Objetivos

  • Escribir, compilar e implementar un servicio en Cloud Run que muestre cómo realizar lo siguiente:

    • Autenticar usuarios finales para acceder al servicio de Cloud Run mediante Identity Platform

    • Conectar un servicio de Cloud Run a una base de datos PostgreSQL mediante Secret Manager para controlar datos sensibles

  • Crear una identidad de servicio con privilegios mínimos para obtener acceso mínimo a los recursos de Google Cloud

Costos

En este instructivo, se usan componentes facturables de Google Cloud, incluidos los siguientes:

Usa la calculadora de precios para generar una estimación de los costos según el uso previsto.

Deberías poder completar este proyecto dentro de los créditos de prueba gratuita.

Los usuarios nuevos de Cloud Platform pueden optar por una prueba gratuita.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. En la página del selector de proyectos de Google Cloud Console, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyecto

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  4. Habilita las API de Cloud Run, Secret Manager, Cloud SQL, Container Registry, and Cloud Build .

    Habilita las API

  5. Instala e inicializa el SDK de Cloud.

Configura los valores predeterminados de gcloud

A fin de configurar gcloud con los valores predeterminados para el servicio de Cloud Run, sigue estos pasos:

  1. Configura el proyecto predeterminado:

    gcloud config set project PROJECT_ID

    Reemplaza PROJECT_ID por el nombre del proyecto que creaste para este instructivo.

  2. Configura gcloud en la región que elegiste:

    gcloud config set run/region REGION

    Reemplaza REGION por la región de Cloud Run compatible que prefieras.

Ubicaciones de Cloud Run

Cloud Run es regional, lo que significa que la infraestructura que ejecuta los servicios se ubica en una región específica, y Google la administra para que esté disponible de manera redundante en todas las zonas de esa región.

El cumplimiento de los requisitos de latencia, disponibilidad o durabilidad es el factor principal para seleccionar la región en la que se ejecutan los servicios de Cloud Run. Por lo general, puedes seleccionar la región más cercana a los usuarios, pero debes considerar la ubicación de los otros productos de Google Cloud que usa el servicio de Cloud Run. Si usas productos de Google Cloud en varias ubicaciones, la latencia y el costo del servicio pueden verse afectados.

Cloud Run está disponible en las siguientes regiones:

Sujetas a los Precios del nivel 1

  • asia-east1 (Taiwán)
  • asia-northeast1 (Tokio)
  • asia-northeast2 (Osaka)
  • europe-north1 (Finlandia)
  • europe-west1 (Bélgica)
  • europe-west4 (Países Bajos)
  • us-central1 (Iowa)
  • us-east1 (Carolina del Sur)
  • us-east4 (Virginia del Norte)
  • us-west1 (Oregón)

Sujetas a los Precios del nivel 2

  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seúl, Corea del Sur)
  • asia-southeast1 (Singapur)
  • asia-southeast2 (Yakarta)
  • asia-south1 (Bombay, India)
  • australia-southeast1 (Sídney)
  • europe-central2 (Varsovia, Polonia)
  • europe-west2 (Londres, Reino Unido)
  • europe-west3 (Fráncfort, Alemania)
  • europe-west6 (Zúrich, Suiza)
  • northamerica-northeast1 (Montreal)
  • southamerica-east1 (São Paulo, Brasil)
  • us-west2 (Los Ángeles)
  • us-west3 (Las Vegas)
  • us-west4 (Salt Lake City)

Si ya creaste un servicio de Cloud Run, puedes ver la región en el panel de Cloud Run en Cloud Console.

Recupera la muestra de código

A fin de recuperar la muestra de código para su uso, haz lo siguiente:

  1. Clona el repositorio de la app de muestra en tu máquina local:

    Node.js

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

    De manera opcional, puedes descargar la muestra como un archivo zip y extraerla.

    Python

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

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

    Java

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

    De manera opcional, puedes descargar la muestra como un archivo ZIP y extraerla.

  2. Ve al directorio que contiene el código de muestra de 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/

Visualiza la arquitectura

Diagrama de arquitectura
En el diagrama, se muestra el acceso de los usuarios mediante una ventana emergente de Acceso con Google que proporciona IdP y, luego, se los redirecciona a Cloud Run con la identidad del usuario.
  1. Un usuario realiza la primera solicitud al servicio.

  2. El servicio de Cloud Run proporciona al cliente un formulario de acceso del usuario.

  3. El usuario accede a través de la ventana emergente de Acceso con Google que usa Identity Platform.

  4. El flujo de autenticación redirecciona al usuario al servicio de Cloud Run junto con la identidad del usuario.

  5. Cuando el usuario emite un voto, el cliente crea un token de ID y lo agrega a las solicitudes del servidor. El servidor verifica el token de ID y otorga acceso de escritura a Cloud SQL.

Información sobre el código

La muestra se implementa como cliente y servidor, como se describe a continuación.

Integración en Identity Platform: código del cliente

En esta muestra, se usan los SDK de Firebase a fin de integrarse a Identity Platform para acceder y administrar usuarios. Para conectarse a Identity Platform, el JavaScript del cliente contiene la referencia a las credenciales del proyecto como un objeto de configuración e importa los SDK de Firebase JavaScript necesarios:

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>

El SDK de Firebase JavaScript controla el flujo de acceso. Para ello, solicita a los usuarios que accedan con sus Cuentas de Google a través de una ventana emergente y, luego, los redirecciona al servicio.

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

Cuando un usuario accede de forma correcta, los métodos de Firebase crean tokens de ID para identificar de forma única al usuario y otorgarle acceso. El cliente se comunica con el servidor mediante la adición del encabezado Authorization con el token de ID.

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

Integración en Identity Platform: código del servidor

El servidor usa el SDK de Firebase Admin para integrarse a Identity Platform y verificar la identidad del usuario desde el token de ID de usuario que se envía desde el cliente. Si el token de ID proporcionado tiene el formato correcto, no está vencido y tiene la firma adecuada, el método muestra el token de ID decodificado que se necesita en la extracción del uid de Identity Platform para ese usuario.

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

Conéctate a Cloud SQL

El servicio se conecta al socket de dominio Unix de la instancia de Cloud SQL con el siguiente 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

Usa la integración del activador de PostgreSQL Spring Cloud Google Cloud para interactuar con tus bases de datos de PostgreSQL en Google Cloud SQL mediante las bibliotecas de JDBC de Spring. Establece tu configuración de Cloud SQL para MySQL para que configure automáticamente un bean DataSource, que, junto con JDBC de Spring, proporciona un bean de objeto JdbcTemplate que permite operaciones como consultar y modificar una base de datos.

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

Controla la configuración sensible con Secret Manager

Secret Manager permite el almacenamiento centralizado y seguro de datos sensibles, como la configuración de Cloud SQL. El servicio carga las credenciales de Cloud SQL desde Secret Manager a través de la biblioteca cliente.

Node.js

const {SecretManagerServiceClient} = require('@google-cloud/secret-manager');
let client;

async function getSecrets(secretName) {
  if (!client) client = new SecretManagerServiceClient();
  try {
    const [version] = await client.accessSecretVersion({name: secretName});
    return version.payload.data;
  } catch (err) {
    throw Error(`Error accessing Secret Manager: ${err}`);
  }
}

Python

def get_cred_config() -> Dict[str, str]:
    if "CLOUD_SQL_CREDENTIALS_SECRET" in os.environ:
        name = os.environ["CLOUD_SQL_CREDENTIALS_SECRET"]
        client = secretmanager.SecretManagerServiceClient()
        response = client.access_secret_version(request={"name": name})
        logger.info("Credentials pulled from CLOUD_SQL_CREDENTIALS_SECRET")
        return json.loads(response.payload.data.decode("UTF-8"))

Java

/** Retrieve config from Secret Manager */
public static HashMap<String, Object> getConfig(
    String secretVersionName) throws IOException {
  // Initialize client that will be used to send requests. This client only needs to be created
  // once, and can be reused for multiple requests. After completing all of your requests, call
  // the "close" method on the client to safely clean up any remaining background resources.
  try (SecretManagerServiceClient client = SecretManagerServiceClient.create()) {
    // Retrieve secret version
    AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);
    String json = response.getPayload().getData().toStringUtf8();

    // Convert JSON secret to a Map
    HashMap<String, Object> config = new Gson().fromJson(json, HashMap.class);
    return config;
  } catch (IOException e) {
    logger.error("Unable to create Secret Manager client: " + e.toString());
    throw new RuntimeException("Unable to retrieve config secrets.");
  } catch (ApiException e) {
    logger.error("Unable to retrieve config secrets: " + e.toString());
    throw new RuntimeException("Unable to retrieve config secrets.");
  }
}

Envía el servicio

Configuración de Identity Platform

Identity Platform requiere una configuración manual en Cloud Console.

  1. Ve a la página Marketplace de Identity Platform en Cloud Console.

    Ir a la página Marketplace de Identity Platform

  2. Haz clic en Habilitar Identity Platform. Se creará un ID de cliente de OAuth2 con el nombre Web client (auto created by Google Service).

  3. Descarga el ID de OAuth2 generado:

    1. En una ventana nueva, ve a la página API y servicios > Credenciales.

      Ir a la página API y servicios > Credenciales

    2. En el registro de “Web client (auto created by Google Service)”, haz clic en el ícono Descargar.

    3. En el archivo JSON descargado, toma nota de client_id y client_secret.

  4. Configura Google como proveedor:

    1. Ve a la página Proveedores de identidad en Cloud Console.

      Ir a la página Proveedores de identidad

    2. Haz clic en Agregar un proveedor.

    3. Selecciona Google de la lista.

    4. En la configuración del SDK web, ingresa los valores del JSON que descargaste antes:

      1. ID de cliente web: client_id

      2. Secreto del cliente web: client_secret

    5. Haz clic en Configurar pantalla.

      1. Selecciona Externos en Tipo de usuario.

      2. Completa los campos obligatorios (correo electrónico de asistencia y del desarrollador).

      3. Continúa hasta la página Resumen.

    6. En “Configurar tu aplicación”, haz clic en Detalles de la configuración. Copia el fragmento en el static/config.js de la muestra para inicializar el SDK de cliente de Identity Platform.

    7. Haz clic en Guardar.

Implementa el servicio

Sigue los pasos que se indican a continuación para completar el aprovisionamiento y la implementación de la infraestructura, o bien automatiza el proceso en Cloud Shell. Para ello, haz clic en “Ejecutar en Google Cloud”:

Ejecutar en Google Cloud

  1. Crea una instancia de Cloud SQL con base de datos PostgreSQL mediante Console o la 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. Agrega los valores de la credencial de 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. Crea un secreto con versión mediante Console o la CLI:

    gcloud secrets create idp-sql-secrets \
        --replication-policy="automatic" \
        --data-file=postgres-secrets.json
  4. Crea una cuenta de servicio mediante Console o la CLI:

    gcloud iam service-accounts create idp-sql-identity
  5. Agrega vinculaciones para el acceso de Cloud SQL y Secret Manager mediante Console o la CLI:

    1. Permite que la cuenta de servicio acceda al secreto creado:

      gcloud secrets add-iam-policy-binding idp-sql-secrets \
        --member serviceAccount:idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/secretmanager.secretAccessor
    2. Permite que la cuenta de servicio acceda a 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. Usa Cloud Build para compilar la imagen de contenedor:

    Node.js

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

    Python

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

    Java

    En esta muestra, se usa Jib para compilar imágenes de Docker mediante herramientas de Java comunes. Jib optimiza las compilaciones de contenedores sin la necesidad de tener un Dockerfile o tener Docker instalado. Obtén más información sobre cómo compilar contenedores de Java con Jib.

    1. Usa el auxiliar de credenciales de gcloud para autorizar a Docker a que envíe contenido a tu Container Registry.

      gcloud auth configure-docker

    2. Usa el complemento de Maven para Jib a fin de compilar y enviar el contenedor a Container Registry.

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

  7. Implementa la imagen de contenedor en Cloud Run mediante Console o la CLI:

    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-env-vars CLOUD_SQL_CREDENTIALS_SECRET=projects/PROJECT_ID/secrets/idp-sql-secrets/versions/latest

    Hazlo mediante las marcas, --service-account, --add-cloudsql-instances y --update-env-vars para especificar la identidad del servicio, la conexión de la instancia de Cloud SQL y el nombre completo de la ruta del secreto como una variable de entorno, respectivamente.

Retoques finales

Autoriza la URL de servicio de Cloud Run como un redireccionamiento permitido después del acceso del usuario:

  1. Para editar el proveedor de Google, haz clic en el ícono de lápiz en la página Proveedores de identidad.

  2. Haz clic en Agregar un dominio en Dominios autorizados e ingresa la URL de servicio de Cloud Run.

    Puedes ubicar la URL del servicio en los registros después de la compilación o implementación, o puedes encontrarla en cualquier momento con el siguiente comando:

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

Haz una prueba

Para probar el servicio completo, haz lo siguiente:

  1. Dirige tu navegador a la URL proporcionada en el paso de implementación anterior.

  2. Haz clic en el botón Acceder con Google y completa el flujo de autenticación.

  3. Agrega tu voto.

    Se verá de la siguiente manera:

    Captura de pantalla de la interfaz de usuario en la que se muestra el recuento de votos de cada equipo y una lista de votos.

Si eliges seguir desarrollando estos servicios, recuerda que tienen acceso restringido de la administración de identidades y accesos (IAM) al resto de Google Cloud y necesitarán tener funciones de IAM adicionales para acceder a muchos otros servicios.

Realice una limpieza

Si creaste un proyecto nuevo para este instructivo, bórralo. Si usaste un proyecto existente y deseas conservarlo sin los cambios que se agregaron en este instructivo, borra los recursos creados para el instructivo.

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

Para borrar el proyecto, sigue estos pasos:

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra los recursos del instructivo

  1. Usa este comando para borrar el servicio de Cloud Run que implementaste en este instructivo:

    gcloud run services delete SERVICE-NAME

    En el ejemplo anterior, SERVICE-NAME es el nombre del servicio que elegiste.

    También puedes borrar los servicios de Cloud Run desde Google Cloud Console.

  2. Quita la configuración de región predeterminada de gcloud que agregaste durante la configuración en el instructivo:

     gcloud config unset run/region
    
  3. Quita la configuración del proyecto:

     gcloud config unset project
    
  4. Borra otros recursos de Google Cloud que creaste en este instructivo:

¿Qué sigue?