I’m working on an Azure Function App that will grab a .pgp file off of Blob Storage, decrypt it, and then upload that decrypted file back to Blob Storage.
I’ve done quite a bit of research and everything usually assumes you are downloading a file to a local drive, decrypt, then upload. However, in my case I’m trying to do everything in Azure.
This is the code I’ve come up with so far. This will connect to and download the file to a stream successfully but I’m not figuring out how to wire it up with the output stream.
The line for the UploadAsync()
is the one I’m having issues with and it needs a value passed into the method but I’m assuming the targetBlobClient already has reference to the Blob Container and file name.
I’m lost here and can’t seem to find any kind of examples to help me figure out what to do. I’m sure this code could be reduced and I will look into that once I can get it to work.
var outputStream = await targetBlobClient.UploadAsync();
Here is the code I’ve come up with so far:
try
{
var privateKeyValue = GetKeyVaultSecretValue(m.KeyVaultURL, m.KeyVaultPrivateSecretName);
var privateKeyPassword = GetKeyVaultSecretValue(m.KeyVaultURL, m.KeyVaultPrivateSecretPassword);
var storageConnString = m.InputStorageConnection;
var containerName = m.InputStorageContainer;
var sourceFile = m.InputFileName;
var targetFile = m.OutputFileName;
var sourceFolder = Path.Combine(m.InputStorageContainer, m.InputStorageFolder);
var targetFolder = Path.Combine(m.OutputStorageContainer, m.OutputFolder);
Console.WriteLine(@"Source full path: " + sourceFolder + "\" + sourceFile);
BlobServiceClient blobServiceClient = new BlobServiceClient(storageConnString);
BlobContainerClient sourceContainerClient = blobServiceClient.GetBlobContainerClient(sourceFolder);
BlobClient sourceBlobClient = sourceContainerClient.GetBlobClient(sourceFile);
BlobContainerClient targetContainerClient = blobServiceClient.GetBlobContainerClient(targetFolder);
BlobClient targetBlobClient = sourceContainerClient.GetBlobClient(targetFile);
if (await sourceBlobClient.ExistsAsync())
{
var inputStream = await sourceBlobClient.DownloadAsync();
var outputStream = await targetBlobClient.UploadAsync();
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKeyValue, privateKeyPassword);
PGP pgp = new PGP(encryptionKeys);
await pgp.DecryptStreamAsync(inputStream, outputStream);
}
else
{
Console.WriteLine(@"Error finding file. " + sourceFolder + "\" + sourceFile);
_log.LogError("Error find file {0}\{1}.", sourceFolder, sourceFile);
}
}
catch (Exception ex)
{
_log.LogError("Error decrypting file. EventType: {0} | File: {1} | {2} | {3} | {4}", m.EventName, m.InputFileName, ex.Message, ex.StackTrace, ex.InnerException);
Console.WriteLine("Error: " + ex.Message);
}
2
Answers
I worked with someone and we finally figured it out. The code could probably be cleaned up some but this seems to work for now. This uses all memory streams to download, decrypt, upload. I'll eventually create an encryption side of the Function App as well. In PROD we will be using a Service Bus message to kick this off but I'm using an HTTP Trigger to make it easy to test with Postman. Hopefully it helps out someone else.
Also note that there is a known bug currently (Nov 2022) with adding Key Vault secret values through the Azure Portal. It strips out all of the formatting which renders the PGP Keys invalid. You have to use Azure Cloud Shell or similar to upload a multi-line file first and then use Powershell (or Bash) to insert the file value into Key Vault in order to keep the formatting.
The UploadAsync method takes a stream. Here is the code from our Encryption routine that writes to Blob Storage: