Hi everyone,
I’m running into a recurring issue when running Pester tests on a PowerShell class that interacts with Azure resources, specifically with Get-AzResource
. The tests fail with an error indicating a need to login via Connect-AzAccount
.
Key Points:
- The class works as intended outside of Pester, making successful Azure API calls.
- The issue arises during Pester tests that involve methods fetching diagnostic settings from Azure, where there is no Azure context already configures.
Error Received Across Multiple Tests:
PSInvalidOperationException: Run Connect-AzAccount to login.
What I’ve done:
The structure of my Powershell Class (simplified):
using namespace System.Collections
class ResourceLogCategory {
[string] $ResourceTypeName
[string] $ResourceGroupName
[array] $LogCategory = @()
[array] $MetricCategory = @()
ResourceLogCategory([string]$ResourceGroupName, [string]$ResourceTypeName) {
$this.ResourceGroupName = $ResourceGroupName
$this.ResourceTypeName = $ResourceTypeName
$this.FetchDiagnosticSettings()
}
[void] FetchDiagnosticSettings() {
try {
$resource = Get-AzResource -ResourceGroupName $this.ResourceGroupName -ResourceType $this.ResourceTypeName | Select-Object -First 1
$diagnosticSettings = Get-AzDiagnosticSetting -ResourceId $resource.ResourceId
foreach ($setting in $diagnosticSettings) {
if ($setting.Logs) {
$this.LogCategory += $setting.Logs.Id
}
if ($setting.Metrics) {
$this.MetricCategory += $setting.Metrics.Id
}
}
} catch {
Write-Error "Failed to fetch diagnostic settings."
}
}
}
My pester test:
# Suppressing this rule because Script Analyzer does not understand Pester's syntax.
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
param ()
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = (Get-ChildItem $ProjectPath\*\*.psd1 | Where-Object {
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop }catch { $false }) }
).BaseName
# Import the project module
Import-Module $ProjectName
# Define tests within the scope of the project module
InModuleScope $ProjectName {
BeforeAll {
# Define properties for valid and invalid test objects
$ValidObj1Properties = @{
SourceType = "Az"
ContainerId = "11111111-1111-1111-1111-111111111111"
ResourceTypeName = "Provider.Resource1/Type"
}
$ValidObj2Properties = @{
SourceType = "Az"
ContainerId = "22222222-2222-2222-2222-222222222222"
ResourceTypeName = "Provider.Resource2/Type"
}
$InvalidObjProperties = @{
SourceType = "Az"
ContainerId = "ObjectId"
ResourceTypeName = "Provider.Resource3/Type"
}
$IncompleteObjProperties = @{
SourceType = "Az"
ContainerId = "00000000-0000-0000-0000-000000000000"
}
# Mock the Get-AzDiagnosticSettingCategory function to return predefined results for testing
Mock -CommandName 'Get-AzDiagnosticSettingCategory' -MockWith {
param($ResourceId)
if ($ResourceId -eq "11111111-1111-1111-1111-111111111111") {
return @(
@{ Name = "LogCategory1"; CategoryType = "Logs" },
@{ Name = "MetricCategory1"; CategoryType = "Metrics" }
)
}
else {
return @()
}
} -Verifiable
# Mock the Get-AzResource function to return predefined results for testing
Mock -CommandName 'Get-AzResource' -MockWith {
param($ResourceId)
if ($ResourceId -eq "11111111-1111-1111-1111-111111111111") {
return @{ ResourceType = "Provider.Resource1/Type" }
}
elseif ($ResourceId -eq "22222222-2222-2222-2222-222222222222") {
return @{ ResourceType = "Provider.Resource2/Type" }
}
else {
return @{ ResourceType = "Provider.Resource3/Type" }
}
} -Verifiable
}
# Describe the group of tests for the ResourceLogCategory class
Describe 'ResourceLogCategory Class Unit Tests' {
# Define setup actions to be performed before all tests
# Describe the group of tests for property initialization
Describe 'ResourceLogCategory Class Property Initialization' {
# Test that valid data input is validated correctly
It 'should validate correct data input' {
{ [ResourceLogCategory]::Validate(
$ValidObj1Properties.ContainerId,
$ValidObj1Properties.ResourceTypeName,
$ValidObj1Properties.SourceType) } | Should -Not -Throw
}
# Test that invalid data input is not validated
It 'should not validate incorrect data input' {
{ [ResourceLogCategory]::Validate(
$InvalidObjProperties.ContainerId,
$InvalidObjProperties.ResourceTypeName,
$InvalidObjProperties.SourceType) } | Should -Throw
}
# Test that empty data input is not validated
It 'should not validate empty data input' {
{ [ResourceLogCategory]::Validate('', '', '') } | Should -Throw
}
}
# Describe the group of tests for constructors
Describe 'ResourceLogCategory Class Constructors Tests' {
# Test that the object is created correctly with separate properties
It 'Should create the object with separate properties' {
{ [ResourceLogCategory]::new(
$ValidObj1Properties.ContainerId,
$ValidObj1Properties.ResourceTypeName,
$ValidObj1Properties.SourceType) } | Should -Not -Throw
}
# Test that the object is created correctly from a hashtable
It 'should create an object with constructor from hashtable' {
{ $obj = [ResourceLogCategory]::new($ValidObj1Properties) } | Should -Not -Throw
}
}
# Describe the group of tests for methods
Describe 'ResourceLogCategory Class Methods Tests' {
# Test that the method handles resource types with diagnostic settings available
It 'should handle resource type with diagnostic settings available' {
$obj = [ResourceLogCategory]::new($ValidObj1Properties)
{ $obj.GetDiagnosticSettings() } | Should -Not -Throw
}
# Test that the method handles resource types with no diagnostic settings
It 'should handle resource type with no diagnostic settings' {
$obj = [ResourceLogCategory]::new($ValidObj2Properties)
{ $obj.GetDiagnosticSettings() } | Should -Not -Throw
}
# Test that the method returns a string representation of the object
It 'should return a string representation of the object' {
$obj = [ResourceLogCategory]::new($ValidObj1Properties)
{ $obj.ToString() } | Should -Not -Throw
}
}
}
}
Errors Details:
[-] should create an object with constructor from hashtable 32ms (29ms|2ms)
[215](https://github.com/CyberShell-app/CyberShell/actions/runs/8466801601/job/23196375390#step:4:217)Message
[216](https://github.com/CyberShell-app/CyberShell/actions/runs/8466801601/job/23196375390#step:4:218) Expected no exception to be thrown, but an exception "Run Connect-AzAccount to login." was thrown from /home/runner/work/CyberShell/CyberShell/output/module/CyberShell/0.1.0/CyberShell.psm1:88 char:21
[217](https://github.com/CyberShell-app/CyberShell/actions/runs/8466801601/job/23196375390#step:4:219) + … $resource = Get-AzResource -ResourceType $this.ResourceTypeName | Sel …
[218](https://github.com/CyberShell-app/CyberShell/actions/runs/8466801601/job/23196375390#step:4:220) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.
[219](https://github.com/CyberShell-app/CyberShell/actions/runs/8466801601/job/23196375390#step:4:221) at { $obj = [ResourceLogCategory]::new($ValidObj1Properties) } | Should -Not -Throw, /home/runner/work/CyberShell/CyberShell/tests/Unit/Class/ResourceLogCategory.tests.ps1:108
Any ideas on how to solve this issue ?