Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for App Insights workspaces #895

Merged
merged 6 commits into from
Mar 15, 2022
Merged
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
6 changes: 2 additions & 4 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
Release Notes
=============

## vNext
* WebApps/Functions: Specify connection string types

## 1.6.30
* WebApps/Functions: Specify connection string types
* WebApps/Functions: Allow adding IP restriction string with CIDR
* Application Insights: Support for Workspace-enabled instances.

## 1.6.29
* CLI: include `--only-show-error` option when executing Azure CLI commands.
Expand Down
4 changes: 4 additions & 0 deletions docs/content/api-overview/resources/app-insights.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ The App Insights builder is used to create Application Insights accounts. Use th

* Application Insights (`Microsoft.Insights/components`)

> This builder supports both "Classic" (standalone) and "Workspace Enabled" (Log Analytics-backed) instances of App Insights. See the `log_analytics_workspace` keyword to see how to create the latter type of instance.

#### Builder Keywords

| Keyword | Purpose |
|-|-|
| name | Sets the name of the App Insights instance. |
| disable_ip_masking | Disable IP masking. |
| sampling_percentage | Define sampling percentage (0-100) |
| log_analytics_workspace | Use a Log Analytics workspace as the backing store for this AI instance. You can supply either a Farmer-generate Log Analytics`WorkspaceConfig` instance that exists in the same resource group, or a fully-qualified Resource ID path to that instance. This will also switch the AI instance over to creating a "workspace enabled" AI instance. |

#### Configuration Members

Expand All @@ -32,5 +35,6 @@ open Farmer.Builders

let ai = appInsights {
name "myAI"
log_analytics_workspace myWorkspace // use to activate workspace-enabled AI instances.
}
```
26 changes: 26 additions & 0 deletions samples/scripts/appinsights-loganalytics.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#r @"nuget:Farmer"

open Farmer
open Farmer.Builders

let workspace = logAnalytics {
name "loganalytics-workspace"
}

let myAppInsights = appInsights {
name "appInsights"
log_analytics_workspace workspace
}

let myFunctions = functions {
name "functions-app"
link_to_app_insights myAppInsights.Name
}

let template = arm {
location Location.NorthEurope
add_resources [ workspace; myAppInsights; myFunctions ]
}

template
|> Deploy.execute "deleteme" Deploy.NoParameters
51 changes: 37 additions & 14 deletions src/Farmer/Arm/Insights.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,55 @@ module Farmer.Arm.Insights

open Farmer

let components = ResourceType("Microsoft.Insights/components", "2014-04-01")
let private createComponents version = ResourceType("Microsoft.Insights/components", version)
/// Classic AI instance
let components = createComponents "2014-04-01"
/// Workspace-enabled AI instance
let componentsWorkspace = createComponents "2020-02-02"

/// The type of AI instance to create.
type InstanceKind =
| Classic
| Workspace of ResourceId
member this.ResourceType =
match this with
| Classic -> components
| Workspace _ -> componentsWorkspace

type Components =
{ Name : ResourceName
Location : Location
LinkedWebsite : ResourceName option
DisableIpMasking : bool
SamplingPercentage : int
Tags: Map<string,string> }
InstanceKind : InstanceKind
Tags: Map<string,string>
Dependencies : ResourceId Set }
interface IArmResource with
member this.ResourceId = components.resourceId this.Name
member this.JsonModel =
let tags =
match this.LinkedWebsite with
| Some linkedWebsite -> this.Tags.Add($"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', '{linkedWebsite.Value}')]", "Resource")
| None -> this.Tags

{| components.Create(this.Name, this.Location, tags = tags) with
kind = "web"
properties =
{| name = this.Name.Value
Application_Type = "web"
ApplicationId =
match this.LinkedWebsite with
| Some linkedWebsite -> linkedWebsite.Value
| None -> null
DisableIpMasking = this.DisableIpMasking
SamplingPercentage = this.SamplingPercentage |}
{| this.InstanceKind.ResourceType.Create(this.Name, this.Location, this.Dependencies, tags) with
kind = "web"
properties =
{|
name = this.Name.Value
Application_Type = "web"
ApplicationId =
match this.LinkedWebsite with
| Some linkedWebsite -> linkedWebsite.Value
| None -> null
DisableIpMasking = this.DisableIpMasking
SamplingPercentage = this.SamplingPercentage
IngestionMode =
match this.InstanceKind with
| Workspace _ -> "LogAnalytics"
| Classic -> null
WorkspaceResourceId =
match this.InstanceKind with
| Workspace resourceId -> resourceId.Eval()
| Classic -> null |}
|}
31 changes: 26 additions & 5 deletions src/Farmer/Builders/Builders.AppInsights.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@ module Farmer.Builders.AppInsights

open Farmer
open Farmer.Arm.Insights
open Farmer.Arm.LogAnalytics

type AppInsights =
static member getInstrumentationKey (resourceId:ResourceId) =
ArmExpression
.reference(components, resourceId)
.reference(resourceId)
.Map(fun r -> r + ".InstrumentationKey")
.WithOwner(resourceId)
static member getInstrumentationKey (name:ResourceName, ?resourceGroup) =
AppInsights.getInstrumentationKey(ResourceId.create (components, name, ?group = resourceGroup))
static member getInstrumentationKey (name:ResourceName, ?resourceGroup, ?resourceType) =
let resourceType = resourceType |> Option.defaultValue components
AppInsights.getInstrumentationKey(ResourceId.create (resourceType, name, ?group = resourceGroup))

type AppInsightsConfig =
{ Name : ResourceName
DisableIpMasking : bool
SamplingPercentage : int
InstanceKind : InstanceKind
Dependencies : ResourceId Set
Tags : Map<string,string> }
/// Gets the ARM expression path to the instrumentation key of this App Insights instance.
member this.InstrumentationKey = AppInsights.getInstrumentationKey this.Name
member this.InstrumentationKey = AppInsights.getInstrumentationKey(this.Name, resourceType = this.InstanceKind.ResourceType)
interface IBuilder with
member this.ResourceId = components.resourceId this.Name
member this.BuildResources location = [
Expand All @@ -28,6 +32,8 @@ type AppInsightsConfig =
LinkedWebsite = None
DisableIpMasking = this.DisableIpMasking
SamplingPercentage = this.SamplingPercentage
Dependencies = this.Dependencies
InstanceKind = this.InstanceKind
Tags = this.Tags }
]

Expand All @@ -36,7 +42,10 @@ type AppInsightsBuilder() =
{ Name = ResourceName.Empty
DisableIpMasking = false
SamplingPercentage = 100
Tags = Map.empty }
Tags = Map.empty
Dependencies = Set.empty
InstanceKind = Classic }

[<CustomOperation "name">]
/// Sets the name of the App Insights instance.
member _.Name(state:AppInsightsConfig, name) = { state with Name = ResourceName name }
Expand All @@ -49,10 +58,22 @@ type AppInsightsBuilder() =
/// Sets the name of the App Insights instance.
member _.SamplingPercentage(state:AppInsightsConfig, samplingPercentage) = { state with SamplingPercentage = samplingPercentage }

/// Links this AI instance to a Log Analytics workspace, using the newer 2020-02-02-preview App Insights version.
[<CustomOperation "log_analytics_workspace">]
member _.Workspace(state:AppInsightsConfig, workspace:ResourceId) =
{ state with
InstanceKind = Workspace workspace
Dependencies = state.Dependencies.Add workspace
}
member this.Workspace(state:AppInsightsConfig, workspace:WorkspaceConfig) =
this.Workspace(state, workspaces.resourceId workspace.Name)

member _.Run (state:AppInsightsConfig) =
if state.SamplingPercentage > 100 then raiseFarmer "Sampling Percentage cannot be higher than 100%"
elif state.SamplingPercentage <= 0 then raiseFarmer "Sampling Percentage cannot be lower than or equal to 0%"
state

interface ITaggable<AppInsightsConfig> with member _.Add state tags = { state with Tags = state.Tags |> Map.merge tags }
interface IDependable<AppInsightsConfig> with member _.Add state resources = { state with Dependencies = state.Dependencies + resources }

let appInsights = AppInsightsBuilder()
10 changes: 6 additions & 4 deletions src/Farmer/Builders/Builders.Functions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ open System
type FunctionsRuntime = DotNet | DotNetIsolated | Node | Java | Python
type VersionedFunctionsRuntime = FunctionsRuntime * string option
type FunctionsRuntime with
// These values are defined on FunctionsRuntime to reduce the need for users to be aware of the distinction
// These values are defined on FunctionsRuntime to reduce the need for users to be aware of the distinction
// between FunctionsRuntime and VersionedFunctionsRuntime as well as to provide parity with WebApp runtime
static member DotNetCore31 = DotNet, Some "3.1"
static member DotNet50 = DotNet, Some "5.0"
Expand Down Expand Up @@ -246,12 +246,12 @@ type FunctionsConfig =
HTTP20Enabled = None
ClientAffinityEnabled = None
WebSocketsEnabled = None
LinuxFxVersion =
LinuxFxVersion =
match this.CommonWebConfig.OperatingSystem with
| Windows -> None
| Linux ->
match this.VersionedRuntime with
| DotNet, Some version ->
| DotNet, Some version ->
match Double.TryParse(version) with
| true, versionNo when versionNo < 4.0 -> Some $"DOTNETCORE|{version}"
| _ -> Some $"DOTNET|{version}"
Expand Down Expand Up @@ -310,6 +310,8 @@ type FunctionsConfig =
Location = location
DisableIpMasking = false
SamplingPercentage = 100
Dependencies = Set.empty
InstanceKind = Classic
LinkedWebsite =
match this.CommonWebConfig.OperatingSystem with
| Windows -> Some this.Name.ResourceName
Expand Down Expand Up @@ -346,7 +348,7 @@ type FunctionsBuilder() =
Slots = Map.empty
WorkerProcess = None
ZipDeployPath = None
HealthCheckPath = None
HealthCheckPath = None
IpSecurityRestrictions = [] }
StorageAccount = derived (fun config ->
let storage = config.Name.ResourceName.Map (sprintf "%sstorage") |> sanitiseStorage |> ResourceName
Expand Down
Loading