Skip to content

About

Provides Microsoft Azure Storage for Arc

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

Repository files navigation

Waffle Azure Storage Provider

Provides a Waffle storage back-end for Azure Blob Storage.

Formerly arc_azure. If you are upgrading, see Migrating from arc_azure.

Installation

Add waffle_azure to your list of dependencies in mix.exs. It requires Elixir 1.16 or later.

def deps do
  [
    {:waffle_azure, "~> 0.3.0"},
    {:waffle, "~> 2.0"}
  ]
end

Run mix deps.get in your shell to fetch the dependencies.

Configuration

Configure Waffle to use the adapter, and give it the name of your container, which must already exist:

config :waffle,
  storage: Waffle.Storage.Azure,
  bucket: "uploads"

The Azure account and access key are read from these environment variables:

export AZURE_ACCOUNT=<your-azure-account>
export AZURE_ACCESS_KEY=<your-azure-access-key>

Or you can set them in your configuration:

config :waffle_azure,
  account: "your-azure-account",
  access_key: "your-azure-access-key"

See the Waffle.Storage.Azure docs for the other options, including per-uploader containers, CDNs, and blob properties such as the content type.

Managed identity

When your app runs on Azure, it can use its managed identity instead of an access key. This works on App Service, Azure Functions and Container Apps, and on hosts with the Instance Metadata Service, such as VMs and AKS nodes.

  1. Turn on a managed identity for the app, then give it the Storage Blob Data Contributor role on the storage account:

    az role assignment create \
      --assignee <the-identity's-principal-id> \
      --role "Storage Blob Data Contributor" \
      --scope "$(az storage account show --name <account> --query id --output tsv)"

    New role assignments can take a few minutes to apply.

  2. Configure the adapter to use it:

    config :waffle_azure,
      account: "your-azure-account",
      credential: :managed_identity

    For a user-assigned identity, give its client ID instead: credential: {:managed_identity, client_id: "..."}.

With a managed identity:

  • No access key is needed, so you can turn off Shared Key access on the storage account.
  • Signed URLs are user delegation SAS URLs. They can be valid for up to 7 days, less an hour.
  • Managed identities only exist on Azure, so keep using an access key or Azurite in development. Azure Arc-enabled servers aren't supported yet.

Planned

These ways to authenticate with Microsoft Entra ID aren't supported yet, but are planned:

  • AKS workload identity, which exchanges the pod's Kubernetes service account token for an Entra token.
  • Service principal with a client secret, for apps that run outside Azure.
  • A custom token function, given as {module, function, args}, for any other way of getting a token.

Usage

Uploaders work as they do with any other Waffle storage:

defmodule MyApp.Avatar do
  use Waffle.Definition

  @versions [:original]

  def storage_dir(_version, {_file, user}), do: "avatars/#{user.id}"

  # Without a content type, browsers download blobs rather than display them.
  def s3_object_headers(_version, {file, _user}) do
    [content_type: MIME.from_path(file.file_name)]
  end
end

{:ok, file_name} = MyApp.Avatar.store({"/path/to/selfie.png", user})

# For a container with anonymous read access
MyApp.Avatar.url({file_name, user})

# For a private container: a read-only URL, valid for an hour
MyApp.Avatar.url({file_name, user}, :original, signed: true, expires_in: 3600)

:ok = MyApp.Avatar.delete({file_name, user})

Migrating from arc_azure

  1. Move your uploaders from Arc to Waffle, starting by replacing use Arc.Definition with use Waffle.Definition.
  2. Replace the arc_azure, arc, and erlazure dependencies with the ones under Installation.
  3. Update your configuration:
arc_azure waffle_azure
config :arc, storage: Arc.Storage.Azure config :waffle, storage: Waffle.Storage.Azure
config :arc_azure, container: "uploads" config :waffle, bucket: "uploads"
config :arc_azure, cdn_url: "https://<account>.blob.core.windows.net" Remove it: this is now the default
config :arc_azure, cdn_url: "https://cdn.example.com" config :waffle, asset_host: "https://cdn.example.com/uploads" (now includes the container)
config :ex_azure, account: ..., access_key: ... config :waffle_azure, account: ..., access_key: ...
config :arc, default_tempurl_ttl: ... Remove it, and pass expires_in: with signed: true

Some behaviour has also changed:

  • url now percent-encodes file names, so a stored a b.png gives a%20b.png rather than an invalid URL.
  • url with signed: true now returns a working signed URL. It used to be ignored, and the temp_url_* options, which had no effect, are gone.
  • delete now accepts a plain file name when there is no scope, as well as a full URL.
  • delete logs any failure and still returns :ok.
  • Blobs keep the same names in the container, so existing files don't need to move.

Development

mix deps.get
mix test

mix test doesn't need network access or an Azure account. To also run the tests against a real Blob Storage implementation, which verifies the request signing, start the Azurite emulator and include its tests:

docker run --rm -p 127.0.0.1:10000:10000 mcr.microsoft.com/azure-storage/azurite azurite-blob --blobHost 0.0.0.0
mix test --include azurite

License

Copyright 2017 Philip Alekseev

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

Provides Microsoft Azure Storage for Arc

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages