<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Roger Martinez on Medium]]></title>
        <description><![CDATA[Stories by Roger Martinez on Medium]]></description>
        <link>https://medium.com/@roger-that-dev?source=rss-e7724d73a96------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*LSUZb8iLQ4P72iCo_PoobA.jpeg</url>
            <title>Stories by Roger Martinez on Medium</title>
            <link>https://medium.com/@roger-that-dev?source=rss-e7724d73a96------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 10:52:04 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@roger-that-dev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Three ways to save a billion tokens with Firebase AI Logic and on-device AI for Chrome]]></title>
            <link>https://medium.com/google-cloud/three-ways-to-save-a-billion-tokens-with-firebase-ai-logic-and-on-device-ai-for-chrome-407eef38e196?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/407eef38e196</guid>
            <category><![CDATA[cost-optimization]]></category>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[gemini]]></category>
            <category><![CDATA[ai-on-device]]></category>
            <category><![CDATA[google-cloud]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Thu, 10 Sep 2026 19:21:07 GMT</pubDate>
            <atom:updated>2026-09-11T00:54:51.980Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4iIIHp58o2d5tRVu_bIslA.png" /></figure><p>Tokens cost money. That becomes abundantly clear when you scale your AI web app to millions of users and receive a hefty price tag associated with token consumption. Every call to a remote cloud-hosted AI model, while miniscule on its own, adds costs that can compound fast. However, as AI models become smaller and smarter, it’s becoming possible and practical to offload some of those workloads to the on-device models on the client before they ever reach your backend.</p><p>Here are three strategies to drastically slash cloud token consumption. Each of them can be implemented on web apps built with<a href="https://g.dev/firebase/on-device-ai"> Firebase AI Logic with on-device AI in Chrome</a>. Together, and over time and with lots of users, these strategies will help you save more than a billion tokens in the AI apps you build.</p><h3>Selective routing: Avoid sending any tokens at all</h3><p>In some cases, a small on-device model can yield comparable results to larger cloud-hosted models. Summarization tasks are especially well suited for an on-device model and could result in a significant reduction in cloud costs over time.</p><p>For example, I built an application that takes a synopsis of an untitled article and outputs suggestions for titles and subtitles in different styles. On the left is the response from my cloud-hosted model and on the right is the response from my on-device model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3JZnjQ52nOrAIz-RWOlepQ.png" /></figure><p>For my purposes, the results from the on-device model are comparable to those from my cloud-hosted model. By using an on-device model, an operation that would normally consume 131 input and output tokens with my cloud-hosted model can be completely offloaded. This brings the token consumption costs down to zero for inference associated with this feature.</p><h3>Prompt summarization: reduce verbose user input</h3><p>Language isn’t always as concise as it could be. In those cases, an on-device model is a great way to summarize the important information in a prompt before sending it off to the cloud model to do deeper thinking. This way you’re not spending input tokens on redundant or unnecessary pieces of information.</p><p>To demonstrate the impact on token consumption with this strategy, I built another web application that takes a transcript from a customer support chat session to generate an issue report in two ways. The first way, on the left, sends the original text of the chat transcript to the cloud-hosted model and returns what it could look like as an issue. The second way, on the right, sends the original text to the on-device AI model first to produce a summary of the transcript before sending that to the cloud model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0laDHAn7x9Vgg2LTFwJGCg.png" /></figure><p>In this case, I was able to reduce input tokens by <strong>44% </strong>by summarizing the transcript locally before sending it to my cloud-hosted model. Effective use of this strategy depends on crafting the right summarization instructions for the on-device model to adequately summarize prompts for your particular use case. In my case, I was able to avoid consuming about 80 tokens associated with this app functionality.</p><h3>Session compacting: shrink session history</h3><p><strong>Selective routing</strong> and <strong>prompt summarization</strong> tackle token consumption at the prompt level. For AI chat applications, <strong>session compacting</strong> is a strategy for reducing token consumption in another part of the context: the session history.</p><p>When session history contains each and every verbose message from the user and ai, the payload sent to the model steadily increases with every message and response, taking up more of the context window. Session compacting reduces the token cost of the payload by replacing entries in the session history with shorter and more concise summaries.</p><p>To demonstrate, I built a basic AI chat app with an option to manually compact the session using an on-device model and show the token reduction for each message the session payload:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dbw-uJnRaA3LMC3JJuabQw.png" /></figure><p>In the example above, only the messages from the model were compacted while the user messages were left intact. As a result, token consumption of the session history is reduced by <strong>71%.</strong></p><p>Similar to prompt summarization, effective use of session compacting requires a thoughtful balance between token reduction and response accuracy. You may find that replacing all the messages with shorter versions reduces token consumption the most, but choose to retain the most recent messages verbatim to maintain a level of accuracy. Or you might craft system instructions for the summarizing model that instruct it to retain particularly important information that might appear in each message. Depending on your application’s needs, your session compacting strategy may vary.</p><h3>Implementing these strategies with Firebase AI Logic</h3><p>Firebase AI Logic supports switching between using cloud-hosted models (through the Gemini Developer API or Agent Platform Gemini API) and an on-device model using the <a href="https://developer.chrome.com/docs/extensions/ai/prompt-api">Prompt API from Chrome</a>.</p><p>Implementing any of the token-saving strategies above comes down to specifying the <em>inference mode</em> in the model parameters. The <em>inference mode</em> controls how your application will handle subsequent AI Logic requests. For example, to implement a selective routing strategy where your app tries to use the on-device model before trying the cloud-hosted model, you’ll set the inference mode to PREFER_ON_DEVICE in model parameters provided to the getGenerativeModel method:</p><pre>import { initializeApp } from &quot;firebase/app&quot;;<br>import { getAI, getGenerativeModel, AgentPlatformBackend, InferenceMode } from &quot;firebase/ai&quot;;<br><br>export async function generateAnyResponse(prompt){<br>  const app = initializeApp();<br>  const ai = getAI(app, { backend: new AgentPlatformBackend() });<br>  <br>  const model = getGenerativeModel(ai, {<br>    mode: InferenceMode.PREFER_ON_DEVICE,<br>    inCloudParams: {<br>      model: &quot;gemini-3.5-flash-lite&quot;,<br>      systemInstruction: &quot;instructions for cloud model&quot;,<br>    },<br>    onDeviceParams: {<br>      createOptions: {<br>        initialPrompts: [<br>          {<br>            role: &quot;system&quot;,<br>            content: [<br>              {<br>                type: &quot;text&quot;,<br>                value: &quot;instructions for on-device model&quot;,<br>              },<br>            ],<br>          },<br>        ],<br>      },<br>    },<br>  });<br><br>  const result = await model.generateContent(prompt);<br>  return result.response.text();<br>}</pre><p>With the PREFER_ON_DEVICE inference mode all subsequent model calls will use the on-device model if it’s available and fall back to the cloud-hosted model if it isn’t.</p><p>To avoid using a cloud-hosted model at all, you could specify the ONLY_ON_DEVICE inference mode, which will throw an exception if the on-device model is not available. This is useful you want to incorporate prompt summarization but don’t want to use your cloud-hosted model for any summarization tasks when the on-device model is unavailable:</p><pre>import { initializeApp } from &quot;firebase/app&quot;;<br>import { getAI, getGenerativeModel, AgentPlatformBackend, InferenceMode } from &quot;firebase/ai&quot;;<br><br>export async function generateLocalResponse(prompt){<br>  const app = initializeApp();<br>  const ai = getAI(app, { backend: new AgentPlatformBackend() });<br>  <br>  const model = getGenerativeModel(ai, {<br>    mode: InferenceMode.ONLY_ON_DEVICE,<br>    onDeviceParams: {<br>      createOptions: {<br>        initialPrompts: [<br>          {<br>            role: &quot;system&quot;,<br>            content: [<br>              {<br>                type: &quot;text&quot;,<br>                value: &quot;Summarization instructions for on-device AI.&quot;,<br>              },<br>            ],<br>          },<br>        ],<br>      },<br>    },<br>  });<br><br>  const result = await model.generateContent(prompt);<br>  return result.response.text();<br>}</pre><p>As you might expect there are additional inference modes like ONLY_IN_CLOUD and PREFER_IN_CLOUD for routing requests primarily to the cloud-hosted model. However, when it comes to conserving token usage from your client, some combination of ONLY_ON_DEVICE and PREFER_ON_DEVICE will be key to implementing any of the 3 strategies discussed.</p><h3>Reaching a billion tokens</h3><p>Saving a billion tokens is a massive milestone, but it isn’t a goal you hit overnight. It’s a cumulative victory that compounds slowly as you scale your user base and offload more of your AI functionality to the client. However, on-device AI isn’t a one-size-fits-all solution. To build a robust architecture that reaches that level, you need to navigate current environment requirements while knowing exactly when to stay in the cloud.</p><h4>Limitations to consider</h4><p>The most significant limitation today is the execution environment. For these strategies to work, your users must be running <strong>Chrome (version 139 or later)</strong> on a device that supports the <strong>Gemini Nano</strong> model. Because the model is downloaded and executed locally, the first run often requires a brief wait while Chrome prepares the environment.</p><p>Additionally, on-device models are typically smaller and more specialized. They excel at pattern recognition and summarization but may struggle with deep logical reasoning or maintaining accuracy over extremely large context windows.</p><h4>Choosing your inference mode</h4><p>Deciding where to run your model depends on the specific requirements of the task:</p><p><strong>Prefer on-device AI when:</strong></p><ul><li><strong>Privacy is paramount:</strong> Processing sensitive user data that should never leave the local machine.</li><li><strong>Connectivity is unreliable:</strong> Ensuring applications remain functional in offline or low-bandwidth environments.</li><li><strong>Handling high-frequency, low-complexity tasks:</strong> Such as real-time text formatting, basic summarization, or local search indexing.</li><li><strong>Prefer in-cloud AI when:</strong></li><li><strong>Tasks require complex reasoning:</strong> If your prompt involves multi-step logic, advanced coding assistance, or nuanced creative writing.</li><li><strong>Working with massive context:</strong> For tasks that require analyzing large documents or entire codebases.</li></ul><p>By using the PREFER_ON_DEVICE mode in Firebase AI Logic, you get the best of both worlds: high-performance local execution for the majority of your users, with a transparent fallback to the cloud when hardware or complexity demands it.</p><h3>Recapping core strategies</h3><p>Token optimization is essential for building resilient, scalable AI architectures that support long-term growth. By strategically moving client-ready AI workloads to the edge by using on-device AI models you’re setting yourself up for success by ensuring that your user base can scale up without token consumption costs spiraling out of control.</p><p>To recap the three core strategies:</p><ul><li><strong>Selective routing:</strong> Eliminate cloud calls entirely for straightforward summarization tasks.</li><li><strong>Prompt summarization:</strong> Compress verbose inputs locally before dispatching them to large cloud models.</li><li><strong>Session compacting:</strong> Keep multi-turn chat context windows lean without losing crucial conversation state.</li></ul><p>Whether you adopt one pattern or combine all three together, these techniques can scale significantly, potentially saving a <strong>billion tokens </strong>over time as your user base grows.</p><p>Ready to optimize your own app? Check out the Prompt API documentation for an <a href="https://g.dev/firebase/chrome-session-compacting-demo">interactive demo of session compacting</a> or dive into the <a href="https://firebase.google.com/docs/ai-logic/hybrid/web/get-started?api=vertex#initialize-service-and-model">Firebase AI Logic documentation</a> to start building hybrid, token-efficient web apps today.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=407eef38e196" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-cloud/three-ways-to-save-a-billion-tokens-with-firebase-ai-logic-and-on-device-ai-for-chrome-407eef38e196">Three ways to save a billion tokens with Firebase AI Logic and on-device AI for Chrome</a> was originally published in <a href="https://medium.com/google-cloud">Google Cloud - Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to protect your agent harness from abuse with Firebase]]></title>
            <link>https://medium.com/google-cloud/how-to-protect-your-agent-harness-from-abuse-with-firebase-ff4edab3207f?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/ff4edab3207f</guid>
            <category><![CDATA[agent-harness]]></category>
            <category><![CDATA[authentication]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[firebase]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Wed, 02 Sep 2026 16:44:02 GMT</pubDate>
            <atom:updated>2026-09-03T00:25:14.662Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*GqhEgS8E-bvtFHup.jpg" /></figure><p>You can keep an AI harness secure in a number of ways, like creating isolated sandbox environments with resource caps or making sure there’s write protection on production data. But these strategies go out the window if you can’t get a handle on <strong>who</strong> can access your harness and from <strong>where</strong>. Without these controls, your backend agent endpoints are left vulnerable to automated bots, credential stuffing, spam account creation, and imposter clients looking to exploit your agent’s tools.</p><p>That’s why securing the human-to-agent boundary is an important first line of defense against external threats and abuse. And this is where <strong>Firebase Authentication</strong> and <strong>Firebase App Check</strong> work in tandem to help protect your harness from abuse.</p><h3>Identify users with Firebase Authentication</h3><p>If you’ve deployed a custom harness using the Antigravity SDK (or really any other framework or library) to Google Cloud and it has a client facing endpoint, like a chat interface,<strong> </strong>you can use<strong> </strong><a href="https://g.dev/firebase/content-agent-harness-authentication"><strong>Firebase Authentication</strong></a> to help you verify the user’s identity using popular and trusted federated identity providers like Sign in with Google, Facebook, and GitHub. As a managed service, you’re able to drop in authentication into your client application with no additional infrastructure to set up or manage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kcwySHUUXLGdWNWc.jpg" /></figure><h3>Attest approved devices with Firebase App Check</h3><p>While Firebase Authentication verifies <strong>who</strong> the user is, Firebase App Check provides device and application attestation to help prove <strong>where</strong> the request is coming from. This helps ensure that the request originates from your authentic, untampered app on a real device. App Check issues a unique App Check token that a client can use to attest that it’s a legitimate source. This token is passed along via the Firebase client SDK to protected Firebase services, like Cloud Firestore and SQL Connect, as well as custom backends, like a proxy service set up in front of an agent. If a token is present, and the risk score is below the threshold, the connection is allowed. Otherwise, access is blocked.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*As9GVOHHfD-0Bcsm.jpg" /></figure><p>App Check is able to achieve device verification by offloading this heavy lift to trusted platform-native attestation providers like DeviceCheck and App Attest for iOS, Play Integrity for Android, and reCAPTCHA Enterprise for web apps. These providers are constantly evolving behind the scenes to keep pace with new emerging attack patterns. That means that the entry to your agent continues to stay protected against device impersonation techniques that may not have existed when you first deployed your agentic workflows. If those providers don’t meet your needs, you can even add a custom attestation provider to mint your own App Check tokens.</p><h3>Replay Protection for extra-sensitive endpoints</h3><p>App Check tokens are the way App Check communicates whether a client device is approved and genuine. Session-based App Check tokens are valid according to a configured time-to-live (TTL) value that can be set to last from 30 minutes to 7 days. Session-based tokens allow a developer to balance latency with security, since acquiring a token has an encryption latency cost. After expiration, the client will silently acquire a new App Check token by calling the attestation provider (encryption cost). For additional protection for especially sensitive endpoints, tokens can be changed from session-based (which are reused) to one-time-use by setting up <strong>replay protection.</strong></p><p>Replay protection works in two parts. First, on the client, an App Check token is requested using the getLimitedUseToken() method from the Firebase Client SDK, instead of getToken(). Next, on the backend where the Firebase Admin SDK checks that the token is valid, it is also marked as consumed. With that designation, the backend is able to reject any request that attempts to reuse that token.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*krSefHIbykkw3nuQ.jpg" /></figure><p>Ultimately, your AI agent’s harness is only as safe as the gateway that governs the access to it. The combination of Firebase Authentication and Firebase App Check go a long way in helping to establish a secure boundary between the human and your AI agent. They work hand in hand to help ensure that your agents are protected from external exploits and threats while you can focus on building your agent harness.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ff4edab3207f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-cloud/how-to-protect-your-agent-harness-from-abuse-with-firebase-ff4edab3207f">How to protect your agent harness from abuse with Firebase</a> was originally published in <a href="https://medium.com/google-cloud">Google Cloud - Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Don’t Create That API Key: Set Up Application Default Credentials Instead]]></title>
            <link>https://roger-that-dev.medium.com/dont-create-that-api-key-set-up-application-default-credentials-instead-7a3add94f0cd?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/7a3add94f0cd</guid>
            <category><![CDATA[google-cloud-security]]></category>
            <category><![CDATA[google-cloud-billing]]></category>
            <category><![CDATA[gemini-agent-platform]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[vertex-ai]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Thu, 02 Jul 2026 20:27:48 GMT</pubDate>
            <atom:updated>2026-07-02T20:27:48.709Z</atom:updated>
            <content:encoded><![CDATA[<p>When you’re using Google Cloud’s Agent Platform (formerly Vertex AI), a leaked API key can be a terrifying (and expensive) disaster. Luckily, you don’t have to juggle API keys to access AI models on Google Cloud from your application code. <strong>Application Default Credentials</strong> are the safer and recommended alternative. The <a href="https://console.cloud.google.com/agent-platform/studio/settings/api-keys">Agent Platform API Keys page in the console</a> will tell you the same:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Els6Rfzn7N9kTWKaTSGNFQ.png" /><figcaption>The Cloud Console recommends Application Default Credentials over API keys</figcaption></figure><p>Conveniently, it provides a shell script that will set you up with application default credentials. The problem is that it only works on Mac and Linux. If you’re on Windows, or if for whatever reason this script fails on your machine, you might be tempted to just hit that “Create API Key” button and avoid application default credentials altogether.</p><p><strong>But don’t.</strong></p><p>Instead, follow these <strong>5 quick steps</strong> to get your application default credentials set up <strong>fast</strong>. (If you want more details on application default credentials, <a href="https://youtu.be/jiolmnh5N2U?si=D_eYXKJHlcqbbjD7">here’s a video I put out on the topic</a> that you should check out.)</p><h3>Step 1: Verify you’ve got gcloud installed</h3><p>You’re gonna need the Google Cloud CLI (gcloud) installed on your machine. Verify it’s installed by running:</p><pre>gcloud version</pre><p>If you already have it, move on to step 2. If not, you can find install instructions <a href="https://docs.cloud.google.com/sdk/docs/install-sdk">right here</a>.</p><h3>Step 2: Log-in and set application default credentials</h3><p>The Google Cloud CLI manages 2 types of authentication. gcloud auth login authorizes gcloud commands to access Google Cloud with your credentials. gcloud auth application default login sets your user credentials as application default credentials.</p><p>Set them both in one swoop with the following:</p><pre>gcloud auth login --update-adc</pre><h3>Step 3: Set your project id</h3><p>We’ll reference your project ID a few times so set that to an environment variable:</p><pre>export PROJECT_ID=&lt;YOUR_PROJECT_ID&gt;</pre><p>The Google Cloud CLI references a project ID in 2 different ways. First, it’s defined in your current workspace config. Set that with:</p><pre>gcloud config set project $PROJECT_ID</pre><p>Now your project will be used in step 4, and any other gcloud commands you run.</p><p>Application default credentials don’t look at this value for a Google Cloud project. Instead it needs a <strong><em>quota project.</em></strong> Set a quota project with the following command:</p><pre>gcloud auth application-default set-quota-project $PROJECT_ID</pre><p>This project will be used when accessing Google Cloud APIs via Cloud Client Libraries used in your application code locally.</p><h3>Step 4: Enable the API</h3><p>Before you can use Agent Platform, your project needs to have the Agent Platform API enabled in it. Enable it with this command:</p><pre>gcloud services enable aiplatform.googleapis.com</pre><p>This makes it so that the service is accessible on your project. Without it, you’ll get a 403 error.</p><h3>Step 5: Verify your access</h3><p>To verify everything is set up correctly, we’ll hit the Agent Platform API with a web request that includes a temporary access token generated via your application default credentials.</p><p>First, retrieve the token and set it to an environment variable:</p><pre>export TOKEN=`gcloud auth application-default print-access-token`</pre><p>Next, set another environment variable to the contents of the request:</p><pre>export REQUEST=$(cat \&lt;\&lt;EOF  <br>{  <br>  &quot;contents&quot;: \[{  <br>    &quot;role&quot;: &quot;user&quot;,  <br>    &quot;parts&quot;: \[{ &quot;text&quot;: &quot;Wave hello to the world&quot; }\]  <br>  }\]  <br>}  <br>EOF  <br>)</pre><p>And finally, put it all together in a single curl command:</p><pre>curl \-s \-X POST \\  <br>\-H &quot;Authorization: Bearer $TOKEN&quot; \\  <br>\-H &quot;Content-Type: application/json&quot; \\  <br>&quot;https://aiplatform.googleapis.com/v1/projects/$PROJECT\_ID/locations/global/publishers/google/models/gemini-2.5-flash:generateContent&quot; \\  <br>\-d “$REQUEST”</pre><p>The output should include a line that reads something like: ”text”: “Hello, world! 🌍👋”</p><p>Now you’re ready to ditch the API key and use application default credentials.<br> Happy trails!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a3add94f0cd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fast Path: Your First Vector Database with Google Cloud AlloyDB]]></title>
            <link>https://roger-that-dev.medium.com/fast-path-your-first-vector-database-with-google-cloud-alloydb-7ca0dc3ba679?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/7ca0dc3ba679</guid>
            <category><![CDATA[vector-database]]></category>
            <category><![CDATA[semantic-search]]></category>
            <category><![CDATA[google-cloud]]></category>
            <category><![CDATA[alloydb]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Mon, 06 Apr 2026 22:51:37 GMT</pubDate>
            <atom:updated>2026-04-07T01:07:19.583Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z_YQxYtdT93tPv7UwV8LNg.png" /></figure><p>Vector database platforms like <a href="http://pinecone.io/">Pinecone</a> and <a href="https://qdrant.tech/">Qdrant</a> offer a great streamlined experience for spinning up and querying a vector database. <a href="https://cloud.google.com/products/alloydb"><strong>AlloyDB</strong></a> on Google Cloud is a different category of product entirely: it’s enterprise-grade PostgreSQL built for high availability, extreme scale, and production workloads. That depth means getting started with vector search on AlloyDB takes a few more steps than a vector-only platform, but if that’s the infrastructure you’re building on, it’s well worth it.</p><p><strong>Here’s the fast path to spinning up your first vector database on AlloyDB.</strong></p><h3>Before we can start</h3><p>We’ll need a few things before we get started:</p><p><strong>A Google Cloud project with billing enabled.</strong> A valid billing account is required to use a lot of Google Cloud services. We’ll be using an AlloyDB free trial cluster to keep costs as low as possible.</p><p><strong>Owner permissions on the project.</strong> The Owner role is generally not recommended, but for learning purposes we’ll use it to complete the steps. I’ll make a separate post about the IAM roles we’d use if we want to apply the principle of least privilege.</p><p><strong>Enabled APIs and services.</strong> To complete the steps below, enable the following APIs on the Google Cloud project:</p><ul><li><a href="https://console.cloud.google.com/apis/library/alloydb.googleapis.com">AlloyDB API</a></li><li><a href="https://console.cloud.google.com/apis/library/aiplatform.googleapis.com">Vertex AI API</a></li><li><a href="https://console.cloud.google.com/apis/library/compute.googleapis.com">Compute Engine API</a></li><li><a href="https://console.cloud.google.com/apis/library/servicenetworking.googleapis.com">Service Networking API</a></li></ul><p><strong>Sample data.</strong> <a href="https://raw.githubusercontent.com/rogerthatdev/rogerthatdev/refs/heads/main/static/posts/alloydb-vector-db/products_truncated.csv">Here is a CSV file</a> of 2,000 products of a mock online store. We’ll be using the product descriptions to generate vector embeddings and perform a semantic search.</p><h3>1. Create an AlloyDB cluster</h3><p>We’ll start on the <a href="https://console.cloud.google.com/alloydb"><strong>AlloyDB Get Started page</strong></a> where there are a few options for clusters we could create. If the option is available, you’ll see a banner to create a <strong>free trial cluster</strong> (sandbox cluster) that you can use at no charge for 30 days. Fill in a password and click <strong>Create free trial </strong>to create a new cluster.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j7QiGXZTtojV8Gf0PYXfVA.png" /></figure><p>If you’re able to create a free trial cluster, jump down to step 2 to continue. Otherwise, if you don’t see this option, the next lowest cost option will be a prototyping cluster. Click <strong>Create 1vCPU cluster.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/966/1*__RuPtNNBGsaNQ1-CxeW6Q.png" /></figure><p>This will load up a template that will deploy the smallest machine available for the C4A machine series in a single availability zone. Fill out the fields for cluster ID and password.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1ySaLVM2ApLDbG0KFNnhQg.png" /></figure><p>Click <strong>Generate</strong> to autofill in a secure password string. We’ll need it later, so keep this value safe somewhere. Keep the rest of the configuration set to their default values.</p><p>Lower on the page, where we choose a private connection method, keep <strong>Private Services Access</strong> selected and click<strong> Confirm network setup</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nMwtKtL_XLRcB7s07YZOQw.png" /></figure><p>This will enable a private services access connection on our default VPC. That will take between 1 and 2 minutes. Once it completes, click<strong> Create cluster</strong> to create an AlloyDB cluster. When it finishes, the <strong>Overview</strong> page for the cluster will load.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7PyJIJrvRAVLYcACoiAxjQ.png" /></figure><p>Copy the value for <strong>Service Account </strong>to your clipboard. We’ll use that in the next step.</p><h3>2. Grant the AlloyDB service account access to Vertex AI</h3><p>AlloyDB’s AI functionality is powered by <strong>Vertex AI</strong>. It’s not automatic, so we have to manually grant the appropriate permissions to the AlloyDB service account that we copied in the last step. To do this, we head over to the <a href="https://console.cloud.google.com/iam-admin/iam"><strong>IAM Admin page</strong></a> and click Grant Access. Specify the AlloyDB service account and choose the <strong>Vertex AI User</strong> role:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gTYCBSabtDHdEoEy.png" /></figure><p>Hit save and head back to the AlloyDB cluster Overview page.</p><h3>3. Create a new database</h3><p>Our new cluster includes a default database called postgres but let’s create our own. There’s no way to do that in the console, so we have to use the in-console <strong>AlloyDB Studio</strong>. A link to AlloyDB Studio can be found in the left side navigation pane of the <strong>Overview</strong> page, next to the magnifying glass icon.</p><p>First it will ask us to log in to our database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*B4lD_VzqsDLpFR2y.png" /></figure><p>For <strong>Database</strong>, we’ll select the default postgres and choose <strong>Built-in database authentication</strong> as the authentication method. We’ll use the default user postgres and the cluster password we set when we created the cluster.</p><p>Once we’re in AlloyDB Studio, we can run the following statement to create a new database in our cluster:</p><pre>CREATE DATABASE myvectordb;</pre><p>At the top left of the AlloyDB Studio Explorer panel, click the person/gear icon to switch over to the new database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*nf23o80W803HpYjV.png" /></figure><p>We can use the same default built-in database authentication that we did to log into the postgres database.</p><h3>4. Install required extensions</h3><p>By default, our database does not have any AI functionality to support a vector database. For that, we have to install the vector and alloydb_scann extensions:</p><pre>CREATE EXTENSION IF NOT EXISTS vector;<br>CREATE EXTENSION IF NOT EXISTS alloydb_scann;</pre><p>The vector extension is an AlloyDB-customized version of the open source <a href="https://github.com/pgvector/pgvector">pgvector PostgreSQL extension</a>. The alloydb_scann extension lets us implement highly efficient indexing using the <a href="https://services.google.com/fh/files/misc/scann_for_alloydb_whitepaper.pdf">ScaNN algorithm</a>. Together, they enhance our ordinary PostgreSQL database so that it can store and query vector embeddings.</p><h3>5. Create a table with a vector embedding column</h3><p>We’ve got a database, but we still need a table to store our data. Since we’re going to import data from a CSV file in the next step, we have to make sure that the order of the columns and their types match those in the sample csv file:</p><pre>CREATE TABLE &quot;public&quot;.&quot;products&quot; (<br> &quot;id&quot; BIGINT NOT NULL,<br> &quot;category&quot; TEXT,<br> &quot;name&quot; TEXT,<br> &quot;brand&quot; TEXT,<br> &quot;retail_price&quot; NUMERIC,<br> &quot;department&quot; TEXT,<br> &quot;sku&quot; TEXT,<br> &quot;product_description&quot; TEXT,<br> &quot;product_description_embedding&quot; vector(3072) <br> GENERATED ALWAYS AS (embedding(&#39;gemini-embedding-001&#39;, product_description))<br> STORED<br>);</pre><p>The product_description_embedding column will be a vector with 3072 dimensions generated with the gemini-embedding-001 text embedding model applied to the product_description column.</p><h3>6. Import data</h3><p>Back on the Overview page, click the <strong>Import</strong> button at the top of the page.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*r_rkN_xGkXNh7yNP.png" /></figure><p>Select CSV as file format, and then choose <strong>Upload files from your computer</strong>. Click <strong>Browse</strong> to select the local test data file.</p><p>Click the second <strong>Browse</strong> link to select Cloud Storage location to upload the sample CSV file to. If one doesn’t exist we can create a new bucket by clicking the new bucket icon at the top of the bucket selection pane.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*b-Zkhi1Abke0SFnt.png" /></figure><p>Give it a universally unique name and click <strong>Create</strong> then <strong>Select</strong>.</p><p>Next, hit <strong>Upload</strong> to upload the file.</p><p>Finally, choose the database myvectordb and the table products, and click Import.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*x9dyRmu727Kzg4r-.png" /></figure><p>This will take a few minutes. When it’s done, our products table will have 2,000 rows, each with a text embedding in the last column generated using the gemini-embedding-001 model as specified when the table was created in the previous step. And any updates to content of the product_description column of any row will update the embedding column as well.</p><h3>7. Create an index</h3><p>At this point, we could perform a semantic search on our data. However, generating an index will pre-organize our vector embeddings in an index and allow similarity searches on millions of rows. We’ll head back to AlloyDB studio and run the following:</p><pre>-- Generating the index requires more maintenance work memory than the default setting allows.<br>SET maintenance_work_mem = &#39;400MB&#39;;<br><br>CREATE INDEX IF NOT EXISTS idx_product_description_embedding ON products<br>USING ScaNN (product_description_embedding cosine)<br>-- The optimal number of leaves is roughly the square root of the number of rows in the table.<br>WITH (num_leaves=173);</pre><h3>8. Do a semantic search</h3><p>With text embeddings and ScaNN index in place we can now do a semantic search on our data. This example query combines a keyword filter on the category column with a semantic search query.</p><pre>SELECT * FROM products<br>WHERE category = &#39;Outerwear &amp; Coats&#39;<br>ORDER BY<br>  product_description_embedding &lt;=&gt; CAST(embedding(&#39;gemini-embedding-001&#39;, &#39;lightweight puffer jackets for hiking&#39;) AS vector)<br>LIMIT 5;</pre><p>The result we get back should be 5 rows, each one semantically matching the search term provided in the query.</p><h3>What next?</h3><p>These steps are dependent on the sample data that I provided at the top of the tutorial, but they have everything you need to apply it to your own data. Everything here was done from the Google Cloud Console. If you have a preferred client, like pgadmin, or if you want to connect your application to the database, check out the <a href="https://docs.cloud.google.com/alloydb/docs/choose-alloydb-connectivity">documentation for setting up a connection to AlloyDB</a>. If you want to walk through the steps as an interactive code lab, check out <a href="https://codelabs.developers.google.com/alloydb-ai-embedding?hl=en#0">Getting started with Vector Embeddings with AlloyDB AI</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7ca0dc3ba679" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Quick dip: Use Google Cloud Vertex AI SDK with the Vercel AI SDK]]></title>
            <link>https://roger-that-dev.medium.com/quick-dip-use-google-cloud-vertex-ai-sdk-with-the-vercel-ai-sdk-fbd60661f544?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/fbd60661f544</guid>
            <category><![CDATA[vertex-ai]]></category>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[genai]]></category>
            <category><![CDATA[vercel]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Mon, 15 Apr 2024 17:48:13 GMT</pubDate>
            <atom:updated>2024-08-06T20:57:58.876Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>🏊‍♀️ This post is a quick dip. The opposite of a deep dive.</blockquote><p><strong><em>Update: Since publishing this post, Vercel has added a </em></strong><a href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-vertex"><strong><em>provider for Google Vertex</em></strong></a><strong><em>.</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*TjFyxLInpGP0-8AHpmZjRQ.png" /></figure><p>The <a href="https://sdk.vercel.ai/docs/guides/providers/google">Vercel AI SDK documentation</a> includes code samples for hooking up the SDK to different providers like <a href="https://sdk.vercel.ai/docs/guides/providers/anthropic">Anthropic</a>, <a href="https://sdk.vercel.ai/docs/guides/providers/openai">OpenAI</a>, and others. This list includes <a href="https://sdk.vercel.ai/docs/guides/providers/google">Google</a>, but only shows you how to use the <a href="https://www.npmjs.com/package/@google/generative-ai">Google AI JavaScript SDK</a>. Here’s some sample code of how to hook it up to the <a href="https://www.npmjs.com/package/@google-cloud/vertexai">Vertex AI SDK</a>:</p><pre>// app/api/chat/route.ts<br>import { VertexAI } from &quot;@google-cloud/vertexai&quot;;<br>import { GoogleGenerativeAIStream, Message, StreamingTextResponse } from &quot;ai&quot;;<br><br>const project = process.env.GOOGLE_CLOUD_PROJECT || &quot;&quot;;<br>const location = &quot;us-central1&quot;;<br><br>const vertexAI = new VertexAI({ project: project, location: location });<br><br>// Google GenAI API can use the edge runtime. This will not work with Vertex AI<br>// export const runtime = &#39;edge&#39;;<br><br>// Instantiate Gemini models<br>const generativeModel = vertexAI.getGenerativeModel({<br>  model: &quot;gemini-pro&quot;,<br>});<br><br>const buildGoogleGenAIPrompt = (messages: Message[]) =&gt; ({<br>  contents: messages<br>    .filter(<br>      (message) =&gt; message.role === &quot;user&quot; || message.role === &quot;assistant&quot;<br>    )<br>    .map((message) =&gt; ({<br>      role: message.role === &quot;user&quot; ? &quot;user&quot; : &quot;model&quot;,<br>      parts: [{ text: message.content }],<br>    })),<br>});<br><br>export async function POST(req: Request) {<br>  // Extract the `prompt` from the body of the request<br>  const { messages } = await req.json();<br><br>  const geminiStream = await generativeModel.generateContentStream(<br>    buildGoogleGenAIPrompt(messages)<br>  );<br><br>  // Convert the response into a friendly text-stream<br>  // GoogleGenerativeAIStream class decodes/extracts the text tokens in the<br>  // response and then re-encodes them properly for simple consumption.<br>  const stream = GoogleGenerativeAIStream(geminiStream);<br><br>  // Respond with the stream<br>  return new StreamingTextResponse(stream);<br>}</pre><p>To run the above code, follow the instructions for the <a href="https://sdk.vercel.ai/docs/getting-started">Getting Started tutorial</a> and use it for the app/api/chat/route.ts file. (Skip the API key step).</p><h3>Notes</h3><p>Some differences between this Vertex AI SDK example and the <a href="https://sdk.vercel.ai/docs/guides/providers/google">one for the Google AI JavaScript SDK</a>:</p><ul><li>We don’t indicate the runtime as &#39;edge&#39; because it is not compatible with the Vertex AI SDK. <a href="https://nextjs.org/docs/messages/node-module-in-edge-runtime">Click here for more information on the Edge Runtime and Node.js modules</a>.</li><li>Instead of API keys, Vertex uses a project ID indicated via the GOOGLE_CLOUD_PROJECT environment variable and application default credentials indicated via the GOOGLE_APPLICATION_CREDENTIALS environment variable.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fbd60661f544" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Push code with GitHub Actions to Google Cloud’s Artifact Registry]]></title>
            <link>https://roger-that-dev.medium.com/push-code-with-github-actions-to-google-clouds-artifact-registry-60d256f8072f?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/60d256f8072f</guid>
            <category><![CDATA[cicd]]></category>
            <category><![CDATA[artifact-registry]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[github-actions]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Thu, 21 Mar 2024 01:17:47 GMT</pubDate>
            <atom:updated>2024-03-22T16:04:50.517Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KBy5XNoP1PdwwYvab37b7A.png" /></figure><p>If you’ve got some application code sitting in GitHub and need to push it to a Docker registry, there are a lot of options. This article is going to cover just one: <strong>pushing to Google Cloud Artifact Registry using GitHub Actions</strong>. And we’re going to make it happen <strong>without using service account keys</strong> by leveraging <a href="https://cloud.google.com/iam/docs/workload-identity-federation">Google Cloud Workload Identity Federation</a>.</p><h3>Where to begin</h3><h4>Some things we need first</h4><p>To follow along with what I’m going to demonstrate, you’ll need a couple of things:</p><p><strong>☑️ A Google Cloud project: </strong>this will be the project where we’ll be creating our resources, which includes an Artifact Registry repository and some IAM resources. Enable the <a href="https://console.cloud.google.com/apis/api/iamcredentials.googleapis.com">IAM Service Account Credentials</a> and <a href="https://console.cloud.google.com/apis/api/artifactregistry.googleapis.com">Artifact Registry</a> APIs on this project.</p><p><strong>☑️ Google Cloud CLI: </strong>we’ll be using the gcloud CLI to interact with Google Cloud. If you don’t want to install it on your local machine, you can use the <a href="https://shell.cloud.google.com/">Cloud Shell</a> in the Google Cloud console which has gcloud built-in.</p><p><strong>☑️ A GitHub repository: </strong>you’ll need a GitHub repository with a Dockerfile so that you can build and push your container image to the Artifact Registry.</p><h4>GitHub Actions workflow file</h4><p>GitHub Action workflows are typically stored as yaml files in the .github/workflows/ directory of your repo. Here’s the GitHub Action workflow file we’re working with:</p><pre>name: Push to Artifact Registry<br><br>on:<br>    push:<br>      branches: [ &quot;main&quot; ]<br><br>env:<br>  IMAGE_NAME: &#39;&#39;<br>  PROJECT_ID: &#39;&#39;<br>  AR_REPO_LOCATION: &#39;&#39;<br>  AR_URL: &#39;&#39;<br>  SERVICE_ACCOUNT: &#39;&#39; <br>  WORKLOAD_IDENTITY_PROVIDER: &#39;&#39;<br><br>jobs:<br>  push_to_ar:<br>    permissions:<br>      contents: &#39;read&#39;<br>      id-token: &#39;write&#39;<br><br>    runs-on: ubuntu-latest<br>    steps:<br>      - name: Checkout<br>        uses: actions/checkout@v2<br><br>      - name: Google Auth<br>        id: auth<br>        uses: &#39;google-github-actions/auth@v2&#39;<br>        with:<br>          token_format: &#39;access_token&#39;<br>          project_id: ${{ env.PROJECT_ID }}<br>          service_account: ${{ env.SERVICE_ACCOUNT }}<br>          workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}<br><br>      - name: Docker Auth<br>        id: docker-auth<br>        uses: &#39;docker/login-action@v1&#39;<br>        with:<br>          username: &#39;oauth2accesstoken&#39;<br>          password: &#39;${{ steps.auth.outputs.access_token }}&#39;<br>          registry: &#39;${{ env.AR_REPO_LOCATION }}-docker.pkg.dev&#39;<br><br>      - name: Build and Push Container<br>        run: |-<br>          docker build -t &quot;${{ env.AR_URL }}/${{ env.IMAGE_NAME }}:${{ github.sha }}&quot; ./<br>          docker push &quot;${{ env.AR_URL }}/${{ env.IMAGE_NAME }}:${{ github.sha }}&quot;</pre><p>This workflow will authenticate with Google Cloud using the <a href="https://github.com/google-github-actions/auth">Google Cloud auth GitHub Action</a> and use Docker to authenticate and push to the registry. To make this workflow work (or flow?) we need to set up some Google Cloud resources and add in those values for our environment variables. Make sure to add in the value for PROJECT_ID where you have permission to create resources. The value for IMAGE_NAME can be anything — it’ll be created the first time this workflow runs:</p><pre>...<br>env:<br>  IMAGE_NAME: &#39;<strong>my-app-image</strong>&#39;<br>  PROJECT_ID: &#39;<strong>my-project-id</strong>&#39;<br>  AR_REPO_LOCATION: &#39;&#39;<br>  AR_URL: &#39;&#39;<br>  SERVICE_ACCOUNT: &#39;&#39; <br>  WORKLOAD_IDENTITY_PROVIDER: &#39;&#39;<br>...</pre><p>Good start! Next we’ll need an Artifact Registry repository.</p><h3>Artifact Registry repository</h3><p>Artifact Registry is Google Cloud’s build artifact management solution. It supports several types of repositories like apt, Maven, and Python. For our purposes, we want a <strong>Docker</strong> repository. You can use an existing one, or create a new one with this gcloud command:</p><pre>gcloud artifacts repositories create my-ar-repo \<br>  --repository-format=docker \<br>  --location=us-central1 \<br>  --description=&quot;Example Docker repository&quot;</pre><p>This will create a Docker repository called my-ar-repo in the us-central1 region. You can see it in the console on the <a href="https://console.cloud.google.com/artifacts">Artifact Registry repositories page</a>. For our workflow YAML file, we need the <strong>Artifact Registry URL</strong>. Let’s retrieve that with the following command:</p><pre>gcloud artifacts repositories describe my-ar-repo --location=us-central1</pre><p>The output will look like this:</p><pre>Encryption: Google-managed key<br>Registry URL: us-central1-docker.pkg.dev/my-project-id/my-ar-repo<br>Repository Size: 5026.459MB<br>createTime: &#39;2024-03-13T23:19:57.701232Z&#39;<br>description: Example Docker repository<br>format: DOCKER<br>mode: STANDARD_REPOSITORY<br>name: projects/my-project-id/locations/us-central1/repositories/my-ar-repo<br>updateTime: &#39;2024-03-18T17:08:49.658081Z&#39;</pre><p>Let’s take that value for the Registry URL and throw it into our GitHub Actions workflow file, along with the repo location:</p><pre>...<br>env:<br>  IMAGE_NAME: &#39;my-app-image&#39;<br>  PROJECT_ID: &#39;my-project-id&#39;<br>  AR_REPO_LOCATION: &#39;<strong>us-central1</strong>&#39;<br>  AR_URL: &#39;<strong>us-central1-docker.pkg.dev/my-project-id/my-ar-repo</strong>&#39;<br>  SERVICE_ACCOUNT: &#39;&#39; <br>  WORKLOAD_IDENTITY_PROVIDER: &#39;&#39;<br>...</pre><h3>Service account</h3><p>Next, we need a service account to serve as the identity that’s going to be doing the pushing to the Artifact Registry repository. Create a service account with gcloud:</p><pre>gcloud iam service-accounts create github-actions-service-account \<br> --description=&quot;A service account for use in a GitHub Actions workflow&quot; \<br> --display-name=&quot;GitHub Actions service account.&quot;</pre><p>Now that we have a service account (see it <a href="http://console.cloud.google.com/iam-admin/serviceaccounts">in the console under IAM</a>), let’s add the service account email to our YAML file. The format is SERVICE_ACCT_NAME@PROJECT_ID.iam.gserviceaccount.com:</p><pre>...<br>env:<br>  IMAGE_NAME: &#39;my-app-image&#39;<br>  PROJECT_ID: &#39;my-project-id&#39;<br>  AR_REPO_LOCATION: &#39;us-central1&#39;<br>  AR_URL: &#39;us-central1-docker.pkg.dev/my-project-id/my-ar-repo&#39;<br>  SERVICE_ACCOUNT: &#39;<strong>github-actions-service-account@my-project-id.iam.gserviceaccount.com</strong>&#39; <br>  WORKLOAD_IDENTITY_PROVIDER: &#39;&#39;<br>...</pre><p>By default, the service account has no permissions on any resources on the project. We only need it to be able to push builds to our Artifact Registry repo. Let’s make it so:</p><pre>gcloud artifacts repositories add-iam-policy-binding my-ar-repo \<br>  --location=us-central1 \<br>  --role=roles/artifactregistry.createOnPushWriter \<br>  --member=serviceAccount:github-actions-service-account@my-project-id.iam.gserviceaccount.com</pre><p>The command above grants the <strong><em>Artifact Registry Create-on-Push Writer</em></strong> IAM role to our service account, but only for our particular Artifact Registry repository. We could grant it on the entire project by adding the IAM binding to the <em>project IAM policy</em>, but since Artifact Registry repos have their own IAM policies, we can practice some least privilege and narrow the scope to just the one we’re working with.</p><p>With that, our service account is able to push builds to our Artifact Registry. Well not really — now we need a way to tell it to do that.</p><blockquote>Note: If the application image already existed in our repository, then <strong>roles/artifactregistry.writer</strong> would suffice. We’re using <strong>roles/artifactregistry.createOnPushWriter</strong> instead because on the first push, it needs to be able to create the initial image for our application.</blockquote><h3>Workload Identity Federation</h3><p>Historically, one way to programmatically access resources with a service account has been to use JSON keys to authenticate with their credentials. That’s still possible, but it is highly discouraged due to the security risk associated with managing them. You’ll see caution signs about that throughout Google Cloud console and documentation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/0*Vob48aKoZ1rvyAy-" /><figcaption>TLDR: service account keys bad</figcaption></figure><p><strong><em>Workload Identity Federation</em></strong> offers an alternative. It lets us grant external identities the ability to impersonate a service account without the burden of service account keys. Our goal is to create a <strong><em>workload identity pool provider</em></strong><em> </em>for our GitHub repository so that we can impersonate our service account from a GitHub Actions workflow.</p><h4>Create a workload identity pool</h4><p>Before we can create a provider, we need to create a <strong><em>workload identity pool</em></strong>. A workload identity pool is a Google Cloud resource that is used to manage external identities. Google Cloud suggests creating a new pool for each non-Google Cloud environment that needs to access resources in our project. For our case, let’s create a workload identity pool for our app’s development environment:</p><pre>gcloud iam workload-identity-pools create &quot;my-app-dev-pool&quot; \<br>  --project=PROJECT_ID \<br>  --location=global \<br>  --display-name=&quot;Identity pool for my test app&quot;</pre><p>This creates a workload identity pool on your project. See it <a href="https://console.cloud.google.com/iam-admin/workload-identity-pools">in the console under IAM</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VV_E9Rk_TfCaauowNRqwZg.png" /><figcaption>Look at that</figcaption></figure><h4>Create a workload identity pool provider</h4><p>A <strong><em>workload identity pool provider </em></strong>describes the relationship between Google Cloud and an identity provider (IdP) that supports OpenID Connect (OIDC). It must be created within a workload identity pool:</p><pre>gcloud iam workload-identity-pools providers create-oidc &quot;github-actions-provider&quot; \<br> --location=&quot;global&quot; \<br> --workload-identity-pool=&quot;my-app-dev-pool&quot; \<br> --display-name=&quot;Provider for GitHub Actions&quot; \<br> --issuer-uri=&quot;https://token.actions.githubusercontent.com&quot; \<br> --attribute-mapping=&quot;google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner&quot;</pre><p>The create-oidc sub-command indicates that we want to create an<strong><em> OpenID Connect (OIDC)</em></strong> provider in our pool. GitHub uses OIDC to authenticate with different cloud providers (see <a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-cloud-providers">GitHub documentation on it here</a>). The issuer-uri parameter specifies the provider URL, as indicated by GitHub’s <a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#adding-the-identity-provider-to-aws">OIDC documentation</a>.</p><p>The <strong>--attribute-mapping</strong> parameter lists our <strong><em>attribute mapping</em></strong>. Attribute mapping defines how values are derived from an external token and mapped to the Google Security Token Service (STS) token attributes. The value for this parameter is a comma-separated list of mappings in the form of <strong>TARGET_ATTRIBUTE=SOURCE_EXPRESSION</strong>. These attributes will be referenced later when we set up permissions.</p><blockquote>Learn more about attribute mapping from <a href="https://cloud.google.com/iam/docs/workload-identity-federation#mapping">Google Cloud’s documentation on workload identity federation</a>.</blockquote><p>With a workload identity pool provider in place we can add the value to our workflow YAML file. First, retrieve the full name of the provider:</p><pre>gcloud iam workload-identity-pools providers describe github-actions-provider \<br>  --location=global \<br>  --workload-identity-pool=&quot;my-app-dev-pool&quot;</pre><p>The output will include the name which will be in the format projects/<strong>PROJECT_NUMBER</strong>/locations/<strong>POOL_LOCATION</strong>/workloadIdentityPools/<strong>POOL_NAME</strong>/providers/<strong>PROVIDER_NAME. </strong>That’s what we need for the WORKLOAD_IDENTITY_PROVIDER environment variable:</p><pre>...<br>env:<br>  IMAGE_NAME: &#39;my-app-image&#39;<br>  PROJECT_ID: &#39;my-project-id&#39;<br>  AR_REPO_LOCATION: &#39;us-central1&#39;<br>  AR_URL: &#39;us-central1-docker.pkg.dev/my-project-id/my-ar-repo&#39;<br>  SERVICE_ACCOUNT: &#39;github-actions-service-account@my-project-id.iam.gserviceaccount.com&#39; <br>  WORKLOAD_IDENTITY_PROVIDER: &#39;<strong>projects/123456789/locations/global/workloadIdentityPools/my-app-dev-pool/providers/github-actions-provider</strong>&#39;<br>...</pre><h3>Setting permissions</h3><p>The only permissions we’ve granted so far have been to our service account to push to the Artifact Registry repository. Now that we have a workload identity provider, we need to grant it permission to act as the service account.</p><p>If we wanted to grant service account impersonation permissions to a user account or service account principal, we would grant the <strong><em>Service Account Token Creator</em></strong> role on the service account’s IAM policy:</p><pre>gcloud iam service-accounts add-iam-policy-binding \<br>  github-actions-service-account@my-project-id.iam.gserviceaccount.com \<br>  --role=roles/iam.serviceAccountTokenCreator \<br>  --member=user:roger@myemail.com<br></pre><p>This command would allow my user account to act as the service account and access whatever the service account can access. With Workload Identity Federation, it’s the same concept with 2 differences: the IAM role and the principal (or the member parameter in the command above).</p><p>Instead of granting the <strong><em>Service Account Token Creator </em></strong>role<strong> </strong>(roles/iam.serviceAccountTokenCreator) to our workload identity pool principal, we need to grant the <strong><em>Workload Identity User</em></strong> role (roles/iam.workloadIdentityUser) instead. This role allows the principal to impersonate service accounts from federated workloads.</p><p>As for the principal, the prefixes user and serviceAccount are used for user accounts and service accounts, respectively, followed by an email address:</p><p>user:roger@myemail.com</p><p>serviceAccount:my-service-account@my-project-id.iam.gserviceaccount.com</p><p>Workload Identity Federation however uses principalSet as a prefix, followed by a <em>member identifier</em> that includes the identity pool and an attribute from the provider’s attribute mapping:</p><p>principalSet://iam.googleapis.com/WORKLOAD_IDENTITY_POOL_NAME/attribute.ATTRIBUTE_NAME/ATTRIBUTE_VALUE</p><p>For example, the following principal can be used to grant access to external identities coming from any GitHub repository that your organization owns:</p><p>principalSet://iam.googleapis.com/WORKLOAD_IDENTITY_POOL_NAME/<strong>attribute.owner/your-github-organization</strong></p><p>We can use any of the attributes that we mapped on our workload identity federation OIDC provider earlier. The principal that we’ll be using, however, will narrow access down to external identities corresponding with one particular GitHub repository using attribute.repository:</p><p>principalSet://iam.googleapis.com/WORKLOAD_IDENTITY_POOL_NAME/attribute.repository/<strong>github-repo-owner/github-repo-name</strong></p><blockquote>Note: Besides attributes, you can grant permissions to external identities by <strong>subject</strong> and by <strong>group</strong>. Learn more about that <a href="https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers#allow_the_external_workload_to_impersonate_the_service_account">here</a>.</blockquote><p>So we’ve got a IAM role and a principal to grant it to. Let’s add an IAM binding to our service account’s IAM policy. First, retrieve the workload identity pool name:</p><pre>gcloud iam workload-identity-pools describe &quot;my-app-dev-pool&quot; \<br>  --location=global</pre><p>The value will be in the format projects/PROJECT_NUMBER/locations/POOL_LOCATION/workloadIdentityPools/POOL_NAME</p><p>It’s a long string, so let’s throw this into an environment variable:</p><pre>export WIP_POOL=projects/123456789/locations/global/workloadIdentityPools/my-app-dev-pool</pre><p>And finally, let’s add the binding to the service account’s IAM policy. Be sure to update the values for GITHUB_REPO_OWNER and GITHUB_REPO_NAME with values for the GitHub repo that you plan on the running the workflow from:</p><pre>gcloud iam service-accounts add-iam-policy-binding \<br>  github-actions-service-account@my-project-id.iam.gserviceaccount.com \<br>  --role=roles/iam.workloadIdentityUser \<br>  --member=principalSet://iam.googleapis.com/${WIP_POOL}/attribute.repository/GITHUB_REPO_OWNER/GITHUB_REPO_NAME</pre><p>With this binding in place, GitHub Actions is able to use Workload Identity Federation to authenticate as your service account and push builds to your Artifact Registry. There’s one more thing we can do to further narrow the scope of access: specify an <strong><em>attribute condition</em></strong> on the identity pool provider.</p><p>An <strong><em>attribute condition</em></strong> is an expression that checks an attribute and must evaluate to true for a given credential in order for it to be accepted. For example, we can define an attribute condition that checks whether the attribute repository_owner matches a GitHub organization or GitHub user that owns a repository:</p><pre>gcloud iam workload-identity-pools providers update-oidc \<br>  github-actions-provider \<br>  --project=PROJECT_ID \<br>  --location=global \<br>  --workload-identity-pool=my-app-dev-pool \<br>  --attribute-condition=&quot;assertion.repository_owner == &#39;GITHUB_REPO_OWNER&#39;&quot;</pre><p>In this example, the restriction added by this attribute condition is redundant, since the IAM policy binding is already limited to a particular repo owner. However, if for whatever reason the IAM policy ever changes to be more broad, this will still restrict access to the specified GitHub repo owner.</p><h3>Let’s test drive</h3><p>Before we can test this out, you’ll need a repository with a valid Dockerfile.</p><p>Here’s the final yaml file for our workflow:</p><pre>name: Push to Artifact Registry<br><br>on:<br>    push:<br>      branches: [ &quot;main&quot; ]<br><br>env:<br>  IMAGE_NAME: &#39;my-app-image&#39;<br>  PROJECT_ID: &#39;my-project-id&#39;<br>  AR_REPO_LOCATION: &#39;us-central1&#39;<br>  AR_URL: &#39;us-central1-docker.pkg.dev/my-project-id/my-ar-repo&#39;<br>  SERVICE_ACCOUNT: &#39;github-actions-service-account@my-project-id.iam.gserviceaccount.com&#39; <br>  WORKLOAD_IDENTITY_PROVIDER: &#39;projects/123456789/locations/global/workloadIdentityPools/my-app-dev-pool/providers/github-actions-provider&#39;<br><br>jobs:<br>  push_to_ar:<br>    permissions:<br>      contents: &#39;read&#39;<br>      id-token: &#39;write&#39;<br><br>    runs-on: ubuntu-latest<br>    steps:<br>      - name: Checkout<br>        uses: actions/checkout@v2<br><br>      - name: Google Auth<br>        id: auth<br>        uses: &#39;google-github-actions/auth@v2&#39;<br>        with:<br>          token_format: &#39;access_token&#39;<br>          project_id: ${{ env.PROJECT_ID }}<br>          service_account: ${{ env.SERVICE_ACCOUNT }}<br>          workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}<br><br>      - name: Docker Auth<br>        id: docker-auth<br>        uses: &#39;docker/login-action@v1&#39;<br>        with:<br>          username: &#39;oauth2accesstoken&#39;<br>          password: &#39;${{ steps.auth.outputs.access_token }}&#39;<br>          registry: &#39;${{ env.AR_REPO_LOCATION }}-docker.pkg.dev&#39;<br><br>      - name: Build and Push Container<br>        run: |-<br>          docker build -t &quot;${{ env.AR_URL }}/${{ env.IMAGE_NAME }}:${{ github.sha }}&quot; ./<br>          docker push &quot;${{ env.AR_URL }}/${{ env.IMAGE_NAME }}:${{ github.sha }}&quot;</pre><p>We’ll place this configuration in our GitHub repository in .github/workflows/push-to-ar.yml. As soon as you merge this file into your main branch, it’ll kick off the workflow and if all permissions and resources are in place, you’ll have a build of your application sitting in your Artifact Registry. Verify by visiting the <a href="https://console.cloud.google.com/artifacts">Artifact Registry page in the Google Cloud Console</a>.</p><h3>What next?</h3><p>Arguably, the star of the show is the <a href="https://cloud.google.com/blog/products/identity-security/enabling-keyless-authentication-from-github-actions">Google Auth GitHub Action</a> - that’s what’s using the Workload Identity Provider and service account to access your Google Cloud project. If you plan on doing other things with this authentication, just be sure to use a service account that has the right access those things. For example, if you’re using the <a href="https://github.com/google-github-actions/get-secretmanager-secrets">Secret Manager GitHub Action</a> to access secrets from Secret Manager, make sure the service account has the <strong><em>Secret Manager Secret Accessor </em></strong>IAM role on the project or secret. To take a look at all of the available actions, check out the repos available in the <a href="https://github.com/orgs/google-github-actions/repositories">Google GitHub Actions GitHub organization</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=60d256f8072f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Terraform using Google Cloud Build: a very basic example]]></title>
            <link>https://blog.devops.dev/terraform-using-google-cloud-build-a-very-basic-example-723f5fb58bca?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/723f5fb58bca</guid>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[cicd]]></category>
            <category><![CDATA[cloud-build]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Tue, 21 Feb 2023 16:58:38 GMT</pubDate>
            <atom:updated>2023-02-22T13:39:48.105Z</atom:updated>
            <content:encoded><![CDATA[<p>CI/CD can get pretty complicated. There are a lot of moving pieces and a lot of automation. But as they say, the only way to eat an elephant is one bite at a time. So here’s a first nibble.</p><h3>How basic are we talking here?</h3><p>We’re going to use Cloud Build to run Terraform for us in our Google Cloud Project. And when I say <em>a very basic example,</em> I mean a <strong>very </strong>basic example. This won’t be anything you’ll use in production, but this will be the start of a series of posts that build towards a more intricate solution, discussing some important concepts and considerations along the way.</p><h3>My friend Terraform and my buddy Cloud Build</h3><p>Those already familiar with Terraform already know the 3 magic commands we use to deploy resources to Google Cloud, or any other cloud provider out there: terraform init to initialize, followed by terraform plan to preview, and terraform apply to deploy. Manually running these commands is useful for learning Terraform or managing your own personal infrastructure in the Cloud, but in practice you’re going to need to learn how to automate Terraform. Let some robots do it for you. There plenty of ways to do that. Using Cloud Build is just one.</p><p>Cloud Build is Google Cloud’s serverless CI/CD platform. You give it a yaml configuration with some instructions and it can build, test and deploy your software. I won’t get into any more details than that — <a href="https://cloud.google.com/build/docs">the documentation</a> does a much better job than I can.</p><h3>Where to begin?</h3><p>First you’ll need a directory to work out of (a root Terraform module) with 2 files in it (for now): cloudbuild.yaml and main.tf. You can go and create these yourself, or just clone <a href="https://github.com/rogerthatdev/cloud-build-terraform/tree/v1">this branch in this repository</a> that has the files already pre-written for you.</p><p>You’ll also need a Google Cloud Project with the <a href="https://console.cloud.google.com/apis/library/cloudbuild.googleapis.com">Cloud Build API</a> enabled. When you enable the Cloud Build API, your project will generate a default Cloud Build service account that looks like this:</p><p>98476438052@cloudbuild.gserviceaccount.com</p><p>This service account automatically has the Cloud Build Service Account IAM role assigned to it, allowing it to do all the Cloud Build things it needs to be able to do, like manage Google Cloud Storage buckets and access Artifact Registry. You can see a full list of permissions <a href="https://cloud.google.com/build/docs/cloud-build-service-account#default_permissions_of_service_account">right here</a>.</p><h3>Tell Terraform what to do</h3><p>Terraform gets its instructions from .tf files. For the sake of simplicity, let’s use this main.tf file I copied and pasted from one of <a href="https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string">Hashicorp’s documentation pages</a>:</p><pre># main.tf <br><br>resource &quot;random_string&quot; &quot;random&quot; {<br>  length           = 16<br>  special          = true<br>  override_special = &quot;/@£$&quot;<br>}<br><br>output &quot;random_string&quot; {<br>    value = random_string.random.id<br>}</pre><p>There’s nothing fancy to see here. It just creates a random string resource and spits its value out as output.</p><h3>Tell Cloud Build what to do</h3><p>Cloud Build accepts yaml configuration files. Here is a simple one that will execute those 3 magic Terraform commands:</p><pre># cloudbuild.yaml<br><br>steps:<br>  - id: &#39;terraform init&#39;<br>    name: &#39;hashicorp/terraform:1.0.0&#39;<br>    script: terraform init<br>  - id: &#39;terraform plan&#39;<br>    name: &#39;hashicorp/terraform:1.0.0&#39;<br>    script: terraform plan<br>  - id: &#39;terraform apply&#39;<br>    name: &#39;hashicorp/terraform:1.0.0&#39;<br>    script: terraform apply --auto-approve</pre><p>The id field is the name that will show up in the logs, and the name field is the public image that it will build and use to execute the provided command.</p><p>You can kick off a Cloud Build job by running gcloud builds submit . in your working directory. It will automatically look for a cloudbuild.yaml file and create a Cloud Build job that will execute the provided instructions. You can see the progress as output in your terminal or head over the <a href="https://console.cloud.google.com/cloud-build/builds">Cloud Build History</a> page where you’ll see your job’s status and output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JaLkrJRueVbGp2Ns5DqK8A.png" /><figcaption>Wow.</figcaption></figure><p>And there you have it! Cloud Build executing Terraform! That’s all there is to it!</p><h3>Wait a second, there must be more to it</h3><p>Okay, actually there’s a lot more to it. While we did indeed run Terraform with Cloud Build we didn’t actually deploy any infrastructure (or anything useful for that matter). There are some things that we need to consider.</p><h4>Permissions</h4><p>Cloud Build creates a default service account with <a href="https://cloud.google.com/build/docs/cloud-build-service-account#default_permissions_of_service_account">those default Cloud Build service account permissions</a> assigned to it. Any additional permissions that this service accounts needs will depend on the cloud resources that you want your Terraform to create. If you’re managing resources across lots of different Google Cloud services, then the Cloud Build service account will inevitably accumulate excessive permissions, resulting in a potential security risk. Yuck.</p><h4>Terraform state file</h4><p>You’ll notice that the Terraform state file is no where to be found. The state file that would have been generated to your local disk had you ran terraform apply locally only existed temporarily in the container that Cloud Build ran the command from. If this Terraform had created a cloud resource, you would have no way to update its parameters using a second terraform apply or destroy it using terraform destroy. Ugh.</p><h4>That — auto-approve flag</h4><p>If this were an automated pipeline, we probably wouldn’t be running running apply automatically after plan. Without approvals, reviews, or staging environments in place, we can’t really make sure our infrastructure doesn’t fall apart after each terraform job. Yikes.</p><h3>Key takeaways</h3><ul><li>Enabling the Cloud Build API will create a default Cloud Build service account in your project. Cloud Build jobs run using this service account by default.</li><li>Cloud Build’s instructions are provided as steps listed in a yaml file, each one with it’s own output that show up in the build logs. If the Terraform creates any cloud resources, the service account will need the appropriate permissions to manage those resources.</li><li>Without any remote state configured, the state file will only exist temporarily and then get lost in the abyss.</li></ul><h3>Well, what next?</h3><p>While limited in its utility, this example can serve as a starting point for your own exploration around the considerations I mentioned above. Service account impersonation will definitely play a role in ironing out the permissions (here’s a blog about Terraform and service account impersonation I published <a href="https://cloud.google.com/blog/topics/developers-practitioners/using-google-cloud-service-account-impersonation-your-terraform-code">here</a>), while Terraform’s support for <a href="https://developer.hashicorp.com/terraform/language/settings/backends/gcs">remote state files on Google Cloud Storage</a> will give us the persistent state that we need to keep our infrastructure updated safely. As for the lack of approvals and reviews, there are unlimited ways to design your pipeline to accommodate such requirements with features like <a href="https://cloud.google.com/build/docs/securing-builds/gate-builds-on-approval">gating builds on approvals</a>. I won’t leave you high and dry though. I will post a follow up soon with another bite of our elephant for us to explore.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=723f5fb58bca" width="1" height="1" alt=""><hr><p><a href="https://blog.devops.dev/terraform-using-google-cloud-build-a-very-basic-example-723f5fb58bca">Terraform using Google Cloud Build: a very basic example</a> was originally published in <a href="https://blog.devops.dev/">DevOps.dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Three Things About Google Cloud Service Accounts]]></title>
            <link>https://roger-that-dev.medium.com/three-things-about-google-cloud-service-accounts-a55466e5efd0?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/a55466e5efd0</guid>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[gcp]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Mon, 08 Mar 2021 18:18:10 GMT</pubDate>
            <atom:updated>2021-03-08T18:18:10.603Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’re just starting out on Google Cloud for the first time, it’s easy to breeze through the concept of service accounts. Some service accounts, like the App Engine or Compute Engine default service accounts, are even created for you as soon as you turn those features on. After that, it’s pretty easy to create a new service account and then throw whatever permissions and roles you think you need to make things work. But before you go and do that, take a sec to understand these 3 things.</p><h3>Service Accounts Deserve a Purpose</h3><p>When you’re working out of a development project, it can be tempting to use a generic all-powerful dev service account with unfettered access to everything via the Editor role. Even some of the default service accounts are created with that access. But you’ve heard of the <a href="https://www.youtube.com/watch?v=8GZ6516Kao4">principle of least privilege</a> right? It’s the thing system administrators have been yelling about for decades whenever someone asks for root access. It means that you should only have access to the minimum permissions you need to do your job. This goes for services accounts too.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/435/0*aua-ALw0jjE5YFWT" /><figcaption>Your purpose is to publish messages to this pub/sub topic</figcaption></figure><p>Before you even assign roles and permissions, think through what you’re going to use that service account for and make it as specific as possible. Then make that purpose clear to you and anyone else who might manage it later on by adding a clear <strong>display name </strong>and <strong>description</strong>. This is a great way to quickly and informally document what a service account is meant to do. You’ll know if a display name and description are good enough if they an answer a few questions by just looking at them:</p><ul><li>What roles could this account probably have assigned?</li><li>What services or APIs could this account probably accessing?</li><li>What applications/users/systems could be using this account?</li><li>What would probably break if I disable this account?</li></ul><p>When it comes to assigning roles and permissions, if the service account is coming from a dummy dev project, then it’s not such a big deal if you misconfigure some permissions here and there. But what better place to figure it out than in development? Finding the right IAM roles and permissions for your service accounts can be pretty straightforward, but it can be tricky if your needs don’t fit perfectly into any of the predefined roles. You need to identify what your service account needs — and in some cases what it will not need — to assign the appropriate roles. So as easy as it is, resist the temptation to assign Editor to your service accounts. It’s worth the extra effort to narrow things down early on and give your service account a clear purpose.</p><h4><em>Something to try:</em></h4><p>If you’ve got some mysterious looking service accounts in your project, update the description and the display name in one shot:</p><pre>gcloud iam service-accounts update SERVICE_ACCOUNT — display-name “A DECENT DISPLAY NAME” — description “A DESCRIPTIVE DESCRIPTION”</pre><p>The display name should be short, sweet and to the point. The description has a character limit of 256 so feel free to go nuts.</p><h3>Service Accounts are Resources Too</h3><p>As it turns out, a service account is more than just an IAM account. They’re resources too. The same way you might want to limit which people have access to a server or a database, you want to make sure you do that for your service accounts, too.</p><p>An easy mistake to make when you want to give someone access to any service account is to grant the permissions on the<strong> project </strong>IAM policy. For example, by granting the <em>Service Account Token Creator </em>role to a user on a project policy, you are giving them the power to impersonate all service accounts on the project, and by extension, access all resources accessed by those service accounts. That’s a bit much.</p><p>Luckily, since service accounts are resources, each one has an IAM policy of its own attached to it. There you can grant service account IAM roles that are binded to users for that specific account instead of project wide. For example, if you add <em>joe@yourorg.com</em> as the Service Account Token Creator on <em>dev-sql-service@yourproject.iam.gserviceaccount.com</em>, then Joe will have that role’s privileges over that service account only. This way, Joe won’t be able to accidentally mess with any of the other important stuff that you don’t want Joe to access. Sorry Joe.</p><h4>Something to try:</h4><p>Check the IAM policy for a service account with this gcloud command:</p><pre>gcloud iam service-accounts get-iam-policy SERVICE_ACCOUNT</pre><p>By default these are blank. Add a user to the policy like this:</p><pre>gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT --member MEMBER — role=ROLE</pre><p>The MEMBER will be the user account’s email address and the ROLE will be IAM role you want to grant that user (in the<em> roles/</em> format). Here’s a list of the<a href="https://cloud.google.com/iam/docs/understanding-roles#service-accounts-roles"> service account roles </a>that currently exist.</p><h3>You don’t always need private keys</h3><p>When you’re accessing Google Cloud from your local machine, how do you access the service accounts you need? If your answer involves generating a json key every time you need one then you deserve to know that there’s an easier and more secure way.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*zQI3meyYhTDnGjc4" /><figcaption>Sheesh</figcaption></figure><p>Instead of using keys, with the right permissions, you can use<strong> short-lived credentials</strong>. It’s a way of doing basically the same thing with a little less hassle. The <strong>Service Account Token Creator</strong> role allows a user to generate short-lived credentials on a service account. If you’re accessing Cloud via REST API then you can generate access tokens for that service account. If you’re mostly just running gcloud commands, then you can stick the --impersonate-service-account flag instead of using a key file.</p><h4>Something to try:</h4><p>First, you’ll need to grant the <strong>Service Account Token Creator</strong> role to the user for the service account they want to use. You can do that with the command I mentioned above:</p><pre>gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT --member MEMBER --role=roles/iam.serviceAccountTokenCreator</pre><p>With that in place, the user can run gcloud commands as a service account by tacking on the — impersonate-service-account flag along with the service account email address. The same functionality is extended to gsutil if you’re working with GCS buckets, except they’ll use the -i flag instead, followed by the service account. Easy.</p><p>If you want to access the Google Cloud REST API as a service account using an access token, first get an access token for your own account and stick it in an environment variable:</p><pre>export MYTOKEN=$(gcloud auth print-access-token)</pre><p>Next, you’ll have to include a request in the form of a json object. Throw that into a file that looks like this:</p><pre>{<br>&quot;delegates&quot;: [<br>],<br>&quot;scope&quot;: [<br>&quot;<a href="https://www.googleapis.com/auth/cloud-platform">https://www.googleapis.com/auth/cloud-platform</a>&quot;,<br>],<br>&quot;lifetime&quot;: &quot;3600s&quot;<br>}</pre><p>For this example, I named that file request.json. This is a pretty barebones version of a request. The “delegates” here are used if you’ve got a conga line (delegation chain) of service accounts that are used to access one another. If that’s not the case, don’t worry about it, you can leave this blank.</p><p>As for the scope, this refers to the Oauth 2.0 scope that you’ll need to access certain API’s. What you include here will depend on what you want to do with the service account you’re requesting a token for. A full list of APIs and their corresponding scopes is documented <a href="https://developers.google.com/identity/protocols/oauth2/scopes">here</a>.</p><p>And finally, the lifetime is simply how long you want the token to be good for, in seconds.</p><p>Once you’ve got all that, you can request a token with this curl command that will send a POST request to the Google Cloud API:</p><pre>curl -X POST <a href="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com:generateAccessToken">https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com:generateAccessToken</a> -H “Authorization: Bearer $MYTOKEN” -H “Content-Type: application/json; charset=utf-8” -d @request.json</pre><p>The output here will be the access token for the service account that you can subsequently use to hit up the API as that service account. Tah dah.</p><h3>More Things About Service Accounts</h3><p>That’s just 3 things about service accounts but there’s a lot more information on best practices when it comes to making them, managing them, and using them. Here’s a link to the official <a href="https://cloud.google.com/iam/docs/understanding-service-accounts">documentation</a> if you’re into that sort of thing. If you want to learn more about short-lived service account credentials, you can find that right <a href="https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials">here</a>. If you’re tired of reading, check out a video series on the Google Cloud Tech Youtube channel <a href="http://goo.gle/WhatsWhat%E2%80%8B">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a55466e5efd0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Habit Stacking for a Balanced Digital Diet]]></title>
            <link>https://roger-that-dev.medium.com/habit-stacking-for-a-balanced-digital-diet-4a5d578e3c5f?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/4a5d578e3c5f</guid>
            <category><![CDATA[digital-detox]]></category>
            <category><![CDATA[the-social-dilemma]]></category>
            <category><![CDATA[social-media]]></category>
            <category><![CDATA[health]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Wed, 23 Sep 2020 01:37:00 GMT</pubDate>
            <atom:updated>2020-09-23T01:59:02.518Z</atom:updated>
            <content:encoded><![CDATA[<h3>Hack Your Habits for a Balanced Digital Diet</h3><blockquote>“You become what you give your attention to…If you yourself don’t choose what thoughts and images you expose yourself to, someone else will.”</blockquote><blockquote>— Epictetus, O.G. Greek Philosopher</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HKGEwmnrKkuE_lx-7uyXVg.jpeg" /></figure><p>You’re familiar with this pattern: You’ve got something you need to work on, an important assignment for work or school. You sit down and decide to get started.</p><p>You’re ready.</p><p>You’re in the zone.</p><p>It’s time to <strong><em>focus</em></strong>.</p><p>Ten seconds in: BZZZZZZ! Without giving it a second thought, reflexively, you pick up your phone and see what the fuss is about. A half hour later you put your phone down and don’t even remember why you picked it up in the first place, but for some reason unexplained reason you now know that the guy you went to high school with had sushi for dinner last night.</p><p>Okay, now it’s time to <strong>focus</strong>.</p><p>For reals this time.</p><p>A few minutes later: BZZZZZ! Rinse and repeat. On a bad day, I’d go through this series of events in an endless loop, resulting in an embarrassingly minuscule amount of actual productivity. For the longest time I chalked it up to a self-diagnosed case of ADHD and I got by the best I could. It wasn’t until I read <em>Atomic Habits </em>by James Clear that I considered that this was actually a series of habits wired in my brain that I’d be able to reprogram with a little self awareness and a bit of effort. I do still have those bad days, but I’ll share 3 strategies that have helped those bad days be less bad.</p><blockquote>“BZZZZZZ!” — algorithms, trying to control your behavior</blockquote><h3>First, a Quick Recap on Habits</h3><p>One of the main takeaways from <em>Atomic Habits </em>is that habits, good or bad, are essentially neurological feedback loops. For any given habit, a <strong>cue</strong> triggers a <strong>craving</strong> that motivates a <strong>response</strong> to get a <strong>reward</strong>. For example, in the case of social media: a notification on my phone (cue) reminds me of whatever warm fuzzy feeling I get from a like or retweet (craving) and causes me to open an app (response) so that i can feel that warm fuzzy feeling (reward).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/971/1*Pc2zEJ3reaNHP4dkt2yEtQ.png" /><figcaption>Source: <a href="https://jamesclear.com/habit-triggers">https://jamesclear.com/habit-triggers</a></figcaption></figure><p>Our days are chock-full of these feedback loops and we don’t even realize it. Take the classic fridge example. Have you ever walked to the kitchen and opened up the fridge 100 times a day even though you already know what’s in there? Yep, that’s an automated feedback loop where the cue is your location in the kitchen, the craving is something yummy to put in your tummy, the action is opening up the fridge and the reward is supposed to be food, which may or may not be there.</p><p>So knowing this, let’s see if we can exploit this loop so that maybe you don’t pick up your phone in the first place.</p><h3>Curb the Cue: Nix Your Notifications</h3><p>John Oliver had a delightful<a href="https://www.youtube.com/watch?v=kxatzHnl7Q8"> segment about push notifications</a> and he nails it. What started out as a really nifty feature has devolved into a way for thirsty corporations desperate for clicks to trick you into unlocking your phone and interacting with their product to sell you things you probably don’t need. It’s time to stand up to those apps and say “no, not today.”</p><p>Most apps have notification options, or you can simply block them on the operating system level on both Android and iOS. Once you get rid of the notifications, the buzz buzz that triggers you to check your phone is gone gone. We’ve eliminated the cue!</p><p>At this point you’re probably thinking to yourself, well duh. I know this isn’t a groundbreaking strategy. Well hold on. Sometimes this isn’t enough. I realized that after nuking my notifications, I was still mindlessly scrolling, swiping and tapping. And that’s because sometimes the cue isn’t as obvious as a notification. When I was already on my phone, just the mere presence of the app’s aesthetically pleasing icon on my home screen was a cue, reminding me to tap that app. So, what can we do about that?</p><h3>Rein in Your Response: Anchor Your Apps</h3><p>I’ve been off of Facebook for about 2 years now, but I’m not here to tell you to #DeleteFacebook (you totally should though) or any other social media service for that matter. There’s a nice middle ground to keep your digital addictions in check, and it targets your response to the craving.</p><p>Way back before we could fit it in our back pockets, the internet was anchored to a chunky box sitting in our homes. As a teenager, I had a home PC. If I wanted to check my MySpace and update my top 8 (younger readers can look this up) I couldn’t do that in the middle of the day while waiting in line at the grocery store as soon as the thought popped in my head. Nope, I had to wait until I got home to my beloved chunky box. And even then, before broadband was available in my neighborhood, I had to wait ages for my mom to get off the phone. Those days it was a little harder for social media to become a bad habit in the first place. In hindsight, it was nice.</p><p>We can recreate this mindset with a bit of effort. I won’t suggest that you uninstall every single smartphone app from your phone, that’s crazy talk. But how about this: choose 1 or 2 apps that frequently take your attention hostage and remove them from your phone. Once you’ve done that, create a new habit of only accessing those apps or websites from a device that you don’t carry around all day, like a tablet, laptop, or PC. Truthfully, some of them are better in a browser anyway. This works great for news apps, especially if you’re trying to detox from the American news cycle. It also works great for Twitter if you’re okay with not live tweeting your day to day. By doing this, now you’re really telling those apps who’s in charge of your attention. Fun fact: Chrissy Teigen tweets from a computer, so maybe she’s got the right idea.</p><h3>Establish a No Phone Zone</h3><p>Remember the example I mentioned about the fridge in the kitchen? Well if you wanted to break that habit of mindlessly opening the refrigerator over and over again, one way of doing it is by straight up removing it from the kitchen. Boom, problem solved. That strategy isn’t very practical for refrigerators but works great for phones.</p><p>I used to charge my phone overnight right next to my bed. On some mornings after snoozing and then dismissing my alarm, I’d just lay there mindlessly scrolling through feeds and half-assedly reading the breaking news of the day (it’s never good). The fact that this would happen first thing in the morning meant that my mood at the start of my day was at the mercy of whatever content some algorithm decides I should care about.</p><p>Nowadays, I place my phone on the other side of the room and as soon as I wake up, I dismiss the alarm and start my day. I don’t even check my messages or apps for another few hours on a typical day. This empowered me to keep my mind clear and have a morning routine that isn’t dictated by apps. Even before bed or during the day, I’m committed to never using my phone near my bed. If you’re in a relationship, bonus points if you can convince your significant other to do the same (good luck).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0TKeZclWD5_Jp6KAZel7mg.jpeg" /></figure><p>Of course your no phone zones can be wherever you’d like: the desk where you do your work, the table where you eat your meals, the bathroom where you.. well you get the point. It doesn’t matter where your zones are, what matters is that you have them so that you can interrupt the attention-stealing habit loop and give you a moment to yourself to be present in the real world.</p><h3>Revisit and Revise</h3><p>So these are just three strategies you can try out to keep your digital diet in check. They’re not perfect and there are tons more. Some days you’ll look back and shake your head at the 2 hours you spent on Reddit between the 3 hours you scrolled on Facebook. Then maybe you’ll tweet about it. That’s fine.</p><p>If nothing else, the one thing you should definitely try to do on occasion is revisit the habit feedback loop and see where you can tighten up and take control. It’ll at least remind you to be mindful of the time you spend with your face inside a screen, and hopefully free yourself from its spell.</p><p>Okay, back to work!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4a5d578e3c5f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Cloud Tips and Tricks: Understanding the Resource Hierarchy]]></title>
            <link>https://medium.com/google-cloud/google-cloud-tips-and-tricks-understanding-the-resource-hierarchy-3227f225a8e7?source=rss-e7724d73a96------2</link>
            <guid isPermaLink="false">https://medium.com/p/3227f225a8e7</guid>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <dc:creator><![CDATA[Roger Martinez]]></dc:creator>
            <pubDate>Fri, 06 Mar 2020 17:40:44 GMT</pubDate>
            <atom:updated>2020-04-09T20:49:01.604Z</atom:updated>
            <content:encoded><![CDATA[<p>So, you want to get started building out your infrastructure on Google Cloud. You’ve read all about how cloud is the future and you’re ready to make moves. But building out on cloud can seem overwhelming. Theoretically, you could spin up a gargantuan server that could wind up costing thousands of dollars in a few short days. Yikes.</p><p>It sounds frightening, but it doesn’t have to be. I’ll share a few important tips (small pieces of advice) and tricks (helpful hints)** for organizing your resources on Google Cloud. My hope is that what I’ll share will help you get started quicker, and with a better understanding of what you’re running in the cloud. Let’s make things a lot more simple and a lot less yikes.</p><h3>Resource Hierarchy</h3><p>I’ve heard that there’s more than one way to skin a cat. While I don’t know what that means, I do know that there’s more than one way to organize your Google Cloud resources. That’s the resource hierarchy.</p><p>The hierarchy can map to your organization’s structure, and allows you to manage access and permissions to all of your cloud resources. Here’s an example of a resource hierarchy, straight from Google Cloud’s official Resource Hierarchy <a href="https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy">documentation</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*Id_WW48rv5_nRhPH" /><figcaption>Your hierarchy may vary</figcaption></figure><p>Your resource hierarchy could be as simple as a single project under an organization, but if you’ve got bigger plans, there are additional methods for organizing things so that you’ll get a better understanding of which teams are using which resources and are responsible for which costs.</p><h4><strong>Tip: Do some planning</strong></h4><p>Understanding the resource hierarchy will enable you to organize your resources in the way that best suits your organization. Do some diagramming similar to the one above to get an idea of what your resource hierarchy might look like. If your organization is grouped by departments, maybe it makes sense for those to map to folders. Take into account the primary attach points for access management — organization level, folder level, and project level — and remember that permissions are inherited from top to bottom.</p><h4><strong>Trick: Start Simple</strong></h4><p>The resource hierarchy is flexible. You can always move projects, folders, and change permissions after the initial setup, so don’t feel overwhelmed by your planning. Get familiar with organizing resources while diagramming so you can make smaller and quicker changes.</p><h3>The Domain and Organization</h3><p>Here’s the tippy-top of the resource hierarchy. When you sign up for Cloud Identity or G Suite, you’ll be asked to provide a <strong>domain</strong>. When that’s verified an <strong>organization</strong> is created. It is essentially the umbrella of all things created in Google Cloud. All projects created will belong to the organization rather than the user who created them. This is especially important in cases where a project owner leaves the organization. Permissions set at the organization level are inherited to folders and projects below.</p><h4><strong>Tip: One organization is all you need</strong></h4><p>You might have the need to manage multiple organizations. However, your G Suite or Cloud Identity account is limited to one single organization and one single domain. Consider organizing your sub-organizations as folders instead.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/736/0*ypowfBIGDA50pxA6" /></figure><p>If that truly doesn’t work for your needs, you can manage multiple G Suite or Cloud Identity accounts and therefore multiple organizations. The downside here is that they will be isolated from each other, require separate domains, and lack centralized access and administration. There’s more information on the effects of using multiple organization nodes <a href="https://cloud.google.com/resource-manager/docs/managing-multiple-orgs#effects_of_using_multiple_organization_nodes">here</a>.</p><h4><strong>Trick: Start with IAM</strong></h4><p>If you’re just getting started and have your organization set up, the initial account you start with will have limited permissions. In fact, you’ll only have the role <strong>Organization Admin</strong>. An <strong>organization admin</strong> will be able to access all projects that belong to the organization and assign IAM roles to other users. Before exploring folder and project creation beneath your organization, take a moment to check the <a href="https://console.cloud.google.com/projectselector2/iam-admin/org-onboarding">Identity &amp; Organization page.</a> From here you’ll be able to migrate projects, create a billing account, expand permissions for yourself or other users, and even delegate the organization admin permissions to others.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Fe5DRKMSvoGUh-qZ" /><figcaption>The Identity and Organization page for IAM &amp; Admin</figcaption></figure><h3>Billing accounts and payments profile</h3><p>A<strong> billing account</strong> is a cloud-level resource that’s used to manage billing-related functions. This is where you’ll define the payment methods you’ll use to pay for your Cloud usage, access reports and forecasts for your costs, generate a budgets to avoid surprises, and pretty much anything else having to do with bills.</p><p>While a billing account is managed in Cloud Console, the<strong> payments profile</strong> is a Google-level resource that connects to all of your Google services like Cloud, Ads or Chrome licenses. I’m mentioning it here because a billing account is required to be linked to an existing payments profile or one will be created. If you don’t already pay for other Google services you may not need to worry about connecting to an existing payments profile.</p><h4><strong>Tip: Use a single billing account</strong></h4><p>You are allowed to configure multiple billing accounts per organization, but I recommend sticking to a single billing account instead. Associating all of your organization’s projects to one billing account will give you a more complete and centralized view of your costs and will make them easier to track.</p><h4><strong>Trick: Understand billing account users, viewers, and admins</strong></h4><p>There are 3 types of billing account roles you can delegate: billing account <strong>users</strong>, billing account <strong>viewers</strong>, and billing account <strong>administrators</strong>. While admins can unlink projects, set budgets and initiate billing exports, billing account users can only associate projects and see spend. Billing account admins are also the only ones who can contact billing support. Billing account viewers have the same permissions as users, but with a bit more visibility. Pay special attention to the differences among users, viewers and admins and delegate each role to groups or people accordingly. As a reference, here are the permissions of each role side by side:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_lQKQKwascMb3jdi" /></figure><h3>Resources</h3><p>The bread and butter. The meat and potatoes. Resources are any Google Cloud service, like BigQuery, Compute Engine, Cloud Storage, etc. Resources are organized into <strong>projects.</strong></p><h4><strong>Tip: Think about your folders</strong></h4><p>Projects can be organized into <strong>folders</strong>. Much like a basic filesystem, a project will inherit the permissions and access from the folders to which it belongs, as well as the organization above that. If you’re planning on having multiple projects on Google Cloud, or if you’ve got multiple teams that will be deploying to Google Cloud under your organization, definitely consider using folders. Like I mentioned earlier, folders are an additional attach point for access management and will make it easier to delegate roles and permissions. It’ll also give you another way to break down billing charges at the end of the month by allowing you to<a href="https://cloud.google.com/blog/products/data-analytics/analyzing-gcp-costs-using-folders-and-bigquery-billing-export"> analyze costs by folder</a> rather than individual projects if you export to Big Query.</p><h4><strong>Trick: Use labels for extra granularity</strong></h4><p>Labels provide a way to horizontally organize your projects’ resources and help you gain a more granular view of costs. For example, let’s say you’ve got a project with VMs and Kubernetes clusters corresponding with either a staging or production environment. By applying a label with a key ‘environment’ and a value of either ‘staging’ or ‘production’, you can see what spending is per environment instead of just resource type. The more granularity you have, the better you’ll be able to understand your costs and keep them under control. You can read more about labels <a href="https://cloud.google.com/blog/products/gcp/use-labels-to-gain-visibility-into-gcp-resource-usage-and-spending?hl=nl">here</a>.</p><h3>More Resources to Get Started</h3><p>To help you get started on Cloud, Google offers a <a href="https://console.developers.google.com/billing/freetrial?hl=en">free trial with a $300 credit.</a> Their pricing landing page is right <a href="https://cloud.google.com/pricing">here</a>. To learn more about billing check out the <a href="https://www.youtube.com/playlist?list=PLIivdWyY5sqKJx6FwJMRcsnFIkkNFtsX9">Beyond Your Bill series</a> on Youtube by <a href="https://twitter.com/markmirch">Mark Mirchandani</a> that goes over most of the content above and much more.</p><p>Good luck!</p><p><em>** No, there is no distinguishable difference between a tip and a trick.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3227f225a8e7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-cloud/google-cloud-tips-and-tricks-understanding-the-resource-hierarchy-3227f225a8e7">Google Cloud Tips and Tricks: Understanding the Resource Hierarchy</a> was originally published in <a href="https://medium.com/google-cloud">Google Cloud - Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>