In part 1, I covered the what’s happening underneath the covers with the usage of OCI artifacts to publish to Azure Container Registry. As a reminder, we published a dummy file as a generic artifact to the container registry. In this guide, I’ll cover how to push a real Helm 3 chart.

Background

Microsoft recommends that when using Helm 3 charts, that you use the native helm chart commands so to manage the charts as OCI artifacts.

Microsoft already has good documentation on how to do this using just terminal commands. This guide is more geared towards applying it with Azure DevOps.

Pre-Requisites

  • An Azure Container Registry
  • Helm Client 3 or later
  • Azure CLI version 2.0.71 or later

Disclosure

Helm 3 support for Azure Container Registry is still a preview feature. Even the support from the Helm CLI is still experimental.

The main way

First, define an environment variable of HELM_EXPERIMENTAL_OCI. Any pipeline variable gets mapped to an environment variable on the agent.

variables:
  HELM_EXPERIMENTAL_OCI: 1
  ACR.Name: '[Your azure container registry name]'
  Azure.ServiceConnection: '[your azure service connection]'

pool:
  vmImage: 'ubuntu-latest'

Then ensure you have Helm installed on the agent running the pipeline.

- task: HelmInstaller@1
  inputs:
    helmVersionToInstall: 'latest'

Next, login to Azure Container Registry using the Azure CLI.

- task: AzureCLI@2
  displayName: Login to Azure Container Registry
  inputs:
    azureSubscription: "$(Azure.ServiceConnection)"
    scriptType: bash
    scriptLocation: inlineScript
    inlineScript: |
      az acr login --name $(ACR.Name)

Lastly, save the chart and push it using Bash.

- bash: |
    helm chart save ./k8s/realworld-backend/ $(ACR.Name).azurecr.io/charts/realworld-backend:$(Build.BuildNumber)
    helm chart push $(ACR.Name).azurecr.io/charts/realworld-backend:$(Build.BuildNumber)

realworld-backend is the name of my Helm chart. It resides under the k8s/realworld-backend/ subdirectory in my repo. It lives on the same repo as the source code. Replace those two things with your respective helm chart name and the relative location on your repo.

Notice how it’s also pushing the the chart to a charts/realworld-backend repository on ACR.

Putting it all together

trigger:
- master

pr: none

variables:
  HELM_EXPERIMENTAL_OCI: 1
  ACR.Name: '[Your azure container registry name]'
  Azure.ServiceConnection: '[your azure service connection]'

stages:
- stage: build
  displayName: Build and Push
  jobs:  
  - job: job_helm
    displayName: Helm Publish
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - task: AzureCLI@2
      displayName: Login to Azure Container Registry
      inputs:
        azureSubscription: "$(Azure.ServiceConnection)"
        scriptType: bash
        scriptLocation: inlineScript
        inlineScript: |
          az acr login --name $(ACR.Name)
    - task: HelmInstaller@1
      inputs:
        helmVersionToInstall: 'latest'
    - bash: |
        helm chart save ./k8s/realworld-backend/ $(ACR.Name).azurecr.io/charts/realworld-backend:latest
        helm chart push $(ACR.Name).azurecr.io/charts/realworld-backend:latest

Open Issue

Today, there’s an open issue on GitHub. For myself, I ran into this when publishing the Helm chart from the Azure DevOps Ubuntu and Windows agents. I did not run into this from my workstation using Windows 10. I also ran into this issue from my Linux workstation.

The issue looks like this.

Error: failed to authorize: failed to fetch anonymous token: unexpected status: 401 Unauthorized

I pretty much ruled out it was not an authentication issue by replicating the issue using my user account on my Linux workstation. It worked on my Windows workstation.

Fallback

If you’re running into the same issues as above, you can try the legacy approach. Instead of issuing helm chart commands, we’ll use the az acr helm commands.

The pipeline steps are as follows.

steps:
  - task: HelmInstaller@1
    inputs:
      helmVersionToInstall: 'latest'
  - bash: |
      helm package k8s/realworld-backend/ --app-version $(Build.BuildNumber)
      mv *.tgz $(Build.ArtifactStagingDirectory)/
  - task: AzureCLI@1
    displayName: 'Push helm chart'
    inputs:
      azureSubscription: $(Azure.ServiceConnection)
      scriptLocation: inlineScript
      inlineScript: 'az acr helm push -n $(ACR.Name) $(Build.ArtifactStagingDirectory)/*.tgz'

realworld-backend is the name of my Helm chart. It resides under the k8s/realworld-backend/ subdirectory in my repo. It lives on the same repo as the source code. Replace those two things with your respective helm chart name and the relative location on your repo.

But, this is what happens here.

First, we package the Helm chart using Helm 2 commands. We also override the app version with the build number. It would be useful to know from which build this chart was generated. For the chart version, it would be up to the authors to change it using a SemVer version.

Secondly, we move the packaged helm chart to the staging directory. Then, we push the Helm chart using the Azure CLI ACR Helm commands. Upon pushing to ACR, you’ll have to follow the format [chart name]-[chart version].tgz.

So, although it looks easy, you might run into some snags because of the preview nature.

Best of luck.