Skip to content

Please note that Emulator for Azure App Configuration is unofficial and not endorsed by Microsoft.

License

Notifications You must be signed in to change notification settings

tnc1997/azure-app-configuration-emulator

Repository files navigation

Emulator for Azure App Configuration

Docker Pulls Docker Stars

Please note that Emulator for Azure App Configuration is unofficial and not endorsed by Microsoft.

Getting Started

docker pull tnc1997/azure-app-configuration-emulator
docker run -p 8080:8080 tnc1997/azure-app-configuration-emulator

Authentication

The emulator supports HMAC authentication and Microsoft Entra ID authentication.

HMAC

The credential and secret may be overridden using the environment variables Authentication__Schemes__Hmac__Credential and Authentication__Schemes__Hmac__Secret respectively.

services:
  azure-app-configuration-emulator:
    environment:
      - Authentication__Schemes__Hmac__Credential=xyz
      - Authentication__Schemes__Hmac__Secret=c2VjcmV0
    image: tnc1997/azure-app-configuration-emulator

.NET

The client may authenticate requests using the connection string for the emulator.

using Azure.Data.AppConfiguration;

var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings__AzureAppConfiguration");
var client = new ConfigurationClient(connectionString);

var setting = new ConfigurationSetting("AzureAppConfigurationEmulator", "Hello World");
await client.SetConfigurationSettingAsync(setting);
services:
  azure-app-configuration-emulator:
    image: tnc1997/azure-app-configuration-emulator
  console-application:
    build:
      context: .
      dockerfile: ./ConsoleApplication/Dockerfile
    depends_on:
      - azure-app-configuration-emulator
    environment:
      - ConnectionStrings__AzureAppConfiguration=Endpoint=http://azure-app-configuration-emulator:8080;Id=abcd;Secret=c2VjcmV0;

Postman

The authentication related headers may be generated using the following script:

const credential = "abcd";
const secret = "c2VjcmV0";

const date = new Date().toUTCString();
const contentHash = CryptoJS.SHA256(CryptoJS.enc.Utf8.parse(pm.request.body.toString())).toString(CryptoJS.enc.Base64);

const signedHeaders = "x-ms-date;Host;x-ms-content-sha256";
const stringToSign = `${pm.request.method}\n${pm.request.url.getPathWithQuery()}\n${date};${pm.request.url.getRemote()};${contentHash}`;
const signature = CryptoJS.HmacSHA256(CryptoJS.enc.Utf8.parse(stringToSign), CryptoJS.enc.Base64.parse(secret)).toString(CryptoJS.enc.Base64);

pm.request.headers.upsert(`x-ms-date: ${date}`);
pm.request.headers.upsert(`x-ms-content-sha256: ${contentHash}`);
pm.request.headers.upsert(`Authorization: HMAC-SHA256 Credential=${credential}&SignedHeaders=${signedHeaders}&Signature=${signature}`);

Microsoft Entra ID

HMAC authentication is recommended because it does not require a Microsoft Entra tenant and an Azure App Configuration resource.

  1. Register an application within the Microsoft Entra tenant.
    1. On the Overview page, in the Essentials accordion, copy the following values:
      • Application (client) ID
      • Directory (tenant) ID
    2. On the Certificates & secrets page, in the Client secrets tab, add a client secret.
  2. Create an Azure App Configuration resource to be emulated.
    1. On the Overview page, in the Essentials accordion, copy the following values:
      • Endpoint
    2. On the Access control (IAM) page, add a role assignment.
      1. In the Role tab, select the App Configuration Data Owner role.
      2. In the Members tab, assign access to the registered application.
  3. Generate a self-signed certificate with the <endpoint> as the Subject Alternative Name.

The metadata address must be set using the environment variable Authentication__Schemes__MicrosoftEntraId__MetadataAddress.

services:
  azure-app-configuration-emulator:
    environment:
      - ASPNETCORE_HTTP_PORTS=80
      - ASPNETCORE_HTTPS_PORTS=443
      - Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
    image: tnc1997/azure-app-configuration-emulator
    networks:
      default:
        aliases:
          - <endpoint>
    volumes:
      - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
      - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro

The valid audience should be overriden using the environment variable Authentication__Schemes__MicrosoftEntraId__ValidAudience.

services:
  azure-app-configuration-emulator:
    environment:
      - ASPNETCORE_HTTP_PORTS=80
      - ASPNETCORE_HTTPS_PORTS=443
      - Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com/<tenant-id>/.well-known/openid-configuration
      - Authentication__Schemes__MicrosoftEntraId__ValidAudience=https://<endpoint>
    image: tnc1997/azure-app-configuration-emulator
    networks:
      default:
        aliases:
          - <endpoint>
    volumes:
      - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
      - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro

.NET

The client may authenticate requests using the Microsoft Entra tenant.

using Azure.Data.AppConfiguration;
using Azure.Identity;

var tenantId = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__TenantId");
var clientId = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__ClientId");
var clientSecret = Environment.GetEnvironmentVariable("Authentication__Schemes__MicrosoftEntraId__ClientSecret");
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);

var endpoint = Environment.GetEnvironmentVariable("Endpoints__AzureAppConfiguration");
var client = new ConfigurationClient(new Uri(endpoint), credential);

var setting = new ConfigurationSetting("AzureAppConfigurationEmulator", "Hello World");
await client.SetConfigurationSettingAsync(setting);
services:
  azure-app-configuration-emulator:
    environment:
      - ASPNETCORE_HTTP_PORTS=80
      - ASPNETCORE_HTTPS_PORTS=443
      - Authentication__Schemes__MicrosoftEntraId__MetadataAddress=https://login.microsoftonline.com/<tenant-id>/.well-known/openid-configuration
      - Authentication__Schemes__MicrosoftEntraId__ValidAudience=https://<endpoint>
    image: tnc1997/azure-app-configuration-emulator
    networks:
      default:
        aliases:
          - <endpoint>
    volumes:
      - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
      - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro
  console-application:
    build:
      context: .
      dockerfile: ./ConsoleApplication/Dockerfile
    depends_on:
      - azure-app-configuration-emulator
    entrypoint: /bin/sh -c "update-ca-certificates && dotnet ConsoleApplication.dll"
    environment:
      - Authentication__Schemes__MicrosoftEntraId__ClientId=<client-id>
      - Authentication__Schemes__MicrosoftEntraId__ClientSecret=<client-secret>
      - Authentication__Schemes__MicrosoftEntraId__TenantId=<tenant-id>
      - Endpoints__AzureAppConfiguration=https://<endpoint>
    volumes:
      - ./emulator.crt:/usr/local/share/ca-certificates/emulator.crt:ro

Postman

The access token may be obtained using the following configuration:

Configuration
Auth Type OAuth 2.0
Grant Type Client Credentials
Access Token URL https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
Client ID <client-id>
Client Secret <client-secret>
Scope https://<endpoint>/.default

Compatibility

The emulator is compatible with the following operations:

Key Values

Operation
Get ✔️
Get (Conditionally) ✔️
Get (Select Fields) ✔️
Get (Time-Based Access) ✔️
List ✔️
List (Pagination)
List (Filtering) ✔️
List (Select Fields) ✔️
List (Time-Based Access) ✔️
Set ✔️
Set (Conditionally) ✔️
Delete ✔️
Delete (Conditionally) ✔️

Keys

Operation
List ✔️
List (Pagination)
List (Filtering) ✔️
List (Select Fields) ✔️
List (Time-Based Access) ✔️

Labels

Operation
List ✔️
List (Pagination)
List (Filtering) ✔️
List (Select Fields) ✔️
List (Time-Based Access) ✔️

Locks

Operation
Lock ✔️
Lock (Conditionally) ✔️
Unlock ✔️
Unlock (Conditionally) ✔️

Revisions

Operation
List
List (Pagination)
List (Range)
List (Filtering)
List (Select Fields)
List (Time-Based Access)

Data

The emulator stores configuration settings in a SQLite database.

The data that is generated by the emulator during a session may be persisted between sessions using a volume.

services:
  azure-app-configuration-emulator:
    image: tnc1997/azure-app-configuration-emulator
    volumes:
      - azure-app-configuration-emulator:/var/lib/azureappconfigurationemulator
volumes:
  azure-app-configuration-emulator:

The connection string for the database may be overridden using the environment variable ConnectionStrings__DefaultConnection.

