From 67e473ee479a6829708b5e72716ce5c5c7ed7c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Praso=C5=82ek?= Date: Sun, 3 Mar 2024 10:58:18 +0100 Subject: [PATCH] Refactor Azure Bicep scripts --- .../AZ204-DocumentVault.csproj | 1 + .../Pages/Upload.cshtml.cs | 39 +++++++++------- .../AZ204-DocumentVault/Program.cs | 6 +++ .../Services/Models/AzureConfig.cs | 3 +- .../Services/Models/DownloadLink.cs | 9 ++++ .../Services/Models/FunctionApp.cs | 9 ++++ .../AZ204-DocumentVault/appsettings.json | 6 +++ .../Bicep/FunctionApp/functionApp.bicep | 2 + .../Bicep/FunctionApp/main.bicep | 2 + .../Bicep/keyVault-accessPolicies.bicep | 33 +++++++++++++ .../Bicep/keyVault-secrets.bicep | 0 AZ204-DocumentVault/Bicep/keyVault.bicep | 46 +------------------ AZ204-DocumentVault/Bicep/main.bicep | 23 ++++++++-- 13 files changed, 114 insertions(+), 65 deletions(-) create mode 100644 AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/DownloadLink.cs create mode 100644 AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/FunctionApp.cs create mode 100644 AZ204-DocumentVault/Bicep/keyVault-accessPolicies.bicep create mode 100644 AZ204-DocumentVault/Bicep/keyVault-secrets.bicep diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/AZ204-DocumentVault.csproj b/AZ204-DocumentVault/AZ204-DocumentVault/AZ204-DocumentVault.csproj index f623def..673db8c 100644 --- a/AZ204-DocumentVault/AZ204-DocumentVault/AZ204-DocumentVault.csproj +++ b/AZ204-DocumentVault/AZ204-DocumentVault/AZ204-DocumentVault.csproj @@ -19,6 +19,7 @@ + diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/Pages/Upload.cshtml.cs b/AZ204-DocumentVault/AZ204-DocumentVault/Pages/Upload.cshtml.cs index 7f2a40b..02e1043 100644 --- a/AZ204-DocumentVault/AZ204-DocumentVault/Pages/Upload.cshtml.cs +++ b/AZ204-DocumentVault/AZ204-DocumentVault/Pages/Upload.cshtml.cs @@ -1,6 +1,7 @@ using AZ204_DocumentVault.Services; using AZ204_DocumentVault.Services.Models; using Azure.Storage.Blobs.Models; +using Flurl; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Options; @@ -13,6 +14,8 @@ public class Upload : PageModel private readonly ILogger _logger; private readonly ICosmosDbService _cosmosDbService; private readonly IStorageAccountService _storageAccountService; + private readonly HttpClient _httpClient; + private readonly AzureConfig _azureConfig; public string Message { get; set; } = string.Empty; public string DocumentName { get; set; } = string.Empty; @@ -21,30 +24,21 @@ public class Upload : PageModel public List Documents { get; set; } = new(); public string? DocumentDownloadLink { get; set; } - // TODO: check if this is needed. Maybe it is null on every request - private string _userId; + private string UserId => User.GetObjectId()!; - private string UserId - { - get - { - if (string.IsNullOrWhiteSpace(_userId)) - _userId = User.GetObjectId()!; - - return _userId; - } - } - public Upload(ILogger logger, IOptions azureConfig, ICosmosDbService cosmosDbService, - IStorageAccountService storageAccountService + IStorageAccountService storageAccountService, + IHttpClientFactory httpClientFactory ) { _logger = logger; _cosmosDbService = cosmosDbService; _storageAccountService = storageAccountService; - + _httpClient = httpClientFactory.CreateClient("AzureFunctionsClient"); + _azureConfig = azureConfig.Value; + } public async Task OnGet() @@ -62,7 +56,20 @@ public async Task OnPostDownloadFile(string fileName) public async Task OnPostGenerateLink(string id, string fileName, int hoursToBeExpired) { - DocumentDownloadLink = await _storageAccountService.GenerateDownloadLink(fileName, hoursToBeExpired); + // DocumentDownloadLink = await _storageAccountService.GenerateDownloadLink(fileName, hoursToBeExpired); + + string url = _azureConfig.FunctionApp.GenerateDownloadFunctionLink + .SetQueryParam("code", _azureConfig.FunctionApp.GenerateDownloadMethodFunctionKey); + + HttpResponseMessage response = await _httpClient.PostAsJsonAsync(url, + new + { + FileName = fileName, + HoursToBeExpired = hoursToBeExpired + }); + + DownloadLink? link = await response.Content.ReadFromJsonAsync(); + DocumentDownloadLink = link!.Value; await _cosmosDbService.UpdateDocument(id, UserId, fileName, hoursToBeExpired); diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/Program.cs b/AZ204-DocumentVault/AZ204-DocumentVault/Program.cs index 782047a..c787575 100644 --- a/AZ204-DocumentVault/AZ204-DocumentVault/Program.cs +++ b/AZ204-DocumentVault/AZ204-DocumentVault/Program.cs @@ -29,6 +29,12 @@ builder.Services.Configure( builder.Configuration.GetSection(nameof(AzureConfig))); +builder.Services.AddHttpClient("AzureFunctionsClient", client => +{ + client.BaseAddress = new Uri(builder.Configuration.GetValue("AzureConfig:FunctionApp:BaseUrl")!); + client.DefaultRequestHeaders.Clear(); +}); + // ---------------- // BUILD APP // ---------------- diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/AzureConfig.cs b/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/AzureConfig.cs index 6df9964..e572949 100644 --- a/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/AzureConfig.cs +++ b/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/AzureConfig.cs @@ -1,6 +1,6 @@ namespace AZ204_DocumentVault.Services.Models; -public class AzureConfig +public sealed class AzureConfig { public string StorageAccountName { get; set; } = string.Empty; public string StorageAccountKey { get; set; } = string.Empty; @@ -8,4 +8,5 @@ public class AzureConfig public string KeyVaultUri { get; set; } = string.Empty; public string CosmosDbUri { get; set; } = string.Empty; public string Test { get; set; } = string.Empty; + public FunctionApp FunctionApp { get; set; } } \ No newline at end of file diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/DownloadLink.cs b/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/DownloadLink.cs new file mode 100644 index 0000000..e3e780f --- /dev/null +++ b/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/DownloadLink.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace AZ204_DocumentVault.Services.Models; + +public class DownloadLink +{ + [JsonPropertyName("DownloadLink")] + public string Value { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/FunctionApp.cs b/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/FunctionApp.cs new file mode 100644 index 0000000..260048c --- /dev/null +++ b/AZ204-DocumentVault/AZ204-DocumentVault/Services/Models/FunctionApp.cs @@ -0,0 +1,9 @@ +namespace AZ204_DocumentVault.Services.Models; + +public sealed class FunctionApp +{ + public string BaseUrl { get; set; } + public string GenerateDownloadFunctionLink { get; set; } + + public string GenerateDownloadMethodFunctionKey { get; set; } +} \ No newline at end of file diff --git a/AZ204-DocumentVault/AZ204-DocumentVault/appsettings.json b/AZ204-DocumentVault/AZ204-DocumentVault/appsettings.json index 567bbd9..163a9bd 100644 --- a/AZ204-DocumentVault/AZ204-DocumentVault/appsettings.json +++ b/AZ204-DocumentVault/AZ204-DocumentVault/appsettings.json @@ -11,6 +11,12 @@ "ContainerName": "documents", "KeyVaultUri": "https://documentskeyvault.vault.azure.net/", "CosmosDbUri": "https://cosdb-documentvault-ne.documents.azure.com:443/", + "FunctionApp": + { + "BaseUrl": "https://functionapp-app.azurewebsites.net/api", + "GenerateDownloadFunctionLink": "GenerateDownloadLink", + "GenerateDownloadMethodFunctionKey": "todo" + }, "Test": "__TOKEN__" }, "AzureAd": { diff --git a/AZ204-DocumentVault/Bicep/FunctionApp/functionApp.bicep b/AZ204-DocumentVault/Bicep/FunctionApp/functionApp.bicep index 1c8f3c6..7ee0b15 100644 --- a/AZ204-DocumentVault/Bicep/FunctionApp/functionApp.bicep +++ b/AZ204-DocumentVault/Bicep/FunctionApp/functionApp.bicep @@ -117,3 +117,5 @@ resource azFunctionApp 'Microsoft.Web/sites@2021-03-01' = { } } } + +output outFunctionAppObjectId string = azFunctionApp.identity.principalId diff --git a/AZ204-DocumentVault/Bicep/FunctionApp/main.bicep b/AZ204-DocumentVault/Bicep/FunctionApp/main.bicep index e1af563..afc9afb 100644 --- a/AZ204-DocumentVault/Bicep/FunctionApp/main.bicep +++ b/AZ204-DocumentVault/Bicep/FunctionApp/main.bicep @@ -23,3 +23,5 @@ module modFunctionApp 'functionApp.bicep' = { parStorageAccountName: parStorageAccountName } } + +output outFunctionAppObjectId string = modFunctionApp.outputs.outFunctionAppObjectId diff --git a/AZ204-DocumentVault/Bicep/keyVault-accessPolicies.bicep b/AZ204-DocumentVault/Bicep/keyVault-accessPolicies.bicep new file mode 100644 index 0000000..a327f26 --- /dev/null +++ b/AZ204-DocumentVault/Bicep/keyVault-accessPolicies.bicep @@ -0,0 +1,33 @@ +// Parameters +param parKeyVaultName string +param parAppServiceObjectId string +param parFunctionAppObjectId string + +// Variables +var varTenantId = subscription().tenantId + +resource resAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-01' = { + name: '${parKeyVaultName}/add' + properties: { + accessPolicies: [ + { + objectId: parAppServiceObjectId + tenantId: varTenantId + permissions: { + secrets: [ + 'get' + ] + } + } + { + objectId: parFunctionAppObjectId + tenantId: varTenantId + permissions: { + secrets: [ + 'get' + ] + } + } + ] + } +} diff --git a/AZ204-DocumentVault/Bicep/keyVault-secrets.bicep b/AZ204-DocumentVault/Bicep/keyVault-secrets.bicep new file mode 100644 index 0000000..e69de29 diff --git a/AZ204-DocumentVault/Bicep/keyVault.bicep b/AZ204-DocumentVault/Bicep/keyVault.bicep index ec2e7fd..a8ea1d9 100644 --- a/AZ204-DocumentVault/Bicep/keyVault.bicep +++ b/AZ204-DocumentVault/Bicep/keyVault.bicep @@ -4,10 +4,6 @@ param parLocation string = resourceGroup().location @description('The Azure user object-id') param parPrincipalId string -param parAppServiceObjectId string -param parStorageAccountName string -param parCosmosDbName string - // Variables var varTenantId = subscription().tenantId @@ -21,17 +17,6 @@ resource resKeyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { name: 'standard' } tenantId: varTenantId - accessPolicies: [ - { - objectId: parAppServiceObjectId - permissions: { - secrets: [ - 'get' - ] - } - tenantId: varTenantId - } - ] } } @@ -47,34 +32,5 @@ resource resRegistryRoleAssignment 'Microsoft.Authorization/roleAssignments@2022 } } -// ------------------- -// Add secrets -// ------------------- - -// Storage account -resource resStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { - name: parStorageAccountName -} - -resource resSecret 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { - parent: resKeyVault - name: 'StorageAccountKey' - properties: { - value: resStorageAccount.listKeys().keys[0].value - } -} - -// Cosmos Db -resource resCosmosDb 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' existing = { - name: parCosmosDbName -} - -resource resSecretCosmosDbKey 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { - parent: resKeyVault - name: 'CosmosDbKey' - properties: { - value: resCosmosDb.listKeys().primaryMasterKey - } -} - +output keyVaultName string = resKeyVault.name output keyVaultUri string = resKeyVault.properties.vaultUri diff --git a/AZ204-DocumentVault/Bicep/main.bicep b/AZ204-DocumentVault/Bicep/main.bicep index ca582fa..e2387c9 100644 --- a/AZ204-DocumentVault/Bicep/main.bicep +++ b/AZ204-DocumentVault/Bicep/main.bicep @@ -32,9 +32,6 @@ module modKeyVault 'keyVault.bicep' = { params: { parLocation: parLocation parPrincipalId: parPrincipalId - parAppServiceObjectId: modAppService.outputs.outAppServiceObjectId - parStorageAccountName: modStorageAccount.outputs.storageAccountName - parCosmosDbName: modCosmosDb.outputs.cosmosDbName } } @@ -50,3 +47,23 @@ module modFunctionApp 'FunctionApp/main.bicep' = { parStorageAccountName: modStorageAccount.outputs.storageAccountName } } + +// Key vault - add access policies +module modAccessPolicies 'keyVault-accessPolicies.bicep' = { + name: 'accessPolicies' + params: { + parAppServiceObjectId: modAppService.outputs.outAppServiceObjectId + parFunctionAppObjectId: modFunctionApp.outputs.outFunctionAppObjectId + parKeyVaultName: modKeyVault.outputs.keyVaultName + } +} + +// Key vault - add secrets +module modSecrets 'keyVault-secrets.bicep' = { + name: 'modSecrets' + params: { + parCosmosDbName: modCosmosDb.outputs.cosmosDbName + parKeyVaultName: modKeyVault.outputs.keyVaultName + parStorageAccountName: modStorageAccount.outputs.storageAccountName + } +}