<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Glenn Bostoen</title>
    <description>The latest articles on Forem by Glenn Bostoen (@gbostoen).</description>
    <link>https://forem.com/gbostoen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F68402%2F0832407b-b72d-4b9d-9ecd-475dbda4bb93.jpeg</url>
      <title>Forem: Glenn Bostoen</title>
      <link>https://forem.com/gbostoen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gbostoen"/>
    <language>en</language>
    <item>
      <title>The Unglamorous Secret to Claude Code Productivity</title>
      <dc:creator>Glenn Bostoen</dc:creator>
      <pubDate>Mon, 26 Jan 2026 22:18:59 +0000</pubDate>
      <link>https://forem.com/gbostoen/the-unglamorous-secret-to-claude-code-productivity-577j</link>
      <guid>https://forem.com/gbostoen/the-unglamorous-secret-to-claude-code-productivity-577j</guid>
      <description>&lt;p&gt;After months of hands-on experience with Claude Code (from personal experiments to production deployments and even onboarding non-technical team members) I've distilled my learnings into a practical guide for anyone looking to move beyond basic usage into truly using this tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The productivity divide is real (but misunderstood)
&lt;/h2&gt;

&lt;p&gt;There's an emerging divide among Claude Code users: those who've figured out how to make it transformative, and those still using it like a slightly smarter Stack Overflow. The difference isn't secret prompts or hidden features. It's a fundamental shift in how you think about the tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key insight&lt;/strong&gt;: effective use of Claude Code is about delegation, not dictation. You need to break down large tasks, define projects clearly, distribute work, and adapt your approach as you learn the model's strengths and limits.&lt;/p&gt;

&lt;p&gt;What doesn't work: treating it as a magic oracle that produces perfect code from vague descriptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool access changes everything
&lt;/h2&gt;

&lt;p&gt;The single biggest upgrade to my Claude Code workflow was giving it access to the actual tools I use as a developer. This transforms it from "smart autocomplete with chat" into an agent that operates within your development environment.&lt;/p&gt;

&lt;p&gt;When Claude Code can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search your codebase and understand file structures&lt;/li&gt;
&lt;li&gt;Run tests and interpret results&lt;/li&gt;
&lt;li&gt;Check code against linters and formatters&lt;/li&gt;
&lt;li&gt;Query your documentation and API references&lt;/li&gt;
&lt;li&gt;Understand git context and branch history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...the results align dramatically better with expectations. The model isn't guessing at your architecture. It's reading it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crucially, tools let it validate its own work.&lt;/strong&gt; Don't use an LLM to check syntax; use a linter. Don't ask it to verify types; run the type checker. Don't have it eyeball whether tests pass; execute them. The model is good at many things, but deterministic validation isn't one of them. Every tool you give it is one less thing it can hallucinate about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where this breaks down:&lt;/strong&gt; The model operates with a mental model of "if it passes locally, it's correct." This is often true—until it isn't. Environment config, data volume, service dependencies, and infrastructure quirks don't exist in your local checkout. No amount of tooling closes this gap entirely. Knowing which changes need extra scrutiny before production is still a human skill.&lt;/p&gt;

&lt;p&gt;This principle led directly to integrating Claude Code into our CI/CD pipelines, giving it real repository access, the ability to respond to MR comments, and run automated reviews. The key was treating it as stateless: inject context via pipeline variables, let it do its work, done. No session management complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLAUDE.md: the instructions that matter
&lt;/h2&gt;

&lt;p&gt;There's an underground circulation of CLAUDE.md configurations and instruction libraries. After experimenting with many approaches, here's what I've found actually moves the needle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What works&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project-specific context (tech stack, patterns used, naming conventions)&lt;/li&gt;
&lt;li&gt;Explicit tool permissions and boundaries&lt;/li&gt;
&lt;li&gt;Clear success criteria for common tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What doesn't work&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overly prescriptive step-by-step instructions (the model handles decomposition better than most instructions)&lt;/li&gt;
&lt;li&gt;Personality instructions (they don't meaningfully improve output)&lt;/li&gt;
&lt;li&gt;Excessive safety guardrails beyond what's already built in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The communities sharing instruction files are useful, but deep understanding comes from hands-on experimentation. What works for a React project won't work for a Python backend, and copying someone else's CLAUDE.md without understanding why it works is cargo culting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context is everything
&lt;/h2&gt;

&lt;p&gt;This might be the most important lesson: Claude Code needs the full picture before it starts working. Without complete context, you'll see duplicated utilities, inconsistent patterns, and solutions that ignore existing infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before starting any task&lt;/strong&gt;, make sure it understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What already exists in the codebase that's relevant&lt;/li&gt;
&lt;li&gt;The patterns and conventions already in use&lt;/li&gt;
&lt;li&gt;What you're trying to achieve and why&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Between tasks&lt;/strong&gt;, clear the slate. Residual context from previous work will bleed into new tasks: correlating unrelated problems, carrying forward assumptions that no longer apply, or "fixing" things that weren't broken. A fresh conversation for each distinct piece of work keeps outputs focused.&lt;/p&gt;

&lt;p&gt;The pattern I've landed on: front-load context aggressively at the start of each session, then let it work. Drip-feeding requirements mid-task leads to patches on patches. Complete context upfront leads to coherent solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  On-distribution choices compound
&lt;/h2&gt;

&lt;p&gt;There's a concept in AI of "on distribution" vs "off distribution": whether the model already knows how to do something well or needs to be taught. This applies directly to tech stack choices.&lt;/p&gt;

&lt;p&gt;When building AI-augmented workflows, choosing technologies Claude is already good at (TypeScript, React, Python, standard tooling) means less fighting and more flow. An exotic or niche stack isn't impossible, but you're spending context teaching the model things it could've known for free.&lt;/p&gt;

&lt;p&gt;This isn't about limiting creativity. It's about recognizing that LLM productivity gains come from working &lt;em&gt;with&lt;/em&gt; the model's strengths, not proving you can make it work despite them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onboarding non-engineers: harder than expected
&lt;/h2&gt;

&lt;p&gt;The promise of Claude Code ("anyone can code now") made me curious. I ran an experiment onboarding a product designer to Claude Code for direct iteration in our codebase. The results were instructive, but not in the way I expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we discovered&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup is the real barrier. SSH keys, Brew, Docker/Colima architecture, Git config, branch management, package dependencies. These took ~1.5 hours with two engineers helping.&lt;/li&gt;
&lt;li&gt;The comment that stuck with me: "Now I get why project setup takes two weeks."&lt;/li&gt;
&lt;li&gt;Once setup was done, simple changes worked well. Complex debugging was still a blocker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The real insight&lt;/strong&gt;: This experiment made me realize how much implicit knowledge developers carry. We forget that "just run the dev server" assumes you know what a dev server is, that ports exist, that something else might be using port 3000. Claude Code lowers the barrier for &lt;em&gt;writing&lt;/em&gt; code, but all the surrounding knowledge (environments, dependencies, version conflicts, debugging strategies) is still required. The tool doesn't eliminate the need for developer knowledge; it just shifts where that knowledge matters most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sustainability question&lt;/strong&gt;: If someone uses this weekly, they build muscle memory and it pays off. If it's monthly, they forget workflows and need hand-holding each time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My current thinking&lt;/strong&gt;: There's a frequency threshold, around every 2-3 days, where this becomes valuable. Below that, a prototyping tool in between might be more practical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipeline-first development: the mindset shift
&lt;/h2&gt;

&lt;p&gt;When AI can generate code faster than humans can review it, your pipeline becomes your primary quality gate, not your colleagues' availability.&lt;/p&gt;

&lt;p&gt;This is the mindset shift: &lt;strong&gt;a proper automated pipeline matters more than ever&lt;/strong&gt;. Tests, linting, type checks, security scans. These can't be "nice to haves" anymore. They're the foundation that makes AI-assisted development viable. If your CI is flaky or your test coverage is patchy, "CI green" means nothing, and you're back to manual review bottlenecks that can't keep pace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The old model&lt;/strong&gt;: Reviews as permission gates, approval required before merge, long-lived branches waiting for sign-off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The new model&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pipeline does the hard work automatically, no human in the loop for mechanical checks&lt;/li&gt;
&lt;li&gt;Reviews become sanity checks for architectural decisions and obviously wrong logic&lt;/li&gt;
&lt;li&gt;Auto-merge for low-risk changes when CI is green&lt;/li&gt;
&lt;li&gt;Fix forward when things slip through. The cost of a bad merge is lower than MRs sitting in queues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Invest in your pipeline first. The teams getting the most from Claude Code aren't the ones with the cleverest prompts. They're the ones whose CI actually catches problems before humans need to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Claude Code isn't a replacement for engineering skill. It's an amplifier. Strong project management and software engineering fundamentals matter more, not less, when AI is generating code at speed.&lt;/p&gt;

&lt;p&gt;The competitive advantage isn't secret prompt libraries. It's the broader skills in task decomposition, workflow orchestration, and knowing when the AI's suggestion is brilliant vs. confidently wrong.&lt;/p&gt;

&lt;p&gt;Start with giving it access to your actual tools. Integrate it into your existing workflows rather than building parallel ones. And remember: clear context in, quality output out.&lt;/p&gt;

&lt;p&gt;original post: &lt;a href="https://gbostoen.dev/blog/claude-code-learnings/" rel="noopener noreferrer"&gt;https://gbostoen.dev/blog/claude-code-learnings/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>tooling</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Surprising Simplicity of Temporal Worker Pools on Cloud Run</title>
      <dc:creator>Glenn Bostoen</dc:creator>
      <pubDate>Mon, 26 Jan 2026 22:17:17 +0000</pubDate>
      <link>https://forem.com/gbostoen/the-surprising-simplicity-of-temporal-worker-pools-on-cloud-run-14db</link>
      <guid>https://forem.com/gbostoen/the-surprising-simplicity-of-temporal-worker-pools-on-cloud-run-14db</guid>
      <description>&lt;p&gt;If you've ever spent an afternoon debugging indentation errors in Google Workflows YAML only to discover the real problem was a cryptic &lt;code&gt;${}&lt;/code&gt; expression, you'll understand why we made the switch to Temporal. What we didn't expect was just how &lt;em&gt;simple&lt;/em&gt; the deployment would be.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem we were solving
&lt;/h2&gt;

&lt;p&gt;Our workflow orchestration setup had all the classic symptoms of YAML-based configuration debt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verbose definitions&lt;/strong&gt;: Representing simple workflows as graphs required dozens of steps and connector definitions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold start delays&lt;/strong&gt;: Every workflow step triggered a Cloud Run job, adding 35-70 seconds of spin-up time per execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No local testing&lt;/strong&gt;: Changes required deployment to validate. The feedback loop was measured in &lt;em&gt;deploys&lt;/em&gt;, not seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split infrastructure&lt;/strong&gt;: Application code lived in one place, workflow definitions in another, and every change risked breaking both&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The worst part? We couldn't even test locally. Every iteration meant committing, deploying, and hoping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Temporal changes everything
&lt;/h2&gt;

&lt;p&gt;Temporal flips the model on its head. Instead of declarative YAML that describes &lt;em&gt;what&lt;/em&gt; should happen, you write actual code that describes &lt;em&gt;how&lt;/em&gt; it happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@workflow.defn&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IndexingWorkflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nd"&gt;@workflow.run&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workspace_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# This is just Python. Full IDE support.
&lt;/span&gt;        &lt;span class="c1"&gt;# Local debugging works out of the box.
&lt;/span&gt;        &lt;span class="n"&gt;connections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;fetch_connections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;workspace_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;start_to_close_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;index_connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;start_to_close_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No separate YAML file. No mysterious DSL. Just code that your IDE understands, your debugger can step through, and your tests can cover. And it runs on &lt;em&gt;your&lt;/em&gt; infrastructure. Temporal handles orchestration and state, but the actual work happens on compute you control.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pull-based architecture
&lt;/h2&gt;

&lt;p&gt;Understanding why Temporal workers are different from Cloud Run jobs unlocks the simplicity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud Run Jobs&lt;/strong&gt; are push-based and ephemeral:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Something triggers them → they spin up → execute → shut down&lt;/li&gt;
&lt;li&gt;Each invocation pays the cold start tax&lt;/li&gt;
&lt;li&gt;No shared state between executions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Temporal Workers&lt;/strong&gt; are pull-based and persistent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workers run on &lt;em&gt;your&lt;/em&gt; infrastructure, not Temporal's&lt;/li&gt;
&lt;li&gt;They maintain a long-polling connection to Temporal for orchestration&lt;/li&gt;
&lt;li&gt;They &lt;em&gt;pull&lt;/em&gt; tasks when they have capacity&lt;/li&gt;
&lt;li&gt;Workers stay warm, eliminating cold starts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pull-based model is exactly what Google designed &lt;a href="https://cloud.google.com/run/docs/deploy-worker-pools" rel="noopener noreferrer"&gt;Cloud Run Worker Pools&lt;/a&gt; for. It's a resource type announced at Google Cloud Next '25 specifically for continuous, non-HTTP, pull-based background processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Cloud Run worker pools
&lt;/h2&gt;

&lt;p&gt;Worker pools solve a real problem for Temporal deployments. Unlike Cloud Run Services (designed for HTTP workloads) or Jobs (designed for batch tasks), Worker Pools are purpose-built for exactly what Temporal workers do: continuously pull tasks from a queue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Worker Pools are perfect for Temporal:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No HTTP endpoint required&lt;/strong&gt;: Workers just poll Temporal. No need to expose ports or manage health check endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower total cost&lt;/strong&gt;: No load balancer, no HTTP endpoint overhead, just compute&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced attack surface&lt;/strong&gt;: No public URL means fewer security concerns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance splitting&lt;/strong&gt;: Deploy canary releases by allocating percentages of instances to different revisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deployment is even simpler than Services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud beta run worker-pools deploy worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt; gcr.io/my-project/worker:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; europe-west1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_cloud_run_v2_worker_pool"&lt;/span&gt; &lt;span class="s2"&gt;"worker"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"temporal-worker"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"europe-west1"&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google-beta&lt;/span&gt;
  &lt;span class="nx"&gt;launch_stage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"BETA"&lt;/span&gt;

  &lt;span class="nx"&gt;scaling&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scaling_mode&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AUTOMATIC"&lt;/span&gt;
    &lt;span class="nx"&gt;min_instance_count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;max_instance_count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;containers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gcr.io/my-project/worker:latest"&lt;/span&gt;
      &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;limits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;cpu&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;
          &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1Gi"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;minScale&lt;/code&gt; hacks. No unused HTTP endpoints. Just a container that runs your worker code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Worker Pools are currently in public preview. For production workloads, you can still use Cloud Run Services with &lt;code&gt;--min-instances 1&lt;/code&gt;. The architecture is identical, just with a bit more overhead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The deployment is just another container
&lt;/h2&gt;

&lt;p&gt;Here's the mental shift: you're not deploying workflows anymore. You're deploying an application that &lt;em&gt;happens to execute workflows&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "-m", "worker"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your worker code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# worker.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;temporalio.client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;temporalio.worker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Worker&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;workflows&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;IndexingWorkflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;GoogleDriveWorkflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;activities&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;fetch_connections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;index_connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sync_drive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;namespace.tmprl.cloud:7233&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TEMPORAL_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;task_queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;indexing-queue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;workflows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;IndexingWorkflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;GoogleDriveWorkflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;activities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;fetch_connections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;index_connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sync_drive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy (with your API key stored in Secret Manager):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud beta run worker-pools deploy worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt; gcr.io/my-project/worker:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--min-instances&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-instances&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt; 1Gi &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cpu&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; europe-west1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set-secrets&lt;/span&gt; &lt;span class="nv"&gt;TEMPORAL_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;temporal-api-key:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire deployment. No Terraform for workflow definitions. No separate infrastructure repo. Just your application container with workflow logic baked in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost reality check
&lt;/h2&gt;

&lt;p&gt;Let's be honest about the trade-offs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (Google Workflows + Cloud Run Jobs)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Workflows: ~$2-3/month&lt;/li&gt;
&lt;li&gt;Cloud Run job invocations: ~$35/month&lt;/li&gt;
&lt;li&gt;Cold start compute waste: ~$40-50/month&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: ~$80-90/month&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After (Temporal Cloud + Cloud Run Worker Pools)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temporal Cloud starter: ~€100/month &lt;em&gt;(orchestration and state only)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Worker Pool (2 instances, always-on): ~$18-24/month &lt;em&gt;(your compute, no load balancer or endpoint overhead)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: ~$120-140/month&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, it costs more. But here's what you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developer time saved&lt;/strong&gt;: 2-3 hours/month not fighting YAML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution speed&lt;/strong&gt;: 73-78% faster workflows (no cold starts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local testing&lt;/strong&gt;: Full workflow debugging before deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real observability&lt;/strong&gt;: See workflow graphs, execution history, parent-child relationships in real-time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At a loaded developer cost of €80/hour, the ROI turns positive immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  What simplicity actually looks like
&lt;/h2&gt;

&lt;p&gt;Kill a running workflow mid-execution. Restart the worker. The workflow &lt;em&gt;resumes exactly where it left off&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That's durability you'd have to build yourself with Google Workflows: tracking state in Cloud Storage, implementing retries, handling partial failures. With Temporal, it's the default behavior.&lt;/p&gt;

&lt;p&gt;Debug a failing activity with your IDE's debugger. Set breakpoints. Inspect state. Validate fixes locally before deploying.&lt;/p&gt;

&lt;p&gt;This is what simplicity means: removing the gap between "I think this will work" and "I know this works."&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the switch
&lt;/h2&gt;

&lt;p&gt;The migration path isn't all-or-nothing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Spike it&lt;/strong&gt;: Implement one workflow in Temporal, run both systems in parallel for a week&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure&lt;/strong&gt;: Compare execution times, reliability, developer experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dark launch&lt;/strong&gt;: Run Temporal workflows in production, keep Google Workflows as fallback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradual rollout&lt;/strong&gt;: 10% → 50% → 100% with rollback ready&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We kept Google Workflows YAML in git history (never delete, just remove from deployment) and maintained the rollback capability for 30 days. We never needed it.&lt;/p&gt;

&lt;p&gt;The simplicity of Temporal isn't in having fewer moving parts. It's in having the &lt;em&gt;right&lt;/em&gt; moving parts. A persistent worker pool on Cloud Run, code-native workflow definitions, and a managed orchestration layer that handles the hard stuff.&lt;/p&gt;

&lt;p&gt;No more YAML debugging. No more cold start delays. No more "deploy to test" cycles.&lt;/p&gt;

&lt;p&gt;Just workflows that work.&lt;/p&gt;

&lt;p&gt;original post: &lt;a href="https://gbostoen.dev/blog/temporal-cloud-run/" rel="noopener noreferrer"&gt;https://gbostoen.dev/blog/temporal-cloud-run/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>python</category>
    </item>
    <item>
      <title>Build containers without using Docker</title>
      <dc:creator>Glenn Bostoen</dc:creator>
      <pubDate>Fri, 01 Apr 2022 14:44:38 +0000</pubDate>
      <link>https://forem.com/gbostoen/build-containers-without-using-docker-bk</link>
      <guid>https://forem.com/gbostoen/build-containers-without-using-docker-bk</guid>
      <description>&lt;p&gt;In this post, I will quickly show you multiple ways of building your containers without having to worry about the details of Dockerfiles.&lt;/p&gt;

&lt;h2&gt;Why&lt;/h2&gt;

&lt;p&gt;Most developers that have set up a pipeline that uses Docker already are familiar with the details that come with building your own Docker image, but for someone that is new, this can be a bit overwhelming. Things that you have to keep into account like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced caching&lt;/li&gt;
&lt;li&gt;Apply best practices regarding security&lt;/li&gt;
&lt;li&gt;Multi-stage setup to have a lightweight final image&lt;/li&gt;
&lt;li&gt;Keeping your base images up to date&lt;/li&gt;
&lt;li&gt;Reproducibility of an image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this is all completely new to you, your best bet to start with containers is by learning Buildpacks as the first step.&lt;/p&gt;

&lt;h1&gt;What&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W9_aTsTN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1400/1%2Aoe6npZ6MQRKInb8VJ4T8XQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W9_aTsTN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1400/1%2Aoe6npZ6MQRKInb8VJ4T8XQ.png" alt="image" title="buildpacks logo" width="880" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Buildpacks was started by Heroku back in 2011 and is now officially part of the Cloud Native Computing Foundation and recently went from sandbox to incubation (&lt;a href="https://medium.com/buildpacks/buildpacks-promoted-to-cncf-incubation-bcfff8f7273"&gt;see post&lt;/a&gt;). It allows you to automatically detect based on source code which build tooling you need and it transforms your code into a ready to use image.&lt;/p&gt;

&lt;h2&gt;Gitlab&lt;/h2&gt;

&lt;p&gt;On Gitlab you have 2 approaches currently used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker-in-docker&lt;/li&gt;
&lt;li&gt;Kaniko build executor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both use a Dockerfile as a starting point, whereas Kaninko doesn't even need a docker daemon to run. This is why this is the most preferred (and advanced) approach in building containers. Below I will show different approaches in building your application starting with Auto DevOps. You can also find these approaches on an example Git repository &lt;a href="https://git.inthepocket.org/glenn.bostoen/fastify-example-buildpacks"&gt;fastify-example-buildpacks&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Buildpacks with Auto DevOps&lt;/h3&gt;

&lt;p&gt;On Gitlab you can use Buildpacks out of the box with "Auto DevOps". "Auto DevOps" automatically uses a Herokuish builds as a default to build your project and publish it to your Gitlab registry. You can override this by making use of an &lt;code&gt;AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED&lt;/code&gt; environment variable on your GitLab agent. This allows you to make use of Buildpacks which is part of the Cloud Native Foundation. For now, this is still in Beta on Gitlab, but they expect this to become a replacement for the current Herokuish builds.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="token key atrule"&gt;include&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;  &lt;span class="token punctuation"&gt;-&lt;/span&gt; &lt;span class="token key atrule"&gt;template&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt; Auto&lt;span class="token punctuation"&gt;-&lt;/span&gt;DevOps.gitlab&lt;span class="token punctuation"&gt;-&lt;/span&gt;ci.yml&lt;br&gt;&lt;br&gt;&lt;span class="token key atrule"&gt;variables&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;  &lt;span class="token key atrule"&gt;AUTO_DEVOPS_BUILD_IMAGE_CNB_ENABLED&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt; &lt;span class="token string"&gt;'true'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Buildpacks with Pack CLI&lt;/h3&gt;

&lt;p&gt;If you want to have more control on which Buildpacks that are used, you can use Pack CLI which needs to be installed on your agent. This is easily done with the following example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="token key atrule"&gt;docker&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;  &lt;span class="token key atrule"&gt;stage&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt; build&lt;br&gt;  &lt;span class="token key atrule"&gt;services&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class="token punctuation"&gt;-&lt;/span&gt; docker&lt;span class="token punctuation"&gt;:&lt;/span&gt;19.03.12&lt;span class="token punctuation"&gt;-&lt;/span&gt;dind&lt;br&gt;  &lt;span class="token key atrule"&gt;variables&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class="token key atrule"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt; tcp&lt;span class="token punctuation"&gt;:&lt;/span&gt;//docker&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;span class="token number"&gt;2375&lt;/span&gt;&lt;br&gt;    &lt;span class="token key atrule"&gt;DOCKER_TLS_CERTDIR&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt; &lt;span class="token string"&gt;''&lt;/span&gt;&lt;br&gt;  &lt;span class="token key atrule"&gt;image&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt; docker&lt;span class="token punctuation"&gt;:&lt;/span&gt;19.03.12&lt;br&gt;  &lt;span class="token key atrule"&gt;before_script&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class="token punctuation"&gt;-&lt;/span&gt; wget https&lt;span class="token punctuation"&gt;:&lt;/span&gt;//github.com/buildpacks/pack/releases/download/v0.13.1/pack&lt;span class="token punctuation"&gt;-&lt;/span&gt;v0.13.1&lt;span class="token punctuation"&gt;-&lt;/span&gt;linux.tgz&lt;br&gt;    &lt;span class="token punctuation"&gt;-&lt;/span&gt; tar xvf pack&lt;span class="token punctuation"&gt;-&lt;/span&gt;v0.13.1&lt;span class="token punctuation"&gt;-&lt;/span&gt;linux.tgz&lt;br&gt;    &lt;span class="token punctuation"&gt;-&lt;/span&gt; rm pack&lt;span class="token punctuation"&gt;-&lt;/span&gt;v0.13.1&lt;span class="token punctuation"&gt;-&lt;/span&gt;linux.tgz&lt;br&gt;  &lt;span class="token key atrule"&gt;script&lt;/span&gt;&lt;span class="token punctuation"&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class="token punctuation"&gt;-&lt;/span&gt; ./pack build $IMAGE_NAME &lt;span class="token punctuation"&gt;-&lt;/span&gt;&lt;span class="token punctuation"&gt;-&lt;/span&gt;builder gcr.io/buildpacks/builder&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This explicitly uses the Buildpacks provided by Google. You can find more information about these on &lt;a href="https://github.com/GoogleCloudPlatform/buildpacks"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Buildpacks with Google Cloud Platform&lt;/h3&gt;

&lt;p&gt;You can now even deploy your app directly to cloud Run. The only command you need is the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gcloud beta run deploy --source&lt;span class="token operator"&gt;=&lt;/span&gt;&lt;span class="token punctuation"&gt;[&lt;/span&gt;DIRECTORY&lt;span class="token punctuation"&gt;]&lt;/span&gt; &lt;span class="token punctuation"&gt;[&lt;/span&gt;CLOUD_RUN_SERVICE&lt;span class="token punctuation"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Downsides&lt;/h2&gt;

&lt;p&gt;Custom build scripts are not supported so you still need to compile your typescript for example to allow your application to be packages as an image. Additionally, certain native dependencies could be an argument to start from your base images in the first place which makes the use of Buildpacks a bit obsolete. Last the images that are created are bigger because they serve a wider variety of use cases that could not be needed by your application.&lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;

&lt;p&gt;Start with buidpacks and if you need to customize your container you can opt-out of the default image created for you and fully customize it to your liking with a Dockerfile.&lt;/p&gt;

</description>
      <category>buildpacks</category>
      <category>docker</category>
    </item>
    <item>
      <title>Tracing with OpenTelemetry</title>
      <dc:creator>Glenn Bostoen</dc:creator>
      <pubDate>Fri, 01 Apr 2022 14:42:11 +0000</pubDate>
      <link>https://forem.com/gbostoen/tracing-with-opentelemetry-p45</link>
      <guid>https://forem.com/gbostoen/tracing-with-opentelemetry-p45</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--owE24NA9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/opentelemetry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--owE24NA9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/opentelemetry.png" alt="image" title="opentelemetry logo" width="880" height="413"&gt;&lt;/a&gt;
Tracing gives you great insights into certain bottlenecks within your application. I'll go over the steps needed to enable &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt; within a Fastify application on Google Cloud Run. I got inspired by the &lt;a href="https://www.nearform.com/"&gt;Nearform&lt;/a&gt; webinar from &lt;a href="https://daily.dev/"&gt;Daily.Dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;https://www.youtube.com/watch?v=UKaJDmwIIpE&lt;/p&gt;

&lt;p&gt;By default Cloud Run already has tracing enabled on requests that enter your cloud run instances. So if you go to &lt;a href="https://console.cloud.google.com/traces"&gt;traces&lt;/a&gt; in the console, you should see some traces for every HTTP endpoint of your application. You can see an example below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9UTSiRWt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/cloud_run_default.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9UTSiRWt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/cloud_run_default.png" alt="image" width="880" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can start adding additional instrumentation for our application. There are multiple libraries proposed by Google:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/trace/docs/setup/nodejs-ot"&gt;OpenTelemetry&lt;/a&gt;: &lt;a href="https://cloud.google.com/trace/docs/setup"&gt;recommended by Google&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://opencensus.io/language-support/"&gt;OpenCensus&lt;/a&gt;: only alpha support and NodeJS support not even mentioned by Google&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/trace/docs/setup/nodejs"&gt;Google Client library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;OpenTelemetry&lt;/h2&gt;

&lt;p&gt;As described on the website of &lt;a href="https://opentelemetry.io/"&gt;OpenTelemetry&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenTelemetry is a collection of tools, APIs, and SDKs. Use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) to help you analyze your software’s performance and behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's still in beta, but general availability should arrive soon.&lt;/p&gt;

&lt;h3&gt;Installing&lt;/h3&gt;

&lt;p&gt;Make sure to initialize all OpenTelemetry packages at the start of your application so that it can patch the necessary calls to underlying libraries. In this example, we add HTTP, TypeORM and Postgress instrumentation with the following instrumentation packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-http"&gt;@opentelemetry/instrumentation-http&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/opentelemetry-instrumentation-typeorm"&gt;@opentelemetry-instrumentation-typeorm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-pg"&gt;@opentelemetry/instrumentation-pg&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; opentelemetry&lt;span class="token punctuation"&gt;,&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; DiagConsoleLogger&lt;span class="token punctuation"&gt;,&lt;/span&gt; DiagLogLevel &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@opentelemetry/api'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; NodeTracerProvider &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@opentelemetry/sdk-trace-node'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; SimpleSpanProcessor &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@opentelemetry/sdk-trace-base'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; TraceExporter &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@google-cloud/opentelemetry-cloud-trace-exporter'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; PgInstrumentation &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@opentelemetry/instrumentation-pg'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; HttpInstrumentation &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@opentelemetry/instrumentation-http'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; registerInstrumentations &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'@opentelemetry/instrumentation'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token keyword"&gt;import&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; TypeormInstrumentation &lt;span class="token punctuation"&gt;}&lt;/span&gt; &lt;span class="token keyword"&gt;from&lt;/span&gt; &lt;span class="token string"&gt;'opentelemetry-instrumentation-typeorm'&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class="token keyword"&gt;export&lt;/span&gt; &lt;span class="token keyword"&gt;function&lt;/span&gt; &lt;span class="token function"&gt;tracing&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// Enable OpenTelemetry exporters to export traces to Google Cloud Trace.&lt;/span&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// Exporters use Application Default Credentials (ADCs) to authenticate.&lt;/span&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// See https://developers.google.com/identity/protocols/application-default-credentials&lt;/span&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// for more details.&lt;/span&gt;&lt;br&gt;  &lt;span class="token keyword"&gt;const&lt;/span&gt; provider &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;NodeTracerProvider&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// Initialize the exporter. When your application is running on Google Cloud,&lt;/span&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// you don't need to provide auth credentials or a project id.&lt;/span&gt;&lt;br&gt;  &lt;span class="token keyword"&gt;const&lt;/span&gt; exporter &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;TraceExporter&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class="token comment"&gt;// Configure the span processor to send spans to the exporter&lt;/span&gt;&lt;br&gt;  provider&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;addSpanProcessor&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;SimpleSpanProcessor&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;exporter&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;  provider&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;register&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;  opentelemetry&lt;span class="token punctuation"&gt;.&lt;/span&gt;trace&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;setGlobalTracerProvider&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;provider&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class="token function"&gt;registerInstrumentations&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;br&gt;    instrumentations&lt;span class="token operator"&gt;:&lt;/span&gt; &lt;span class="token punctuation"&gt;[&lt;/span&gt;&lt;br&gt;      &lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;HttpInstrumentation&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;PgInstrumentation&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;TypeormInstrumentation&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class="token comment"&gt;// see under for available configuration&lt;/span&gt;&lt;br&gt;      &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class="token punctuation"&gt;]&lt;/span&gt;&lt;span class="token punctuation"&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class="token keyword"&gt;return&lt;/span&gt; &lt;span class="token punctuation"&gt;{&lt;/span&gt; provider &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;a href="https://inthepocket.dev/posts/2022-02-04-tracing-with-opentelemetry/@google-cloud/opentelemetry-cloud-trace-exporter"&gt;Google Cloud trace exporter&lt;/a&gt; automatically uses the Cloud Run service account, so make sure this service account has access to utilize the Tracing API. You can configure or verify this in the &lt;a href="https://console.cloud.google.com/iam-admin"&gt;IAM Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have this added to your application and deployed on Cloud Run, you should be able to see more in-depth traces. The trace ID can always be found in the response header of your request: &lt;strong&gt;'X-Cloud-Trace-Context'&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_1Np0v1H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/cloud_run_enhanced.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_1Np0v1H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/cloud_run_enhanced.png" alt="image" width="880" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Google Coud - Daily analysis reports&lt;/h2&gt;

