diff --git a/infra/functions.bicep b/infra/functions.bicep
index e86ff08..8b88313 100644
--- a/infra/functions.bicep
+++ b/infra/functions.bicep
@@ -144,12 +144,8 @@ resource functionApp 'Microsoft.Web/sites@2021-02-01' = {
value: imageStorageSettings.folderPath
}
{
- name: '${imageIndexStorageKey}__ConnectionString'
- value: imageStorageSettings.connectionString
- }
- {
- name: '${imageIndexStorageKey}__ContainerName'
- value: 'index'
+ name: '${imageIndexStorageKey}__BlobContainerUri'
+ value: '${functionStorageAccount.properties.primaryEndpoints.blob}index'
}
]
}
diff --git a/infra/main.bicep b/infra/main.bicep
index fbd1923..e40f61d 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -54,8 +54,7 @@ resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@20
}
var imageSettings = {
- connectionString: 'DefaultEndpointsProtocol=https;AccountName=${imageStorage.name};EndpointSuffix=${az.environment().suffixes.storage};AccountKey=${imageStorage.listKeys().keys[0].value}'
- blobContainerUri: '${imageStorage.properties.primaryEndpoints.blob}/${imageContainerName}'
+ blobContainerUri: '${imageStorage.properties.primaryEndpoints.blob}${imageContainerName}'
folderPath: 'root'
}
@@ -72,18 +71,17 @@ module functions 'functions.bicep' = {
}
}
-// TODO refactor this. this should only require reading permission. Image INDEX requires more permissions and currently these are in same place
// TODO Also this assignment could probably be a separate module etc.
-var storageBlobDataOwnerRoleDefinitionId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
+var storageBlobDataReaderRoleDefinitionId = '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1'
resource functionAppFunctionBlobStorageAccess 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
- scope: imageStorage
- name: guid(functions.name, storageBlobDataOwnerRoleDefinitionId, imageStorage.id)
+ scope: container
+ name: guid(functions.name, storageBlobDataReaderRoleDefinitionId, container.id)
properties: {
principalId: functions.outputs.functionAppPrincipalId
principalType: 'ServicePrincipal'
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
- storageBlobDataOwnerRoleDefinitionId
+ storageBlobDataReaderRoleDefinitionId
)
}
}
diff --git a/infra/types.bicep b/infra/types.bicep
index 3bd40d9..78e415c 100644
--- a/infra/types.bicep
+++ b/infra/types.bicep
@@ -8,6 +8,5 @@ type DiscordSettings = {
@export()
type ImageStorageSettings = {
blobContainerUri: string
- connectionString: string
folderPath: string
}
diff --git a/src/Common/BlobStorageImageService/BlobStorageImageSourceOptions.cs b/src/Common/BlobStorageImageService/BlobStorageImageSourceOptions.cs
index 16a7e32..15047cc 100644
--- a/src/Common/BlobStorageImageService/BlobStorageImageSourceOptions.cs
+++ b/src/Common/BlobStorageImageService/BlobStorageImageSourceOptions.cs
@@ -17,6 +17,7 @@ public class BlobStorageImageSourceOptions
///
/// The name of the container where the images are stored.
+ /// This is not needed (nor used) if BlobContainerUri is used.
///
public required string? ContainerName { get; set; }
diff --git a/src/Common/IndexService/BlobStorageIndexStorageService.cs b/src/Common/IndexService/BlobStorageIndexStorageService.cs
index e063027..c263bd7 100644
--- a/src/Common/IndexService/BlobStorageIndexStorageService.cs
+++ b/src/Common/IndexService/BlobStorageIndexStorageService.cs
@@ -2,29 +2,32 @@
using Azure.Storage.Blobs;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace DiscordImagePoster.Common.IndexService;
public class BlobStorageIndexStorageService : IIndexStorageService
{
- private ILogger _logger;
+ private readonly ILogger _logger;
+ private readonly ImageIndexOptions _options;
private readonly BlobContainerClient _blobContainerClient;
- private const string IndexBlobName = "index.json";
-
public BlobStorageIndexStorageService
(
ILogger logger,
+ IOptions options,
[FromKeyedServices(KeyedServiceConstants.ImageIndexBlobContainerClient)] BlobContainerClient blobContainerClient
)
{
_logger = logger;
+ _options = options.Value;
_blobContainerClient = blobContainerClient;
}
public async Task GetImageIndexAsync()
{
- var blobClient = _blobContainerClient.GetBlobClient(IndexBlobName);
+ _logger.LogTrace("Getting image index from {IndexFileName}", _options.IndexFileName);
+ var blobClient = _blobContainerClient.GetBlobClient(_options.IndexFileName);
var exists = await blobClient.ExistsAsync();
if (!exists)
{
@@ -38,9 +41,10 @@ public BlobStorageIndexStorageService
public async Task UpdateIndexAsync(ImageIndex index)
{
+ _logger.LogTrace("Updating image index to {IndexFileName}", _options.IndexFileName);
await _blobContainerClient.CreateIfNotExistsAsync();
var bytes = JsonSerializer.SerializeToUtf8Bytes(index);
- var blobClient = _blobContainerClient.GetBlobClient(IndexBlobName);
+ var blobClient = _blobContainerClient.GetBlobClient(_options.IndexFileName);
await blobClient.UploadAsync(new MemoryStream(bytes), true);
}
}
diff --git a/src/Common/IndexService/ImageIndexOptions.cs b/src/Common/IndexService/ImageIndexOptions.cs
index 531dee2..e28ca08 100644
--- a/src/Common/IndexService/ImageIndexOptions.cs
+++ b/src/Common/IndexService/ImageIndexOptions.cs
@@ -11,12 +11,25 @@ public class ImageIndexOptions
{
///
/// The connection string to the Azure Storage account.
+ /// Use this only for development. Use managed identity in production.
///
public required string ConnectionString { get; set; }
///
- /// Container name where the index is stored.
+ /// The name of the container where the images are stored.
+ /// This is not needed (nor used) if BlobContainerUri is used.
///
- [Required]
- public required string ContainerName { get; set; }
+ public required string? ContainerName { get; set; }
+
+ ///
+ /// The URI to the container where the images are stored.
+ /// https://{account_name}.blob.core.windows.net/{container_name}
+ ///
+ /// If this is used, the ConnectionString is not needed and managed identity is used
+ ///
+ public required string? BlobContainerUri { get; set; }
+
+ ///
+ /// The path to the index file. Defaults to index.json in root.
+ public required string IndexFileName { get; set; } = "index.json";
}
diff --git a/src/FunctionApp.Isolated/Program.cs b/src/FunctionApp.Isolated/Program.cs
index faa4dfc..cc233ff 100644
--- a/src/FunctionApp.Isolated/Program.cs
+++ b/src/FunctionApp.Isolated/Program.cs
@@ -36,7 +36,6 @@
services.AddKeyedTransient(KeyedServiceConstants.ImageBlobContainerClient, (services, _) =>
{
- //https://{account_name}.blob.core.windows.net/{container_name}
var options = services.GetRequiredService>().Value;
if (!string.IsNullOrWhiteSpace(options.BlobContainerUri))
{
@@ -48,6 +47,10 @@
services.AddKeyedTransient(KeyedServiceConstants.ImageIndexBlobContainerClient, (services, _) =>
{
var options = services.GetRequiredService>().Value;
+ if (!string.IsNullOrWhiteSpace(options.BlobContainerUri))
+ {
+ return new BlobContainerClient(new Uri(options.BlobContainerUri), new DefaultAzureCredential());
+ }
return new BlobContainerClient(options.ConnectionString, options.ContainerName);
});