services:
  azure-app-configuration-emulator:
    environment:
      - ConnectionStrings__DefaultConnection=Data Source=/var/lib/azureappconfigurationemulator/emulator.db
    image: tnc1997/azure-app-configuration-emulator

The database may be seeded with data and mounted into the container however care should be taken to ensure that the database has the required schema.

services:
  azure-app-configuration-emulator:
    image: tnc1997/azure-app-configuration-emulator
    volumes:
      - ./emulator.db:/var/lib/azureappconfigurationemulator/emulator.db

Messaging

The emulator integrates with Azure Event Grid to publish events using the Event Grid event schema when configuration settings are deleted or modified.

The endpoint and key for the Event Grid Topic may be set using the environment variables Messaging__EventGridTopics__xyz__Endpoint and Messaging__EventGridTopics__xyz__Credential__Key respectively where xyz is an arbitrary name.

services:
  azure-app-configuration-emulator:
    environment:
      - Messaging__EventGridTopics__Contoso__Credential__Key=a2V5
      - Messaging__EventGridTopics__Contoso__Endpoint=https://contoso.uksouth-1.eventgrid.azure.net/api/events
    image: tnc1997/azure-app-configuration-emulator

Observability

The emulator integrates with OpenTelemetry to provide metrics and traces.

The endpoint for the OpenTelemetry Protocol (OTLP) Exporter may be overridden using the environment variable OTEL_EXPORTER_OTLP_ENDPOINT.

services:
  azure-app-configuration-emulator:
    depends_on:
      - opentelemetry-collector
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://opentelemetry-collector:4317
    image: tnc1997/azure-app-configuration-emulator
  opentelemetry-collector:
    image: otel/opentelemetry-collector-contrib

SSL / TLS

The emulator may be configured to serve requests over HTTPS using a self-signed certificate.

openssl req -x509 -out ./emulator.crt -keyout ./emulator.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=azure-app-configuration-emulator' -addext 'subjectAltName=DNS:azure-app-configuration-emulator'

The port for HTTPS must be set using the environment variable ASPNETCORE_HTTPS_PORTS.

The paths for the certificate and key must be set using the environment variables Kestrel__Certificates__Default__Path and Kestrel__Certificates__Default__KeyPath respectively.

The certificate and key must be mounted into the container at the paths that are set above.

services:
  azure-app-configuration-emulator:
    environment:
      - ASPNETCORE_HTTP_PORTS=8080
      - ASPNETCORE_HTTPS_PORTS=8081
      - Kestrel__Certificates__Default__Path=/usr/local/share/azureappconfigurationemulator/emulator.crt
      - Kestrel__Certificates__Default__KeyPath=/usr/local/share/azureappconfigurationemulator/emulator.key
    image: tnc1997/azure-app-configuration-emulator
    volumes:
      - ./emulator.crt:/usr/local/share/azureappconfigurationemulator/emulator.crt:ro
      - ./emulator.key:/usr/local/share/azureappconfigurationemulator/emulator.key:ro

Testcontainers

The emulator integrates with Testcontainers to ease the integration testing of applications that use Azure App Configuration.

var container = new ContainerBuilder()
    .WithImage("tnc1997/azure-app-configuration-emulator:1.0")
    .WithPortBinding(8080, true)
    .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Now listening on"))
    .Build();

await container.StartAsync();

var client = new ConfigurationClient($"Endpoint={new UriBuilder(Uri.UriSchemeHttp, container.Hostname, container.GetMappedPublicPort(8080))};Id=abcd;Secret=c2VjcmV0");

await client.SetConfigurationSettingAsync(nameof(ConfigurationSetting.Key), nameof(ConfigurationSetting.Value));

var response = await client.GetConfigurationSettingAsync(nameof(ConfigurationSetting.Key));

Coming Soon testcontainers/testcontainers-dotnet#1198

var container = new AzureAppConfigurationBuilder().Build();

await container.StartAsync();

var client = new ConfigurationClient(container.GetConnectionString());

await client.SetConfigurationSettingAsync(nameof(ConfigurationSetting.Key), nameof(ConfigurationSetting.Value));

var response = await client.GetConfigurationSettingAsync(nameof(ConfigurationSetting.Key));