Skip to content

Commit 27fbcc6

Browse files
authored
Forward merge into dev. Preparing new release (#1473)
Prepare new release.
2 parents 26b8c37 + bcd41bb commit 27fbcc6

126 files changed

Lines changed: 14393 additions & 1556 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Connect-ZtAssessment
1717
Invoke-ZtAssessment
1818
```
1919

20+
By default, `Invoke-ZtAssessment` now runs all current pillars: Identity, Devices, Network, Data, Infrastructure, SecOps, and AI. The `-Preview` switch is reserved for future preview-only features.
21+
2022
## Infrastructure Pillar Scope
2123

2224
Infrastructure pillar results are based on Microsoft Defender for Cloud recommendations and only include Azure subscriptions tagged with `ZeroTrustAssessment:Infrastructure`.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
Describe "Add-ZtAgentOverview" {
2+
BeforeAll {
3+
$here = $PSScriptRoot
4+
$srcRoot = Join-Path $here "../../src/powershell"
5+
6+
if (-not (Get-Command Write-PSFMessage -ErrorAction SilentlyContinue)) {
7+
function global:Write-PSFMessage {
8+
param($Level, $Message, $Tag)
9+
}
10+
}
11+
12+
if (-not (Get-Command Write-ZtProgress -ErrorAction SilentlyContinue)) {
13+
function global:Write-ZtProgress {
14+
param($Activity, $Status)
15+
}
16+
}
17+
18+
if (-not (Get-Command Invoke-ZtGraphRequest -ErrorAction SilentlyContinue)) {
19+
function global:Invoke-ZtGraphRequest {
20+
param(
21+
$RelativeUri,
22+
$ApiVersion,
23+
$Select,
24+
$Filter,
25+
$Top,
26+
$QueryParameters,
27+
$ConsistencyLevel,
28+
$Headers,
29+
[switch] $DisablePaging,
30+
[switch] $DisableCache
31+
)
32+
}
33+
}
34+
35+
if (-not (Get-Command Add-ZtTenantInfo -ErrorAction SilentlyContinue)) {
36+
function global:Add-ZtTenantInfo {
37+
param($Name, $Value)
38+
}
39+
}
40+
41+
. (Join-Path $srcRoot "private/tenantinfo/ai/Add-ZtAgentOverview.ps1")
42+
}
43+
44+
BeforeEach {
45+
$script:tenantInfo = $null
46+
$script:activeUserFilter = $null
47+
48+
Mock Write-PSFMessage {}
49+
Mock Write-ZtProgress {}
50+
Mock Add-ZtTenantInfo {
51+
param($Name, $Value)
52+
$script:tenantInfo = [pscustomobject]@{
53+
Name = $Name
54+
Value = $Value
55+
}
56+
}
57+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -eq 'servicePrincipals' } -MockWith {
58+
@{ '@odata.count' = 42; value = @(@{ id = 'agent-1' }) }
59+
}
60+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -like 'auditLogs/getSummarizedNonInteractiveSignIns*' } -MockWith {
61+
$script:activeUserFilter = $Filter
62+
@(
63+
[pscustomobject]@{ userPrincipalName = 'alice@contoso.com' }
64+
[pscustomobject]@{ userPrincipalName = 'Alice@Contoso.com' }
65+
[pscustomobject]@{ userPrincipalName = 'bob@contoso.com' }
66+
[pscustomobject]@{ userPrincipalName = $null }
67+
[pscustomobject]@{ userPrincipalName = ' ' }
68+
)
69+
}
70+
}
71+
72+
It "Should collect total agents and unique active users using the expected Graph requests" {
73+
Add-ZtAgentOverview
74+
75+
$script:tenantInfo.Name | Should -Be 'AgentOverview'
76+
$script:tenantInfo.Value.TotalAgents | Should -Be 42
77+
$script:tenantInfo.Value.TotalAgents | Should -BeOfType [int]
78+
$script:tenantInfo.Value.ActiveUsers | Should -Be 2
79+
$script:tenantInfo.Value.ActiveUsers | Should -BeOfType [int]
80+
$startMatch = [regex]::Match($script:activeUserFilter, 'firstSignInDateTime ge (?<Start>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)')
81+
$endMatch = [regex]::Match($script:activeUserFilter, 'firstSignInDateTime lt (?<End>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)')
82+
$startMatch.Success | Should -BeTrue
83+
$endMatch.Success | Should -BeTrue
84+
$lookbackStart = [datetimeoffset]::ParseExact($startMatch.Groups['Start'].Value, 'yyyy-MM-ddTHH:mm:ssZ', [Globalization.CultureInfo]::InvariantCulture)
85+
$lookbackEnd = [datetimeoffset]::ParseExact($endMatch.Groups['End'].Value, 'yyyy-MM-ddTHH:mm:ssZ', [Globalization.CultureInfo]::InvariantCulture)
86+
($lookbackEnd - $lookbackStart).TotalDays | Should -Be 30
87+
88+
Should -Invoke Invoke-ZtGraphRequest -Times 1 -Exactly -ParameterFilter {
89+
$RelativeUri -eq 'servicePrincipals' -and
90+
$ApiVersion -eq 'v1.0' -and
91+
$Select -eq 'id' -and
92+
$Filter -eq "(isof('microsoft.graph.agentIdentity') OR (tags/any(p:startswith(p, 'power-virtual-agents-')) OR tags/any(p:p eq 'AgenticInstance')))" -and
93+
$Top -eq 1 -and
94+
$QueryParameters['$count'] -eq 'true' -and
95+
$ConsistencyLevel -eq 'eventual' -and
96+
$DisablePaging -and
97+
$DisableCache
98+
}
99+
Should -Invoke Invoke-ZtGraphRequest -Times 1 -Exactly -ParameterFilter {
100+
$RelativeUri -eq "auditLogs/getSummarizedNonInteractiveSignIns(aggregationWindow='d1')" -and
101+
$ApiVersion -eq 'beta' -and
102+
$Select -eq 'userPrincipalName' -and
103+
$Filter -match "agent/agentType eq 'agenticAppInstance'" -and
104+
$Filter -match "agent/agentSubjectType ne 'agentIDuser'" -and
105+
$Filter -match 'firstSignInDateTime ge \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z' -and
106+
$Filter -match 'firstSignInDateTime lt \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z' -and
107+
$Top -eq 1000 -and
108+
$QueryParameters['$orderby'] -eq 'firstSignInDateTime desc' -and
109+
$Headers.Prefer -eq 'include-unknown-enum-members' -and
110+
$DisableCache
111+
}
112+
Should -Invoke Add-ZtTenantInfo -Times 1 -Exactly -ParameterFilter { $Name -eq 'AgentOverview' }
113+
Should -Invoke Write-PSFMessage -Times 0 -Exactly
114+
Should -Invoke Write-ZtProgress -Times 1 -Exactly -ParameterFilter { $Status -eq 'Processing' }
115+
Should -Invoke Write-ZtProgress -Times 1 -Exactly -ParameterFilter { $Status -eq 'Completed' }
116+
}
117+
118+
It "Should emit zero values when Graph returns no agents or sign-ins" {
119+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -eq 'servicePrincipals' } -MockWith {
120+
@{ '@odata.count' = 0; value = @() }
121+
}
122+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -like 'auditLogs/getSummarizedNonInteractiveSignIns*' } -MockWith { @() }
123+
124+
Add-ZtAgentOverview
125+
126+
$script:tenantInfo.Value.TotalAgents | Should -Be 0
127+
$script:tenantInfo.Value.ActiveUsers | Should -Be 0
128+
Should -Invoke Write-PSFMessage -Times 0 -Exactly
129+
}
130+
131+
It "Should preserve active users when the total-agent query fails" {
132+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -eq 'servicePrincipals' } -MockWith {
133+
throw 'Agent count request failed'
134+
}
135+
136+
Add-ZtAgentOverview
137+
138+
$script:tenantInfo.Value.TotalAgents | Should -BeNullOrEmpty
139+
$script:tenantInfo.Value.ActiveUsers | Should -Be 2
140+
Should -Invoke Invoke-ZtGraphRequest -Times 1 -Exactly -ParameterFilter { $RelativeUri -like 'auditLogs/getSummarizedNonInteractiveSignIns*' }
141+
Should -Invoke Write-PSFMessage -Times 1 -Exactly -ParameterFilter {
142+
$Level -eq 'Warning' -and $Message -eq 'Unable to retrieve the total agent count from Microsoft Graph.'
143+
}
144+
}
145+
146+
It "Should preserve total agents when the active-user query fails" {
147+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -like 'auditLogs/getSummarizedNonInteractiveSignIns*' } -MockWith {
148+
throw 'Active user request failed'
149+
}
150+
151+
Add-ZtAgentOverview
152+
153+
$script:tenantInfo.Value.TotalAgents | Should -Be 42
154+
$script:tenantInfo.Value.ActiveUsers | Should -BeNullOrEmpty
155+
Should -Invoke Write-PSFMessage -Times 1 -Exactly -ParameterFilter {
156+
$Level -eq 'Warning' -and $Message -eq 'Unable to retrieve active agent users from Microsoft Graph.'
157+
}
158+
Should -Invoke Add-ZtTenantInfo -Times 1 -Exactly -ParameterFilter { $Name -eq 'AgentOverview' }
159+
}
160+
161+
It "Should treat a missing agent count as a failed total-agent query" {
162+
Mock Invoke-ZtGraphRequest -ParameterFilter { $RelativeUri -eq 'servicePrincipals' } -MockWith {
163+
@{ value = @() }
164+
}
165+
166+
Add-ZtAgentOverview
167+
168+
$script:tenantInfo.Value.TotalAgents | Should -BeNullOrEmpty
169+
$script:tenantInfo.Value.ActiveUsers | Should -Be 2
170+
Should -Invoke Write-PSFMessage -Times 1 -Exactly -ParameterFilter {
171+
$Level -eq 'Warning' -and $Message -eq 'Unable to retrieve the total agent count from Microsoft Graph.'
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)