<?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: Dariusz Parys</title>
    <description>The latest articles on Forem by Dariusz Parys (@dariuszparys).</description>
    <link>https://forem.com/dariuszparys</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%2F335552%2F26a7866a-3e51-4c7e-800b-a33c00d99acb.jpeg</url>
      <title>Forem: Dariusz Parys</title>
      <link>https://forem.com/dariuszparys</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dariuszparys"/>
    <language>en</language>
    <item>
      <title>Just Your Commands</title>
      <dc:creator>Dariusz Parys</dc:creator>
      <pubDate>Mon, 31 Mar 2025 10:07:15 +0000</pubDate>
      <link>https://forem.com/dariuszparys/just-your-commands-4c6f</link>
      <guid>https://forem.com/dariuszparys/just-your-commands-4c6f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Just&lt;/strong&gt; is a simple command runner. You can get if from &lt;a href="https://just.systems" rel="noopener noreferrer"&gt;https://just.systems&lt;/a&gt;. I use it meanwhile regularly, and it improves consistency between my local environment and my CI/CD pipelines. Let me explain this with a trivial walkthrough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying Infrastructure
&lt;/h2&gt;

&lt;p&gt;A common task is to deploy infrastructure. As I use mostly Azure for infrastructure deployments, I use bicep to do them. For this scenario a simple storage deployment will act as the canonical Hello, World. example. You can find the code for the scripts at my GitHub.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dariuszparys" rel="noopener noreferrer"&gt;
        dariuszparys
      &lt;/a&gt; / &lt;a href="https://github.com/dariuszparys/just-your-commands" rel="noopener noreferrer"&gt;
        just-your-commands
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Sample source code for the blog post https://www.dariuszparys.com/just-your-commands/
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Azure Bicep Deployment with Just&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This project uses &lt;a href="https://github.com/casey/just" rel="noopener noreferrer"&gt;Just&lt;/a&gt; as a command runner to manage Azure Bicep deployments.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://github.com/casey/just#installation" rel="noopener noreferrer"&gt;Just&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli" rel="nofollow noopener noreferrer"&gt;Azure CLI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://github.com/PowerShell/PowerShell#get-powershell" rel="noopener noreferrer"&gt;PowerShell&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Log in to Azure CLI:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;az login&lt;/pre&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Environment Variables&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Set the following environment variables before running the deployment:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;export&lt;/span&gt; AZURE_SUBSCRIPTION_ID=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;your-subscription-id&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; DEPLOYMENT_ENVIRONMENT=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;dev&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;  &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Optional, defaults to "dev"&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; AZURE_LOCATION=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;northeurope&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;  &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Optional, defaults to "northeurope"&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; DEPLOYMENT_NAME=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;just-demo&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Optional, defaults to "just-demo"&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; RESOURCE_GROUP_NAME=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;rg-just-demo&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;  &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Optional, defaults to "rg-just-demo"&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Available Commands&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Validate Deployment&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;To validate the Bicep template and see what changes would be made:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;just validate-all&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This command will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lint the Bicep files&lt;/li&gt;
&lt;li&gt;Validate the deployment&lt;/li&gt;
&lt;li&gt;Show what-if changes&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Deploy&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;To deploy the infrastructure:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;just deploy&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Clean Up&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;To delete the deployment and resource group:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;just destroy&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Additional Commands&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;just help&lt;/code&gt; - Lists all available commands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;just lint&lt;/code&gt; -…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dariuszparys/just-your-commands" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The workflow usually consists of these steps to make the deployment&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lint the file &lt;code&gt;az bicep lint --file main.bicep&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Validate the script &lt;code&gt;az deployment sub validate --template-file main.bicep --parameters environments/dev.bicepparam&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Make a what-if analysis &lt;code&gt;az deployment sub what-if --template-file main.bicep --parameters environments/dev.bicepparam&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deploy to Infrastructure &lt;code&gt;az deployment sub create --template-file main.bicep --parameters environments/dev.bicepparam&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This can now be scripted or in this case I use &lt;strong&gt;just&lt;/strong&gt; so I will write recipes to execute those steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Just Anatomy
&lt;/h2&gt;

&lt;p&gt;I don't want to explain things that are already explained in the &lt;a href="https://just.systems/man/en/" rel="noopener noreferrer"&gt;Just manuals&lt;/a&gt;, but it helps to have a quick context without digging into this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;environment :&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DEPLOYMENT_ENVIRONMENT"&lt;/span&gt;, &lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Lists all available recipies&lt;/span&gt;
&lt;span class="nb"&gt;help&lt;/span&gt;:
  @just &lt;span class="nt"&gt;--list&lt;/span&gt;

_hello &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"world"&lt;/span&gt;:
  @echo &lt;span class="s2"&gt;"Hello, {{name}}"&lt;/span&gt;

&lt;span class="c"&gt;# Deploys infrastructure to defined environment&lt;/span&gt;
deploy: _hello
  @echo &lt;span class="s2"&gt;"Deploying to {{environment}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A lot happens here&lt;/p&gt;

