20 Commits

Author SHA1 Message Date
Doug Rios
d06875700d Merge pull request #4 from CriticalSolutionsNetwork/Add-manual-checks-as-automated-checks
Enhancements to CSV Testing and Excel Benchmark Integration
2024-04-29 11:17:10 -05:00
DrIOS
822e2f51a3 docs: Update help documentation 2024-04-29 11:03:41 -05:00
DrIOS
cee453a8eb docs: Update Changelog and automation candidates 2024-04-29 11:01:42 -05:00
DrIOS
a8c7da2b7d fix: add project uri and icon to manifest. 2024-04-16 13:33:19 -05:00
DrIOS
02529c9cba docs: Update help 2024-04-16 09:50:29 -05:00
DrIOS
bbb1dd3586 fix: Properties for skip and include tests 2024-04-16 09:04:14 -05:00
DrIOS
8e2fab701c docs: Update comments for new functions 2024-04-15 17:03:39 -05:00
DrIOS
8f44424962 fix: Error handling in 1.1.1 test 2024-04-15 16:27:01 -05:00
DrIOS
fe503509ea fix: Test-AdministrativeAccountCompliance filename 2024-04-15 16:02:13 -05:00
DrIOS
ad2c85d034 fix: format TestDefinitions.csv 2024-04-15 15:58:12 -05:00
DrIOS
b381421f45 add: Error handling for Get-AdminRoleUserLicense 2024-04-15 15:54:52 -05:00
DrIOS
a4dce52825 add: future changes to be committed 2024-04-15 15:51:00 -05:00
DrIOS
d037f82f60 add: test for 1.1.4 guest users 2024-04-08 13:59:35 -05:00
DrIOS
ba0a3819b9 add: Create public function for merge 2024-04-05 18:35:21 -05:00
DrIOS
1e510f311b add: Functions to merge tests into excel benchmark 2024-04-05 18:13:16 -05:00
DrIOS
d033d7ae1b add: 1.1.1 test as automated and organized csv. 2024-04-05 16:13:08 -05:00
Doug Rios
90b34efa1b Merge pull request #3 from CriticalSolutionsNetwork/Fix-Test-ModernAuthExchange
Fix: Test-ModernAuthExchangeOnline and add notes.
2024-04-02 10:34:09 -05:00
DrIOS
a2020ff5ce Fix: Test-ModernAuthExchangeOnline and add notes. 2024-04-02 10:32:19 -05:00
Doug Rios
690b1c4667 Update README.md 2024-04-02 09:10:21 -05:00
Doug Rios
21bf1cbab8 Update README.md
Add link to benchmarks.
2024-04-02 09:09:49 -05:00
25 changed files with 700 additions and 152 deletions

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"cSpell.words": [
"Msol"
]
}

View File

@@ -6,6 +6,40 @@ The format is based on and uses the types of changes according to [Keep a Change
### Added
- Automated and organized CSV testing and added test 1.1.1.
- Functions to merge tests into an Excel benchmark.
- Public function for merging tests.
- Testing for guest users under test 1.1.4.
- Error handling for `Get-AdminRoleUserLicense`.
- Project URI and icon added to manifest.
### Fixed
- Format for `TestDefinitions.csv`.
- Filename for `Test-AdministrativeAccountCompliance`.
- Error handling in test 1.1.1.
- Properties for skipping and including tests.
### Docs
- Updated comments for new functions.
- Updated help documentation.
## [0.1.1] - 2024-04-02
### Fixed
- Fixed Test-ModernAuthExchangeOnline Profile Level in object.
### Added
- CIS Download Notes to Comment-Help Block.
- Notes to README.md for CIS Download.
## [0.1.0-preview0001] - 2024-03-25
### Added
- Initial release of the M365FoundationsCISReport PowerShell module v0.0.1.
- Function `Invoke-M365SecurityAudit` for conducting a comprehensive security audit in Microsoft 365 environments.
- Support for multiple parameter sets including ELevelFilter, IGFilters, RecFilter, and SkipRecFilter to cater to diverse audit requirements.

93
README copy.md Normal file
View File

@@ -0,0 +1,93 @@
# M365FoundationsCISReport Module
## License
This PowerShell module is based on CIS benchmarks and is distributed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. This means:
- **Non-commercial**: You may not use the material for commercial purposes.
- **ShareAlike**: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
- **Attribution**: Appropriate credit must be given, provide a link to the license, and indicate if changes were made.
For full license details, please visit [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en).
[Register for and download CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks)
## Invoke-M365SecurityAudit
### Synopsis
Invokes a security audit for Microsoft 365 environments.
### Syntax
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-ELevel <String>] [-ProfileLevel <String>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG1] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG2] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG3] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-SkipRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### Parameters
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
| - | - | - | - | - | - |
| <nobr>TenantAdminUrl</nobr> | | The URL of the tenant admin. This parameter is mandatory. | true | false | |
| <nobr>DomainName</nobr> | | The domain name of the Microsoft 365 environment. This parameter is mandatory. | true | false | |
| <nobr>ELevel</nobr> | | Specifies the E-Level \(E3 or E5\) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter. | false | false | |
| <nobr>ProfileLevel</nobr> | | Specifies the profile level \(L1 or L2\) for the audit. This parameter is optional and can be combined with the ELevel parameter. | false | false | |
| <nobr>IncludeIG1</nobr> | | If specified, includes tests where IG1 is true. | false | false | False |
| <nobr>IncludeIG2</nobr> | | If specified, includes tests where IG2 is true. | false | false | False |
| <nobr>IncludeIG3</nobr> | | If specified, includes tests where IG3 is true. | false | false | False |
| <nobr>IncludeRecommendation</nobr> | | Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers. | false | false | |
| <nobr>SkipRecommendation</nobr> | | Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers. | false | false | |
| <nobr>DoNotConnect</nobr> | | If specified, the cmdlet will not establish a connection to Microsoft 365 services. | false | false | False |
| <nobr>DoNotDisconnect</nobr> | | If specified, the cmdlet will not disconnect from Microsoft 365 services after execution. | false | false | False |
| <nobr>NoModuleCheck</nobr> | | If specified, the cmdlet will not check for the presence of required modules. | false | false | False |
| <nobr>WhatIf</nobr> | wi | | false | false | |
| <nobr>Confirm</nobr> | cf | | false | false | |
### Inputs
- None. You cannot pipe objects to Invoke-M365SecurityAudit.
### Outputs
- CISAuditResult\\[\] The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
### Note
This module is based on CIS benchmarks and is governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. For more details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
### Examples
**EXAMPLE 1**
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
```
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
**EXAMPLE 2**
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
```
Performs an audit including all tests where IG1 is true.
**EXAMPLE 3**
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
```
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
**EXAMPLE 4**
```powershell
$auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
```
Captures the audit results into a variable and exports them to a CSV file.
### Links
- [Online Version: [GitHub Repository URL]](#Online Version: [GitHub Repository URL])

BIN
README.md

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,52 @@
# Automation Candidates
## 5.1.1.1 (L1) Ensure Security Defaults is disabled on Azure Active Directory
- `Connect-MgGraph -Scopes "Policy.Read.All"`
- `Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy | ft IsEnabled`
## 5.1.2.1 (L1) Ensure 'Per-user MFA' is disabled
- `Connect-MsolService`
- Commands:
```powershell
$UserList = Get-MsolUser -All | Where-Object { $_.UserType -eq 'Member' }
$Report = @()
foreach ($user in $UserList) {
$PerUserMFAState = $null
if ($user.StrongAuthenticationRequirements) {
$PerUserMFAState = $user.StrongAuthenticationRequirements.State
}
else {
$PerUserMFAState = 'Disabled'
}
$obj = [pscustomobject][ordered]@{
UserPrincipalName = $User.UserPrincipalName
DisplayName = $User.DisplayName
PerUserMFAState = $PerUserMFAState
}
$Report += $obj
}
$Report
```
## 5.1.3.1 (L1) Ensure a dynamic group for guest users is created
- `Connect-MgGraph -Scopes "Group.Read.All"`
- Commands:
```powershell
$groups = Get-MgGroup | Where-Object { $_.GroupTypes -contains "DynamicMembership" }
$groups | ft DisplayName,GroupTypes,MembershipRule
```
## 6.1.4 (L1) Ensure 'AuditBypassEnabled' is not enabled on mailboxes
- `Connect-ExchangeOnline`
- Commands:
```powershell
$MBX = Get-MailboxAuditBypassAssociation -ResultSize unlimited
$MBX | where {$_.AuditBypassEnabled -eq $true} | Format-Table Name,AuditBypassEnabled
```

View File

@@ -1,3 +1,17 @@
Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir docs -template ".\helpers\psDoc-master\src\out-html-template.ps1"
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir ".\" -template ".\helpers\psDoc-master\src\out-markdown-template.ps1" -fileName ".\README.md"
<#
$ver = "v0.1.1"
git checkout main
git pull origin main
git tag -a $ver -m "Release version $ver Bugfix Update"
git push origin $ver
"Fix: PR #37"
git push origin $ver
# git tag -d $ver
#>

View File

@@ -0,0 +1,45 @@
function Get-AdminRoleUserLicense {
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[bool]$SkipGraphConnection = $false
)
# Connect to Microsoft Graph if not skipping connection
if (-not $SkipGraphConnection) {
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
}
$adminRoleUsers = @()
$userIds = @()
$adminroles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
foreach ($role in $adminroles) {
$usersInRole = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($user in $usersInRole) {
$userIds += $user.PrincipalId
$userDetails = Get-MgUser -UserId $user.PrincipalId -Property "DisplayName, UserPrincipalName, Id, onPremisesSyncEnabled"
$adminRoleUsers += [PSCustomObject]@{
RoleName = $role.DisplayName
UserName = $userDetails.DisplayName
UserPrincipalName = $userDetails.UserPrincipalName
UserId = $userDetails.Id
HybridUser = $userDetails.onPremisesSyncEnabled
Licenses = "" # Placeholder for licenses, to be filled later
}
}
}
foreach ($userId in $userIds | Select-Object -Unique) {
$licenses = Get-MgUserLicenseDetail -UserId $userId
$licenseList = ($licenses.SkuPartNumber -join '|')
$adminRoleUsers | Where-Object { $_.UserId -eq $userId } | ForEach-Object {
$_.Licenses = $licenseList
}
}
return $adminRoleUsers
}

View File

@@ -102,10 +102,10 @@ PrivateData = @{
LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en'
# A URL to the main website for this project.
# ProjectUri = ''
ProjectUri = 'https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport'
# A URL to an icon representing this module.
# IconUri = ''
IconUri = 'https://csn-source.s3.us-east-2.amazonaws.com/CSN-Icon.png'
# ReleaseNotes of this module
ReleaseNotes = ''

View File

@@ -0,0 +1,47 @@
function Merge-CISExcelAndCsvData {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true)]
[string]$CsvPath
)
process {
# Import data from Excel and CSV
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
$csvData = Import-Csv -Path $CsvPath
# Define a function to create a merged object
function CreateMergedObject($excelItem, $csvRow) {
$newObject = New-Object PSObject
foreach ($property in $excelItem.PSObject.Properties) {
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
}
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $csvRow.Status
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $csvRow.Details
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $csvRow.FailureReason
return $newObject
}
# Iterate over each item in the imported Excel object and merge with CSV data
$mergedData = foreach ($item in $import) {
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
if ($csvRow) {
CreateMergedObject -excelItem $item -csvRow $csvRow
} else {
CreateMergedObject -excelItem $item -csvRow ([PSCustomObject]@{Status=$null; Details=$null; FailureReason=$null})
}
}
# Return the merged data
return $mergedData
}
}

View File

@@ -0,0 +1,33 @@
function Update-CISExcelWorksheet {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true)]
[psobject[]]$Data,
[Parameter(Mandatory = $false)]
[int]$StartingRowIndex = 2 # Default starting row index, assuming row 1 has headers
)
process {
# Load the existing Excel sheet
$excelPackage = Open-ExcelPackage -Path $ExcelPath
$worksheet = $excelPackage.Workbook.Worksheets[$WorksheetName]
if (-not $worksheet) {
throw "Worksheet '$WorksheetName' not found in '$ExcelPath'"
}
# Update the worksheet with the provided data
Update-WorksheetCells -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
# Save and close the Excel package
Close-ExcelPackage $excelPackage
}
}

View File

@@ -0,0 +1,28 @@
function Update-WorksheetCells {
param (
$Worksheet,
$Data,
$StartingRowIndex
)
# Check and set headers
$firstItem = $Data[0]
$colIndex = 1
foreach ($property in $firstItem.PSObject.Properties) {
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
$Worksheet.Cells[1, $colIndex].Value = $property.Name
}
$colIndex++
}
# Iterate over each row in the data and update cells
$rowIndex = $StartingRowIndex
foreach ($item in $Data) {
$colIndex = 1
foreach ($property in $item.PSObject.Properties) {
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
$colIndex++
}
$rowIndex++
}
}

View File

@@ -0,0 +1,82 @@
<#
.SYNOPSIS
Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API.
.DESCRIPTION
The Get-AdminRoleUserLicense function connects to Microsoft Graph and retrieves all users who are assigned administrative roles along with their user details and licenses. This function is useful for auditing and compliance checks to ensure that administrators have appropriate licenses and role assignments.
.PARAMETER SkipGraphConnection
A switch parameter that, when set, skips the connection to Microsoft Graph if already established. This is useful for batch processing or when used within scripts where multiple calls are made and the connection is managed externally.
.EXAMPLE
PS> Get-AdminRoleUserLicense
This example retrieves all administrative role users along with their licenses by connecting to Microsoft Graph using the default scopes.
.EXAMPLE
PS> Get-AdminRoleUserLicense -SkipGraphConnection
This example retrieves all administrative role users along with their licenses without attempting to connect to Microsoft Graph, assuming that the connection is already established.
.INPUTS
None. You cannot pipe objects to Get-AdminRoleUserLicense.
.OUTPUTS
PSCustomObject
Returns a custom object for each user with administrative roles that includes the following properties: RoleName, UserName, UserPrincipalName, UserId, HybridUser, and Licenses.
.NOTES
Creation Date: 2024-04-15
Purpose/Change: Initial function development to support Microsoft 365 administrative role auditing.
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
#>
function Get-AdminRoleUserLicense {
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[switch]$SkipGraphConnection
)
begin {
if (-not $SkipGraphConnection) {
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
}
$adminRoleUsers = @()
$userIds = @()
}
Process {
$adminroles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
foreach ($role in $adminroles) {
$usersInRole = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($user in $usersInRole) {
$userDetails = Get-MgUser -UserId $user.PrincipalId -Property "DisplayName, UserPrincipalName, Id, onPremisesSyncEnabled" -ErrorAction SilentlyContinue
if ($userDetails) {
$userIds += $user.PrincipalId
$adminRoleUsers += [PSCustomObject]@{
RoleName = $role.DisplayName
UserName = $userDetails.DisplayName
UserPrincipalName = $userDetails.UserPrincipalName
UserId = $userDetails.Id
HybridUser = $userDetails.onPremisesSyncEnabled
Licenses = $null # Initialize as $null
}
}
}
}
foreach ($userId in $userIds | Select-Object -Unique) {
$licenses = Get-MgUserLicenseDetail -UserId $userId -ErrorAction SilentlyContinue
if ($licenses) {
$licenseList = ($licenses.SkuPartNumber -join '|')
$adminRoleUsers | Where-Object { $_.UserId -eq $userId } | ForEach-Object {
$_.Licenses = $licenseList
}
}
}
}
End {
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
Disconnect-MgGraph | Out-Null
return $adminRoleUsers
}
}

View File

@@ -55,8 +55,9 @@
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
- Modifications and sharing are allowed under the same license.
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
.LINK
Online Version: [GitHub Repository URL]
Online Version: https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport
#>
function Invoke-M365SecurityAudit {
@@ -91,7 +92,7 @@ function Invoke-M365SecurityAudit {
# Inclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'RecFilter')]
[ValidateSet(
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'1.1.1','1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
@@ -104,7 +105,7 @@ function Invoke-M365SecurityAudit {
# Exclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'SkipRecFilter')]
[ValidateSet(
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'1.1.1','1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
@@ -247,12 +248,12 @@ function Invoke-M365SecurityAudit {
}
End {
# Return all collected audit results
return $allAuditResults
# Check if the Disconnect switch is present
if (!($DoNotDisconnect)) {
# Clean up sessions
Disconnect-M365Suite
}
# Return all collected audit results
return $allAuditResults
# Check if the Disconnect switch is present
}
}

View File

@@ -0,0 +1,61 @@
<#
.SYNOPSIS
Synchronizes data between an Excel file and a CSV file and optionally updates the Excel worksheet.
.DESCRIPTION
The Sync-CISExcelAndCsvData function merges data from a specified Excel file and a CSV file based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file while preserving the original formatting and structure of the Excel worksheet.
.PARAMETER ExcelPath
The path to the Excel file that contains the original data. This parameter is mandatory.
.PARAMETER WorksheetName
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
.PARAMETER CsvPath
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory.
.PARAMETER SkipUpdate
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
.EXAMPLE
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv"
Merges data from 'data.csv' into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
.EXAMPLE
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
Retrieves the merged data object for preview without updating the Excel worksheet.
.INPUTS
None. You cannot pipe objects to Sync-CISExcelAndCsvData.
.OUTPUTS
Object[]
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
.NOTES
- Ensure that the 'ImportExcel' module is installed and up to date.
- It is recommended to backup the Excel file before running this script to prevent accidental data loss.
- This function is part of the CIS Excel and CSV Data Management Toolkit.
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
#>
function Sync-CISExcelAndCsvData {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true)]
[string]$CsvPath,
[Parameter(Mandatory = $false)]
[switch]$SkipUpdate
)
process {
# Merge Excel and CSV data
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
# Output the merged data if the user chooses to skip the update
if ($SkipUpdate) {
return $mergedData
} else {
# Update the Excel worksheet with the merged data
Update-CISExcelWorksheet -ExcelPath $ExcelPath -WorksheetName $WorksheetName -Data $mergedData
}
}
}

View File

@@ -1,51 +1,52 @@
Index,TestFileName,Rec,ELevel,ProfileLevel,IG1,IG2,IG3
1,Test-AntiPhishingPolicy.ps1,2.1.7,E5,L1,FALSE,FALSE,TRUE
2,Test-AuditDisabledFalse.ps1,6.1.1,E3,L1,TRUE,TRUE,TRUE
3,Test-AuditLogSearch.ps1,3.1.1,E3,L1,TRUE,TRUE,TRUE
4,Test-BlockChannelEmails.ps1,8.1.2,E3,L1,FALSE,FALSE,FALSE
5,Test-BlockMailForwarding.ps1,6.2.1,E3,L1,FALSE,FALSE,FALSE
6,Test-BlockSharedMailboxSignIn.ps1,1.2.2,E3,L1,FALSE,FALSE,FALSE
7,Test-CommonAttachmentFilter.ps1,2.1.2,E3,L1,FALSE,TRUE,TRUE
8,Test-CustomerLockbox.ps1,1.3.6,E5,L2,FALSE,FALSE,FALSE
9,Test-DialInBypassLobby.ps1,8.5.4,E3,L1,FALSE,FALSE,FALSE
10,Test-DisallowInfectedFilesDownload.ps1,7.3.1,E5,L2,TRUE,TRUE,TRUE
11,Test-EnableDKIM.ps1,2.1.9,E3,L1,FALSE,TRUE,TRUE
12,Test-ExternalNoControl.ps1,8.5.7,E3,L1,FALSE,FALSE,FALSE
13,Test-ExternalSharingCalendars.ps1,1.3.3,E3,L2,FALSE,TRUE,TRUE
14,Test-GlobalAdminsCount.ps1,1.1.3,E3,L1,TRUE,TRUE,TRUE
15,Test-GuestAccessExpiration.ps1,7.2.9,E3,L1,FALSE,FALSE,FALSE
16,Test-IdentifyExternalEmail.ps1,6.2.3,E3,L1,FALSE,FALSE,FALSE
17,Test-LinkSharingRestrictions.ps1,7.2.7,E3,L1,TRUE,TRUE,TRUE
18,Test-MailboxAuditingE3.ps1,6.1.2,E3,L1,TRUE,TRUE,TRUE
19,Test-MailboxAuditingE5.ps1,6.1.3,E5,L1,TRUE,TRUE,TRUE
20,Test-MailTipsEnabled.ps1,6.5.2,E3,L2,FALSE,FALSE,FALSE
21,Test-ManagedApprovedPublicGroups.ps1,1.2.1,E3,L2,TRUE,TRUE,TRUE
22,Test-MeetingChatNoAnonymous.ps1,8.5.5,E3,L1,FALSE,FALSE,FALSE
23,Test-ModernAuthExchangeOnline.ps1,6.5.1,E3,L1,FALSE,TRUE,TRUE
24,Test-ModernAuthSharePoint.ps1,7.2.1,E3,L1,FALSE,TRUE,TRUE
25,Test-NoAnonymousMeetingJoin.ps1,8.5.1,E3,L2,FALSE,FALSE,FALSE
26,Test-NoAnonymousMeetingStart.ps1,8.5.2,E3,L1,FALSE,FALSE,FALSE
27,Test-NotifyMalwareInternal.ps1,2.1.3,E3,L1,FALSE,TRUE,TRUE
28,Test-NoWhitelistDomains.ps1,6.2.2,E3,L1,FALSE,FALSE,FALSE
29,Test-OneDriveContentRestrictions.ps1,7.2.4,E3,L2,TRUE,TRUE,TRUE
30,Test-OneDriveSyncRestrictions.ps1,7.3.2,E3,L2,FALSE,FALSE,FALSE
31,Test-OrganizersPresent.ps1,8.5.6,E3,L1,FALSE,FALSE,FALSE
32,Test-OrgOnlyBypassLobby.ps1,8.5.3,E3,L1,FALSE,FALSE,TRUE
33,Test-PasswordHashSync.ps1,5.1.8.1,E3,L1,FALSE,TRUE,TRUE
34,Test-PasswordNeverExpirePolicy.ps1,1.3.1,E3,L1,TRUE,TRUE,TRUE
35,Test-ReauthWithCode.ps1,7.2.10,E3,L1,FALSE,FALSE,FALSE
36,Test-ReportSecurityInTeams.ps1,8.6.1,E3,L1,FALSE,FALSE,FALSE
37,Test-RestrictCustomScripts.ps1,7.3.4,E3,L1,FALSE,FALSE,TRUE
38,Test-RestrictExternalSharing.ps1,7.2.3,E3,L1,TRUE,TRUE,TRUE
39,Test-RestrictOutlookAddins.ps1,6.3.1,E3,L2,FALSE,TRUE,TRUE
40,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,E3,L2,TRUE,TRUE,TRUE
41,Test-RestrictTenantCreation.ps1,5.1.2.3,E3,L1,FALSE,FALSE,FALSE
42,Test-SafeAttachmentsPolicy.ps1,2.1.4,E5,L2,FALSE,FALSE,TRUE
43,Test-SafeAttachmentsTeams.ps1,2.1.5,E5,L2,TRUE,TRUE,TRUE
44,Test-SafeLinksOfficeApps.ps1,2.1.1,E5,L2,TRUE,TRUE,TRUE
45,Test-SharePointAADB2B.ps1,7.2.2,E3,L1,FALSE,FALSE,FALSE
46,Test-SharePointExternalSharingDomains.ps1,7.2.6,E3,L2,TRUE,TRUE,TRUE
47,Test-SharePointGuestsItemSharing.ps1,7.2.5,E3,L2,TRUE,TRUE,TRUE
48,Test-SpamPolicyAdminNotify.ps1,2.1.6,E3,L1,FALSE,TRUE,TRUE
49,Test-TeamsExternalAccess.ps1,8.2.1,E3,L2,FALSE,FALSE,FALSE
50,Test-TeamsExternalFileSharing.ps1,8.1.1,E3,L2,TRUE,TRUE,TRUE
Index,TestFileName,Rec,ELevel,ProfileLevel,IG1,IG2,IG3,Automated
1,Test-AdministrativeAccountCompliance.ps1,1.1.1,E3,L1,TRUE,TRUE,TRUE,FALSE
2,Test-GlobalAdminsCount.ps1,1.1.3,E3,L1,TRUE,TRUE,TRUE,TRUE
3,Test-ManagedApprovedPublicGroups.ps1,1.2.1,E3,L2,TRUE,TRUE,TRUE,TRUE
4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,E3,L1,FALSE,FALSE,FALSE,TRUE
5,Test-PasswordNeverExpirePolicy.ps1,1.3.1,E3,L1,TRUE,TRUE,TRUE,TRUE
6,Test-ExternalSharingCalendars.ps1,1.3.3,E3,L2,FALSE,TRUE,TRUE,TRUE
7,Test-CustomerLockbox.ps1,1.3.6,E5,L2,FALSE,FALSE,FALSE,TRUE
8,Test-SafeLinksOfficeApps.ps1,2.1.1,E5,L2,TRUE,TRUE,TRUE,TRUE
9,Test-CommonAttachmentFilter.ps1,2.1.2,E3,L1,FALSE,TRUE,TRUE,TRUE
10,Test-NotifyMalwareInternal.ps1,2.1.3,E3,L1,FALSE,TRUE,TRUE,TRUE
11,Test-SafeAttachmentsPolicy.ps1,2.1.4,E5,L2,FALSE,FALSE,TRUE,TRUE
12,Test-SafeAttachmentsTeams.ps1,2.1.5,E5,L2,TRUE,TRUE,TRUE,TRUE
13,Test-SpamPolicyAdminNotify.ps1,2.1.6,E3,L1,FALSE,TRUE,TRUE,TRUE
14,Test-AntiPhishingPolicy.ps1,2.1.7,E5,L1,FALSE,FALSE,TRUE,TRUE
15,Test-EnableDKIM.ps1,2.1.9,E3,L1,FALSE,TRUE,TRUE,TRUE
16,Test-AuditLogSearch.ps1,3.1.1,E3,L1,TRUE,TRUE,TRUE,TRUE
17,Test-RestrictTenantCreation.ps1,5.1.2.3,E3,L1,FALSE,FALSE,FALSE,TRUE
18,Test-PasswordHashSync.ps1,5.1.8.1,E3,L1,FALSE,TRUE,TRUE,TRUE
19,Test-AuditDisabledFalse.ps1,6.1.1,E3,L1,TRUE,TRUE,TRUE,TRUE
20,Test-MailboxAuditingE3.ps1,6.1.2,E3,L1,TRUE,TRUE,TRUE,TRUE
21,Test-MailboxAuditingE5.ps1,6.1.3,E5,L1,TRUE,TRUE,TRUE,TRUE
22,Test-BlockMailForwarding.ps1,6.2.1,E3,L1,FALSE,FALSE,FALSE,TRUE
23,Test-NoWhitelistDomains.ps1,6.2.2,E3,L1,FALSE,FALSE,FALSE,TRUE
24,Test-IdentifyExternalEmail.ps1,6.2.3,E3,L1,FALSE,FALSE,FALSE,TRUE
25,Test-RestrictOutlookAddins.ps1,6.3.1,E3,L2,FALSE,TRUE,TRUE,TRUE
26,Test-ModernAuthExchangeOnline.ps1,6.5.1,E3,L1,FALSE,TRUE,TRUE,TRUE
27,Test-MailTipsEnabled.ps1,6.5.2,E3,L2,FALSE,FALSE,FALSE,TRUE
28,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,E3,L2,TRUE,TRUE,TRUE,TRUE
29,Test-ModernAuthSharePoint.ps1,7.2.1,E3,L1,FALSE,TRUE,TRUE,TRUE
30,Test-SharePointAADB2B.ps1,7.2.2,E3,L1,FALSE,FALSE,FALSE,TRUE
31,Test-RestrictExternalSharing.ps1,7.2.3,E3,L1,TRUE,TRUE,TRUE,TRUE
32,Test-OneDriveContentRestrictions.ps1,7.2.4,E3,L2,TRUE,TRUE,TRUE,TRUE
33,Test-SharePointGuestsItemSharing.ps1,7.2.5,E3,L2,TRUE,TRUE,TRUE,TRUE
34,Test-SharePointExternalSharingDomains.ps1,7.2.6,E3,L2,TRUE,TRUE,TRUE,TRUE
35,Test-LinkSharingRestrictions.ps1,7.2.7,E3,L1,TRUE,TRUE,TRUE,TRUE
36,Test-GuestAccessExpiration.ps1,7.2.9,E3,L1,FALSE,FALSE,FALSE,TRUE
37,Test-ReauthWithCode.ps1,7.2.10,E3,L1,FALSE,FALSE,FALSE,TRUE
38,Test-DisallowInfectedFilesDownload.ps1,7.3.1,E5,L2,TRUE,TRUE,TRUE,TRUE
39,Test-OneDriveSyncRestrictions.ps1,7.3.2,E3,L2,FALSE,FALSE,FALSE,TRUE
40,Test-RestrictCustomScripts.ps1,7.3.4,E3,L1,FALSE,FALSE,TRUE,TRUE
41,Test-TeamsExternalFileSharing.ps1,8.1.1,E3,L2,TRUE,TRUE,TRUE,TRUE
42,Test-BlockChannelEmails.ps1,8.1.2,E3,L1,FALSE,FALSE,FALSE,TRUE
43,Test-TeamsExternalAccess.ps1,8.2.1,E3,L2,FALSE,FALSE,FALSE,TRUE
44,Test-NoAnonymousMeetingJoin.ps1,8.5.1,E3,L2,FALSE,FALSE,FALSE,TRUE
45,Test-NoAnonymousMeetingStart.ps1,8.5.2,E3,L1,FALSE,FALSE,FALSE,TRUE
46,Test-OrgOnlyBypassLobby.ps1,8.5.3,E3,L1,FALSE,FALSE,TRUE,TRUE
47,Test-DialInBypassLobby.ps1,8.5.4,E3,L1,FALSE,FALSE,FALSE,TRUE
48,Test-MeetingChatNoAnonymous.ps1,8.5.5,E3,L1,FALSE,FALSE,FALSE,TRUE
49,Test-OrganizersPresent.ps1,8.5.6,E3,L1,FALSE,FALSE,FALSE,TRUE
50,Test-ExternalNoControl.ps1,8.5.7,E3,L1,FALSE,FALSE,FALSE,TRUE
51,Test-ReportSecurityInTeams.ps1,8.6.1,E3,L1,FALSE,FALSE,FALSE,TRUE
1 Index TestFileName Rec ELevel ProfileLevel IG1 IG2 IG3 Automated
2 1 Test-AntiPhishingPolicy.ps1 Test-AdministrativeAccountCompliance.ps1 2.1.7 1.1.1 E5 E3 L1 FALSE TRUE FALSE TRUE TRUE FALSE
3 2 Test-AuditDisabledFalse.ps1 Test-GlobalAdminsCount.ps1 6.1.1 1.1.3 E3 L1 TRUE TRUE TRUE TRUE
4 3 Test-AuditLogSearch.ps1 Test-ManagedApprovedPublicGroups.ps1 3.1.1 1.2.1 E3 L1 L2 TRUE TRUE TRUE TRUE
5 4 Test-BlockChannelEmails.ps1 Test-BlockSharedMailboxSignIn.ps1 8.1.2 1.2.2 E3 L1 FALSE FALSE FALSE TRUE
6 5 Test-BlockMailForwarding.ps1 Test-PasswordNeverExpirePolicy.ps1 6.2.1 1.3.1 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
7 6 Test-BlockSharedMailboxSignIn.ps1 Test-ExternalSharingCalendars.ps1 1.2.2 1.3.3 E3 L1 L2 FALSE FALSE TRUE FALSE TRUE TRUE
8 7 Test-CommonAttachmentFilter.ps1 Test-CustomerLockbox.ps1 2.1.2 1.3.6 E3 E5 L1 L2 FALSE TRUE FALSE TRUE FALSE TRUE
9 8 Test-CustomerLockbox.ps1 Test-SafeLinksOfficeApps.ps1 1.3.6 2.1.1 E5 L2 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
10 9 Test-DialInBypassLobby.ps1 Test-CommonAttachmentFilter.ps1 8.5.4 2.1.2 E3 L1 FALSE FALSE TRUE FALSE TRUE TRUE
11 10 Test-DisallowInfectedFilesDownload.ps1 Test-NotifyMalwareInternal.ps1 7.3.1 2.1.3 E5 E3 L2 L1 TRUE FALSE TRUE TRUE TRUE
12 11 Test-EnableDKIM.ps1 Test-SafeAttachmentsPolicy.ps1 2.1.9 2.1.4 E3 E5 L1 L2 FALSE TRUE FALSE TRUE TRUE
13 12 Test-ExternalNoControl.ps1 Test-SafeAttachmentsTeams.ps1 8.5.7 2.1.5 E3 E5 L1 L2 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
14 13 Test-ExternalSharingCalendars.ps1 Test-SpamPolicyAdminNotify.ps1 1.3.3 2.1.6 E3 L2 L1 FALSE TRUE TRUE TRUE
15 14 Test-GlobalAdminsCount.ps1 Test-AntiPhishingPolicy.ps1 1.1.3 2.1.7 E3 E5 L1 TRUE FALSE TRUE FALSE TRUE TRUE
16 15 Test-GuestAccessExpiration.ps1 Test-EnableDKIM.ps1 7.2.9 2.1.9 E3 L1 FALSE FALSE TRUE FALSE TRUE TRUE
17 16 Test-IdentifyExternalEmail.ps1 Test-AuditLogSearch.ps1 6.2.3 3.1.1 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
18 17 Test-LinkSharingRestrictions.ps1 Test-RestrictTenantCreation.ps1 7.2.7 5.1.2.3 E3 L1 TRUE FALSE TRUE FALSE TRUE FALSE TRUE
19 18 Test-MailboxAuditingE3.ps1 Test-PasswordHashSync.ps1 6.1.2 5.1.8.1 E3 L1 TRUE FALSE TRUE TRUE TRUE
20 19 Test-MailboxAuditingE5.ps1 Test-AuditDisabledFalse.ps1 6.1.3 6.1.1 E5 E3 L1 TRUE TRUE TRUE TRUE
21 20 Test-MailTipsEnabled.ps1 Test-MailboxAuditingE3.ps1 6.5.2 6.1.2 E3 L2 L1 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
22 21 Test-ManagedApprovedPublicGroups.ps1 Test-MailboxAuditingE5.ps1 1.2.1 6.1.3 E3 E5 L2 L1 TRUE TRUE TRUE TRUE
23 22 Test-MeetingChatNoAnonymous.ps1 Test-BlockMailForwarding.ps1 8.5.5 6.2.1 E3 L1 FALSE FALSE FALSE TRUE
24 23 Test-ModernAuthExchangeOnline.ps1 Test-NoWhitelistDomains.ps1 6.5.1 6.2.2 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE
25 24 Test-ModernAuthSharePoint.ps1 Test-IdentifyExternalEmail.ps1 7.2.1 6.2.3 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE
26 25 Test-NoAnonymousMeetingJoin.ps1 Test-RestrictOutlookAddins.ps1 8.5.1 6.3.1 E3 L2 FALSE FALSE TRUE FALSE TRUE TRUE
27 26 Test-NoAnonymousMeetingStart.ps1 Test-ModernAuthExchangeOnline.ps1 8.5.2 6.5.1 E3 L1 FALSE FALSE TRUE FALSE TRUE TRUE
28 27 Test-NotifyMalwareInternal.ps1 Test-MailTipsEnabled.ps1 2.1.3 6.5.2 E3 L1 L2 FALSE TRUE FALSE TRUE FALSE TRUE
29 28 Test-NoWhitelistDomains.ps1 Test-RestrictStorageProvidersOutlook.ps1 6.2.2 6.5.3 E3 L1 L2 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
30 29 Test-OneDriveContentRestrictions.ps1 Test-ModernAuthSharePoint.ps1 7.2.4 7.2.1 E3 L2 L1 TRUE FALSE TRUE TRUE TRUE
31 30 Test-OneDriveSyncRestrictions.ps1 Test-SharePointAADB2B.ps1 7.3.2 7.2.2 E3 L2 L1 FALSE FALSE FALSE TRUE
32 31 Test-OrganizersPresent.ps1 Test-RestrictExternalSharing.ps1 8.5.6 7.2.3 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
33 32 Test-OrgOnlyBypassLobby.ps1 Test-OneDriveContentRestrictions.ps1 8.5.3 7.2.4 E3 L1 L2 FALSE TRUE FALSE TRUE TRUE TRUE
34 33 Test-PasswordHashSync.ps1 Test-SharePointGuestsItemSharing.ps1 5.1.8.1 7.2.5 E3 L1 L2 FALSE TRUE TRUE TRUE TRUE
35 34 Test-PasswordNeverExpirePolicy.ps1 Test-SharePointExternalSharingDomains.ps1 1.3.1 7.2.6 E3 L1 L2 TRUE TRUE TRUE TRUE
36 35 Test-ReauthWithCode.ps1 Test-LinkSharingRestrictions.ps1 7.2.10 7.2.7 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
37 36 Test-ReportSecurityInTeams.ps1 Test-GuestAccessExpiration.ps1 8.6.1 7.2.9 E3 L1 FALSE FALSE FALSE TRUE
38 37 Test-RestrictCustomScripts.ps1 Test-ReauthWithCode.ps1 7.3.4 7.2.10 E3 L1 FALSE FALSE TRUE FALSE TRUE
39 38 Test-RestrictExternalSharing.ps1 Test-DisallowInfectedFilesDownload.ps1 7.2.3 7.3.1 E3 E5 L1 L2 TRUE TRUE TRUE TRUE
40 39 Test-RestrictOutlookAddins.ps1 Test-OneDriveSyncRestrictions.ps1 6.3.1 7.3.2 E3 L2 FALSE TRUE FALSE TRUE FALSE TRUE
41 40 Test-RestrictStorageProvidersOutlook.ps1 Test-RestrictCustomScripts.ps1 6.5.3 7.3.4 E3 L2 L1 TRUE FALSE TRUE FALSE TRUE TRUE
42 41 Test-RestrictTenantCreation.ps1 Test-TeamsExternalFileSharing.ps1 5.1.2.3 8.1.1 E3 L1 L2 FALSE TRUE FALSE TRUE FALSE TRUE TRUE
43 42 Test-SafeAttachmentsPolicy.ps1 Test-BlockChannelEmails.ps1 2.1.4 8.1.2 E5 E3 L2 L1 FALSE FALSE TRUE FALSE TRUE
44 43 Test-SafeAttachmentsTeams.ps1 Test-TeamsExternalAccess.ps1 2.1.5 8.2.1 E5 E3 L2 TRUE FALSE TRUE FALSE TRUE FALSE TRUE
45 44 Test-SafeLinksOfficeApps.ps1 Test-NoAnonymousMeetingJoin.ps1 2.1.1 8.5.1 E5 E3 L2 TRUE FALSE TRUE FALSE TRUE FALSE TRUE
46 45 Test-SharePointAADB2B.ps1 Test-NoAnonymousMeetingStart.ps1 7.2.2 8.5.2 E3 L1 FALSE FALSE FALSE TRUE
47 46 Test-SharePointExternalSharingDomains.ps1 Test-OrgOnlyBypassLobby.ps1 7.2.6 8.5.3 E3 L2 L1 TRUE FALSE TRUE FALSE TRUE TRUE
48 47 Test-SharePointGuestsItemSharing.ps1 Test-DialInBypassLobby.ps1 7.2.5 8.5.4 E3 L2 L1 TRUE FALSE TRUE FALSE TRUE FALSE TRUE
49 48 Test-SpamPolicyAdminNotify.ps1 Test-MeetingChatNoAnonymous.ps1 2.1.6 8.5.5 E3 L1 FALSE TRUE FALSE TRUE FALSE TRUE
50 49 Test-TeamsExternalAccess.ps1 Test-OrganizersPresent.ps1 8.2.1 8.5.6 E3 L2 L1 FALSE FALSE FALSE TRUE
51 50 Test-TeamsExternalFileSharing.ps1 Test-ExternalNoControl.ps1 8.1.1 8.5.7 E3 L2 L1 TRUE FALSE TRUE FALSE TRUE FALSE TRUE
52 51 Test-ReportSecurityInTeams.ps1 8.6.1 E3 L1 FALSE FALSE FALSE TRUE

View File

@@ -0,0 +1,75 @@
function Test-AdministrativeAccountCompliance {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
#. C:\Temp\CISAuditResult.ps1
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
}
process {
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
$adminRoleUsers = @()
foreach ($role in $adminRoles) {
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($assignment in $roleAssignments) {
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
if ($userDetails) {
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
$adminRoleUsers += [PSCustomObject]@{
UserName = $userDetails.UserPrincipalName
RoleName = $role.DisplayName
UserId = $userDetails.Id
HybridUser = $userDetails.OnPremisesSyncEnabled
Licenses = $licenseString
}
}
}
}
$uniqueAdminRoleUsers = $adminRoleUsers | Group-Object -Property UserName | ForEach-Object {
$first = $_.Group | Select-Object -First 1
$roles = ($_.Group.RoleName -join ', ')
$licenses = (($_.Group | Select-Object -ExpandProperty Licenses) -join ',').Split(',') | Select-Object -Unique
$first | Select-Object UserName, UserId, HybridUser, @{Name = 'Roles'; Expression = { $roles } }, @{Name = 'Licenses'; Expression = { $licenses -join '|' } }
}
$nonCompliantUsers = $uniqueAdminRoleUsers | Where-Object {
$_.HybridUser -or
-not ($_.Licenses -split '\|' | Where-Object { $validLicenses -contains $_ })
}
$failureReasons = $nonCompliantUsers | ForEach-Object {
$accountType = if ($_.HybridUser) { "Hybrid" } else { "Cloud-Only" }
$missingLicenses = $validLicenses | Where-Object { $_ -notin ($_.Licenses -split '\|') }
"$($_.UserName)|$($_.Roles)|$accountType|Missing: $($missingLicenses -join ',')"
}
$failureReasons = $failureReasons -join "`n"
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($nonCompliantUsers) { 'Fail' } else { 'Pass' }
$auditResult.ELevel = 'E3'
$auditResult.ProfileLevel = 'L1'
$auditResult.Rec = '1.1.1'
$auditResult.RecDescription = "Ensure Administrative accounts are separate and cloud-only"
$auditResult.CISControlVer = 'v8'
$auditResult.CISControl = "5.4"
$auditResult.CISDescription = "Restrict Administrator Privileges to Dedicated Administrator Accounts"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $nonCompliantUsers.Count -eq 0
$auditResult.Details = "Compliant Accounts: $($uniqueAdminRoleUsers.Count - $nonCompliantUsers.Count); Non-Compliant Accounts: $($nonCompliantUsers.Count)"
$auditResult.FailureReason = if ($nonCompliantUsers) { "Non-compliant accounts: `nUsername | Roles | HybridStatus | Missing Licence`n$failureReasons" } else { "N/A" }
}
end {
# Output the result
return $auditResult
}
}

