Skip to content

Commit

Permalink
AddAzureStorage CDK Edition (#2563)
Browse files Browse the repository at this point in the history
* AddAzureStorage CDK Edition
  • Loading branch information
mitchdenny authored Mar 2, 2024
1 parent eeb1266 commit 20d98e3
Show file tree
Hide file tree
Showing 14 changed files with 451 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<PackageVersion Include="Azure.ResourceManager.Authorization" Version="1.2.0-alpha.20240227.2" />
<PackageVersion Include="Azure.ResourceManager.KeyVault" Version="1.3.0-alpha.20240222.2" />
<PackageVersion Include="Azure.ResourceManager.Resources" Version="1.8.0-alpha.20240222.2" />
<PackageVersion Include="Azure.Provisioning" Version="1.0.0-alpha.20240229.2" />
<PackageVersion Include="Azure.Provisioning" Version="1.0.0-alpha.20240301.3" />
<!-- ASP.NET Core dependencies -->
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="$(MicrosoftAspNetCoreOpenApiPackageVersion)" />
<PackageVersion Include="Microsoft.AspNetCore.OutputCaching.StackExchangeRedis" Version="$(MicrosoftAspNetCoreOutputCachingStackExchangeRedisPackageVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Components\Aspire.Azure.Storage.Blobs\Aspire.Azure.Storage.Blobs.csproj" />
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
</ItemGroup>

Expand Down
23 changes: 21 additions & 2 deletions playground/cdk/CdkSample.ApiService/Program.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Azure.Storage.Blobs;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.AddAzureBlobService("blobs");

var app = builder.Build();

app.MapGet("/", (IConfiguration config) =>
app.MapGet("/", async (BlobServiceClient bsc) =>
{
return $"TABLE_URI is: {config["TABLE_URI"]}";
var container = bsc.GetBlobContainerClient("mycontainer");
await container.CreateIfNotExistsAsync();
var blobNameAndContent = Guid.NewGuid().ToString();
await container.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));
var blobs = container.GetBlobsAsync();
var blobNames = new List<string>();
await foreach (var blob in blobs)
{
blobNames.Add(blob.Name);
}
return blobNames;
});

app.Run();
17 changes: 13 additions & 4 deletions playground/cdk/CdkSample.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,30 @@
builder.AddAzureProvisioning();

var sku = builder.AddParameter("storagesku");
var locationOverride = builder.AddParameter("locationOverride");

