From 92016dcd557ce2cd67430283725a52d05c424445 Mon Sep 17 00:00:00 2001 From: Ryan Palmer Date: Fri, 23 Jan 2026 16:30:04 +0000 Subject: [PATCH 1/2] Tested working builder and ARM deploy --- src/Farmer/Arm/ConfigurationStore.fs | 76 ++++++++++++++++ .../Builders/Builders.ConfigurationStore.fs | 90 +++++++++++++++++++ src/Farmer/Farmer.fsproj | 2 + 3 files changed, 168 insertions(+) create mode 100644 src/Farmer/Arm/ConfigurationStore.fs create mode 100644 src/Farmer/Builders/Builders.ConfigurationStore.fs diff --git a/src/Farmer/Arm/ConfigurationStore.fs b/src/Farmer/Arm/ConfigurationStore.fs new file mode 100644 index 000000000..13ba1c727 --- /dev/null +++ b/src/Farmer/Arm/ConfigurationStore.fs @@ -0,0 +1,76 @@ +[] +module Farmer.Arm.ConfigurationStore + +open Farmer + +let configurationStores = + ResourceType("Microsoft.AppConfiguration/configurationStores", "2024-05-01") + +let keyValues = + ResourceType("Microsoft.AppConfiguration/configurationStores/keyValues", "2024-05-01") + +type AzFeatureFlag = { + Name: string + Description: string + Label: string + State: bool + ConfigurationStoreId: ResourceId +} with + member this.ResourceName = ResourceName $"[format('{{0}}/{{1}}', '{this.ConfigurationStoreId.Name.Value}', format('.appconfig.featureflag~2F{{0}}${{1}}', '{this.Name}', '{this.Label}'))]" + 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 ConfigSku = + | Free + | Developer + | Standard + | Premium + member this.ArmValue = + match this with + | Free -> "free" + | Developer -> "developer" + | Standard -> "standard" + | Premium -> "premium" + +type DataPlaneAuthenticationMode = + | Local + | Passthrough + member this.ArmValue = + match this with + | Local -> "Local" + | Passthrough -> "Pass-through" + +type ConfigurationStore = { + Name: ResourceName + Location: Location + Sku: ConfigSku + DisableLocalAuth: bool + DataPlaneAuthenticationMode: DataPlaneAuthenticationMode + Tags: Map +} with + + interface IArmResource with + member this.ResourceId = configurationStores.resourceId this.Name + + member this.JsonModel = {| + configurationStores.Create(this.Name, this.Location, tags = this.Tags) with + sku = {| + name = this.Sku.ArmValue + |} + properties = {| + disableLocalAuth = this.DisableLocalAuth + dataPlaneProxy = {| + authenticationMode = this.DataPlaneAuthenticationMode.ArmValue + |} + |} + |} \ No newline at end of file diff --git a/src/Farmer/Builders/Builders.ConfigurationStore.fs b/src/Farmer/Builders/Builders.ConfigurationStore.fs new file mode 100644 index 000000000..af08caec2 --- /dev/null +++ b/src/Farmer/Builders/Builders.ConfigurationStore.fs @@ -0,0 +1,90 @@ +[] +module Farmer.Builders.ConfigurationStore + +open Farmer +open Farmer.Arm.ConfigurationStore + +type FeatureFlagConfig = + { + Name: string + Description: string + Label: string + State: bool + } + member this.BuildResource (configurationStore: ResourceId) = + { + Name = this.Name + Description = this.Description + Label = this.Label + State = this.State + ConfigurationStoreId = configurationStore + } + :> IArmResource + +type ConfigurationStoreConfig = + { + Name: ResourceName + Location: Location + Sku: ConfigSku + DisableLocalAuth: bool + DataPlaneAuthenticationMode: DataPlaneAuthenticationMode + FeatureFlags: FeatureFlagConfig list + Tags: Map + } + interface IBuilder with + member this.ResourceId = configurationStores.resourceId this.Name + member this.BuildResources location = [ + { + Name = this.Name + Location = location + Sku = this.Sku + DisableLocalAuth = this.DisableLocalAuth + DataPlaneAuthenticationMode = this.DataPlaneAuthenticationMode + Tags = this.Tags + } + yield! this.FeatureFlags |> List.map (fun ff -> ff.BuildResource (configurationStores.resourceId this.Name)) + ] + +type ConfigurationStoreBuilder() = + member _.Yield _ = { + Name = ResourceName.Empty + Location = Location.NorthEurope + Sku = ConfigSku.Free + DisableLocalAuth = true + DataPlaneAuthenticationMode = DataPlaneAuthenticationMode.Passthrough + FeatureFlags = [] + Tags = Map.empty + } + + /// Sets the name of the configuration store. + [] + member _.Name(state: ConfigurationStoreConfig, name) = { state with Name = ResourceName name } + + /// Sets the location of the configuration store. + [] + member _.Location(state: ConfigurationStoreConfig, location) = { state with Location = location } + + /// Sets the SKU of the configuration store. + [] + member _.Sku(state: ConfigurationStoreConfig, sku) = { state with Sku = sku } + + /// Disables local authentication for the configuration store. + [] + member _.DisableLocalAuth(state: ConfigurationStoreConfig, disable: bool) = { state with DisableLocalAuth = disable } + + /// Sets the data plane authentication mode for the configuration store. + [] + member _.DataPlaneAuthenticationMode(state: ConfigurationStoreConfig, mode) = { state with DataPlaneAuthenticationMode = mode } + + /// Adds a feature flag to the configuration store. + [] + member _.FeatureFlags(state: ConfigurationStoreConfig, featureFlags: FeatureFlagConfig list) = { state with FeatureFlags = featureFlags } + + interface ITaggable with + /// Adds a tag to this Configuration Store. + member _.Add state tags = { + state with + Tags = state.Tags |> Map.merge tags + } + +let configurationStore = ConfigurationStoreBuilder() \ No newline at end of file diff --git a/src/Farmer/Farmer.fsproj b/src/Farmer/Farmer.fsproj index 1a05dd366..8b5e39893 100644 --- a/src/Farmer/Farmer.fsproj +++ b/src/Farmer/Farmer.fsproj @@ -64,6 +64,7 @@ + @@ -126,6 +127,7 @@ + From f851bcf59d608bdcdf99eb4eb41ea52872481bf9 Mon Sep 17 00:00:00 2001 From: Ryan Palmer Date: Fri, 23 Jan 2026 16:34:19 +0000 Subject: [PATCH 2/2] Fantomas --- src/Farmer/Arm/ConfigurationStore.fs | 20 +++++-- .../Builders/Builders.ConfigurationStore.fs | 58 +++++++++++-------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/Farmer/Arm/ConfigurationStore.fs b/src/Farmer/Arm/ConfigurationStore.fs index 13ba1c727..69cef4d77 100644 --- a/src/Farmer/Arm/ConfigurationStore.fs +++ b/src/Farmer/Arm/ConfigurationStore.fs @@ -16,12 +16,20 @@ type AzFeatureFlag = { State: bool ConfigurationStoreId: ResourceId } with - member this.ResourceName = ResourceName $"[format('{{0}}/{{1}}', '{this.ConfigurationStoreId.Name.Value}', format('.appconfig.featureflag~2F{{0}}${{1}}', '{this.Name}', '{this.Label}'))]" + + member this.ResourceName = + ResourceName + $"[format('{{0}}/{{1}}', '{this.ConfigurationStoreId.Name.Value}', format('.appconfig.featureflag~2F{{0}}${{1}}', '{this.Name}', '{this.Label}'))]" + 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}}}""" + + let featureFlagValue = + $"""{{"id":"{this.Name}","description":"{this.Description}","enabled":{enabled}}}""" + {| keyValues.Create(this.ResourceName, dependsOn = [ this.ConfigurationStoreId ]) with properties = {| @@ -35,6 +43,7 @@ type ConfigSku = | Developer | Standard | Premium + member this.ArmValue = match this with | Free -> "free" @@ -45,6 +54,7 @@ type ConfigSku = type DataPlaneAuthenticationMode = | Local | Passthrough + member this.ArmValue = match this with | Local -> "Local" @@ -58,15 +68,13 @@ type ConfigurationStore = { DataPlaneAuthenticationMode: DataPlaneAuthenticationMode Tags: Map } with - + interface IArmResource with member this.ResourceId = configurationStores.resourceId this.Name member this.JsonModel = {| configurationStores.Create(this.Name, this.Location, tags = this.Tags) with - sku = {| - name = this.Sku.ArmValue - |} + sku = {| name = this.Sku.ArmValue |} properties = {| disableLocalAuth = this.DisableLocalAuth dataPlaneProxy = {| diff --git a/src/Farmer/Builders/Builders.ConfigurationStore.fs b/src/Farmer/Builders/Builders.ConfigurationStore.fs index af08caec2..632276b9c 100644 --- a/src/Farmer/Builders/Builders.ConfigurationStore.fs +++ b/src/Farmer/Builders/Builders.ConfigurationStore.fs @@ -4,14 +4,14 @@ module Farmer.Builders.ConfigurationStore open Farmer open Farmer.Arm.ConfigurationStore -type FeatureFlagConfig = - { - Name: string - Description: string - Label: string - State: bool - } - member this.BuildResource (configurationStore: ResourceId) = +type FeatureFlagConfig = { + Name: string + Description: string + Label: string + State: bool +} with + + member this.BuildResource(configurationStore: ResourceId) = { Name = this.Name Description = this.Description @@ -21,19 +21,20 @@ type FeatureFlagConfig = } :> IArmResource -type ConfigurationStoreConfig = - { - Name: ResourceName - Location: Location - Sku: ConfigSku - DisableLocalAuth: bool - DataPlaneAuthenticationMode: DataPlaneAuthenticationMode - FeatureFlags: FeatureFlagConfig list - Tags: Map - } +type ConfigurationStoreConfig = { + Name: ResourceName + Location: Location + Sku: ConfigSku + DisableLocalAuth: bool + DataPlaneAuthenticationMode: DataPlaneAuthenticationMode + FeatureFlags: FeatureFlagConfig list + Tags: Map +} with + interface IBuilder with member this.ResourceId = configurationStores.resourceId this.Name - member this.BuildResources location = [ + + member this.BuildResources location = [ { Name = this.Name Location = location @@ -42,7 +43,9 @@ type ConfigurationStoreConfig = DataPlaneAuthenticationMode = this.DataPlaneAuthenticationMode Tags = this.Tags } - yield! this.FeatureFlags |> List.map (fun ff -> ff.BuildResource (configurationStores.resourceId this.Name)) + yield! + this.FeatureFlags + |> List.map (fun ff -> ff.BuildResource(configurationStores.resourceId this.Name)) ] type ConfigurationStoreBuilder() = @@ -70,15 +73,24 @@ type ConfigurationStoreBuilder() = /// Disables local authentication for the configuration store. [] - member _.DisableLocalAuth(state: ConfigurationStoreConfig, disable: bool) = { state with DisableLocalAuth = disable } + member _.DisableLocalAuth(state: ConfigurationStoreConfig, disable: bool) = { + state with + DisableLocalAuth = disable + } /// Sets the data plane authentication mode for the configuration store. [] - member _.DataPlaneAuthenticationMode(state: ConfigurationStoreConfig, mode) = { state with DataPlaneAuthenticationMode = mode } + member _.DataPlaneAuthenticationMode(state: ConfigurationStoreConfig, mode) = { + state with + DataPlaneAuthenticationMode = mode + } /// Adds a feature flag to the configuration store. [] - member _.FeatureFlags(state: ConfigurationStoreConfig, featureFlags: FeatureFlagConfig list) = { state with FeatureFlags = featureFlags } + member _.FeatureFlags(state: ConfigurationStoreConfig, featureFlags: FeatureFlagConfig list) = { + state with + FeatureFlags = featureFlags + } interface ITaggable with /// Adds a tag to this Configuration Store.