0

I have two functions in a function app. Sometimes multiple instances of the functions run simultaneously. The functions run in a tenant that has many subscriptions.

Because the functions do configurations on virtual networks in these subscriptions, the script has to set context to the subscription a specific network is in. The Get/Set/New-AzVirtualNetwork cmdlets have no -SubscriptionId parameter, so they depend on the context that is set.

I found that when context is set in an instance of the function the context in the other instance(s) is changed as well, causing errors and/or operations performed in the wrong context

I created two simple function scripts that prove my assertion. HttpTrigger1 sets a different context every 60 seconds. HttpTrigger2 gets the context every 60 seconds. Both functions run in the same function app.

HttpTrigger1

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

Get-AzContext
Set-AzContext -SubscriptionName 'test1'
Start-Sleep -seconds 60
Set-AzContext -SubscriptionName 'test2'
Start-Sleep -seconds 60
Set-AzContext -SubscriptionName 'test3'

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
})

HttpTrigger2

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

Get-AzContext
Start-Sleep -seconds 60
Get-AzContext
Start-Sleep -seconds 60
Get-AzContext

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = [HttpStatusCode]::OK
    Body = $body
})

When I trigger the first function, and trigger the second function a few seconds after the first function is triggered, the functions show this output:

HttpTrigger1