var construct1 = builder.AddAzureConstruct("construct1", (construct) =>
var cdkstorage1 = builder.AddAzureConstruct("cdkstorage1", (construct) =>
{
var account = construct.AddStorageAccount(
name: "bob",
kind: StorageKind.BlobStorage,
name: "cdkstorage1",
kind: StorageKind.Storage,
sku: StorageSkuName.StandardLrs
);
account.AssignParameter(a => a.Sku.Name, sku);
account.AddOutput(data => data.PrimaryEndpoints.TableUri, "tableUri", isSecure: true);
});

var cdkstorage2 = builder.AddAzureConstructStorage("cdkstorage2", (_, account) =>
{
account.AssignParameter(sa => sa.Sku.Name, sku);
account.AssignParameter(sa => sa.Location, locationOverride);
});

var blobs = cdkstorage2.AddBlobs("blobs");

builder.AddProject<Projects.CdkSample_ApiService>("api")
.WithEnvironment("TABLE_URI", construct1.GetOutput("tableUri"));
.WithReference(blobs);

// This project is only added in playground projects to support development/debugging
// of the dashboard. It is not required in end developer code. Comment out this code
Expand Down
29 changes: 26 additions & 3 deletions playground/cdk/CdkSample.AppHost/aspire-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,43 @@
}
}
},
"construct1": {
"locationOverride": {
"type": "parameter.v0",
"value": "{locationOverride.inputs.value}",
"inputs": {
"value": {
"type": "string"
}
}
},
"cdkstorage1": {
"type": "azure.bicep.v0",
"path": "construct1.module.bicep",
"path": "cdkstorage1.module.bicep",
"params": {
"storagesku": "{storagesku.value}"
}
},
"cdkstorage2": {
"type": "azure.bicep.v0",
"path": "cdkstorage2.module.bicep",
"params": {
"principalId": "",
"principalType": "",
"storagesku": "{storagesku.value}",
"locationOverride": "{locationOverride.value}"
}
},
"blobs": {
"type": "value.v0",
"connectionString": "{cdkstorage2.outputs.blobEndpoint}"
},
"api": {
"type": "project.v0",
"path": "../CdkSample.ApiService/CdkSample.ApiService.csproj",
"env": {
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true",
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true",
"TABLE_URI": "{construct1.outputs.tableUri}"
"ConnectionStrings__blobs": "{blobs.connectionString}"
},
"bindings": {
"http": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ param location string = resourceGroup().location
param storagesku string


resource storageAccount_WFnvkltok 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: toLower(take(concat('bob', uniqueString(resourceGroup().id)), 24))
resource storageAccount_EMiEZBbox 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: toLower(take(concat('cdkstorage1', uniqueString(resourceGroup().id)), 24))
location: location
sku: {
name: storagesku
}
kind: 'StorageV2'
kind: 'Storage'
properties: {
}
}

output tableUri string = storageAccount_WFnvkltok.properties.primaryEndpoints.table
output tableUri string = storageAccount_EMiEZBbox.properties.primaryEndpoints.table
69 changes: 69 additions & 0 deletions playground/cdk/CdkSample.AppHost/cdkstorage2.module.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
targetScope = 'resourceGroup'

@description('')
param location string = resourceGroup().location

@description('')
param storagesku string

@description('')
param locationOverride string

@description('')
param principalId string

@description('')
param principalType string


resource storageAccount_Tn41yfrBn 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: toLower(take(concat('cdkstorage2', uniqueString(resourceGroup().id)), 24))
location: locationOverride
sku: {
name: storagesku
}
kind: 'Storage'
properties: {
}
}

resource blobService_L4VtbX3sf 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
parent: storageAccount_Tn41yfrBn
name: 'default'
properties: {
}
}

resource roleAssignment_UtfaPuAd8 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: storageAccount_Tn41yfrBn
name: guid(storageAccount_Tn41yfrBn.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'))
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
principalId: principalId
principalType: principalType
}
}

resource roleAssignment_sw5JL5brt 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: storageAccount_Tn41yfrBn
name: guid(storageAccount_Tn41yfrBn.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'))
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
principalId: principalId
principalType: principalType
}
}

resource roleAssignment_iCksqTYss 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: storageAccount_Tn41yfrBn
name: guid(storageAccount_Tn41yfrBn.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88'))
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
principalId: principalId
principalType: principalType
}
}

output blobEndpoint string = storageAccount_Tn41yfrBn.properties.primaryEndpoints.blob
output queueEndpoint string = storageAccount_Tn41yfrBn.properties.primaryEndpoints.queue
output tableEndpoint string = storageAccount_Tn41yfrBn.properties.primaryEndpoints.table
36 changes: 36 additions & 0 deletions src/Aspire.Hosting.Azure/AzureBlobStorageResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,39 @@ internal void WriteToManifest(ManifestPublishingContext context)
context.WriteConnectionString(this);
}
}

/// <summary>
/// A resource that represents an Azure Blob Storage account.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="storage">The <see cref="AzureStorageResource"/> that the resource is stored in.</param>
public class AzureBlobStorageConstructResource(string name, AzureStorageConstructResource storage) : Resource(name),
IResourceWithConnectionString,
IResourceWithParent<AzureStorageConstructResource>
{
/// <summary>
/// Gets the parent AzureStorageResource of this AzureBlobStorageResource.
/// </summary>
public AzureStorageConstructResource Parent => storage;

/// <summary>
/// Gets the connection string template for the manifest for the Azure Blob Storage resource.
/// </summary>
public string ConnectionStringExpression => Parent.BlobEndpoint.ValueExpression;

/// <summary>
/// Gets the connection string for the Azure Blob Storage resource.
/// </summary>
/// <returns>The connection string for the Azure Blob Storage resource.</returns>
public string? GetConnectionString() => Parent.GetBlobConnectionString();

/// <summary>
/// Called by manifest publisher to write manifest resource.
/// </summary>
/// <param name="context">The context for the manifest publishing operation.</param>
internal void WriteToManifest(ManifestPublishingContext context)
{
context.Writer.WriteString("type", "value.v0");
context.WriteConnectionString(this);
}
}
10 changes: 10 additions & 0 deletions src/Aspire.Hosting.Azure/AzureConstructResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,14 @@ internal ResourceModuleConstruct(AzureConstructResource resource, Configuration
/// The Azure cosntruct resource that this resource module construct represents.
/// </summary>
public AzureConstructResource Resource { get; }

/// <summary>
/// TODO:
/// </summary>
public Parameter PrincipalIdParameter => new Parameter("principalId");

/// <summary>
/// TODO:
/// </summary>
public Parameter PrincipalTypeParameter => new Parameter("principalType");
}
32 changes: 32 additions & 0 deletions src/Aspire.Hosting.Azure/AzureQueueStorageResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,35 @@ internal void WriteToManifest(ManifestPublishingContext context)
context.WriteConnectionString(this);
}
}

