Skip to content

Commit e38416a

Browse files
authored
Disabling key access for storage by default. (#4744)
* Disabling key access for storage by default.
1 parent 724a1de commit e38416a

File tree

2 files changed

+328
-0
lines changed

2 files changed

+328
-0
lines changed

src/Aspire.Hosting.Azure.Storage/AzureStorageExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public static IResourceBuilder<AzureStorageResource> AddAzureStorage(this IDistr
6060
// with the pending deprecation of TLS 1.0 and 1.1.
6161
storageAccount.AssignProperty(p => p.MinimumTlsVersion, "'TLS1_2'");
6262

63+
// Disable shared key access to the storage account as managed identity is configured
64+
// to access the storage account by default.
65+
storageAccount.AssignProperty(p => p.AllowSharedKeyAccess, "false");
66+
6367
var blobService = new BlobService(construct);
6468

6569
var blobRole = storageAccount.AssignRole(RoleDefinition.StorageBlobDataContributor);

tests/Aspire.Hosting.Tests/Azure/AzureBicepResourceTests.cs

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,6 +2023,168 @@ param storagesku string
20232023
defaultAction: 'Allow'
20242024
}
20252025
minimumTlsVersion: 'TLS1_2'
2026+
allowSharedKeyAccess: false
2027+
}
2028+
}
2029+
2030+
resource blobService_vTLU20GRg 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
2031+
parent: storageAccount_1XR3Um8QY
2032+
name: 'default'
2033+
properties: {
2034+
}
2035+
}
2036+
2037+
resource roleAssignment_Gz09cEnxb 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2038+
scope: storageAccount_1XR3Um8QY
2039+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'))
2040+
properties: {
2041+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
2042+
principalId: principalId
2043+
principalType: principalType
2044+
}
2045+
}
2046+
2047+
resource roleAssignment_HRj6MDafS 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2048+
scope: storageAccount_1XR3Um8QY
2049+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'))
2050+
properties: {
2051+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
2052+
principalId: principalId
2053+
principalType: principalType
2054+
}
2055+
}
2056+
2057+
resource roleAssignment_r0wA6OpKE 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2058+
scope: storageAccount_1XR3Um8QY
2059+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88'))
2060+
properties: {
2061+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
2062+
principalId: principalId
2063+
principalType: principalType
2064+
}
2065+
}
2066+
2067+
output blobEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.blob
2068+
output queueEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.queue
2069+
output tableEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.table
2070+
2071+
""";
2072+
output.WriteLine(storageManifest.BicepText);
2073+
Assert.Equal(expectedBicep, storageManifest.BicepText);
2074+
2075+
// Check blob resource.
2076+
var blob = storage.AddBlobs("blob");
2077+
2078+
var connectionStringBlobResource = (IResourceWithConnectionString)blob.Resource;
2079+
2080+
Assert.Equal("https://myblob", await connectionStringBlobResource.GetConnectionStringAsync());
2081+
var expectedBlobManifest = """
2082+
{
2083+
"type": "value.v0",
2084+
"connectionString": "{storage.outputs.blobEndpoint}"
2085+
}
2086+
""";
2087+
var blobManifest = await ManifestUtils.GetManifest(blob.Resource);
2088+
Assert.Equal(expectedBlobManifest, blobManifest.ToString());
2089+
2090+
// Check queue resource.
2091+
var queue = storage.AddQueues("queue");
2092+
2093+
var connectionStringQueueResource = (IResourceWithConnectionString)queue.Resource;
2094+
2095+
Assert.Equal("https://myqueue", await connectionStringQueueResource.GetConnectionStringAsync());
2096+
var expectedQueueManifest = """
2097+
{
2098+
"type": "value.v0",
2099+
"connectionString": "{storage.outputs.queueEndpoint}"
2100+
}
2101+
""";
2102+
var queueManifest = await ManifestUtils.GetManifest(queue.Resource);
2103+
Assert.Equal(expectedQueueManifest, queueManifest.ToString());
2104+
2105+
// Check table resource.
2106+
var table = storage.AddTables("table");
2107+
2108+
var connectionStringTableResource = (IResourceWithConnectionString)table.Resource;
2109+
2110+
Assert.Equal("https://mytable", await connectionStringTableResource.GetConnectionStringAsync());
2111+
var expectedTableManifest = """
2112+
{
2113+
"type": "value.v0",
2114+
"connectionString": "{storage.outputs.tableEndpoint}"
2115+
}
2116+
""";
2117+
var tableManifest = await ManifestUtils.GetManifest(table.Resource);
2118+
Assert.Equal(expectedTableManifest, tableManifest.ToString());
2119+
}
2120+
2121+
[Fact]
2122+
public async Task AddAzureStorageViaRunModeAllowSharedKeyAccessOverridesDefaultFalse()
2123+
{
2124+
using var builder = TestDistributedApplicationBuilder.Create();
2125+
2126+
var storagesku = builder.AddParameter("storagesku");
2127+
var storage = builder.AddAzureStorage("storage", (_, _, sa) =>
2128+
{
2129+
sa.AssignProperty(x => x.Sku.Name, storagesku);
2130+
sa.AssignProperty(x => x.AllowSharedKeyAccess, "true");
2131+
});
2132+
2133+
storage.Resource.Outputs["blobEndpoint"] = "https://myblob";
2134+
storage.Resource.Outputs["queueEndpoint"] = "https://myqueue";
2135+
storage.Resource.Outputs["tableEndpoint"] = "https://mytable";
2136+
2137+
// Check storage resource.
2138+
Assert.Equal("storage", storage.Resource.Name);
2139+
2140+
var storageManifest = await ManifestUtils.GetManifestWithBicep(storage.Resource);
2141+
2142+
var expectedStorageManifest = """
2143+
{
2144+
"type": "azure.bicep.v0",
2145+
"path": "storage.module.bicep",
2146+
"params": {
2147+
"principalId": "",
2148+
"principalType": "",
2149+
"storagesku": "{storagesku.value}"
2150+
}
2151+
}
2152+
""";
2153+
Assert.Equal(expectedStorageManifest, storageManifest.ManifestNode.ToString());
2154+
2155+
var expectedBicep = """
2156+
targetScope = 'resourceGroup'
2157+
2158+
@description('')
2159+
param location string = resourceGroup().location
2160+
2161+
@description('')
2162+
param principalId string
2163+
2164+
@description('')
2165+
param principalType string
2166+
2167+
@description('')
2168+
param storagesku string
2169+
2170+
2171+
resource storageAccount_1XR3Um8QY 'Microsoft.Storage/storageAccounts@2022-09-01' = {
2172+
name: toLower(take('storage${uniqueString(resourceGroup().id)}', 24))
2173+
location: location
2174+
tags: {
2175+
'aspire-resource-name': 'storage'
2176+
}
2177+
sku: {
2178+
name: storagesku
2179+
}
2180+
kind: 'StorageV2'
2181+
properties: {
2182+
accessTier: 'Hot'
2183+
networkAcls: {
2184+
defaultAction: 'Allow'
2185+
}
2186+
minimumTlsVersion: 'TLS1_2'
2187+
allowSharedKeyAccess: true
20262188
}
20272189
}
20282190
@@ -2182,6 +2344,168 @@ param storagesku string
21822344
defaultAction: 'Allow'
21832345
}
21842346
minimumTlsVersion: 'TLS1_2'
2347+
allowSharedKeyAccess: false
2348+
}
2349+
}
2350+
2351+
resource blobService_vTLU20GRg 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
2352+
parent: storageAccount_1XR3Um8QY
2353+
name: 'default'
2354+
properties: {
2355+
}
2356+
}
2357+
2358+
resource roleAssignment_Gz09cEnxb 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2359+
scope: storageAccount_1XR3Um8QY
2360+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'))
2361+
properties: {
2362+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
2363+
principalId: principalId
2364+
principalType: principalType
2365+
}
2366+
}
2367+
2368+
resource roleAssignment_HRj6MDafS 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2369+
scope: storageAccount_1XR3Um8QY
2370+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'))
2371+
properties: {
2372+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
2373+
principalId: principalId
2374+
principalType: principalType
2375+
}
2376+
}
2377+
2378+
resource roleAssignment_r0wA6OpKE 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
2379+
scope: storageAccount_1XR3Um8QY
2380+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88'))
2381+
properties: {
2382+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
2383+
principalId: principalId
2384+
principalType: principalType
2385+
}
2386+
}
2387+
2388+
output blobEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.blob
2389+
output queueEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.queue
2390+
output tableEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.table
2391+
2392+
""";
2393+
output.WriteLine(storageManifest.BicepText);
2394+
Assert.Equal(expectedBicep, storageManifest.BicepText);
2395+
2396+
// Check blob resource.
2397+
var blob = storage.AddBlobs("blob");
2398+
2399+
var connectionStringBlobResource = (IResourceWithConnectionString)blob.Resource;
2400+
2401+
Assert.Equal("https://myblob", await connectionStringBlobResource.GetConnectionStringAsync());
2402+
var expectedBlobManifest = """
2403+
{
2404+
"type": "value.v0",
2405+
"connectionString": "{storage.outputs.blobEndpoint}"
2406+
}
2407+
""";
2408+
var blobManifest = await ManifestUtils.GetManifest(blob.Resource);
2409+
Assert.Equal(expectedBlobManifest, blobManifest.ToString());
2410+
2411+
// Check queue resource.
2412+
var queue = storage.AddQueues("queue");
2413+
2414+
var connectionStringQueueResource = (IResourceWithConnectionString)queue.Resource;
2415+
2416+
Assert.Equal("https://myqueue", await connectionStringQueueResource.GetConnectionStringAsync());
2417+
var expectedQueueManifest = """
2418+
{
2419+
"type": "value.v0",
2420+
"connectionString": "{storage.outputs.queueEndpoint}"
2421+
}
2422+
""";
2423+
var queueManifest = await ManifestUtils.GetManifest(queue.Resource);
2424+
Assert.Equal(expectedQueueManifest, queueManifest.ToString());
2425+
2426+
// Check table resource.
2427+
var table = storage.AddTables("table");
2428+
2429+
var connectionStringTableResource = (IResourceWithConnectionString)table.Resource;
2430+
2431+
Assert.Equal("https://mytable", await connectionStringTableResource.GetConnectionStringAsync());
2432+
var expectedTableManifest = """
2433+
{
2434+
"type": "value.v0",
2435+
"connectionString": "{storage.outputs.tableEndpoint}"
2436+
}
2437+
""";
2438+
var tableManifest = await ManifestUtils.GetManifest(table.Resource);
2439+
Assert.Equal(expectedTableManifest, tableManifest.ToString());
2440+
}
2441+
2442+
[Fact]
2443+
public async Task AddAzureStorageViaPublishModeEnableAllowSharedKeyAccessOverridesDefaultFalse()
2444+
{
2445+
using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
2446+
2447+
var storagesku = builder.AddParameter("storagesku");
2448+
var storage = builder.AddAzureStorage("storage", (_, _, sa) =>
2449+
{
2450+
sa.AssignProperty(x => x.Sku.Name, storagesku);
2451+
sa.AssignProperty(x => x.AllowSharedKeyAccess, "true");
2452+
});
2453+
2454+
storage.Resource.Outputs["blobEndpoint"] = "https://myblob";
2455+
storage.Resource.Outputs["queueEndpoint"] = "https://myqueue";
2456+
storage.Resource.Outputs["tableEndpoint"] = "https://mytable";
2457+
2458+
// Check storage resource.
2459+
Assert.Equal("storage", storage.Resource.Name);
2460+
2461+
var storageManifest = await ManifestUtils.GetManifestWithBicep(storage.Resource);
2462+
2463+
var expectedStorageManifest = """
2464+
{
2465+
"type": "azure.bicep.v0",
2466+
"path": "storage.module.bicep",
2467+
"params": {
2468+
"principalId": "",
2469+
"principalType": "",
2470+
"storagesku": "{storagesku.value}"
2471+
}
2472+
}
2473+
""";
2474+
Assert.Equal(expectedStorageManifest, storageManifest.ManifestNode.ToString());
2475+
2476+
var expectedBicep = """
2477+
targetScope = 'resourceGroup'
2478+
2479+
@description('')
2480+
param location string = resourceGroup().location
2481+
2482+
@description('')
2483+
param principalId string
2484+
2485+
@description('')
2486+
param principalType string
2487+
2488+
@description('')
2489+
param storagesku string
2490+
2491+
2492+
resource storageAccount_1XR3Um8QY 'Microsoft.Storage/storageAccounts@2022-09-01' = {
2493+
name: toLower(take('storage${uniqueString(resourceGroup().id)}', 24))
2494+
location: location
2495+
tags: {
2496+
'aspire-resource-name': 'storage'
2497+
}
2498+
sku: {
2499+
name: storagesku
2500+
}
2501+
kind: 'StorageV2'
2502+
properties: {
2503+
accessTier: 'Hot'
2504+
networkAcls: {
2505+
defaultAction: 'Allow'
2506+
}
2507+
minimumTlsVersion: 'TLS1_2'
2508+
allowSharedKeyAccess: true
21852509
}
21862510
}
21872511

0 commit comments

Comments
 (0)