&lt;p&gt;First, the variable environment gets either assigned an available environment variable called &lt;code&gt;DEPLOYMENT_ENVIRONMENT&lt;/code&gt; or if not available, the value &lt;code&gt;dev&lt;/code&gt;. So &lt;strong&gt;just&lt;/strong&gt; has functions that can be used in recipes&lt;/p&gt;

&lt;p&gt;Second, the first function will be executed when no parameter is applied to the &lt;code&gt;just&lt;/code&gt; command. Therefore, I often use &lt;code&gt;help&lt;/code&gt; as my first recipe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ just 
Available recipes:
    deploy &lt;span class="c"&gt;# Deploys infrastructure to defined environment&lt;/span&gt;
    &lt;span class="nb"&gt;help&lt;/span&gt;   &lt;span class="c"&gt;# Lists all available recipies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third, &lt;strong&gt;just&lt;/strong&gt; can have private recipes like &lt;code&gt;_hello&lt;/code&gt; which also contains a parameter with a default value&lt;/p&gt;

&lt;p&gt;Fourth, &lt;code&gt;deploy&lt;/code&gt; leverages the private recipe when called and executes it first without providing any parameter. Therefore, the default is taken.&lt;/p&gt;

&lt;p&gt;Please note that comments applied before recipes are used as description in the &lt;code&gt;--list&lt;/code&gt; parameter of &lt;code&gt;just&lt;/code&gt; and private recipes are not listed.&lt;/p&gt;

&lt;p&gt;Calling now &lt;code&gt;just deploy&lt;/code&gt; outputs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ just deploy
Hello, world
Deploying to dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My Just Approach
&lt;/h2&gt;

&lt;p&gt;In my working environment and especially in the teams I am working with a lot of people still use Windows. PowerShell is set as the de-facto shell across all operating systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  PowerShell Shebang
&lt;/h3&gt;

&lt;p&gt;To enable just now to execute all commands using PowerShell I always define at the very top of my &lt;code&gt;justfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shebang :&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;os&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'windows'&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'pwsh.exe'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'/usr/bin/env pwsh'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;set &lt;/span&gt;shell :&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pwsh"&lt;/span&gt;, &lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;set &lt;/span&gt;windows-shell :&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pwsh.exe"&lt;/span&gt;, &lt;span class="s2"&gt;"-NoProfile"&lt;/span&gt;, &lt;span class="s2"&gt;"-Command"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will instruct the execution engine to use PowerShell as the execution shell. There are two variables depending on the environment &lt;code&gt;shell&lt;/code&gt; and &lt;code&gt;windows-shell&lt;/code&gt; that are used for that.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;shebang&lt;/code&gt; is used when a recipe will spawn multiple lines and needs to keep the context. If you don't specify this, the commands will be executed in their own environment without knowing anything about the previous line.&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;# Test without&lt;/span&gt;
without:
  Write-Host &lt;span class="s2"&gt;"Without"&lt;/span&gt;
  &lt;span class="nv"&gt;$some_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Without"&lt;/span&gt;
  Write-Host &lt;span class="nv"&gt;$some_var&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When executing &lt;code&gt;without&lt;/code&gt; you see in the output that the variable is set, but it is not available when writing to console anymore, as each line is in its own context&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ just without
Write-Host &lt;span class="s2"&gt;"Without"&lt;/span&gt;
Without
&lt;span class="nv"&gt;$some_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Without"&lt;/span&gt;
Write-Host &lt;span class="nv"&gt;$some_var&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using now the shebang defined above, it works&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test with&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;with:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;#!{{shebang}}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Set-PSDebug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Trace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"With"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$some_var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"With"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$some_var&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Set-PSDebug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Off&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output does keep the variable because it executes it with the defined shell environment in one context&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; just with
DEBUG:   32+  &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; Write-Host &lt;span class="s2"&gt;"With"&lt;/span&gt;

With
DEBUG:   33+  &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$some_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"With"&lt;/span&gt;

DEBUG:   34+  &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; Write-Host &lt;span class="nv"&gt;$some_var&lt;/span&gt;

With
DEBUG:   35+  &lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; Set-PSDebug &lt;span class="nt"&gt;-Off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The First Recipe is Help
&lt;/h3&gt;

&lt;p&gt;I always define this as my very first recipe&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;# Lists all available recipies&lt;/span&gt;
&lt;span class="nb"&gt;help&lt;/span&gt;:
  @just &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, when I invoke &lt;code&gt;just&lt;/code&gt; every recipe is listed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ just
Available recipes:
    deploy  &lt;span class="c"&gt;# Deploys infrastructure to defined environment&lt;/span&gt;
    &lt;span class="nb"&gt;help&lt;/span&gt;    &lt;span class="c"&gt;# Lists all available recipies&lt;/span&gt;
    with    &lt;span class="c"&gt;# Test with&lt;/span&gt;
    without &lt;span class="c"&gt;# Test without&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Short Recipe Scripts
&lt;/h3&gt;

&lt;p&gt;Using a shebang allows writing scripts as complex as you want. Usually if I tend to use more than one branching path in my script, I extract them into my own PowerShell modules. I invoke them e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;module_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path/to/psmd"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Install k3s using Ansible&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;k3s-install:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;#!{{ shebang }}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Import-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./{{module_path}}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Install-K3sCluster&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Verbose&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  No Here Strings
&lt;/h3&gt;