View File

@@ -11,6 +11,7 @@ function Test-AntiPhishingPolicy {
}
process {
# 2.1.7 Ensure that an anti-phishing policy has been created
# Retrieve and validate the anti-phishing policies
$antiPhishPolicies = Get-AntiPhishPolicy

View File

@@ -14,6 +14,7 @@ function Test-BlockSharedMailboxSignIn {
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
# Pass if all shared mailboxes have AccountEnabled set to False.
# Fail if any shared mailbox has AccountEnabled set to True.
# Review: Details property - Add verbosity.
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }

View File

@@ -0,0 +1,61 @@
function Test-GuestUsersBiweeklyReview {
[CmdletBinding()]
param ()
begin {
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @()
}
process {
# 1.1.4 (L1) Ensure Guest Users are reviewed at least biweekly
# The function will fail if guest users are found since they should be reviewed manually biweekly.
try {
# Connect to Microsoft Graph - placeholder for connection command
# Connect-MgGraph -Scopes "User.Read.All"
$guestUsers = Get-MgUser -All -Filter "UserType eq 'Guest'"
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControl = "5.1, 5.3"
$auditResult.CISDescription = "Establish and Maintain an Inventory of Accounts, Disable Dormant Accounts"
$auditResult.Rec = "1.1.4"
$auditResult.RecDescription = "Ensure Guest Users are reviewed at least biweekly"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.CISControlVer = 'v8'
if ($guestUsers) {
$auditCommand = "Get-MgUser -All -Property UserType,UserPrincipalName | Where {`$_.UserType -ne 'Member'} | Format-Table UserPrincipalName, UserType"
$auditResult.Status = "Fail"
$auditResult.Result = $false
$auditResult.Details = "Manual review required. To list guest users, run: `"$auditCommand`"."
$auditResult.FailureReason = "Guest users present: $($guestUsers.Count)"
} else {
$auditResult.Status = "Pass"
$auditResult.Result = $true
$auditResult.Details = "No guest users found."
$auditResult.FailureReason = "N/A"
}
}
catch {
$auditResult.Status = "Error"
$auditResult.Result = $false
$auditResult.Details = "Error while attempting to check guest users. Error message: $($_.Exception.Message)"
$auditResult.FailureReason = "An error occurred during the audit check."
}
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -13,6 +13,7 @@ function Test-IdentifyExternalEmail {
process {
# 6.2.3 (L1) Ensure email from external senders is identified
# Requirement is to have external sender tagging enabled
# Review
$externalInOutlook = Get-ExternalInOutlook
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true

View File

@@ -27,7 +27,7 @@ function Test-ModernAuthExchangeOnline {
$auditResults.IG2 = $true # As per CIS Control v8 mapping for IG2
$auditResults.IG3 = $true # As per CIS Control v8 mapping for IG3
$auditResults.ELevel = "E3" # Based on your environment (E3, E5, etc.)
$auditResults.Profile = "L1"
$auditResults.ProfileLevel = "L1"
$auditResults.Rec = "6.5.1"
$auditResults.RecDescription = "Ensure modern authentication for Exchange Online is enabled (Automated)"
$auditResults.Result = $orgConfig.OAuth2ClientProfileEnabled

View File

@@ -10,6 +10,8 @@ function Test-NotifyMalwareInternal {
}
process {
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
# Retrieve all 'Custom' malware filter policies and check notification settings
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
$policiesToReport = @()

View File

@@ -10,6 +10,8 @@ function Test-SpamPolicyAdminNotify {
}
process {
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
# Get the default hosted outbound spam filter policy
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }

View File

@@ -1,91 +0,0 @@
BeforeAll {
$script:dscModuleName = 'M365FoundationsCISReport'
Import-Module -Name $script:dscModuleName
}
AfterAll {
# Unload the module being tested so that it doesn't impact any other tests.
Get-Module -Name $script:dscModuleName -All | Remove-Module -Force
}
Describe Get-Something {
BeforeAll {
Mock -CommandName Get-PrivateFunction -MockWith {
# This return the value passed to the Get-PrivateFunction parameter $PrivateData.
$PrivateData
} -ModuleName $dscModuleName
}
Context 'When passing values using named parameters' {
It 'Should call the private function once' {
{ Get-Something -Data 'value' } | Should -Not -Throw
Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 1 -Scope It -ModuleName $dscModuleName
}
It 'Should return a single object' {
$return = Get-Something -Data 'value'
($return | Measure-Object).Count | Should -Be 1
}
It 'Should return the correct string value' {
$return = Get-Something -Data 'value'
$return | Should -Be 'value'
}
}
Context 'When passing values over the pipeline' {
It 'Should call the private function two times' {
{ 'value1', 'value2' | Get-Something } | Should -Not -Throw
Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 2 -Scope It -ModuleName $dscModuleName
}
It 'Should return an array with two items' {
$return = 'value1', 'value2' | Get-Something
$return.Count | Should -Be 2
}
It 'Should return an array with the correct string values' {
$return = 'value1', 'value2' | Get-Something
$return[0] | Should -Be 'value1'
$return[1] | Should -Be 'value2'
}
It 'Should accept values from the pipeline by property name' {
$return = 'value1', 'value2' | ForEach-Object {
[PSCustomObject]@{
Data = $_
OtherProperty = 'other'
}
} | Get-Something
$return[0] | Should -Be 'value1'
$return[1] | Should -Be 'value2'
}
}
Context 'When passing WhatIf' {
It 'Should support the parameter WhatIf' {
(Get-Command -Name 'Get-Something').Parameters.ContainsKey('WhatIf') | Should -Be $true
}
It 'Should not call the private function' {
{ Get-Something -Data 'value' -WhatIf } | Should -Not -Throw
Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 0 -Scope It -ModuleName $dscModuleName
}
It 'Should return $null' {
$return = Get-Something -Data 'value' -WhatIf
$return | Should -BeNullOrEmpty
}
}
}