/// <summary>
/// Represents an Azure Queue Storage resource.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="storage">The <see cref="AzureStorageResource"/> that the resource is stored in.</param>
public class AzureQueueStorageConstructResource(string name, AzureStorageConstructResource storage) : Resource(name),
IResourceWithConnectionString,
IResourceWithParent<AzureStorageConstructResource>
{
/// <summary>
/// Gets the parent AzureStorageResource of this AzureQueueStorageResource.
/// </summary>
public AzureStorageConstructResource Parent => storage;

/// <summary>
/// Gets the connection string template for the manifest for the Azure Queue Storage resource.
/// </summary>
public string ConnectionStringExpression => Parent.QueueEndpoint.ValueExpression;

/// <summary>
/// Gets the connection string for the Azure Queue Storage resource.
///</summary>
///<returns> The connection string for the Azure Queue Storage resource.</returns>
public string? GetConnectionString() => Parent.GetQueueConnectionString();

internal void WriteToManifest(ManifestPublishingContext context)
{
context.Writer.WriteString("type", "value.v0");
context.WriteConnectionString(this);
}
}
47 changes: 47 additions & 0 deletions src/Aspire.Hosting.Azure/AzureStorageResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,53 @@

namespace Aspire.Hosting.Azure;

/// <summary>
/// Represents an Azure Storage resource.
/// </summary>
/// <param name="name"></param>
/// <param name="configureConstruct"></param>
public class AzureStorageConstructResource(string name, Action<ResourceModuleConstruct> configureConstruct) : AzureConstructResource(name, configureConstruct)
{
/// <summary>
/// Gets the "blobEndpoint" output reference from the bicep template for the Azure Storage resource.
/// </summary>
public BicepOutputReference BlobEndpoint => new("blobEndpoint", this);

/// <summary>
/// Gets the "queueEndpoint" output reference from the bicep template for the Azure Storage resource.
/// </summary>
public BicepOutputReference QueueEndpoint => new("queueEndpoint", this);

/// <summary>
/// Gets the "tableEndpoint" output reference from the bicep template for the Azure Storage resource.
/// </summary>
public BicepOutputReference TableEndpoint => new("tableEndpoint", this);

/// <summary>
/// Gets a value indicating whether the Azure Storage resource is running in the local emulator.
/// </summary>
public bool IsEmulator => this.IsContainer();

internal string? GetTableConnectionString() => IsEmulator
? AzureStorageEmulatorConnectionString.Create(tablePort: GetEmulatorPort("table"))
: TableEndpoint.Value;

internal string? GetQueueConnectionString() => IsEmulator
? AzureStorageEmulatorConnectionString.Create(queuePort: GetEmulatorPort("queue"))
: QueueEndpoint.Value;

internal string? GetBlobConnectionString() => IsEmulator
? AzureStorageEmulatorConnectionString.Create(blobPort: GetEmulatorPort("blob"))
: BlobEndpoint.Value;

private int GetEmulatorPort(string endpointName) =>
Annotations
.OfType<AllocatedEndpointAnnotation>()
.FirstOrDefault(x => x.Name == endpointName)
?.Port
?? throw new DistributedApplicationException($"Azure storage resource does not have endpoint annotation with name '{endpointName}'.");
}

/// <summary>
/// Represents an Azure Storage resource.
/// </summary>
Expand Down
Loading

0 comments on commit 20d98e3

Please sign in to comment.