&lt;p&gt;I haven't found a way to use here strings with just. test. For instance, if I have a recipe like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initializes the configuration toml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;init:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;#!{{shebang}}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$toml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@"
[package]
name = "my-project"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = "0.7"
"@&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nv"&gt;$toml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cargo.toml"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Created Cargo.toml"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would just complain about the content inside the here string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ just &lt;span class="nt"&gt;--list&lt;/span&gt;
error: Unknown attribute &lt;span class="sb"&gt;`&lt;/span&gt;package&lt;span class="sb"&gt;`&lt;/span&gt;
 ——▶ justfile:5:2
  │
5 │ &lt;span class="o"&gt;[&lt;/span&gt;package]
  │  ^^^^^^^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, I put things like this in an external PowerShell function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Just Recipes in CI/CD
&lt;/h3&gt;

&lt;p&gt;I aim to have my recipes agnostic to the environment. That said, I use them as well in the CI/CD environment. One drawback to using &lt;strong&gt;just&lt;/strong&gt; is that the current GitHub runners or Azure DevOps agent don't have just pre-configure on their environments. So before using them, e.g. in Azure DevOps I have a task installing &lt;strong&gt;just&lt;/strong&gt;&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash@3&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&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;wget -qO - 'https://proget.makedeb.org/debian-feeds/prebuilt-mpr.pub' | gpg --dearmor | sudo tee /usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg 1&amp;gt; /dev/null&lt;/span&gt;
        &lt;span class="s"&gt;echo "deb [arch=all,$(dpkg --print-architecture) signed-by=/usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg] https://proget.makedeb.org prebuilt-mpr $(lsb_release -cs)" | sudo tee /etc/apt/sources.list.d/prebuilt-mpr.list&lt;/span&gt;
        &lt;span class="s"&gt;sudo apt update&lt;/span&gt;
        &lt;span class="s"&gt;sudo apt install -y just&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;just'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also only use either &lt;code&gt;AzureCLI@2&lt;/code&gt; or &lt;code&gt;Bash@3&lt;/code&gt; tasks in my piplines (on Azure DevOps). To invoke a recipe e.g.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash@3&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;APIs'&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&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;just containerize-apis&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(registry)&lt;/span&gt;
      &lt;span class="na"&gt;TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(buildTag)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recipe (simplified version) looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;targetRegistry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"REGISTRY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost:5050"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;buildTag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"TAG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest-dev"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Build all API containers&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;containerize-apis:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerize-facilitymanagement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerize-storymanagement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerize-queuemanagement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerize-videostreaming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Build Docker container for Queue Management API&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;containerize-queuemanagement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;registry:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;#!{{ shebang }}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="n"&gt;/api-queuemanagement:&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;buildTag&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src/Api.QueueManagement/Dockerfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Back Deploying Infrastructure
&lt;/h2&gt;

&lt;p&gt;I started to discuss how I deploy infrastructure using &lt;strong&gt;just&lt;/strong&gt;. When you have a look at the sample &lt;code&gt;justfile&lt;/code&gt; I published, you will see that my approach is applied to this. You could argue that destroy is too many lines of code. Yes, this can be extracted to an external script or module. I usually like to use PowerShell modules, but one function is not enough for me to consider this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;shebang&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'windows'&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;span class="s1"&gt;'pwsh.exe'&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;span class="kr"&gt;else&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;span class="s1"&gt;'/usr/bin/env pwsh'&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;span class="n"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;shell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pwsh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;windows-shell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pwsh.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-NoProfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-Command"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;subscription-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"AZURE_SUBSCRIPTION_ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DEPLOYMENT_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;"dev"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"AZURE_LOCATION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"northeurope"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;deployment-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DEPLOYMENT_NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"just-demo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"RESOURCE_GROUP_NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rg-just-demo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Lists all available recipies&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;help:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--list&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Lint the bicep files&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;lint:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bicep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main.bicep&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Validates the bicep deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;validate:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_ensure-subscription&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;#!{{ shebang }}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;deployment-name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--template-file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main.bicep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environments/&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bicepparam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Shows what changes would be made by the deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;what-if:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_ensure-subscription&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;#!{{ shebang }}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;what-if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;deployment-name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--template-file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main.bicep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environments/&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bicepparam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Validates and shows what changes would be made by the deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;validate-all:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;what-if&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;_&lt;/span&gt;&lt;span class="n"&gt;ensure-subscription:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;subscription-id&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Deploys the bicep deployment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;deploy:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_ensure-subscription&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;#!{{ shebang }}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;deployment-name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--template-file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main.bicep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environments/&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bicepparam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--parameters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Deletes the deployment and the resource group&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;destroy:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_ensure-subscription&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;#!{{ shebang }}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deleting deployment..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deployment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;deployment-name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;--no-wait&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Checking if resource group exists..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$rgExists&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--resource-group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rgExists&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Resource group '{{resourceGroupName}}' exists. Deleting..."&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--resource-group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;resourceGroupName&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--yes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--no-wait&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Resource group deletion initiated"&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;span class="kr"&gt;else&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;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Resource group '{{resourceGroupName}}' does not exist"&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;The only environment variable you have to provide is &lt;code&gt;AZURE_SUBSCRIPTION_ID&lt;/code&gt; and you are good to execute the commands. I assume that you have subscription ownership, else you have to tweak the bicep scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ just
Available recipes:
    deploy       &lt;span class="c"&gt;# Deploys the bicep deployment&lt;/span&gt;
    destroy      &lt;span class="c"&gt;# Deletes the deployment and the resource group&lt;/span&gt;
    &lt;span class="nb"&gt;help&lt;/span&gt;         &lt;span class="c"&gt;# Lists all available recipies&lt;/span&gt;
    lint         &lt;span class="c"&gt;# Lint the bicep files&lt;/span&gt;
    validate     &lt;span class="c"&gt;# Validates the bicep deployment&lt;/span&gt;
    validate-all &lt;span class="c"&gt;# Validates and shows what changes would be made by the deployment&lt;/span&gt;
    what-if      &lt;span class="c"&gt;# Shows what changes would be made by the deployment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are many other runners you could use, starting from good old &lt;a href="https://www.gnu.org/software/make/manual/make.html" rel="noopener noreferrer"&gt;&lt;code&gt;make&lt;/code&gt;&lt;/a&gt; to a python-based one like &lt;a href="https://tox.wiki/en/latest/user_guide.html" rel="noopener noreferrer"&gt;&lt;code&gt;tox&lt;/code&gt;&lt;/a&gt; and many others out there. You can achieve similar things with all of them, I stick to &lt;a href="https://just.systems" rel="noopener noreferrer"&gt;Just&lt;/a&gt;. I like it.&lt;/p&gt;

</description>
      <category>infrastructureascode</category>
      <category>azure</category>
      <category>bicep</category>
      <category>shell</category>
    </item>
    <item>
      <title>Azure Container Registry Cache: Updated Guide with Docker Hub Integration</title>
      <dc:creator>Dariusz Parys</dc:creator>
      <pubDate>Wed, 29 Jan 2025 12:20:30 +0000</pubDate>
      <link>https://forem.com/dariuszparys/azure-container-registry-cache-updated-guide-with-docker-hub-integration-566p</link>
      <guid>https://forem.com/dariuszparys/azure-container-registry-cache-updated-guide-with-docker-hub-integration-566p</guid>
      <description>&lt;p&gt;Some time ago, I wrote an article about &lt;a href="https://www.dariuszparys.com/how-to-avoid-docker-hub-rate-limits-and-improve-your-workflow/" rel="noopener noreferrer"&gt;Docker Hub rate limits&lt;/a&gt; and described ways to mitigate them. Unfortunately, the method I suggested using Azure Container Registry cache no longer works without authentication.&lt;/p&gt;

&lt;p&gt;You can find more details about this in the &lt;a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-artifact-cache?pivots=development-environment-azure-portal" rel="noopener noreferrer"&gt;Artifact cache in Azure Container Registry&lt;/a&gt; documentation, specifically in the &lt;a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-artifact-cache?pivots=development-environment-azure-portal#upstream-support" rel="noopener noreferrer"&gt;Upstream support&lt;/a&gt; section.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Azure Container Registry supports only authenticated pulls to Docker Hub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to Enable Caching, the correct way
&lt;/h2&gt;

&lt;p&gt;Let me walk you through enabling the cache. Here are the steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a free Docker Hub account if you don't have one yet&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;❗ Check your eligibility for a personal account first. You may need a paid subscription tier for Docker if you're not using it for personal or open source projects.&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;a href="https://app.docker.com/settings/personal-access-tokens" rel="noopener noreferrer"&gt;personal access token&lt;/a&gt; in Docker Hub&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store your Docker Hub username and token credentials in Azure KeyVault&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create an Azure Container Registry credential cache to retrieve the KeyVault credentials for Docker Hub&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create an Azure Container Registry caching rule using that credential set&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To simplify this process, I've created a GitHub repository with all the necessary automation.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dariuszparys" rel="noopener noreferrer"&gt;
        dariuszparys
      &lt;/a&gt; / &lt;a href="https://github.com/dariuszparys/acr-cache-docker-images" rel="noopener noreferrer"&gt;
        acr-cache-docker-images
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Project Documentation for Bicep Project&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project configures an Azure Container Registry with Docker Hub credentials
stored securely in Azure Key Vault, using managed identities for secure access.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Project Structure&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The project is organized into the following directories and files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;/src&lt;/strong&gt;: Contains the main Bicep file and modules for resource deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/src/modules&lt;/strong&gt;: Includes reusable Bicep modules for creating and managing
Azure resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;deploy.sh&lt;/strong&gt;: Script to automate the deployment process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.env&lt;/strong&gt;: File for environment variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;README.md&lt;/strong&gt;: Project documentation and instructions.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Azure subscription&lt;/li&gt;
&lt;li&gt;Azure CLI installed&lt;/li&gt;
&lt;li&gt;Bicep CLI installed (if necessary)&lt;/li&gt;
&lt;li&gt;Docker Hub account&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deployment&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Set environment variables:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;export&lt;/span&gt; DOCKER_HUB_USERNAME=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;your-username&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; DOCKER_HUB_PAT=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;your-personal-access-token&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Configure parameters&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Update &lt;code&gt;main.bicepparam&lt;/code&gt; to your liking.&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Run deployment:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;chmod +x deploy.sh
./deploy.sh&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Caching Repositories&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Once this is set up we can now cache any public repository from docker hub in
the Azure Container Registry. The following…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dariuszparys/acr-cache-docker-images" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Bicep Automation
&lt;/h2&gt;

&lt;p&gt;The GitHub repository uses Bicep to automate resource creation in Azure. Before running it, you'll need a &lt;a href="https://app.docker.com/settings/personal-access-tokens?ref=dariuszparys.com" rel="noopener noreferrer"&gt;personal access token from Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18f4uhc0il0v1ovhspi3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18f4uhc0il0v1ovhspi3.png" alt="PAT in Docker Hub using Public Repo Read-only" width="800" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to choose access permissions that make sense for your needs. Since I'm only working with public repositories, I selected that option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Deployment
&lt;/h2&gt;

&lt;p&gt;To test this, you can either export the variables &lt;code&gt;DOCKER_HUB_USERNAME&lt;/code&gt; and &lt;code&gt;DOCKER_HUB_PAT&lt;/code&gt; and run the &lt;code&gt;deploy.sh&lt;/code&gt; script, or modify the &lt;code&gt;src/main.bicepparam&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The credentials will be stored in a newly created Azure KeyVault within the example resource group. You can modify the resource group name and location in the parameter file.&lt;/p&gt;

&lt;p&gt;After deployment, you can link your public Docker Hub repository to Azure Container Registry by creating a cache with the credentials set from the Bicep deployment.&lt;/p&gt;

&lt;p&gt;For example, to cache the public Python repository, you can&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az acr cache create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--registry&lt;/span&gt; acrdemoxtpogsljytqrc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mypython &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-repo&lt;/span&gt; docker.io/library/python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-repo&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cred-set&lt;/span&gt; dockerhub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the cache rule is created we can evaluate it in the Azure Portal&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj0osjn2nqs6cqhfz7hgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj0osjn2nqs6cqhfz7hgs.png" alt="Caching rule in ACR" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To pull new images, simply reference your Azure Container Registry repository wherever you need it—whether in Azure Pipelines, GitHub Actions, or directly in the shell like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ az acr login &lt;span class="nt"&gt;--name&lt;/span&gt; acrdemoxtpogsljytqrc
Login Succeeded

~ took 5s
❯ docker pull acrdemoxtpogsljytqrc.azurecr.io/python
Using default tag: latest
latest: Pulling from python
Digest: sha256:d57ec66c94b9497b9f3c66f6cdddc1e4e0bad4c584397e0b57a721baef0e6fdc
Status: Image is up to &lt;span class="nb"&gt;date &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;acrdemoxtpogsljytqrc.azurecr.io/python:latest
acrdemoxtpogsljytqrc.azurecr.io/python:latest
~
❯ docker pull acrdemoxtpogsljytqrc.azurecr.io/python:alpine3.20
alpine3.20: Pulling from python
c4f340f38ec0: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;0152682790bb: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;bb4d68a0c460: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;05da92a20133: Download &lt;span class="nb"&gt;complete
&lt;/span&gt;Digest: sha256:9ab3b6ef4afb7582afaa84e97d40a36f192595bb0578561c282cecc22a45de49
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;acrdemoxtpogsljytqrc.azurecr.io/python:alpine3.20
acrdemoxtpogsljytqrc.azurecr.io/python:alpine3.20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to the cache rule, the images automatically transfer to your Azure Container Registry and appear in your ACR repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwrehurg27puwpwux6on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwrehurg27puwpwux6on.png" alt="3 Images already cached in the ACR" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;p&gt;In the shared GitHub project, I use a system-assigned identity, which requires splitting KeyVault creation and KeyVault Access Policies. This setup can be simplified once the &lt;a href="https://github.com/Azure/acr/issues/790?ref=dariuszparys.com" rel="noopener noreferrer"&gt;corresponding issue is resolved&lt;/a&gt; to allow user-assigned managed identities.&lt;/p&gt;

&lt;p&gt;I hope this helps!&lt;/p&gt;

&lt;p&gt;Cover image by Photo by &lt;a href="https://unsplash.com/@guibolduc?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Guillaume Bolduc&lt;/a&gt; / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>containers</category>
      <category>docker</category>
      <category>bicep</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Generate Sequence Diagrams for Azure B2C User Journeys with ChatGPT</title>
      <dc:creator>Dariusz Parys</dc:creator>
      <pubDate>Tue, 06 Aug 2024 07:43:04 +0000</pubDate>
      <link>https://forem.com/dariuszparys/generate-sequence-diagrams-for-azure-b2c-user-journeys-with-chatgpt-3fol</link>
      <guid>https://forem.com/dariuszparys/generate-sequence-diagrams-for-azure-b2c-user-journeys-with-chatgpt-3fol</guid>
      <description>&lt;p&gt;Working with Azure B2C is an XML battle, especially when it comes to customizing user journeys using custom policies. A starter pack you can get from GitHub helps you set up your own policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack" rel="noopener noreferrer"&gt;Azure B2C custom policy starter pack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The readme is comprehensive and has all the necessary information, but sometimes, it helps to have a flow sequence diagram. Sequence diagrams can be created using Mermaid, given a syntax that can be included in your Markdown documentation. The renderer will then visualize the sequences for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generative AI can help here
