Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Release Notes
=============

## 1.9.28
* App Configuration: Add support for Azure App Configuration stores, key-value items, and feature flags.

## 1.9.27
* Storage Accounts: Add `AccountKey` member to return just the storage account key and `ConnectionString` member to return the connection string. The existing `Key` member is now obsolete (it incorrectly returned a connection string instead of just the key).
* Network Manager: Add support for Azure Network Manager with security admin configurations, rule collections, rules with IP ranges and service tags, and network groups.
Expand Down
111 changes: 111 additions & 0 deletions docs/content/api-overview/resources/app-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
title: "App Configuration"
date: 2026-04-17T00:00:00+00:00
chapter: false
weight: 1
---

#### Overview
The App Configuration builder creates Azure App Configuration stores, which are a central repository for managing application settings and feature flags.

* Configuration Store (`Microsoft.AppConfiguration/configurationStores`)
* Key Values (`Microsoft.AppConfiguration/configurationStores/keyValues`)

#### Builder Keywords

##### configurationStore

| Keyword | Purpose |
|-|-|
| name | Sets the name of the App Configuration store. |
| sku | Sets the SKU of the store. Defaults to `Free`. Options: `Free`, `Developer`, `Standard`, `Premium`. |
| disable_local_auth | Disables local (access-key) authentication, requiring Azure AD / RBAC only. |
| enable_purge_protection | Enables purge protection (Standard SKU only). Once enabled, it cannot be disabled. |
| public_network_access | Sets public network access: `Enabled` or `Disabled`. |
| soft_delete_retention_in_days | Sets the soft-delete retention period in days (Standard SKU only, 1-7 days). |
| data_plane_authentication_mode | Sets the data plane authentication mode: `Local` or `Passthrough`. |
| add_feature_flag | Adds a single feature flag to the store. |
| add_feature_flags | Adds a list of feature flags to the store. |
| add_key_value | Adds a single key-value item to the store. |
| add_key_values | Adds a list of key-value items to the store. |
| add_tag | Adds an ARM tag to the store. |
| add_tags | Adds multiple ARM tags to the store. |
| depends_on | Adds a dependency to the store. |

#### Key Value Fields

| Field | Purpose |
|-|-|
| Key | The key name for the item. |
| Label | An optional label to distinguish between different environments or configurations. |
| Value | The value to store. |
| ContentType | Optional MIME content type (e.g. `application/json` for JSON values). |
| KeyValueTags | Optional App Configuration item tags (not ARM resource tags). |

#### Feature Flag Fields

| Field | Purpose |
|-|-|
| Name | The name of the feature flag. |
| Description | A human-readable description of the flag. |
| Label | An optional label (can be empty string for no label). |
| State | `true` if the flag is enabled, `false` if disabled. |

#### Configuration Members

| Member | Purpose |
|-|-|
| ResourceId | The ARM resource ID of the App Configuration store. |
| Endpoint | Returns an ARM expression for the data plane endpoint URL of the store. |

#### Example

```fsharp
open Farmer
open Farmer.Builders
open Farmer.ConfigurationStore

let myConfig = configurationStore {
name "my-app-config"
sku Standard
disable_local_auth
add_tags [ "env", "prod"; "team", "platform" ]

add_feature_flags [
{
Name = "DarkMode"
Description = "Enable dark mode UI"
Label = ""
State = true
}
{
Name = "BetaFeature"
Description = "Beta feature available to select users"
Label = "beta"
State = false
}
]

add_key_values [
{
Key = "Api:BaseUrl"
Label = Some "prod"
Value = "https://api.example.com"
ContentType = None
KeyValueTags = Map.empty
}
{
Key = "FeatureSettings"
Label = None
Value = """{"timeout":30,"retries":3}"""
ContentType = Some "application/json"
KeyValueTags = Map [ "environment", "production" ]
}
]
}

let deployment = arm {
location Location.NorthEurope
add_resource myConfig
}
```
116 changes: 116 additions & 0 deletions src/Farmer/Arm/ConfigurationStore.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
[<AutoOpen>]
module Farmer.Arm.ConfigurationStore

open Farmer
open Farmer.ConfigurationStore

let configurationStores =
ResourceType("Microsoft.AppConfiguration/configurationStores", "2024-05-01")

let keyValues =
ResourceType("Microsoft.AppConfiguration/configurationStores/keyValues", "2024-05-01")

type ConfigFeatureFlag = {
Name: string
Description: string
Label: string
State: bool
ConfigurationStoreId: ResourceId
} with

member this.ResourceName =
let labelSuffix = if this.Label = "" then "" else $"${this.Label}"
ResourceName $"{this.ConfigurationStoreId.Name.Value}/.appconfig.featureflag~2F{this.Name}{labelSuffix}"

interface IArmResource with
member this.ResourceId = keyValues.resourceId this.ResourceName

member this.JsonModel =
let enabled = this.State |> string |> _.ToLower()

let featureFlagValue =
$"""{{"id":"{this.Name}","description":"{this.Description}","enabled":{enabled}}}"""

{|
keyValues.Create(this.ResourceName, dependsOn = [ this.ConfigurationStoreId ]) with
properties = {|
value = featureFlagValue
contentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"
|}
|}

type KeyValue = {
/// The name of the key value in the format `{storeName}/{key}` or `{storeName}/{key}$label`.
Name: ResourceName
Value: string
ContentType: string option
/// Tags applied to the App Configuration key-value item (not ARM resource tags).
KeyValueTags: Map<string, string>
Dependencies: ResourceId Set
} with

interface IArmResource with
member this.ResourceId = keyValues.resourceId this.Name

member this.JsonModel = {|
keyValues.Create(this.Name, dependsOn = this.Dependencies) with
properties = {|
value = this.Value
contentType = this.ContentType |> Option.toObj
tags =
if this.KeyValueTags.IsEmpty then
null
else
box this.KeyValueTags
|}
|}

type ConfigurationStore = {
Name: ResourceName
Location: Location
Sku: Sku
DisableLocalAuth: bool option
EnablePurgeProtection: bool option
PublicNetworkAccess: Farmer.FeatureFlag option
SoftDeleteRetentionInDays: int option
DataPlaneAuthenticationMode: DataPlaneAuthenticationMode option
Tags: Map<string, string>
Dependencies: ResourceId Set
} with

interface IArmResource with
member this.ResourceId = configurationStores.resourceId this.Name

member this.JsonModel = {|
configurationStores.Create(this.Name, this.Location, this.Dependencies, this.Tags) with
sku = {|
name =
match this.Sku with
| Free -> "free"
| Developer -> "developer"
| Standard -> "standard"
| Premium -> "premium"
|}
properties = {|
disableLocalAuth = this.DisableLocalAuth |> Option.toNullable
enablePurgeProtection = this.EnablePurgeProtection |> Option.toNullable
publicNetworkAccess =
this.PublicNetworkAccess
|> Option.map (fun f ->
match f with
| Enabled -> "Enabled"
| Disabled -> "Disabled")
|> Option.toObj
softDeleteRetentionInDays = this.SoftDeleteRetentionInDays |> Option.toNullable
dataPlaneProxy =
this.DataPlaneAuthenticationMode
|> Option.map (fun mode ->
box {|
authenticationMode =
match mode with
| Local -> "Local"
| Passthrough -> "Pass-through"
|})
|> Option.toObj
|}
|}
Loading