Uploading File to Storage Account Using Powershell

Cover image for Upload Files to Azure Storage using a PowerShell Function App

Marcel.L

Marcel.L

Posted on • Updated on

Upload Files to Azure Storage using a PowerShell Office App

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                                                  

Enter fullscreen mode Exit fullscreen mode

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              "                                          }                                                  

Enter fullscreen style Exit fullscreen style

Lets have a closer look, step-past-step what the above script does every bit part of setting up the surround.

  1. Create a resource grouping called Function-App-Storage. image.png
  2. Create an azure storage account, secure store where file uploads will be kept. image.png
  3. Create an azure storage business relationship for the role app. image.png
  4. Create a PowerShell Function App with SystemAssigned managed identity, consumption app service plan and insights. image.png image.png
  5. Configure Function App surround variables. (Will be consumed inside of role app later). image.png
  6. Create fileuploads container in secure store storage account. image.png
  7. Assign Role App SystemAssigned managed identity permissions to Storage account(Read) and container(Write). image.png image.png
  8. Remember I mentioned before in that location is one manual stride. In the next footstep we will modify the requirements.psd1 file on our function to let the AZ module inside of our function past uncommenting the following: image.png

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.

  1. Navigate to the function app we created in the previous section and select + Create nether Functions. image.png
  2. Select Develop in portal and for the template select HTTP trigger, proper noun the part uploadfile and hit Create. image.png
  3. Navigate to Lawmaking + Examination and replace all the code nether run.ps1 with the following powershell lawmaking and hit save: image.png
                          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                                          })                                                  

Enter fullscreen mode Exit fullscreen mode

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=="                                          }                                                  

Enter fullscreen fashion Exit fullscreen mode

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                                          "============================================"                                                  

Enter fullscreen mode Exit fullscreen fashion

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."                                          }                                                  

Enter fullscreen mode Exit fullscreen mode

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              )              ],                            $_              "                                          }                                          }                                                  

Enter fullscreen mode Go out fullscreen mode

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                                          })                                                  

Enter fullscreen mode Exit fullscreen mode

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):

image.png

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:

image.png

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                                                  

Enter fullscreen mode Exit fullscreen style

Lets try information technology out with a txt file:

image.gif

Lets do some other test but with an prototype file this time:

image.gif

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

pwd9000 image

garzapece1984.blogspot.com

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

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel