0
votes

I am writing a small program in Powershell which connects to Office 365 to download audit logs, make some changes and then export a CSV to an Azure Data Lake Storage account. To run this process on a schedule, I have created an Azure Function app (timer template) to run the program. To avoid hard-coded credentials, I created an Azure Key Vault to store the credential secrets. I created a managed identity in the Azure Function, created the secrets in Azure Key Vault with the credentials and then created three application settings in Azure Function under "Configuration" with the URL to point at the secrets stored in Azure Key Vault.

Key Vault References under "Configuration" in Azure Function

The three application settings are called "SecretUsername", "SecretPassword" (to point to the Office 365) and "SecretSAS" (to store the CSV in ADLS).

How do I refer to these variables in my Powershell script? I have tried different variations in my code, but none appear to work. Examples:

  • $uSecret = $SecretUsername

  • $uSecret = $ENV:SecretUsername

  • $uSecret = ENV:SecretUsername

  • $uSecret = (Get-ChildItem ENV:SecretUsername).SecretValueText

     # Input bindings are passed in via param block.
     param($Timer)
    
     # Get the current universal time in the default string format.
     $currentUTCtime = (Get-Date).ToUniversalTime()
    
     # The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
     if ($Timer.IsPastDue) {
         Write-Host "PowerShell timer is running late!"
     }
    
     # Write an information log with the current time.
     Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
    
     Set-ExecutionPolicy AllSigned 
     Set-Item ENV:\SuppressAzurePowerShellBreakingChangeWarnings "true"
    
     $uSecret = (Get-ChildItem ENV:SecretUsername).SecretValueText 
     $pSecret = (Get-ChildItem ENV:SecretPassword).SecretValueText 
     $sasSecret = (Get-ChildItem ENV:SecretSAS).SecretValueText 
     $securePassword = ConvertTo-SecureString -String $pSecret -AsPlainText -Force
     $UserCredential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $uSecret, $securePassword
    
     $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
     Import-PSSession $session
    
     $startDate=(get-date).AddDays(-10)
     $endDate=(get-date)
     $scriptStart=(get-date)
    
     $sessionName = (get-date -Format 'u')+'pbiauditlog'
    
     $aggregateResults = @()
     $i = 0 # Loop counter
     Do { 
         $currentResults = Search-UnifiedAuditLog -StartDate $startDate -EndDate $enddate -SessionId $sessionName -SessionCommand ReturnLargeSet -ResultSize 1000 -RecordType PowerBIAudit
         if ($currentResults.Count -gt 0) {
             Write-Host ("Finished {3} search #{1}, {2} records: {0} min" -f [math]::Round((New-TimeSpan -Start $scriptStart).TotalMinutes,4), $i, $currentResults.Count, $user.UserPrincipalName )
             # Accumulate the data.
             $aggregateResults += $currentResults
             # No need to do another query if the # records returned <1000 - should save around 5-10 seconds per user.
             if ($currentResults.Count -lt 1000) {
                 $currentResults = @()
             } else {
                 $i++
             }
         }
     } Until ($currentResults.Count -eq 0) # End of Session Search Loop.
    
     $data=@()
    
     foreach ($auditlogitem in $aggregateResults) {
         $datum = New-Object -TypeName PSObject  
         $d = ConvertFrom-json $auditlogitem.AuditData
         $datum | Add-Member -MemberType NoteProperty -Name Id -Value $d.Id
         $datum | Add-Member -MemberType NoteProperty -Name CreationTDateTime -Value $d.CreationDate
         $datum | Add-Member -MemberType NoteProperty -Name CreationTime -Value $d.CreationTime
         $datum | Add-Member -MemberType NoteProperty -Name RecordType -Value $d.RecordType
         $datum | Add-Member -MemberType NoteProperty -Name Operation -Value $d.Operation
         $datum | Add-Member -MemberType NoteProperty -Name OrganizationId -Value $d.OrganizationId
         $datum | Add-Member -MemberType NoteProperty -Name UserType -Value $d.UserType
         $datum | Add-Member -MemberType NoteProperty -Name UserKey -Value $d.UserKey
         $datum | Add-Member -MemberType NoteProperty -Name Workload -Value $d.Workload        
         $datum | Add-Member -MemberType NoteProperty -Name UserId -Value $d.UserId
         $datum | Add-Member -MemberType NoteProperty -Name ClientIPAddress -Value $d.ClientIPAddress
         $datum | Add-Member -MemberType NoteProperty -Name UserAgent -Value $d.UserAgent
         $datum | Add-Member -MemberType NoteProperty -Name Activity -Value $d.Activity
         $datum | Add-Member -MemberType NoteProperty -Name ItemName -Value $d.ItemName
         $datum | Add-Member -MemberType NoteProperty -Name WorkSpaceName -Value $d.WorkSpaceName
         $datum | Add-Member -MemberType NoteProperty -Name DashboardName -Value $d.DashboardName
         $datum | Add-Member -MemberType NoteProperty -Name DatasetName -Value $d.DatasetName
         $datum | Add-Member -MemberType NoteProperty -Name ReportName -Value $d.ReportName
         $datum | Add-Member -MemberType NoteProperty -Name WorkspaceId -Value $d.WorkspaceId
         $datum | Add-Member -MemberType NoteProperty -Name ObjectId -Value $d.ObjectId
         $datum | Add-Member -MemberType NoteProperty -Name DashboardId -Value $d.DashboardId
         $datum | Add-Member -MemberType NoteProperty -Name DatasetId -Value $d.DatasetId
         $datum | Add-Member -MemberType NoteProperty -Name ReportId -Value $d.ReportId
         $datum | Add-Member -MemberType NoteProperty -Name OrgAppPermission -Value $d.OrgAppPermission
    
         # Option to include the below JSON column however for large amounts of data it may be difficult for PBI to parse
         $datum | Add-Member -MemberType NoteProperty -Name Datasets -Value (ConvertTo-Json $d.Datasets)
    
         # Below is a simple PowerShell statement to grab one of the entries and place in the DatasetName if any exist
         foreach ($dataset in $d.datasets) {
             $datum.DatasetName = $dataset.DatasetName
             $datum.DatasetId = $dataset.DatasetId
         }
         $data+=$datum
     }
    
     $dateTimestring = $startDate.ToString("yyyyMMdd") + "_" + (Get-Date -Format "yyyyMMdd") + "_" + (Get-Date -Format "HHmm")
     $fileName = ($dateTimestring + ".csv")
     Write-Host ("Writing to file {0}" -f $fileName) 
     $filePath = "$Env:temp/" + $fileName
     $data | Export-csv -Path $filePath
    
     Connect-AzAccount -Credential $UserCredential
     Get-AzVM -ResourceGroupName "Audit" -status
     $Context = New-AzStorageContext -StorageAccountName "auditingstorage" -StorageAccountKey $sasSecret
     Set-AzStorageBlobContent -Force -Context $Context -Container "auditlogs" -File $filePath -Blob $filename 
    
     Remove-PSSession -Id $Session.Id
    

