diff --git a/playground/bicep/BicepSample.AppHost/Program.cs b/playground/bicep/BicepSample.AppHost/Program.cs
index f2aae2b42e..ccf8b21921 100644
--- a/playground/bicep/BicepSample.AppHost/Program.cs
+++ b/playground/bicep/BicepSample.AppHost/Program.cs
@@ -44,7 +44,11 @@
var cosmosDb = builder.AddAzureCosmosDB("cosmos")
.AddDatabase("db3");
-var appInsights = builder.AddAzureApplicationInsights("ai");
+var logAnalytics = builder.AddAzureLogAnalyticsWorkspace("lawkspc");
+var appInsights = builder.AddAzureApplicationInsights("ai", logAnalytics);
+
+// To verify that AZD will populate the LAW parameter.
+builder.AddAzureApplicationInsights("aiwithoutlaw");
// Redis takes forever to spin up...
var redis = builder.AddRedis("redis")
diff --git a/playground/bicep/BicepSample.AppHost/aiwithoutlaw.module.bicep b/playground/bicep/BicepSample.AppHost/aiwithoutlaw.module.bicep
new file mode 100644
index 0000000000..049db095d4
--- /dev/null
+++ b/playground/bicep/BicepSample.AppHost/aiwithoutlaw.module.bicep
@@ -0,0 +1,29 @@
+targetScope = 'resourceGroup'
+
+@description('')
+param location string = resourceGroup().location
+
+@description('')
+param applicationType string = 'web'
+
+@description('')
+param kind string = 'web'
+
+@description('')
+param logAnalyticsWorkspaceId string
+
+
+resource applicationInsightsComponent_YlZN71uia 'Microsoft.Insights/components@2020-02-02' = {
+ name: toLower(take(concat('aiwithoutlaw', uniqueString(resourceGroup().id)), 24))
+ location: location
+ tags: {
+ 'aspire-resource-name': 'aiwithoutlaw'
+ }
+ kind: kind
+ properties: {
+ Application_Type: applicationType
+ WorkspaceResourceId: logAnalyticsWorkspaceId
+ }
+}
+
+output appInsightsConnectionString string = applicationInsightsComponent_YlZN71uia.properties.ConnectionString
diff --git a/playground/bicep/BicepSample.AppHost/aspire-manifest.json b/playground/bicep/BicepSample.AppHost/aspire-manifest.json
index 3e9afe47ef..3736a1611b 100644
--- a/playground/bicep/BicepSample.AppHost/aspire-manifest.json
+++ b/playground/bicep/BicepSample.AppHost/aspire-manifest.json
@@ -23,7 +23,7 @@
},
"test0": {
"type": "azure.bicep.v0",
- "path": "../../../../../../Users/davifowl/AppData/Local/Temp/tmppj1k21.tmp.bicep"
+ "path": "test0.bicep"
},
"kv3": {
"type": "azure.bicep.v0",
@@ -118,10 +118,25 @@
"keyVaultName": ""
}
},
+ "lawkspc": {
+ "type": "azure.bicep.v0",
+ "path": "lawkspc.module.bicep"
+ },
"ai": {
"type": "azure.bicep.v0",
"connectionString": "{ai.outputs.appInsightsConnectionString}",
- "path": "ai.module.bicep"
+ "path": "ai.module.bicep",
+ "params": {
+ "logAnalyticsWorkspaceId": "{lawkspc.outputs.logAnalyticsWorkspaceId}"
+ }
+ },
+ "aiwithoutlaw": {
+ "type": "azure.bicep.v0",
+ "connectionString": "{aiwithoutlaw.outputs.appInsightsConnectionString}",
+ "path": "aiwithoutlaw.module.bicep",
+ "params": {
+ "logAnalyticsWorkspaceId": ""
+ }
},
"redis": {
"type": "azure.bicep.v0",
diff --git a/playground/bicep/BicepSample.AppHost/lawkspc.module.bicep b/playground/bicep/BicepSample.AppHost/lawkspc.module.bicep
new file mode 100644
index 0000000000..ec2bea6b78
--- /dev/null
+++ b/playground/bicep/BicepSample.AppHost/lawkspc.module.bicep
@@ -0,0 +1,20 @@
+targetScope = 'resourceGroup'
+
+@description('')
+param location string = resourceGroup().location
+
+
+resource operationalInsightsWorkspace_cxL77xv9Y 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
+ name: toLower(take(concat('lawkspc', uniqueString(resourceGroup().id)), 24))
+ location: location
+ tags: {
+ 'aspire-resource-name': 'lawkspc'
+ }
+ properties: {
+ sku: {
+ name: 'PerGB2018'
+ }
+ }
+}
+
+output logAnalyticsWorkspaceId string = operationalInsightsWorkspace_cxL77xv9Y.id
diff --git a/playground/bicep/BicepSample.AppHost/test0.bicep b/playground/bicep/BicepSample.AppHost/test0.bicep
new file mode 100644
index 0000000000..4d513a6cd0
--- /dev/null
+++ b/playground/bicep/BicepSample.AppHost/test0.bicep
@@ -0,0 +1,2 @@
+param location string = ''
+output val0 string = location
\ No newline at end of file
diff --git a/src/Aspire.Hosting.Azure.ApplicationInsights/Aspire.Hosting.Azure.ApplicationInsights.csproj b/src/Aspire.Hosting.Azure.ApplicationInsights/Aspire.Hosting.Azure.ApplicationInsights.csproj
index af645d7ba6..512f1dd125 100644
--- a/src/Aspire.Hosting.Azure.ApplicationInsights/Aspire.Hosting.Azure.ApplicationInsights.csproj
+++ b/src/Aspire.Hosting.Azure.ApplicationInsights/Aspire.Hosting.Azure.ApplicationInsights.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/Aspire.Hosting.Azure.ApplicationInsights/AzureApplicationInsightsExtensions.cs b/src/Aspire.Hosting.Azure.ApplicationInsights/AzureApplicationInsightsExtensions.cs
index 290b00d37e..60125641be 100644
--- a/src/Aspire.Hosting.Azure.ApplicationInsights/AzureApplicationInsightsExtensions.cs
+++ b/src/Aspire.Hosting.Azure.ApplicationInsights/AzureApplicationInsightsExtensions.cs
@@ -23,7 +23,21 @@ public static class AzureApplicationInsightsExtensions
public static IResourceBuilder AddAzureApplicationInsights(this IDistributedApplicationBuilder builder, string name)
{
#pragma warning disable ASPIRE0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
- return builder.AddAzureApplicationInsights(name, (_, _, _) => { });
+ return builder.AddAzureApplicationInsights(name, null, null);
+#pragma warning restore ASPIRE0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ }
+
+ ///
+ /// Adds an Azure Application Insights resource to the application model.
+ ///
+ /// The .
+ /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
+ /// A resource builder for the log analytics workspace.
+ /// A reference to the .
+ public static IResourceBuilder AddAzureApplicationInsights(this IDistributedApplicationBuilder builder, string name, IResourceBuilder? logAnalyticsWorkspace)
+ {
+#pragma warning disable ASPIRE0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ return builder.AddAzureApplicationInsights(name, logAnalyticsWorkspace, null);
#pragma warning restore ASPIRE0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
}
@@ -36,6 +50,20 @@ public static IResourceBuilder AddAzureApplica
///
[Experimental("ASPIRE0001", UrlFormat = "https://aka.ms/dotnet/aspire/diagnostics#{0}")]
public static IResourceBuilder AddAzureApplicationInsights(this IDistributedApplicationBuilder builder, string name, Action, ResourceModuleConstruct, ApplicationInsightsComponent>? configureResource)
+ {
+ return builder.AddAzureApplicationInsights(name, null, configureResource);
+ }
+
+ ///
+ /// Adds an Azure Application Insights resource to the application model.
+ ///
+ /// The builder for the distributed application.
+ /// The name of the resource.
+ /// A resource builder for the log analytics workspace.
+ /// Optional callback to configure the Application Insights resource.
+ ///
+ [Experimental("ASPIRE0001", UrlFormat = "https://aka.ms/dotnet/aspire/diagnostics#{0}")]
+ public static IResourceBuilder AddAzureApplicationInsights(this IDistributedApplicationBuilder builder, string name, IResourceBuilder? logAnalyticsWorkspace, Action, ResourceModuleConstruct, ApplicationInsightsComponent>? configureResource)
{
builder.AddAzureProvisioning();
@@ -45,7 +73,19 @@ public static IResourceBuilder AddAzureApplica
appInsights.Properties.Tags["aspire-resource-name"] = construct.Resource.Name;
appInsights.AssignProperty(p => p.ApplicationType, new Parameter("applicationType", defaultValue: "web"));
appInsights.AssignProperty(p => p.Kind, new Parameter("kind", defaultValue: "web"));
- appInsights.AssignProperty(p => p.WorkspaceResourceId, new Parameter(AzureBicepResource.KnownParameters.LogAnalyticsWorkspaceId));
+
+ if (logAnalyticsWorkspace != null)
+ {
+ appInsights.AssignProperty(p => p.WorkspaceResourceId, logAnalyticsWorkspace.Resource.WorkspaceId, AzureBicepResource.KnownParameters.LogAnalyticsWorkspaceId);
+ }
+ else
+ {
+ // If the user does not supply a log analytics workspace of their own we still create a parameter on the Aspire
+ // side and the CDK side so that AZD can fill the value in with the one it generates.
+ construct.Resource.Parameters.Add(AzureBicepResource.KnownParameters.LogAnalyticsWorkspaceId, "");
+ appInsights.AssignProperty(p => p.WorkspaceResourceId, new Parameter(AzureBicepResource.KnownParameters.LogAnalyticsWorkspaceId));
+
+ }
appInsights.AddOutput("appInsightsConnectionString", p => p.ConnectionString);
@@ -56,6 +96,7 @@ public static IResourceBuilder AddAzureApplica
configureResource(resourceBuilder, construct, appInsights);
}
};
+
var resource = new AzureApplicationInsightsResource(name, configureConstruct);
return builder.AddResource(resource)
diff --git a/src/Aspire.Hosting.Azure/AzureBicepResource.cs b/src/Aspire.Hosting.Azure/AzureBicepResource.cs
index e3d41d3fcf..63d7fe89d9 100644
--- a/src/Aspire.Hosting.Azure/AzureBicepResource.cs
+++ b/src/Aspire.Hosting.Azure/AzureBicepResource.cs
@@ -74,7 +74,7 @@ public virtual BicepTemplateFile GetBicepTemplateFile(string? directory = null,
isTempFile = directory is null;
path = TempDirectory is null
- ? Path.GetTempFileName() + ".bicep"
+ ? Path.Combine(directory ?? Directory.CreateTempSubdirectory("aspire").FullName, $"{Name.ToLowerInvariant()}.bicep")
: Path.Combine(TempDirectory, $"{Name.ToLowerInvariant()}.bicep");
if (TemplateResourceName is null)
diff --git a/tests/Aspire.Hosting.Tests/Azure/AzureBicepResourceTests.cs b/tests/Aspire.Hosting.Tests/Azure/AzureBicepResourceTests.cs
index 77b1e60589..451be873fb 100644
--- a/tests/Aspire.Hosting.Tests/Azure/AzureBicepResourceTests.cs
+++ b/tests/Aspire.Hosting.Tests/Azure/AzureBicepResourceTests.cs
@@ -344,7 +344,7 @@ param principalType string
}
[Fact]
- public async Task AddApplicationInsights()
+ public async Task AddApplicationInsightsWithoutExplicitLawGetsDefaultLawParameter()
{
using var builder = TestDistributedApplicationBuilder.Create();
@@ -363,7 +363,74 @@ public async Task AddApplicationInsights()
{
"type": "azure.bicep.v0",
"connectionString": "{appInsights.outputs.appInsightsConnectionString}",
- "path": "appInsights.module.bicep"
+ "path": "appInsights.module.bicep",
+ "params": {
+ "logAnalyticsWorkspaceId": ""
+ }
+ }
+ """;
+ Assert.Equal(expectedManifest, appInsightsManifest.ManifestNode.ToString());
+
+ var expectedBicep = """
+ targetScope = 'resourceGroup'
+
+ @description('')
+ param location string = resourceGroup().location
+
+ @description('')
+ param applicationType string = 'web'
+
+ @description('')
+ param kind string = 'web'
+
+ @description('')
+ param logAnalyticsWorkspaceId string
+
+
+ resource applicationInsightsComponent_fo9MneV12 'Microsoft.Insights/components@2020-02-02' = {
+ name: toLower(take(concat('appInsights', uniqueString(resourceGroup().id)), 24))
+ location: location
+ tags: {
+ 'aspire-resource-name': 'appInsights'
+ }
+ kind: kind
+ properties: {
+ Application_Type: applicationType
+ WorkspaceResourceId: logAnalyticsWorkspaceId
+ }
+ }
+
+ output appInsightsConnectionString string = applicationInsightsComponent_fo9MneV12.properties.ConnectionString
+
+ """;
+ Assert.Equal(expectedBicep, appInsightsManifest.BicepText);
+ }
+
+ [Fact]
+ public async Task AddApplicationInsightsWithExplicitLawArgumentDoesntGetDefaultParameter()
+ {
+ using var builder = TestDistributedApplicationBuilder.Create();
+
+ var law = builder.AddAzureLogAnalyticsWorkspace("mylaw");
+ var appInsights = builder.AddAzureApplicationInsights("appInsights", law);
+
+ appInsights.Resource.Outputs["appInsightsConnectionString"] = "myinstrumentationkey";
+
+ var connectionStringResource = (IResourceWithConnectionString)appInsights.Resource;
+
+ Assert.Equal("appInsights", appInsights.Resource.Name);
+ Assert.Equal("myinstrumentationkey", await connectionStringResource.GetConnectionStringAsync());
+ Assert.Equal("{appInsights.outputs.appInsightsConnectionString}", appInsights.Resource.ConnectionStringExpression.ValueExpression);
+
+ var appInsightsManifest = await ManifestUtils.GetManifestWithBicep(appInsights.Resource);
+ var expectedManifest = """
+ {
+ "type": "azure.bicep.v0",
+ "connectionString": "{appInsights.outputs.appInsightsConnectionString}",
+ "path": "appInsights.module.bicep",
+ "params": {
+ "logAnalyticsWorkspaceId": "{mylaw.outputs.logAnalyticsWorkspaceId}"
+ }
}
""";
Assert.Equal(expectedManifest, appInsightsManifest.ManifestNode.ToString());