Announcing the new Azure Container Registry libraries

Mario Guerra

We’re pleased to announce that version 1.1.0 of the Azure Container Registry (ACR) libraries for .NET, Java, JavaScript/TypeScript, and Python are now generally available. A Go library is currently available as a preview; the stable release is expected later this year.

Our new libraries follow our modern Azure SDK Guidelines, meaning that you can expect an idiomatic, consistent, approachable, diagnosable, and dependable library experience. These libraries use the language-specific Azure Core packages, which handle requests, errors, and credentials.

ACR is a private registry service that allows you to create, store, and manage container images and related artifacts. It’s compatible with Docker and Open Container Initiative (OCI) images and artifacts, which means you can use it with your existing container development and deployment pipelines. If you need to build container images on demand, you can use ACR Tasks.

New features

One of the most significant improvements in this version of the ACR library is the ability to upload and download images to ACR. Previously, version 1.0 only allowed developers to list repositories and images, get and update metadata, and delete them.

Library availability

The new ACR libraries can be downloaded from each language’s preferred package manager.

Language Package Command Project Get started
.NET NuGet dotnet add package Azure.Containers.ContainerRegistry link link
Java Java Add to POM.xml file link link
JavaScript/TypeScript npm npm install @azure/container-registry link link
Python PyPI pip install azure-containerregistry link link

Code samples

The following samples illustrate how to upload and download OCI and Docker images to an ACR instance using the ACR library for .NET.

Samples are also available for Java, TypeScript/JavaScript, and Python.

Create a client

Create a ContainerRegistryContentClient for the registry, passing the repository, credentials, and client options.

// Get the service endpoint from the environment
Uri endpoint = new(Environment.GetEnvironmentVariable("REGISTRY_ENDPOINT"));

string repository = "sample-oci-image";
string tag = "demo";

// Create a new ContainerRegistryContentClient
ContainerRegistryContentClient client = new(endpoint, repository, new DefaultAzureCredential());

Upload an OCI image

To upload an OCI image, upload its config file, layers, and manifest. In this sample, the manifest is updated with information about each file associated with the image, and then uploaded as a final step.

// Create a manifest to list files in this image
OciImageManifest manifest = new(schemaVersion: 2);

// Upload a config file
BinaryData config = BinaryData.FromString("Sample config");
UploadRegistryBlobResult uploadConfigResult = await client.UploadBlobAsync(config);

// Update manifest with config info
manifest.Configuration = new OciDescriptor()
  Digest = uploadConfigResult.Digest,
  SizeInBytes = uploadConfigResult.SizeInBytes,
  MediaType = "application/vnd.oci.image.config.v1+json"

// Upload a layer file
BinaryData layer = BinaryData.FromString("Sample layer");
UploadRegistryBlobResult uploadLayerResult = await client.UploadBlobAsync(layer);

// Update manifest with layer info
manifest.Layers.Add(new OciDescriptor()
  Digest = uploadLayerResult.Digest,
  SizeInBytes = uploadLayerResult.SizeInBytes,
  MediaType = "application/vnd.oci.image.layer.v1.tar"

// Finally, upload the manifest file
await client.SetManifestAsync(manifest, tag);

Download an OCI image

To download an OCI image, first download its manifest. The manifest describes the files that need to be downloaded to pull the full image.

// Download the manifest to obtain the list of files in the image
GetManifestResult result = await client.GetManifestAsync(tag);
OciImageManifest manifest = result.Manifest.ToObjectFromJson<OciImageManifest>();

string manifestFile = Path.Combine(path, "manifest.json");

using (FileStream stream = File.Create(manifestFile))
  await result.Manifest.ToStream().CopyToAsync(stream);

// Download and write out the config
DownloadRegistryBlobResult configBlob = await client.DownloadBlobContentAsync(manifest.Configuration.Digest);
string configFile = Path.Combine(path, "config.json");

using (FileStream stream = File.Create(configFile))
  await configBlob.Content.ToStream().CopyToAsync(stream);

// Download and write out the layers
foreach (OciDescriptor layerInfo in manifest.Layers)
  string layerFile = Path.Combine(path, TrimSha(layerInfo.Digest));
  using (FileStream stream = File.Create(layerFile))
    await client.DownloadBlobToAsync(layerInfo.Digest, stream);

static string TrimSha(string digest)
  int index = digest.IndexOf(':');
  if (index > -1)
    return digest.Substring(index + 1);
  return digest;

Upload a custom manifest type

To upload an image with a custom manifest type, pass the ManifestMediaType to the UploadManifest method.

// Create a manifest file in the Docker v2 Manifest List format
dynamic manifestList = new
  schemaVersion = 2,
  mediaType = "application/vnd.docker.distribution.manifest.list.v2+json",
  manifests = new[]
      digest = "sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4",
      mediaType = ManifestMediaType.DockerManifest.ToString(),
      platform = new {
        architecture = ArtifactArchitecture.Amd64.ToString(),
        os = ArtifactOperatingSystem.Linux.ToString()

// Finally, upload the manifest file
BinaryData content = BinaryData.FromObjectAsJson(manifestList);
await client.SetManifestAsync(content, tag: "sample", ManifestMediaType.DockerManifestList);

Download a manifest of an unknown type

If you’re downloading a manifest where the media type isn’t known ahead of time, check the MediaType property returned on the DownloadManifestResult to determine the type.

GetManifestResult result = await client.GetManifestAsync("sample");
if (result.MediaType == "application/vnd.docker.distribution.manifest.list.v2+json")
  Console.WriteLine("Manifest is a Docker manifest list.");
else if (result.MediaType == "application/vnd.oci.image.index.v1+json")
  Console.WriteLine("Manifest is an OCI index.");

Delete a manifest

A manifest can be deleted as shown in the following example. It’s also possible to delete a full image using the ContainerRegistryClient as shown in a separate example.

GetManifestResult manifestResult = await client.GetManifestAsync(tag);
await client.DeleteManifestAsync(manifestResult.Digest);

Delete a blob

A blob can be deleted as shown in the following example. It’s also possible to delete a full image using the ContainerRegistryClient as shown in a separate example.

GetManifestResult result = await client.GetManifestAsync(tag);
OciImageManifest manifest = result.Manifest.ToObjectFromJson<OciImageManifest>();

foreach (OciDescriptor layerInfo in manifest.Layers)
  await client.DeleteBlobAsync(layerInfo.Digest);

Learn more

We hope this article was a useful introduction to the new and improved ACR libraries and how you can use them today to build and ship rich experiences.

To learn more and to get started, see these links to our official documentation:

Give us your feedback

We appreciate your feedback and encourage you to share your thoughts with us. We thrive on improvement and would welcome any suggestions you may have. Let’s work together to make our experience even better!

You can reach out to us by filing issues in the GitHub repo of the language you’re interested in:

Include the “[Container Registry]” string in the issue title so it gets routed to the right people.