add: New process for collecting MgGraph output to make pester testing easier

This commit is contained in:
DrIOS
2024-06-23 11:39:14 -05:00
parent 84c16ac16e
commit 39ba3c3ad7
12 changed files with 224 additions and 68 deletions

View File

@@ -7,10 +7,14 @@ The format is based on and uses the types of changes according to [Keep a Change
### Fixed ### Fixed
- Fixed test 1.3.1 to include notification window for password expiration. - Fixed test 1.3.1 to include notification window for password expiration.
- Fixed 6.1.1 test definition to include the correct connection.
### Added ### Added
- Added export to excel to `Export-M365SecurityAuditTable` function. - Added export to excel to `Export-M365SecurityAuditTable` function.
- `Get-AdminRoleUserLicense` function to get the license of a user with admin roles for 1.1.1.
- Skip MSOL connection confirmation to `Get-MFAStatus` function.
- Get-MgOutput function to get the output of the Microsoft Graph API per test and adjusted tests to utilize.
## [0.1.13] - 2024-06-18 ## [0.1.13] - 2024-06-18

View File

@@ -0,0 +1,38 @@
function Get-AdminRoleUserAndAssignment {
[CmdletBinding()]
param ()
$result = @{}
# Get the DisplayNames of all admin roles
$adminRoleNames = (Get-MgDirectoryRole | Where-Object { $null -ne $_.RoleTemplateId }).DisplayName
# Get Admin Roles
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { ($adminRoleNames -contains $_.DisplayName) -and ($_.DisplayName -ne "Directory Synchronization Accounts") }
foreach ($role in $adminRoles) {
Write-Verbose "Processing role: $($role.DisplayName)"
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($assignment in $roleAssignments) {
Write-Verbose "Processing role assignment for principal ID: $($assignment.PrincipalId)"
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
if ($userDetails) {
Write-Verbose "Retrieved user details for: $($userDetails.UserPrincipalName)"
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
if (-not $result[$role.DisplayName]) {
$result[$role.DisplayName] = @()
}
$result[$role.DisplayName] += [PSCustomObject]@{
AssignmentId = $assignment.Id
UserDetails = $userDetails
Licenses = $licenses
}
}
}
}
return $result
}

View File

@@ -0,0 +1,85 @@
function Get-MgOutput {
<#
.SYNOPSIS
This is a sample Private function only visible within the module.
.DESCRIPTION
This sample function is not exported to the module and only return the data passed as parameter.
.EXAMPLE
$null = Get-MgOutput -PrivateData 'NOTHING TO SEE HERE'
.PARAMETER PrivateData
The PrivateData parameter is what will be returned without transformation.
#>
[cmdletBinding()]
[OutputType([string])]
param(
[Parameter(Mandatory = $true)]
[String]
$Rec
)
begin {
# Begin Block #
}
process {
switch ($rec) {
'1.1.3' {
# Step: Retrieve global admin role
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
# Step: Retrieve global admin members
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
return $globalAdmins
}
'1.2.1' {
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
return $allGroups
}
'5.1.2.3' {
# Retrieve the tenant creation policy
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
return $tenantCreationPolicy
}
'5.1.8.1' {
# Retrieve password hash sync status (Condition A and C)
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
return $passwordHashSync
}
'6.1.2' {
$tenantSkus = Get-MgSubscribedSku -All
$e3SkuPartNumber = "SPE_E3"
$founde3Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e3SkuPartNumber }
if ($founde3Sku.Count -ne 0) {
$allE3Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde3Sku.SkuId) )" -All
return $allE3Users
}
else {
return $null
}
}
'6.1.3' {
$tenantSkus = Get-MgSubscribedSku -All
$e5SkuPartNumber = "SPE_E5"
$founde5Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber }
if ($founde5Sku.Count -ne 0) {
$allE5Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde5Sku.SkuId) )" -All
return $allE5Users
}
else {
return $null
}
}
Default {
# 1.1.1
$AdminRoleAssignmentsAndUsers = Get-AdminRoleUserAndAssignment
return $AdminRoleAssignmentsAndUsers
}
}
}
end {
Write-Verbose "Retuning data for Rec: $Rec"
}
} # end function Get-MgOutput

View File

@@ -1,76 +1,59 @@
function Test-AdministrativeAccountCompliance { function Test-AdministrativeAccountCompliance {
[CmdletBinding()] [CmdletBinding()]
param ( param ()
# Aligned
# Parameters can be added if needed
)
begin { begin {
# The following conditions are checked: # The following conditions are checked:
# Condition A: The administrative account is cloud-only (not synced). # Condition A: The administrative account is cloud-only (not synced).
# Condition B: The account is assigned a valid license (e.g., Microsoft Entra ID P1 or P2). # Condition B: The account is assigned a valid license (e.g., Microsoft Entra ID P1 or P2).
# Condition C: The administrative account does not have any other application assignments (only valid licenses). # Condition C: The administrative account does not have any other application assignments (only valid licenses).
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2') $validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
$recnum = "1.1.1" $recnum = "1.1.1"
Write-Verbose "Starting Test-AdministrativeAccountCompliance with Rec: $recnum" Write-Verbose "Starting Test-AdministrativeAccountCompliance with Rec: $recnum"
} }
process { process {
try {
# Retrieve all admin roles
Write-Verbose "Retrieving all admin roles"
# Get the DisplayNames of all admin roles
$adminRoleNames = (Get-MgDirectoryRole | Where-Object { $null -ne $_.RoleTemplateId }).DisplayName
# Use the DisplayNames to filter the roles in Get-MgRoleManagementDirectoryRoleDefinition try {
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { ($adminRoleNames -contains $_.DisplayName) -and ($_.DisplayName -ne "Directory Synchronization Accounts")} # Retrieve admin roles, assignments, and user details including licenses
Write-Verbose "Retrieving admin roles, assignments, and user details including licenses"
$adminRoleAssignments = Get-MgOutput -Rec $recnum
$adminRoleUsers = @() $adminRoleUsers = @()
# Loop through each admin role to get role assignments and user details foreach ($roleName in $adminRoleAssignments.Keys) {
foreach ($role in $adminRoles) { $assignments = $adminRoleAssignments[$roleName]
Write-Verbose "Processing role: $($role.DisplayName)" foreach ($assignment in $assignments) {
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'" $userDetails = $assignment.UserDetails
$userId = $userDetails.Id
$userPrincipalName = $userDetails.UserPrincipalName
$licenses = $assignment.Licenses
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
foreach ($assignment in $roleAssignments) { # Condition A: Check if the account is cloud-only
Write-Verbose "Processing role assignment for principal ID: $($assignment.PrincipalId)" $cloudOnlyStatus = if ($userDetails.OnPremisesSyncEnabled) { "Fail" } else { "Pass" }
# Get user details for each principal ID
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
if ($userDetails) {
Write-Verbose "Retrieved user details for: $($userDetails.UserPrincipalName)"
# Get user license details
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
# Condition A: Check if the account is cloud-only # Condition B: Check if the account has valid licenses
$cloudOnlyStatus = if ($userDetails.OnPremisesSyncEnabled) { "Fail" } else { "Pass" } $hasValidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -contains $_ }
$validLicensesStatus = if ($hasValidLicense) { "Pass" } else { "Fail" }
# Condition B: Check if the account has valid licenses # Condition C: Check if the account has no other licenses
$hasValidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -contains $_ } $hasInvalidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -notcontains $_ }
$validLicensesStatus = if ($hasValidLicense) { "Pass" } else { "Fail" } $invalidLicenses = $licenses.SkuPartNumber | Where-Object { $validLicenses -notcontains $_ }
$applicationAssignmentStatus = if ($hasInvalidLicense) { "Fail" } else { "Pass" }
# Condition C: Check if the account has no other licenses Write-Verbose "User: $userPrincipalName, Cloud-Only: $cloudOnlyStatus, Valid Licenses: $validLicensesStatus, Invalid Licenses: $($invalidLicenses -join ', ')"
$hasInvalidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -notcontains $_ }
$invalidLicenses = $licenses.SkuPartNumber | Where-Object { $validLicenses -notcontains $_ }
$applicationAssignmentStatus = if ($hasInvalidLicense) { "Fail" } else { "Pass" }
Write-Verbose "User: $($userDetails.UserPrincipalName), Cloud-Only: $cloudOnlyStatus, Valid Licenses: $validLicensesStatus, Invalid Licenses: $($invalidLicenses -join ', ')" # Collect user information
$adminRoleUsers += [PSCustomObject]@{
# Collect user information UserName = $userPrincipalName
$adminRoleUsers += [PSCustomObject]@{ RoleName = $roleName
UserName = $userDetails.UserPrincipalName UserId = $userId
RoleName = $role.DisplayName HybridUser = $userDetails.OnPremisesSyncEnabled
UserId = $userDetails.Id Licenses = $licenseString
HybridUser = $userDetails.OnPremisesSyncEnabled CloudOnlyStatus = $cloudOnlyStatus
Licenses = $licenseString ValidLicensesStatus = $validLicensesStatus
CloudOnlyStatus = $cloudOnlyStatus ApplicationAssignmentStatus = $applicationAssignmentStatus
ValidLicensesStatus = $validLicensesStatus
ApplicationAssignmentStatus = $applicationAssignmentStatus
}
}
else {
Write-Verbose "No user details found for principal ID: $($assignment.PrincipalId)"
} }
} }
} }

View File

@@ -30,11 +30,7 @@ function Test-GlobalAdminsCount {
process { process {
try { try {
# Step: Retrieve global admin role $globalAdmins = Get-MgOutput -Rec $recnum
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
# Step: Retrieve global admin members
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
# Step: Count the number of global admins # Step: Count the number of global admins
$globalAdminCount = $globalAdmins.Count $globalAdminCount = $globalAdmins.Count

View File

@@ -29,7 +29,6 @@ function Test-MailboxAuditingE3 {
# Dot source the class script if necessary # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1 #. .\source\Classes\CISAuditResult.ps1
$e3SkuPartNumber = "SPE_E3"
$actionDictionaries = Get-Action -Dictionaries $actionDictionaries = Get-Action -Dictionaries
# E3 specific actions # E3 specific actions
@@ -38,14 +37,14 @@ function Test-MailboxAuditingE3 {
$OwnerActions = $actionDictionaries.OwnerActions.Keys | Where-Object { $_ -notin @("MailItemsAccessed", "Send") } $OwnerActions = $actionDictionaries.OwnerActions.Keys | Where-Object { $_ -notin @("MailItemsAccessed", "Send") }
$allFailures = @() $allFailures = @()
$founde3Sku = Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq $e3SkuPartNumber }
$processedUsers = @{} # Dictionary to track processed users
$recnum = "6.1.2" $recnum = "6.1.2"
$allUsers = Get-MgOutput -Rec $recnum
$processedUsers = @{} # Dictionary to track processed users
} }
process { process {
if ($founde3Sku.Count -ne 0) { if ($null -ne $allUsers) {
$allUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde3Sku.SkuId) )" -All
$mailboxes = Get-EXOMailbox -PropertySets Audit $mailboxes = Get-EXOMailbox -PropertySets Audit
try { try {
foreach ($user in $allUsers) { foreach ($user in $allUsers) {

View File

@@ -27,9 +27,6 @@ function Test-MailboxAuditingE5 {
# - Condition C: AuditDelegate actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, UpdateFolderPermissions, UpdateInboxRules. # - Condition C: AuditDelegate actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, UpdateFolderPermissions, UpdateInboxRules.
# - Condition D: AuditOwner actions do not include all of the following: ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules. # - Condition D: AuditOwner actions do not include all of the following: ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
$e5SkuPartNumber = "SPE_E5"
$founde5Sku = Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber }
$actionDictionaries = Get-Action -Dictionaries $actionDictionaries = Get-Action -Dictionaries
$AdminActions = $actionDictionaries.AdminActions.Keys $AdminActions = $actionDictionaries.AdminActions.Keys
$DelegateActions = $actionDictionaries.DelegateActions.Keys $DelegateActions = $actionDictionaries.DelegateActions.Keys
@@ -38,11 +35,11 @@ function Test-MailboxAuditingE5 {
$allFailures = @() $allFailures = @()
$processedUsers = @{} $processedUsers = @{}
$recnum = "6.1.3" $recnum = "6.1.3"
$allUsers = Get-MgOutput -Rec $recnum
} }
process { process {
if (($founde5Sku.count) -ne 0) { if ($null -ne $allUsers) {
$allUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde5Sku.SkuId) )" -All
$mailboxes = Get-EXOMailbox -PropertySets Audit $mailboxes = Get-EXOMailbox -PropertySets Audit
try { try {
foreach ($user in $allUsers) { foreach ($user in $allUsers) {

View File

@@ -30,7 +30,7 @@ function Test-ManagedApprovedPublicGroups {
process { process {
try { try {
# Step: Retrieve all groups with visibility set to 'Public' # Step: Retrieve all groups with visibility set to 'Public'
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility $allGroups = Get-MgOutput -Rec $recnum
# Step: Determine failure reasons based on the presence of public groups # Step: Determine failure reasons based on the presence of public groups
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) { $failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {

View File

@@ -34,7 +34,7 @@ function Test-PasswordHashSync {
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments # 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
# Retrieve password hash sync status (Condition A and C) # Retrieve password hash sync status (Condition A and C)
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled $passwordHashSync = Get-MgOutput -Rec $recnum
$hashSyncResult = $passwordHashSync $hashSyncResult = $passwordHashSync
# Prepare failure reasons and details based on compliance # Prepare failure reasons and details based on compliance

View File

@@ -35,7 +35,7 @@ function Test-RestrictTenantCreation {
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes' # 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
# Retrieve the tenant creation policy # Retrieve the tenant creation policy
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants $tenantCreationPolicy = Get-MgOutput -Rec $recnum
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants $tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
# Prepare failure reasons and details based on compliance # Prepare failure reasons and details based on compliance

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}