Skip to content

Commit

Permalink
Add MSI support for index
Browse files Browse the repository at this point in the history
  • Loading branch information
Hekku2 committed Jun 23, 2024
1 parent 7a3a0d0 commit 343c443
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 23 deletions.
8 changes: 2 additions & 6 deletions infra/functions.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
]
}
Expand Down
12 changes: 5 additions & 7 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}

Expand All @@ -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
)
}
}
1 change: 0 additions & 1 deletion infra/types.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ type DiscordSettings = {
@export()
type ImageStorageSettings = {
blobContainerUri: string
connectionString: string
folderPath: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class BlobStorageImageSourceOptions

/// <summary>
/// The name of the container where the images are stored.
/// This is not needed (nor used) if BlobContainerUri is used.
/// </summary>
public required string? ContainerName { get; set; }

Expand Down
14 changes: 9 additions & 5 deletions src/Common/IndexService/BlobStorageIndexStorageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BlobStorageIndexStorageService> _logger;
private readonly ILogger<BlobStorageIndexStorageService> _logger;
private readonly ImageIndexOptions _options;
private readonly BlobContainerClient _blobContainerClient;

private const string IndexBlobName = "index.json";

public BlobStorageIndexStorageService
(
ILogger<BlobStorageIndexStorageService> logger,
IOptions<ImageIndexOptions> options,
[FromKeyedServices(KeyedServiceConstants.ImageIndexBlobContainerClient)] BlobContainerClient blobContainerClient
)
{
_logger = logger;
_options = options.Value;
_blobContainerClient = blobContainerClient;
}

public async Task<ImageIndex?> 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)
{
Expand All @@ -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);
}
}
19 changes: 16 additions & 3 deletions src/Common/IndexService/ImageIndexOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,25 @@ public class ImageIndexOptions
{
/// <summary>
/// The connection string to the Azure Storage account.
/// Use this only for development. Use managed identity in production.
/// </summary>
public required string ConnectionString { get; set; }

/// <summary>
/// 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.
/// </summary>
[Required]
public required string ContainerName { get; set; }
public required string? ContainerName { get; set; }

/// <summary>
/// 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
/// </summary>
public required string? BlobContainerUri { get; set; }

/// <summary>
/// The path to the index file. Defaults to index.json in root.
public required string IndexFileName { get; set; } = "index.json";
}
5 changes: 4 additions & 1 deletion src/FunctionApp.Isolated/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@

services.AddKeyedTransient(KeyedServiceConstants.ImageBlobContainerClient, (services, _) =>
{
//https://{account_name}.blob.core.windows.net/{container_name}
var options = services.GetRequiredService<IOptions<BlobStorageImageSourceOptions>>().Value;
if (!string.IsNullOrWhiteSpace(options.BlobContainerUri))
{
Expand All @@ -48,6 +47,10 @@
services.AddKeyedTransient(KeyedServiceConstants.ImageIndexBlobContainerClient, (services, _) =>
{
var options = services.GetRequiredService<IOptions<ImageIndexOptions>>().Value;
if (!string.IsNullOrWhiteSpace(options.BlobContainerUri))
{
return new BlobContainerClient(new Uri(options.BlobContainerUri), new DefaultAzureCredential());
}
return new BlobContainerClient(options.ConnectionString, options.ContainerName);
});

Expand Down

0 comments on commit 343c443

Please sign in to comment.