In some of my previous blogs here, we have seen how we could use Azure Functions to to automate processes and SharePoint workloads.
Most of these jobs run using elevated or stored privileged accounts as the Azure Function is in a different context than the user context. There are various ways we could setup these accounts. Some of these approaches are below:
- Azure AD Service Accounts
-
- Suitable for all operations
-
- Need access to resource
-
- Reusable across multiple workloads
- Azure AD Apps
-
- Suitable for Graph Access
-
- Need exact permissions set up
-
- Might need Tenant Admin authentication
- SharePoint App Accounts
-
- Suitable for SharePoint workloads.
-
- Need Site and App specific privileges
The details of these accounts could be stored in the Azure Functions App Settings (for dev and production) or local.settings.json file during local development.
The most important consideration would be to prevent from exposing password details in the Azure functions in case of unauthorized access. There are two ways we could achieve this:
- Encrypting the password and store in the Azure Function (PowerShell)
- Using Azure Key Vault to store and access password details (C#)
Encrypting Passwords in Azure Functions
For doing this, first lets’ create an encrypted password using PowerShell using the script below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Create an Encrypted Password ## | |
$AESKey = New-Object Byte[] 32 | |
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey) | |
Set-Content C:\Temp\<PasswordFileName>.key $AESKey | |
## Run the above code first, then upload the above file to the Function App## | |
Function Get-EncryptedPassword | |
{ | |
param ( | |
[Parameter(Mandatory=$true,HelpMessage='Please specify the key file path')][ValidateScript({Test-Path $_})][String]$KeyPath, | |
[Parameter(Mandatory=$true,HelpMessage='Please specify password in clear text')][ValidateNotNullOrEmpty()][String]$Password | |
) | |
$secPw = ConvertTo-SecureString -AsPlainText $Password -Force | |
$AESKey = Get-content $KeyPath | |
$Encryptedpassword = $secPw | ConvertFrom-SecureString -Key $AESKey | |
$Encryptedpassword | |
} | |
Get-EncryptedPassword -KeyPath C:\Temp\<PasswordFileName>.key -Password <Password> |
Next, copy the file to a bin folder in Azure Function using Azure File Explorer (Application Settings -> App Service Editor) and decrypt using the code below
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$userPass = $env:EncryptedPass | |
$keyPath = 'D:\home\site\wwwroot\<FunctionName>\bin\<PasswordFileName>.key' | |
$secPass = $userPass | ConvertTo-SecureString -Key (Get-Content $keyPath) |
Using Azure Key Vault
For using Azure Key Vault, the steps are as below
- Create an Azure AD App and get the Client ID and Client Secret
- Create a Azure Key Vault and add the above Azure AD app to have Get Access to the key vault. The below permissions will suffix to read the secret.
- Create Secret in key vault, then store the password and the secure Uri
- Store the Secret Uri, Client ID and Client Secret in Azure App Settings
- Use the below code to get the secure pass.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public SecureString GetSecret() | |
{ | |
try | |
{ | |
// Get the Secret Uri from Key Vault | |
string SecretUri = System.Environment.GetEnvironmentVariable("<KeyValutSecretUri>"); | |
var kvToken = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken)); | |
var kvSecret = kvToken.GetSecretAsync(SecretUri).Result; | |
SecureString secpass = new SecureString(); | |
foreach (char charpass in kvSecret.Value) | |
{ | |
secpass.AppendChar(charpass); | |
} | |
return secpass; | |
} | |
catch (Exception ex) | |
{ | |
throw ex; | |
} | |
} | |
public async Task<string> GetToken(string authority, string resource, string scope) | |
{ | |
AuthenticationResult authResult = null; | |
try | |
{ | |
string ClientId = System.Environment.GetEnvironmentVariable("ClientId"); | |
string ClientSecret = System.Environment.GetEnvironmentVariable("ClientSecret"); | |
var authContext = new AuthenticationContext(authority); | |
ClientCredential clientCred = new ClientCredential(ClientId, ClientSecret); | |
Task authTask = Task.Run(async () => authResult = await authContext.AcquireTokenAsync(resource, clientCred)); | |
authTask.Wait(); | |
} | |
catch (Exception ex) | |
{ | |
throw new Exception($"Function Error at Token Retrieval: {ex.Message}"); | |
} | |
if (authResult == null) | |
throw new InvalidOperationException("Failed to obtain the JWT token"); | |
return authResult.AccessToken; | |
} |
Conclusion
Hence above we saw how we could set up accounts in Azure Function for elevated access to SharePoint and Resource locations.
Leave a Reply