How do I reference the application settings in Azure Function so that I can use the stored secrets in my program?

Please assist! Many thanks!

1

1 Answers

1
votes

To access the app settings, keyvault or not, you must retrieve it trhough : $env:APPSETTING_YourSettingName

Thus, for your keyvault referenced secret, you would access it through the following variables.

$env:APPSETTING_SecretUserName
$env:APPSETTING_SecretPassword
$env:APPSETTING_SecretSAS

And if ever you need to produce a list of them.

Get-ChildItem env:APPSETTING_*

Note, the values returned will plain text unencrypted string. Therefore, in your code, this:

 $uSecret = (Get-ChildItem ENV:SecretUsername).SecretValueText 

becomes that:

 $uSecret = $env:APPSETTING_SecretUserName

Additional note

Since it was pointed out in the comments, I'll mention it. I am not advocating the use of clear text secret in app settings at all.

App settings should be a keyvault referene for any sensitive data. I am simply stating that it can be retrieved within the function at runtime as clear-text through the $env:APPSETTING_YourSettingName variable.

Example: AppSetting name : MySecretUser AppSetting value: @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931) Actual secret value (In the keyvault) : I_AM_Secret

At runtime, getting the value of $env:APPSETTING_MySecretUser will return a String Object with the value I_AM_Secret