&lt;p&gt;If you feed Google Cloud with this tracing data, you will also get more detailed insights. An example where we had a significant impact on specific API calls is also noticed automatically by Google Cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---FyWubi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/graphql_performance.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---FyWubi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://inthepocket.dev/img/2022-02-04-tracing-with-opentelemetry/graphql_performance.png" alt="image" width="880" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;

&lt;p&gt;Easy to implement and can give you more insights over time. The Google Cloud Trace dashboard is quite simple and easy to use, so the entry barrier is relatively low.&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>googlecloud</category>
      <category>node</category>
    </item>
    <item>
      <title>Integrate Gitlab with Google Cloud workload identity federation</title>
      <dc:creator>Glenn Bostoen</dc:creator>
      <pubDate>Fri, 11 Mar 2022 11:21:50 +0000</pubDate>
      <link>https://forem.com/gbostoen/integrate-gitlab-with-google-cloud-workload-identity-federation-3041</link>
      <guid>https://forem.com/gbostoen/integrate-gitlab-with-google-cloud-workload-identity-federation-3041</guid>
      <description>&lt;p&gt;In &lt;a href="https://about.gitlab.com/releases/2022/01/22/gitlab-14-7-released/#openid-connect-support-for-gitlab-cicd"&gt;Gitlab 14.7&lt;/a&gt;, connecting to AWS, GCP and vault, and other cloud services is now possible by introducing the CI_JOB_JWT_V2 environment variable. I'll use this environment variable to impersonate a service account via workload identity federation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload identity federation
&lt;/h2&gt;

&lt;p&gt;Workload identity federation allows you to impersonate an existing service account on Google Cloud. Everyday use cases for workload identity federation include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enabling a background application or continuous integration/continuous delivery (CI/CD) pipeline that runs outside of Google Cloud to access Google Cloud resources and APIs..&lt;/li&gt;
&lt;li&gt;Enabling users of a web application that runs outside of Google Cloud to access data stored in a Google Cloud service, such as Cloud Storage or BigQuery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use workload identity federation, you configure Google Cloud to trust an external identity provider such as Amazon Web Services (AWS), Azure Active Directory (AD), an OIDC-compatible identity provider, or a SAML 2.0-compatible identity provider Preview. Applications can then use credentials issued by the external identity provider to impersonate a service account by following these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup the workload identity provider.&lt;/li&gt;
&lt;li&gt;Obtain a credential from the trusted identity provider.&lt;/li&gt;
&lt;li&gt;Exchange the credential for a token from the Security Token Service.&lt;/li&gt;
&lt;li&gt;Use the token from the Security Token Service to impersonate a service account and obtain a short-lived Google access token.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup the workload identity provider
&lt;/h3&gt;

&lt;p&gt;Setting up the proper Identity Pool&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam workload-identity-pools create POOL_ID &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"global"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"DESCRIPTION"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--display-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"DISPLAY_NAME"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding an OpenID Connect Provider to the pool (more specifically our Gitlab instance).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"global"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--workload-identity-pool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"POOL_ID"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--issuer-uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ISSUER"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--allowed-audiences&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"AUDIENCE"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--attribute-mapping&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"MAPPINGS"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--attribute-condition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"CONDITIONS""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assigning service accounts that can be impersonated by these identities and the conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT_EMAIL &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/iam.workloadIdentityUser &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"MEMBER_EXPRESSION"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remark&lt;/strong&gt;: the member expression is not clear within the workload identity federation console, but you can find more details if you navigate to the connected service account and click on &lt;strong&gt;permissions&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conditions
&lt;/h4&gt;

&lt;p&gt;Conditions make it especially useful, because this allows you to incorperate fine grained permissions in your pipeline. You could scope to certain branches, branches and even limit the users who can trigger the build.&lt;/p&gt;

&lt;p&gt;For mapping we could do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MAPPINGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"attribute.project_path=assertion.project_path"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For conditions we could then add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONDITIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"attribute.custom_path==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;glenn.bostoen/workload-federation-poc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Obtaining a credential from the trusted identity provider
&lt;/h3&gt;

&lt;p&gt;It's quite easy on Gitlab to retrieve your credentials, it's injected in the environment variable CI_JOB_JWT_V2. The specification of the token can be found &lt;a href="https://docs.gitlab.com/ee/ci/cloud_services/"&gt;here&lt;/a&gt;, it looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jti"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c82eeb0c-5c6f-4a33-abf5-4c474b92b558"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://gitlab.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://gitlab.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1585710286&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nbf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1585798372&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1585713886&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project_path:mygroup/myproject:ref_type:branch:ref:main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namespace_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namespace_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mygroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"project_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mygroup/myproject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"42"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_login"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myuser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myuser@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1212"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline_source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"job_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1212"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auto-deploy-2020-04-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ref_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"branch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ref_protected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment_protected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could use this environment variable directly or output it to a temporary file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CI_JOB_JWT_V2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .ci_job_jwt_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exchange the credential for a token from STS
&lt;/h3&gt;

&lt;p&gt;The following shell scripts allow you to exchange your JWT token for e federated token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh -x&lt;/span&gt;

&lt;span class="nv"&gt;PAYLOAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
"audience": "//iam.googleapis.com/projects/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_NUMBER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/locations/global/workloadIdentityPools/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POOL_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/providers/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROVIDER_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;",
"grantType": "urn:ietf:params:oauth:grant-type:token-exchange",
"requestedTokenType": "urn:ietf:params:oauth:token-type:access_token",
"scope": "https://www.googleapis.com/auth/cloud-platform",
"subjectTokenType": "urn:ietf:params:oauth:token-type:jwt",
"subjectToken": "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CI_JOB_JWT_V2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;FEDERATED_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;
    curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://sts.googleapis.com/v1/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAYLOAD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; |
        jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.access_token'&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Impersonate service account
&lt;/h3&gt;

&lt;p&gt;You can now use this federated token to impersonate a service account by getting an access token for this service account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;
    curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE_ACCOUNT_EMAIL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:generateAccessToken"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FEDERATED_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"scope": ["https://www.googleapis.com/auth/cloud-platform"]}'&lt;/span&gt; |
        jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.accessToken'&lt;/span&gt;
&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ACCESS_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integration on Gitlab
&lt;/h2&gt;

&lt;p&gt;You could use the scripts defined above and integrate these directly or you could use the gcloud CLI to easily impersonate a service account. So before we had the setup with permanent keys on Gitlab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;gcp-auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google/cloud-sdk:slim&lt;/span&gt;
    &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gcloud auth activate-service-account --key-file ${GOOGLE_APPLICATION_CREDENTIALS}&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gcloud config set project ${GOOGLE_PROJECT}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can now be replaced by the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;gcp-auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google/cloud-sdk:slim&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo ${CI_JOB_JWT_V2} &amp;gt; .ci_job_jwt_file&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gcloud iam workload-identity-pools create-cred-config "${GCP_WORKLOAD_IDENTITY_PROVIDER}"&lt;/span&gt;
      &lt;span class="s"&gt;--service-account="${GCP_SERVICE_ACCOUNT}"&lt;/span&gt;
      &lt;span class="s"&gt;--output-file=.gcp_temp_cred.json&lt;/span&gt;
      &lt;span class="s"&gt;--credential-source-file=.ci_job_jwt_file&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gcloud auth login --cred-file=`pwd`/.gcp_temp_cred.json&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gcloud config set project ${GOOGLE_PROJECT}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We just need to specify the workload identity provider we want to use and the service account we want to impersonate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you have already isolated the authentication step to Google Cloud in your Gitlab templates, it should be quite straightforward to switch to this new mechanism. It gives you more granularity on who or what has access to your Cloud provider and is more secure in general by not having a permanent key on your instance. It does require some extra work on your Google Cloud project, but this can also be tackled by having some &lt;a href="https://gitlab.com/guided-explorations/gcp/configure-openid-connect-in-gcp/-/blob/main/main.tf"&gt;Terraform project templating&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>googlecloud</category>
      <category>security</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
