Uploading File to Storage Account Using Powershell
Overview
With Hacktoberfest 2021 coming to an end soon, I idea I would share with you a trivial experiment I did using an Azure serverless Function App with Powershell as the code base. The idea was to create an easy to use, reusable File Uploader API that would allow someone to upload a file to an Azure Storage Account blob container by posting a HTTP request.
The HTTP request would exist a JSON body and only requires the file proper name and the file Content/data in a serialized Base64 string. The PowerShell Office App would then deserialize the base64 cord into a temporary file, rename and copy the file as a blob into a storage business relationship container called fileuploads.
Ready environs upward automatically
To phase and setup the entire environment for my API automatically I wrote a PowerShell script using AZ CLI, that would build and configure all the things I would need to beginning work on my function. In that location was one transmission step however I will cover a bit later on. But for now you lot can find the script I used on my github code folio called setup_environment.ps1.
First we will log into Azure by running:
az login
After logging into Azure and selecting the subscription, we tin can run the script that volition create all the resource and prepare the environment up:
# Setup Variables. $randomInt = Get-Random -Maximum 9999 $subscriptionId = $ ( az account evidence --query id -o tsv ) $resourceGroupName = "Function-App-Storage" $storageName = "storagefuncsa $randomInt " $functionAppName = "storagefunc $randomInt " $region = "uksouth" $secureStore = "securesa $randomInt " $secureContainer = "fileuploads" # Create a resource resourceGroupName az grouping create --name " $resourceGroupName " --location " $region " # Create an azure storage business relationship for secure store (uploads) az storage account create ` --name " $secureStore " ` --location " $region " ` --resource-group " $resourceGroupName " ` --sku "Standard_LRS" ` --kind "StorageV2" ` --https-only true ` --min-tls-version "TLS1_2" # Create an azure storage account for function app az storage account create ` --name " $storageName " ` --location " $region " ` --resource-group " $resourceGroupName " ` --sku "Standard_LRS" ` --kind "StorageV2" ` --https-but true ` --min-tls-version "TLS1_2" # Create a Function App az functionapp create ` --name " $functionAppName " ` --storage-account " $storageName " ` --consumption-plan-location " $region " ` --resource-group " $resourceGroupName " ` --os-type "Windows" ` --runtime "powershell" ` --runtime-version "7.0" ` --functions-version "iii" ` --assign-identity #Configure Role App environment variables: $settings = @( "SEC_STOR_RGName= $resourceGroupName " "SEC_STOR_StorageAcc= $secureStore " "SEC_STOR_StorageCon= $secureContainer " ) az functionapp config appsettings set ` --name " $functionAppName " ` --resource-grouping " $resourceGroupName " ` --settings @ settings # Authorize the operation to create the container - Signed in User (Storage Blob Data Contributor Part) az ad signed-in-user show --query objectId -o tsv | foreach-object { az function assignment create ` --role "Storage Blob Data Contributor" ` --assignee " $_ " ` --telescopic "/subscriptions/ $subscriptionId /resourceGroups/ $resourceGroupName /providers/Microsoft.Storage/storageAccounts/ $secureStore " } #Create Upload container in secure store First-Sleep -south 30 az storage container create ` --account-name " $secureStore " ` --name " $secureContainer " ` --auth-fashion login #Assign Function System MI permissions to Storage account(Read) and container(Write) $functionMI = $ ( az resource list --proper noun $functionAppName --query [ * ] . identity . principalId --out tsv ) | foreach-object { az role assignment create ` --role "Reader and Information Access" ` --assignee " $_ " ` --scope "/subscriptions/ $subscriptionId /resourceGroups/ $resourceGroupName /providers/Microsoft.Storage/storageAccounts/ $secureStore " ` az part assignment create ` --office "Storage Blob Information Contributor" ` --assignee " $_ " ` --scope "/subscriptions/ $subscriptionId /resourceGroups/ $resourceGroupName /providers/Microsoft.Storage/storageAccounts/ $secureStore /blobServices/default/containers/ $secureContainer " }
Lets have a closer look, step-past-step what the above script does every bit part of setting up the surround.
- Create a resource grouping called
Function-App-Storage.
- Create an azure storage account,
secure storewhere file uploads will be kept.
- Create an azure storage business relationship for the role app.
- Create a PowerShell Function App with
SystemAssignedmanaged identity,consumptionapp service plan andinsights.
- Configure Function App surround variables. (Will be consumed inside of role app later).
- Create
fileuploadscontainer in secure store storage account.
- Assign Role App
SystemAssignedmanaged identity permissions to Storage account(Read) and container(Write).
- Remember I mentioned before in that location is one manual stride. In the next footstep we will modify the
requirements.psd1file on our function to let theAZmodule inside of our function past uncommenting the following:
NOTE: Remember to save the transmission change we made on requirements.psd1 above. That is information technology, our surroundings is set upwardly and in the next section we will configure the file uploader part API powershell code.
File Uploader Role
The following function app code can as well be found under my github lawmaking page chosen run.ps1.
- Navigate to the function app we created in the previous section and select
+ CreatenetherFunctions.
- Select
Develop in portaland for the template selectHTTP trigger, proper noun the partuploadfileand hitCreate.
- Navigate to
Lawmaking + Examinationand replace all the code netherrun.ps1with the following powershell lawmaking and hitsave:
using namespace System.Net # Input bindings are passed in via param block. param ( $Request , $TriggerMetadata ) # Write to the Azure Functions log stream. Write-Host "POST request - File Upload triggered." #Set up Status $statusGood = $true #Set Vars (Func App Env Settings): $resourceGroupName = $ env : SEC_STOR_RGName $storageAccountName = $ env : SEC_STOR_StorageAcc $blobContainer = $ env : SEC_STOR_StorageCon #Set Vars (From request Torso): $fileName = $Asking . Body [ "fileName" ] $fileContent = $Request . Torso [ "fileContent" ] Write-Host "============================================" Write-Host "Delight await, uploading new hulk: [ $fileName ]" Write-Host "============================================" #Construct temp file from fileContent (Base64String) try { $bytes = [ Convert ]:: FromBase64String ( $fileContent ) $tempFile = New-TemporaryFile [ io.file ]:: WriteAllBytes ( $tempFile , $bytes ) } grab { $statusGood = $false $body = "FAIL: Failed to receive file information." } #Get secureStore details and upload blob. If ( $tempFile ) { try { $storageAccount = Go-AzStorageAccount -ResourceGroupName $resourceGroupName -Proper noun $storageAccountName $storageContext = $storageAccount . Context $container = ( Get-AzStorageContainer -Proper name $blobContainer -Context $storageContext ) . CloudBlobContainer Set-AzStorageBlobContent -File $tempFile -Blob $fileName -Container $container . Proper name -Context $storageContext } catch { $statusGood = $false $trunk = "FAIL: Failure connecting to Azure blob container: [ $( $container . Name ) ], $_ " } } if ( ! $statusGood ) { $status = [ HttpStatusCode ]:: BadRequest } else { $status = [ HttpStatusCode ]:: OK $body = "SUCCESS: File [ $fileName ] Uploaded OK to Secure Store container [ $( $container . Proper noun ) ]" } # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([ HttpResponseContext ]@{ StatusCode = $condition Torso = $body })
Lets take a closer look at what this lawmaking really does. In the first few lines we tin can meet that the role app volition take a request input parameter chosen $request. This parameter will be our chief input and request trunk JSON object. Nosotros will use the JSON body to send details into our API about the file we want to upload. We besides set up a status and some variables.
Hither is an example of a valid JSON request body for our role app:
//JSON request Body Case { "fileName" : "hello-globe.txt" , "fileContent" : "VXBsb2FkIHRoaXMgZmlsZSB0byBBenVyZSBjbG91ZCBzdG9yYWdlIHVzaW5nIEZ1bmN0aW9uIEFwcCBGaWxlIHVwbG9hZGVyIEFQSQ==" }
Note that our $Request input parameter is linked to $Request.trunk, and we set two variables that volition be taken from the JSON request body namely, fileName and fileContent. Nosotros will use these two values from the incoming POST request to shop the serialized file content (Base64String) in a variable chosen $fileContent and the hulk name in a variable called $fileName.
NOTE: Retrieve in the previous section footstep 5 we ready some environment variables on our function app settings, nosotros can reference these environment variables values in our office code with $env:appSettingsKey as show below):
#// code/run.ps1#L1-L22 using namespace System.Net # Input bindings are passed in via param block. param ( $Request , $TriggerMetadata ) # Write to the Azure Functions log stream. Write-Host "Mail asking - File Upload triggered." #Prepare Condition $statusGood = $true #Set Vars (Func App Env Settings): $resourceGroupName = $ env : SEC_STOR_RGName $storageAccountName = $ env : SEC_STOR_StorageAcc $blobContainer = $ env : SEC_STOR_StorageCon #Set Vars (From JSON asking Trunk): $fileName = $Request . Torso [ "fileName" ] $fileContent = $Request . Body [ "fileContent" ] Write-Host "============================================" Write-Host "Delight expect, uploading new blob: [ $fileName ]" Write-Host "============================================"
Next we have a endeavour/catch block where we take the serialized Base64 String from the JSON request torso fileContent stored in the PowerShell variable $fileContent and attempt to deserialize it into a temporary file:
#// code/run.ps1#L25-L33 try { $bytes = [ Catechumen ]:: FromBase64String ( $fileContent ) $tempFile = New-TemporaryFile [ io.file ]:: WriteAllBytes ( $tempFile , $bytes ) } catch { $statusGood = $fake $body = "Fail: Failed to receive file information." }
Then nosotros have an if argument with a try/catch block where we accept the deserialized temp file from the previous footstep and rename and salve the file into our fileuploads container using the $fileName variable which takes the fileName value from our JSON request trunk. Considering our part apps managed identity has been given permission against the container using RBAC earlier when we ready upwards the environment, nosotros should have no problems hither:
#// code/run.ps1#L36-L48 If ( $tempFile ) { attempt { $storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName $storageContext = $storageAccount . Context $container = ( Get-AzStorageContainer -Proper name $blobContainer -Context $storageContext ) . CloudBlobContainer Set-AzStorageBlobContent -File $tempFile -Blob $fileName -Container $container . Proper noun -Context $storageContext } catch { $statusGood = $simulated $body = "Neglect: Failure connecting to Azure blob container: [ $( $container . Name ) ], $_ " } }
Finally in the last few lines, given our status is still good, we return a message body to the user to say that the file has been uploaded successfully.
#// lawmaking/run.ps1#L50-L62 if ( ! $statusGood ) { $status = [ HttpStatusCode ]:: BadRequest } else { $status = [ HttpStatusCode ]:: OK $trunk = "SUCCESS: File [ $fileName ] Uploaded OK to Secure Store container [ $( $container . Proper name ) ]" } # Acquaintance values to output bindings by calling 'Push-OutputBinding'. Push button-OutputBinding -Proper noun Response -Value ([ HttpResponseContext ]@{ StatusCode = $status Body = $torso })
Testing the role app
Lets examination our role app and see if it does what information technology says on the tin can.
Earlier nosotros test the office lets create a new temporary function fundamental to examination with. Navigate to the part app function and select Function Keys. Create a + New function key and call the key temp_token (Brand a notation of the token equally we volition use information technology in the test script):
Also make a note of the Function App URL. If you followed this tutorial it would be: https://<FunctionAppName>.azurewebsites.net/api/uploadfile. Or you lot can too get this under Lawmaking + Test and selecting Get function URL and drop downward using the key: temp_token:
I accept created the post-obit powershell script to test the file uploader API. The post-obit test script tin be plant nether my github code folio called test_upload.ps1.
#File and path to upload $fileToUpload = "C:\temp\howdy-globe.txt" #Gear up variables for Part App (URI + Token) $functionUri = "https://<functionAppname>.azurewebsites.net/api/uploadfile" $temp_token = "<TokenSecretValue>" #Set fileName and serialize file content for JSON body $fileName = Separate-Path $fileToUpload -Leaf $fileContent = [ Convert ]:: ToBase64String (( Go-Content -Path $fileToUpload -Encoding Byte )) #Create JSON torso $body = @{ "fileName" = $fileName "fileContent" = $fileContent } | ConvertTo-Json -Compress #Create Header $header = @{ "x-functions-key" = $temp_token "Content-Type" = "application/json" } #Trigger Office App API Invoke-RestMethod -Uri $functionUri -Method 'POST' -Body $body -Headers $header
Lets try information technology out with a txt file:
Lets do some other test but with an prototype file this time:
Notation: Ensure to continue your function app tokens safe. (Yous can delete your temp_token after testing).
I hope you take enjoyed this post and have learned something new. You can also find the code samples used in this blog post on my Github folio. ❤️
Author
Like, share, follow me on: 🐙 GitHub | 🐧 Twitter | 👾 LinkedIn
Source: https://dev.to/pwd9000/upload-files-to-azure-storage-using-a-powershell-function-app-15li
0 Response to "Uploading File to Storage Account Using Powershell"
Post a Comment