&lt;/h2&gt;

&lt;p&gt;There is an excellent way to do this with Generative AI. In my case, I use ChatGPT. The steps are as follows.&lt;/p&gt;

&lt;p&gt;First, upload the XML policy files you want to analyze. In my example, I'm interested in the sign-in and sign-up user journey, so I'm only uploading &lt;strong&gt;TrustFrameworkBase.xml&lt;/strong&gt;, &lt;strong&gt;TrustFrameworkExtensions.xml&lt;/strong&gt;, and &lt;strong&gt;SignUpOrSignIn.xml&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next, prompt ChatGPT to generate the sequence diagram based on the uploaded files. In the case I provide here, I used the following prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a sequence diagram for the &amp;lt;User Journey Name&amp;gt; user journey in Azure AD B2C. The diagram should integrate the flow of claims directly into the main sequence and use notes and background colors to distinguish between different flows. 

### Sequence Diagram Details:
1. The initial request where the user opens the page and requests the policy.
2. The user decision point where they choose between different options (e.g., Sign Up or Sign In).
3. The detailed flow for each option, including:
    - Collecting claims
    - Validating and transforming claims
    - Interactions with Azure AD for validation and user data retrieval
    - Displaying success messages to the user
4. The token refresh flow if applicable.
5. Use notes to highlight claims collection and validation.
6. Use different background colors to distinguish between different options (e.g., Sign Up and Sign In).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace  with the user journey I uploaded, in the example &lt;em&gt;Sign In or Sign Up&lt;/em&gt;. The chatbot will start to analyze the files, and as a result, I get back this Mermaid sequence diagram.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant User
    participant Browser
    participant AzureAD_B2C as Azure AD B2C
    participant IDP as Identity Provider
    participant App as Relying Party Application

    Note over User, Browser: Step 1: Access Sign Up or Sign In page
    User-&amp;gt;&amp;gt;Browser: Access Sign Up/Sign In Page
    Browser-&amp;gt;&amp;gt;AzureAD_B2C: Request Sign Up/Sign In Page
    AzureAD_B2C-&amp;gt;&amp;gt;Browser: Return Sign Up/Sign In Page

    Note over User, Browser: Step 2: Enter credentials or select IDP
    User-&amp;gt;&amp;gt;Browser: Enter credentials/select IDP
    Browser-&amp;gt;&amp;gt;AzureAD_B2C: Submit credentials/IDP selection

    alt Local Account
        AzureAD_B2C-&amp;gt;&amp;gt;AzureAD_B2C: Validate credentials
        AzureAD_B2C-&amp;gt;&amp;gt;Browser: Prompt for additional claims (if needed)
        Browser-&amp;gt;&amp;gt;User: Display additional claims form
        User-&amp;gt;&amp;gt;Browser: Submit additional claims
        Browser-&amp;gt;&amp;gt;AzureAD_B2C: Submit additional claims
    else Social IDP
        AzureAD_B2C-&amp;gt;&amp;gt;IDP: Redirect to IDP
        IDP-&amp;gt;&amp;gt;User: Display IDP login page
        User-&amp;gt;&amp;gt;IDP: Enter credentials
        IDP-&amp;gt;&amp;gt;AzureAD_B2C: Return user claims
    end

    Note over AzureAD_B2C: Step 4: Process user claims
    AzureAD_B2C-&amp;gt;&amp;gt;AzureAD_B2C: Validate and transform claims
    AzureAD_B2C-&amp;gt;&amp;gt;App: Send claims to relying party application

    Note over App, User: Step 5: User is authenticated
    App-&amp;gt;&amp;gt;User: Welcome Page or Application Landing Page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can look at the result in GitHub, which supports rendering Mermaid diagrams.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/dariuszparys/904d64978f9448f23bfb7698fee0cf6a" rel="noopener noreferrer"&gt;https://gist.github.com/dariuszparys/904d64978f9448f23bfb7698fee0cf6a&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As with all generative models, the output may vary depending on how you prompt it and what model you use. It is more about the basic idea that we can transform one format to another, which helps us create documentation we don't have out of the box. Give it a try.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>chatgpt</category>
      <category>mermaidjs</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Avoid Docker Hub Rate Limits (and Improve Your Workflow)</title>
      <dc:creator>Dariusz Parys</dc:creator>
      <pubDate>Tue, 14 May 2024 13:27:35 +0000</pubDate>
      <link>https://forem.com/dariuszparys/how-to-avoid-docker-hub-rate-limits-and-improve-your-workflow-50e9</link>
      <guid>https://forem.com/dariuszparys/how-to-avoid-docker-hub-rate-limits-and-improve-your-workflow-50e9</guid>
      <description>&lt;p&gt;Docker Hub has a rate limit that is effectively in place since November 2020 &lt;a href="https://www.docker.com/blog/checking-your-current-docker-pull-rate-limits-and-status/" rel="noopener noreferrer"&gt;Checking Your Current Docker Pull Rate Limits and Status | Docker&lt;/a&gt;. The detailed documentation about this rate limit is described here &lt;a href="https://docs.docker.com/docker-hub/download-rate-limit/" rel="noopener noreferrer"&gt;Docker Hub rate limit | Docker Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is nothing new, but somehow, there is. I recently received an email from Azure stating that I should mitigate the effects of rate limiting by June 30th, 2024.&lt;/p&gt;

&lt;p&gt;This date made me think, and I did some investigations. First, I started to look up precisely what the limits are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking Local Rate Limits
&lt;/h2&gt;

&lt;p&gt;If you don't have a docker subscription you pay for, you might hit the docker rate limit, which is for the free account - 200 image pulls per 6 hours. Our anonymous pulls were already just 100 image pulls per 6 hours.&lt;/p&gt;

&lt;p&gt;I never log in to Docker Hub, so I run under the anonymous tier. I also never hit this limit locally. Also, I have had builds often on certain days, and 100 pulls per 6 hours seems not too much. It turns out that the pull only counts if the image is not already cached on your system. That is good, as you usually don't wipe your image cache each time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieving Rate Limits on Your System
&lt;/h3&gt;

&lt;p&gt;Checking my local pulled docker images shows only one python base image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ docker images
REPOSITORY   TAG                IMAGE ID       CREATED         SIZE
python       3.11-slim-buster   db841a2e8ab3   11 months ago   120MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Following the &lt;a href="https://www.docker.com/blog/checking-your-current-docker-pull-rate-limits-and-status/" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; I retrieve with the anonymous bearer token the current limits I have.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ curl &lt;span class="nt"&gt;--head&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: &lt;span class="s2"&gt;"sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 07 May 2024 10:31:48 GMT
strict-transport-security: max-age&lt;span class="o"&gt;=&lt;/span&gt;31536000
ratelimit-limit: 100&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21600
ratelimit-remaining: 94&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21600
docker-ratelimit-source: 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I pulled now an additional &lt;code&gt;ubuntu:latest&lt;/code&gt; image and re-executed the curl call to the rate limit endpoint. The output states correctly that my limit was reduced by one.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ curl &lt;span class="nt"&gt;--head&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: &lt;span class="s2"&gt;"sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 07 May 2024 10:32:19 GMT
strict-transport-security: max-age&lt;span class="o"&gt;=&lt;/span&gt;31536000
ratelimit-limit: 100&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21600
ratelimit-remaining: 93&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21600
docker-ratelimit-source: 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Pulling again an image that is now locally cached, in this case the &lt;code&gt;ubuntu:latest&lt;/code&gt; again, doesn't affect the rate limit at all.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ curl &lt;span class="nt"&gt;--head&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: &lt;span class="s2"&gt;"sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 07 May 2024 10:32:36 GMT
strict-transport-security: max-age&lt;span class="o"&gt;=&lt;/span&gt;31536000
ratelimit-limit: 100&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21600
ratelimit-remaining: 93&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;21600
docker-ratelimit-source: 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Azure &amp;amp; Azure DevOps MS Hosted Agents: Handling Rate Limits
&lt;/h2&gt;

&lt;p&gt;But what about the Azure and Azure DevOps environments with Microsoft-hosted agents? Are they supposed to have the same issue?&lt;/p&gt;

&lt;p&gt;I created a simple test pipeline to see how Azure DevOps handles rate limits.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The docker images cached in the Ubuntu:22.04 runner image, can be found here &lt;a href="https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md#cached-docker-images" rel="noopener noreferrer"&gt;runner-images/images/ubuntu/Ubuntu2204-Readme.md&lt;/a&gt;. It doesn't have any of the images cached that I just pulled in the pipeline.&lt;/p&gt;

&lt;p&gt;The exciting thing about this pipeline run is that the output of the "Check rate limit" step doesn't show any rate limits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: &lt;span class="s2"&gt;"sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 07 May 2024 10:05:56 GMT
strict-transport-security: max-age&lt;span class="o"&gt;=&lt;/span&gt;31536000
docker-ratelimit-source: 13.79.31.179
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did the same for Azure, opened a CLI in the Portal, and entered the same rate limit query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dariusz &lt;span class="o"&gt;[&lt;/span&gt; ~ &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$ TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="s2"&gt;"https://auth.docker.io/token?service=registry.docker.io&amp;amp;scope=repository:ratelimitpreview/test:pull"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5429    0  5429    0     0  20653      0 &lt;span class="nt"&gt;--&lt;/span&gt;:--:-- &lt;span class="nt"&gt;--&lt;/span&gt;:--:-- &lt;span class="nt"&gt;--&lt;/span&gt;:--:-- 20721

dariusz &lt;span class="o"&gt;[&lt;/span&gt; ~ &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--head&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: &lt;span class="s2"&gt;"sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;: Tue, 07 May 2024 11:00:31 GMT
strict-transport-security: max-age&lt;span class="o"&gt;=&lt;/span&gt;31536000
docker-ratelimit-source: 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I inspected the retrieved token with &lt;a href="https://jwt.ms" rel="noopener noreferrer"&gt;https://jwt.ms&lt;/a&gt; and received the anonymous tier output, with an untouched rate limit of 100.&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;"alg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RS256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typ"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JWT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"x5c"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="s2"&gt;"MIIEFj...Cx5Q3"&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;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="s2"&gt;"pull"&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;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ratelimitpreview/test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"pull_limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"pull_limit_interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"21600"&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;span class="nl"&gt;"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;"repository"&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;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;"registry.docker.io"&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;1715079853&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;1715079553&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;"auth.docker.io"&lt;/span&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;"dckr_jti_CjpzWM-iQQaK_7QXf8wlUkz8kJI="&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;1715079253&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;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Signature&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;Based on this claim &lt;a href="https://forums.docker.com/t/testing-dockerhub-pull-limits-in-azure/120981" rel="noopener noreferrer"&gt;Testing Dockerhub pull limits in Azure - Docker Community Forums&lt;/a&gt; there must have been some agreement that was already known back in 2022 when this was written, and probably this agreement will now run out by end of June 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Rate Limit Mitigation
&lt;/h2&gt;

&lt;p&gt;The rate limits will likely affect future CI pipeline runs. In our case, we have a CI pipeline that builds images on each push to the Azure DevOps git repository using the Azure Container Registry build task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current State: CI Pipeline Configuration in Azure DevOps
&lt;/h3&gt;

&lt;p&gt;Below is a snippet from a pipeline step in Azure DevOps to build our image.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AzureCLI@2&lt;/span&gt;
&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;push&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;container&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;image"&lt;/span&gt;
&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;azureSubscription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(serviceConnection)&lt;/span&gt;
  &lt;span class="na"&gt;addSpnToEnvironment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;workingDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(projectRoot)&lt;/span&gt;
  &lt;span class="na"&gt;scriptType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
  &lt;span class="na"&gt;scriptLocation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inlineScript&lt;/span&gt;
  &lt;span class="na"&gt;inlineScript&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;az acr build --registry "${REGISTRY}" --image "${REGISTRY}.azurecr.io/backend:dev-${BUILD_NUMBER}" --image "${REGISTRY}.azurecr.io/backend:dev-latest" --platform linux --file Dockerfile .&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;REGISTRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(containerRegistryName)&lt;/span&gt;
  &lt;span class="na"&gt;BUILD_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Build.BuildNumber)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corresponding Dockerfile still refers to the base image on the Docker hub.&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-buster&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./requirements.txt /code/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;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /code/requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app /code/app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "app.main:app", "--host=0.0.0.0", "--port=8000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Azure Container Registry Caching
&lt;/h3&gt;

&lt;p&gt;As the Azure Container Registry is in place, we changed the behavior using the Azure Container Registry Cache feature.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; In its basic tier, Azure Container Registry costs nearly as much as the Pro subscription for Docker Hub. We are using Azure Container Registry for our project anyway, so we mitigate the additional costs. Your mileage may vary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This feature allows the ACR to pull and cache an image from another registry based on your defined rules. In the case of the Python-based images, a rule might look 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;az acr cache create &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--registry&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGISTRY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; python-base-images &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--source-repo&lt;/span&gt; docker.io/library/python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We changed the Dockerfile to pull from our ACR instead of the Docker hub where the base image originally lives.&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;ARG&lt;/span&gt;&lt;span class="s"&gt; REGISTRY=your-acr-registry-name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; $REGISTRY.azurecr.io/python:3.11-slim-buster&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./requirements.txt /code/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;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /code/requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app /code/app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "app.main:app", "--host=0.0.0.0", "--port=8000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time this runs, ACR will pull this image from Docker Hub and cache it under the &lt;strong&gt;python&lt;/strong&gt; repository, as specified in the shell command above.&lt;/p&gt;

&lt;p&gt;Once the pipeline runs and pulls the image through the Azure Container Registry, you can evaluate the cache rule and look up the cached repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az acr cache list &lt;span class="nt"&gt;--registry&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGISTRY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
az acr repository show &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGISTRY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--repository&lt;/span&gt; python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on, using Azure Container Registry Cache will mitigate rate limits that might affect our pipeline runs on Microsoft-hosted Agents and offer benefits like improved build performance, reduced network traffic, enhanced reliability, and simplified management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Optimizing Development Workflows in the Face of Rate Limits
&lt;/h2&gt;

&lt;p&gt;Docker Hub rate limits present an opportunity to optimize your development workflow and improve performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take Action&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explore caching strategies:&lt;/strong&gt; Understand how effective caching can reduce your reliance on Docker Hub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage your cloud provider's registry:&lt;/strong&gt;  Cloud registries often offer integrated solutions that streamline image management and can boost build speeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you need a high volume of pulls, consider a paid Docker Hub subscription:&lt;/strong&gt; This ensures reliable access and support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By proactively addressing Docker Hub's limits, you can enhance the efficiency and resilience of your CI/CD pipelines.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>azure</category>
      <category>containers</category>
    </item>
  </channel>
</rss>