onnected!
2023-08-08T08:39:22Z   [Information]   Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=78060401-7388-4c41-9639-9cce01e888c1)
2023-08-08T08:39:22Z   [Vercose]   Sending invocation id: '78060401-7388-4c41-9639-9cce01e888c1
2023-08-08T08:39:22Z   [Vercose]   Posting invocation id:78060401-7388-4c41-9639-9cce01e88444 on workerId:74cdc1af-1f11-439a-889d-3d81616f2222
2023-08-08T08:39:34Z   [Information]   OUTPUT: 
2023-08-08T08:39:34Z   [Information]   OUTPUT: Account                              SucscriptionName  TenantId                             Environment
2023-08-08T08:39:34Z   [Information]   OUTPUT: -------                              ----------------  --------                             -----------
2023-08-08T08:39:34Z   [Information]   OUTPUT: 22222222-2222-2222-9e94-3fc108fa96a8 mngmnt 11111111-1111-1111-96cc-8736a88e4a8e AzureCloud
2023-08-08T08:39:34Z   [Information]   OUTPUT: 
2023-08-08T08:39:38Z   [Information]   OUTPUT: 
2023-08-08T08:39:38Z   [Information]   OUTPUT: Name                                     Account             SucscriptionName    Environment   TenantId
2023-08-08T08:39:38Z   [Information]   OUTPUT: ----                                     -------             ----------------    -----------   --------
2023-08-08T08:39:38Z   [Information]   OUTPUT: mngmnt (fa918aed-3cc9-4ca8-a…            22222222-2222-2222…            mngmnt   AzureCloud    11111111-1111-1111…
2023-08-08T08:39:36Z   [Information]   OUTPUT: test1 (1476c99c-8864-4837-c…             22222222-2222-2222…             test1   AzureCloud    11111111-1111-1111…
2023-08-08T08:40:36Z   [Information]   OUTPUT: test2 (88dafc79-731c-48a8-9…             22222222-2222-2222…             test2   AzureCloud    11111111-1111-1111…
2023-08-08T08:41:36Z   [Information]   OUTPUT: test3 (cc0d1dea-3f43-46cc-c…             22222222-2222-2222…             test3   AzureCloud    11111111-1111-1111…
2023-08-08T08:41:36Z   [Information]   OUTPUT: 
2023-08-08T08:41:36Z   [Information]   Executed 'Functions.HttpTrigger1' (Succeeded, Id=78060401-7388-4c41-9639-9cce01e88555, Duration=133803ms)

HttpTrigger2

Connected!
2023-08-08T08:39:39Z   [Information]   Executing 'Functions.HttpTrigger2' (Reason='This function was programmatically called via the host APIs.', Id=16f66ac8-1edc-4615-8c25-fff5c4c80605)
2023-08-08T08:39:39Z   [Verbose]   Sending invocation id: '16f66ac8-1edc-4615-8c25-fff5c4c80605
2023-08-08T08:39:39Z   [Verbose]   Posting invocation id:16f66ac8-1edc-4615-8c25-fff5c4c80444 on workerId:74bdc1af-1f11-439a-889d-3d51616f2222
2023-08-08T08:39:44Z   [Information]   OUTPUT: 
2023-08-08T08:39:44Z   [Information]   OUTPUT: Account                              SubscriptionName  TenantId                             Environment
2023-08-08T08:39:44Z   [Information]   OUTPUT: -------                              ----------------  --------                             -----------
2023-08-08T08:39:44Z   [Information]   OUTPUT: 22222222-2222-2222-9e94-3fb108fa96a5 mngmnt 11111111-1111-1111-96cb-5736a55e4a5e AzureCloud
2023-08-08T08:39:44Z   [Information]   OUTPUT: 
2023-08-08T08:39:44Z   [Information]   OUTPUT: 
2023-08-08T08:39:44Z   [Information]   OUTPUT: Name                                     Account             SubscriptionName    Environment         TenantId
2023-08-08T08:39:44Z   [Information]   OUTPUT: ----                                     -------             ----------------    -----------         --------
2023-08-08T08:39:44Z   [Information]   OUTPUT: mngmnt (fa918aed-3cc9-4ca8-a…            22222222-2222-2222…            mngmnt    AzureCloud         11111111-1111-1111…
2023-08-08T08:40:44Z   [Information]   OUTPUT: test2 (88dafc79-731c-48a8-9…             22222222-2222-2222…             test2    AzureCloud         11111111-1111-1111…
2023-08-08T08:41:44Z   [Information]   OUTPUT: test3 (cc0d1dea-3f43-46cc-c…             22222222-2222-2222…             test3    AzureCloud         11111111-1111-1111…
2023-08-08T08:41:44Z   [Information]   OUTPUT: 
2023-08-08T08:41:44Z   [Information]   Executed 'Functions.HttpTrigger2' (Succeeded, Id=16f66ac8-1edc-4615-8c25-fff5c4c80444, Duration=124521ms)

When comparing the timestamps and output it can be observed that the context set in HttpTrigger1 is also set in HttpTrigger2

I assume this behaviour is by design. Can anyone explain why this is? This is not how pwsh works on my machine.

I figured out I can avoid the context switches by not using the -AzVirtualNetwork cmdlets and do virtual network operations through the REST api and avoid the AZ. modules that depend on Set-AzContext

My question is: is there a way to prevent/change this behaviour in Azure functions, alowing me to change context without the risk of other instances interfering?

  • what happens when you try to use `select-azcontext` https://github.com/Azure/azure-powershell/issues/8201#issuecomment-450679223 – TheGameiswar Aug 08 '23 at 11:55
  • No. A machine is multi-user and the OS switches between process using Priority among other factors. So you cannot control when the OS switches between the two processes. – jdweng Aug 08 '23 at 12:11
  • This question is not about processes, priority or multitasking. It is about two separate powershell scripts that influence each other in on the level of the AZ module – Gert Jan Kraaijeveld Aug 08 '23 at 14:16
  • In the meantime I found the cause and a solution. Will update shortly – Gert Jan Kraaijeveld Aug 09 '23 at 08:34
  • @GertJanKraaijeveld The issue might be due to context switching in Azure for more details refer here- https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/automation/context-switching.md – SiddheshDesai Aug 09 '23 at 09:00
  • You can disable it by using this command - Disable-AzContextAutosave -Scope Process – SiddheshDesai Aug 09 '23 at 09:00
  • 1
    Another option is to use Default profile -Get-AzVM -ResourceGroupName \"myGroup\" -DefaultProfile $AzureContext and set the context – SiddheshDesai Aug 09 '23 at 09:01

1 Answers1

1

Functions can run in several instances or threads at once in an Azure Function App. The Azure PowerShell context and the underlying resources are shared by all of these instances. All other function instances executing in the same function app are affected when a context is changed in one function instance using the Set-AzContext cmdlet. The context interference is being caused by the Az module's default behavior, which is why you are experiencing it.

You can make use of Defaultprofile parameter, to allow you to specify which context to use for each cmdlet and to support executing several scripts in the same process. Also, to set your Azcontext to the correct subscription and reference it in every az powershell command lets like below:-

$AzureContext = Set-AzContext -SubscriptionId <subscription-id> -TenantId <tenant-id> -Credential <credential-object>

# Use the context to perform an operation
$vm = Get-AzVM -ResourceGroupName <resource-group-name> -Name <vm-name> -DefaultProfile $AzureContext

# Use the same context to perform another operation
$nic = Get-AzNetworkInterface -ResourceGroupName  "ResourceGroup1" | Where-Object {$_.ProvisioningState -eq  'Succeeded'} -DefaultProfile $AzureContext

An alternative method is to use Service principal authentication with client secret credentials in your Powershell Function app, This way all the subscriptions will be isolated by their individual Azure AD application authentication. Refer my SO thread answer code where I have utilized service principal authentication in Azure Powershell http trigger to get storage accounts

SiddheshDesai
  • 3,668
  • 1
  • 2
  • 11