I'm writing a script using Azure PS and I am used to typing all variables explicitly as it helps me avoid surprises. I found that when I type the variables storing results of API calls returning Azure.Response<T>
instances, PowerShell is unable to find the type.
When digging deeper into the objects returned by the Azure PS cmdlets, the APIs belonging to Azure SDK for .NET are exposed -- e.g. Get-AzStorageQueue returns Azure PS-specific AzureStorageQueue but its property QueueClient exposes an instance of the Azure SDK for .NET type QueueClient.
PS > [Azure.Storage.Queues.QueueClient]
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False QueueClient System.Object
So far so good. But the methods of the client often return instances of Azure.Response<T>
. E.g., QueueClient.Exists() returns Azure.Response<bool>
, QueueClient.PeekMessage() returns Azure.Response<Azure.Storage.Queues.Models.PeekedMessage>
, etc. When trying to work with that type in PowerShell, I am getting the error mentioned in the title of this question:
PS > [Azure.Response[bool]]
InvalidOperation: Unable to find type [Azure.Response].
PS > [Azure.Response`1]
InvalidOperation: Unable to find type [Azure.Response`1].
PS > [Azure.Response]
InvalidOperation: Unable to find type [Azure.Response].
The actual code I am trying to get to work, simplified and made self-contained:
#require -Modules Az
using namespace Microsoft.WindowsAzure.Commands.Common.Storage.ResourceModel
using namespace Microsoft.WindowsAzure.Commands.Storage.Common
Connect-AzAccount
[AzureStorageContext]$context = New-AzStorageContext -StorageAccountName mystorage -UseConnectedAccount
[AzureStorageQueue]$queue = Get-AzStorageQueue -Context $context | Select-Object -First 1
[Azure.Response[bool]]$existsResponse = $queue.QueueClient.Exists()
Write-Output $existsResponse.Value
The error is:
InvalidOperation: C:\Users\Palec\AppData\Local\Temp\script.ps1:8
Line |
8 | [Azure.Response[bool]]$existsResponse = $queue.QueueClient.Exists()
| ~~~~~~~~~~~~~~~~~~~~~~
| Unable to find type [Azure.Response].
When I inspect the result of the Exists() call, it is of an internal subtype of the public Azure.Response<bool>
type, so the typing of the variable seems to be correct:
PS > $queue.QueueClient.Exists().GetType() | Format-List -Property FullName,Assembly,{$_.BaseType.FullName},{$_.BaseType.Assembly}
FullName : Azure.ValueResponse`1[[System.Boolean, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
Assembly : Azure.Core, Version=1.44.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8
$_.BaseType.FullName : Azure.Response`1[[System.Boolean, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
$_.BaseType.Assembly : Azure.Core, Version=1.44.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8
I believe it is not just about generics being somewhat problematic in PowerShell, because when I try to access a non-generic type from the same Azure.Core assembly, namely Azure.ETag, I get the same error:
PS > [Azure.ETag]
InvalidOperation: Unable to find type [Azure.ETag].
I even tried explicitly loading the Azure.Core assembly using Add-Type but it did not help. However, it can be loaded and contains the right types.
PS > Add-Type -AssemblyName Azure.Core -PassThru | Where-Object { $_.Name -contains "Response" -or $_.Name -contains "ETag" }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ETag System.ValueType
True False Response System.Object
What am I doing wrong? I am using PowerShell 7.5.0 and Azure PS 13.2.0, if that makes any difference.
The Az.Storage module loads the Azure.Core assembly in a custom assembly load context and PowerShell does not search assemblies loaded in a custom assembly load context for types.
That behavior is by design -- such assemblies are supposed to be isolated.
A workaround is to use assembly-qualified name of the type, i.e.:
[Azure.ETag, Azure.Core]
[Azure.Response`1[bool], Azure.Core]
[Azure.Response`1[PeekedMessage], Azure.Core] # Relies on `using namespace Azure.Storage.Queues.Models`
[Azure.Response`1[[Azure.ETag, Azure.Core]], Azure.Core]
Then, the Type.GetType(string, bool, bool)
call internally used by PowerShell is somehow able to locate the assembly even in the custom assembly load context. However, you must not omit the namespace
(using namespace Azure
is not applied to assembly-qualified names) and you must not forget that the number of type parameters (the ...`1
in the examples above) is a part of the assembly-qualified name even for closed generics. In type parameters, you may use the normal way of referencing types unless the type itself suffers from the assembly load context issue.
See GitHub Azure/azure-powershell issue #21134 for more details.