Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4db0fd3742 | ||
|
83a8e31aa5 | ||
|
b9de0638bb | ||
|
5a0475c253 | ||
|
312aabc81c | ||
|
e6da6d9d47 | ||
|
014c42b3fe | ||
|
fbfb5b5986 | ||
|
03b5bb47e2 | ||
|
9dc99636d3 | ||
|
afe657ffc0 | ||
|
702f557579 | ||
|
f855ef7d0b | ||
|
270e980a57 | ||
|
ff90669984 | ||
|
f2e799af2f | ||
|
4a4d200197 | ||
|
9199d97fc2 | ||
|
5d681f3d72 | ||
|
f926c63533 |
25
CHANGELOG.md
25
CHANGELOG.md
@@ -6,6 +6,31 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added output type to functions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Whatif support for `Invoke-M365SecurityAudit`.
|
||||||
|
- Whatif module output and module install process.
|
||||||
|
|
||||||
|
## [0.1.7] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated `Connect-M365Suite` to make `TenantAdminUrl` an optional parameter.
|
||||||
|
- Updated `Invoke-M365SecurityAudit` to make `TenantAdminUrl` an optional parameter.
|
||||||
|
- Improved connection handling and error messaging in `Connect-M365Suite`.
|
||||||
|
- Enhanced `Invoke-M365SecurityAudit` to allow flexible inclusion and exclusion of specific recommendations, IG filters, and profile levels.
|
||||||
|
- SupportsShoudProcess to also bypass connection checks in `Invoke-M365SecurityAudit` as well as Disconnect-M365Suite.
|
||||||
|
|
||||||
|
## [0.1.6] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
## [0.1.5] - 2024-06-08
|
## [0.1.5] - 2024-06-08
|
||||||
|
BIN
docs/index.html
BIN
docs/index.html
Binary file not shown.
@@ -4,7 +4,7 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
|||||||
|
|
||||||
|
|
||||||
<#
|
<#
|
||||||
$ver = "v0.1.6"
|
$ver = "v0.1.7"
|
||||||
git checkout main
|
git checkout main
|
||||||
git pull origin main
|
git pull origin main
|
||||||
git tag -a $ver -m "Release version $ver refactor Update"
|
git tag -a $ver -m "Release version $ver refactor Update"
|
||||||
@@ -14,4 +14,72 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
|||||||
# git tag -d $ver
|
# git tag -d $ver
|
||||||
#>
|
#>
|
||||||
|
|
||||||
|
# Refresh authentication to ensure the correct scopes
|
||||||
|
gh auth refresh -s project,read:project,write:project,repo
|
||||||
|
|
||||||
|
# Create the project
|
||||||
|
gh project create --owner CriticalSolutionsNetwork --title "Test Validation Project"
|
||||||
|
|
||||||
|
$repoOwner = "CriticalSolutionsNetwork"
|
||||||
|
$repoName = "M365FoundationsCISReport"
|
||||||
|
$directoryPath = ".\source\tests"
|
||||||
|
$projectName = "Test Validation Project"
|
||||||
|
|
||||||
|
# Function to create GitHub issues
|
||||||
|
function Create-GitHubIssue {
|
||||||
|
param (
|
||||||
|
[string]$title,
|
||||||
|
[string]$body,
|
||||||
|
[string]$project
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the issue and add it to the specified project
|
||||||
|
$issue = gh issue create --repo "$repoOwner/$repoName" --title "$title" --body "$body" --project "$project"
|
||||||
|
return $issue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load test definitions from CSV
|
||||||
|
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
|
||||||
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
|
|
||||||
|
# Iterate over each .ps1 file in the directory
|
||||||
|
Get-ChildItem -Path $directoryPath -Filter "*.ps1" | ForEach-Object {
|
||||||
|
$fileName = $_.Name
|
||||||
|
$testDefinition = $testDefinitions | Where-Object { $_.TestFileName -eq $fileName }
|
||||||
|
|
||||||
|
if ($testDefinition) {
|
||||||
|
$rec = $testDefinition.Rec
|
||||||
|
$elevel = $testDefinition.ELevel
|
||||||
|
$profileLevel = $testDefinition.ProfileLevel
|
||||||
|
$ig1 = $testDefinition.IG1
|
||||||
|
$ig2 = $testDefinition.IG2
|
||||||
|
$ig3 = $testDefinition.IG3
|
||||||
|
$connection = $testDefinition.Connection
|
||||||
|
|
||||||
|
$issueTitle = "Rec: $rec - Validate $fileName, ELevel: $elevel, ProfileLevel: $profileLevel, IG1: $ig1, IG2: $ig2, IG3: $ig3, Connection: $connection"
|
||||||
|
$issueBody = @"
|
||||||
|
# Validation for $fileName
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [ ] Validate test for a pass
|
||||||
|
- Description of passing criteria:
|
||||||
|
- [ ] Validate test for a fail
|
||||||
|
- Description of failing criteria:
|
||||||
|
- [ ] Add notes and observations
|
||||||
|
- Placeholder for additional notes:
|
||||||
|
"@
|
||||||
|
|
||||||
|
# Create the issue using GitHub CLI
|
||||||
|
try {
|
||||||
|
Create-GitHubIssue -title "$issueTitle" -body "$issueBody" -project "$projectName"
|
||||||
|
Write-Output "Created issue for $fileName"
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to create issue for $fileName : $_"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Introduce a delay of 2 seconds
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
} else {
|
||||||
|
Write-Warning "No matching test definition found for $fileName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,33 +1,37 @@
|
|||||||
function Assert-ModuleAvailability {
|
function Assert-ModuleAvailability {
|
||||||
|
[OutputType([void]) ]
|
||||||
param(
|
param(
|
||||||
[string]$ModuleName,
|
[string]$ModuleName,
|
||||||
[string]$RequiredVersion,
|
[string]$RequiredVersion,
|
||||||
[string]$SubModuleName
|
[string[]]$SubModules = @()
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
||||||
|
|
||||||
if ($null -eq $module) {$auditResult.Profile
|
if ($null -eq $module) {
|
||||||
Write-Host "Installing $ModuleName module..."
|
Write-Information "Installing $ModuleName module..." -InformationAction Continue
|
||||||
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
||||||
}
|
}
|
||||||
elseif ($module.Version -lt [version]$RequiredVersion) {
|
elseif ($module.Version -lt [version]$RequiredVersion) {
|
||||||
Write-Host "Updating $ModuleName module to required version..."
|
Write-Information "Updating $ModuleName module to required version..." -InformationAction Continue
|
||||||
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Host "$ModuleName module is already at required version or newer."
|
Write-Information "$ModuleName module is already at required version or newer." -InformationAction Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($SubModuleName) {
|
if ($SubModules.Count -gt 0) {
|
||||||
Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
foreach ($subModule in $SubModules) {
|
||||||
}
|
Write-Information "Importing submodule $ModuleName.$subModule..." -InformationAction Continue
|
||||||
else {
|
Import-Module -Name "$ModuleName.$subModule" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Information "Importing module $ModuleName..." -InformationAction Continue
|
||||||
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "An error occurred with module $ModuleName`: $_"
|
Write-Warning "An error occurred with module $ModuleName`: $_"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
function Connect-M365Suite {
|
function Connect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory=$false)]
|
||||||
[string]$TenantAdminUrl,
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Disconnect-M365Suite {
|
function Disconnect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string[]]$RequiredConnections
|
[string[]]$RequiredConnections
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
function Format-MissingAction {
|
function Format-MissingAction {
|
||||||
param ([array]$missingActions)
|
[CmdletBinding()]
|
||||||
|
[OutputType([hashtable])]
|
||||||
|
param (
|
||||||
|
[array]$missingActions
|
||||||
|
)
|
||||||
|
|
||||||
$actionGroups = @{
|
$actionGroups = @{
|
||||||
"Admin" = @()
|
"Admin" = @()
|
||||||
@@ -22,4 +26,4 @@ function Format-MissingAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $formattedResults
|
return $formattedResults
|
||||||
}
|
}
|
||||||
|
19
source/Private/Format-RequiredModuleList.ps1
Normal file
19
source/Private/Format-RequiredModuleList.ps1
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
function Format-RequiredModuleList {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[System.Object[]]$RequiredModules
|
||||||
|
)
|
||||||
|
|
||||||
|
$requiredModulesFormatted = ""
|
||||||
|
foreach ($module in $RequiredModules) {
|
||||||
|
if ($module.SubModules -and $module.SubModules.Count -gt 0) {
|
||||||
|
$subModulesFormatted = $module.SubModules -join ', '
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName) (SubModules: $subModulesFormatted), "
|
||||||
|
} else {
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName), "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $requiredModulesFormatted.TrimEnd(", ")
|
||||||
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
function Get-MostCommonWord {
|
function Get-MostCommonWord {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string[]]$InputStrings
|
[string[]]$InputStrings
|
||||||
@@ -19,4 +21,4 @@ function Get-MostCommonWord {
|
|||||||
} else {
|
} else {
|
||||||
return $null
|
return $null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,22 +12,16 @@ function Get-RequiredModule {
|
|||||||
switch ($PSCmdlet.ParameterSetName) {
|
switch ($PSCmdlet.ParameterSetName) {
|
||||||
'AuditFunction' {
|
'AuditFunction' {
|
||||||
return @(
|
return @(
|
||||||
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" },
|
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
|
||||||
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" },
|
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() },
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" },
|
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" },
|
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Groups" },
|
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "DirectoryObjects" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Domains" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Reports" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Mail" },
|
|
||||||
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000" },
|
|
||||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0" }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
'SyncFunction' {
|
'SyncFunction' {
|
||||||
return @(
|
return @(
|
||||||
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9" }
|
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9"; SubModules = @() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
default {
|
default {
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
function Get-TestDefinitionsObject {
|
function Get-TestDefinitionsObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([object[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[object[]]$TestDefinitions,
|
[object[]]$TestDefinitions,
|
||||||
@@ -60,4 +62,4 @@ function Get-TestDefinitionsObject {
|
|||||||
|
|
||||||
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
||||||
return $TestDefinitions
|
return $TestDefinitions
|
||||||
}
|
}
|
||||||
|
28
source/Private/Get-UniqueConnection.ps1
Normal file
28
source/Private/Get-UniqueConnection.ps1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
function Get-UniqueConnection {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Connections
|
||||||
|
)
|
||||||
|
|
||||||
|
$uniqueConnections = @()
|
||||||
|
|
||||||
|
if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "AzureAD"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "Microsoft Graph"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "EXO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "SPO") {
|
||||||
|
$uniqueConnections += "SPO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Teams" -or $Connections -contains "Microsoft Teams | EXO") {
|
||||||
|
$uniqueConnections += "Microsoft Teams"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uniqueConnections | Sort-Object -Unique
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
function Initialize-CISAuditResult {
|
function Initialize-CISAuditResult {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Rec,
|
[string]$Rec,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Invoke-TestFunction {
|
function Invoke-TestFunction {
|
||||||
|
[OutputType([CISAuditResult[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[PSObject]$FunctionFile,
|
[PSObject]$FunctionFile,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Measure-AuditResult {
|
function Measure-AuditResult {
|
||||||
|
[OutputType([void])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[System.Collections.ArrayList]$AllAuditResults,
|
[System.Collections.ArrayList]$AllAuditResults,
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Merge-CISExcelAndCsvData {
|
function Merge-CISExcelAndCsvData {
|
||||||
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
|
[OutputType([PSCustomObject[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$ExcelPath,
|
[string]$ExcelPath,
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
function New-MergedObject {
|
function New-MergedObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([PSCustomObject])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[psobject]$ExcelItem,
|
[psobject]$ExcelItem,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Update-CISExcelWorksheet {
|
function Update-CISExcelWorksheet {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
|
@@ -1,28 +1,29 @@
|
|||||||
function Update-WorksheetCell {
|
function Update-WorksheetCell {
|
||||||
param (
|
[OutputType([void])]
|
||||||
$Worksheet,
|
param (
|
||||||
$Data,
|
$Worksheet,
|
||||||
$StartingRowIndex
|
$Data,
|
||||||
)
|
$StartingRowIndex
|
||||||
|
)
|
||||||
|
|
||||||
# Check and set headers
|
# Check and set headers
|
||||||
$firstItem = $Data[0]
|
$firstItem = $Data[0]
|
||||||
$colIndex = 1
|
$colIndex = 1
|
||||||
foreach ($property in $firstItem.PSObject.Properties) {
|
foreach ($property in $firstItem.PSObject.Properties) {
|
||||||
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
||||||
$Worksheet.Cells[1, $colIndex].Value = $property.Name
|
$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++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$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++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
|
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
|
||||||
.PARAMETER TenantAdminUrl
|
.PARAMETER TenantAdminUrl
|
||||||
The URL of the tenant admin. This parameter is mandatory.
|
The URL of the tenant admin. If not specified, none of the SharePoint Online tests will run.
|
||||||
.PARAMETER M365DomainForPWPolicyTest
|
.PARAMETER M365DomainForPWPolicyTest
|
||||||
The domain name of the Microsoft 365 environment to test. This parameter is not mandatory and by default it will pass/fail all found domains as a group if a specific domain is not specified.
|
The domain name of the Microsoft 365 environment to test. This parameter is not mandatory and by default it will pass/fail all found domains as a group if a specific domain is not specified.
|
||||||
.PARAMETER ELevel
|
.PARAMETER ELevel
|
||||||
@@ -28,34 +28,89 @@
|
|||||||
.PARAMETER NoModuleCheck
|
.PARAMETER NoModuleCheck
|
||||||
If specified, the cmdlet will not check for the presence of required modules.
|
If specified, the cmdlet will not check for the presence of required modules.
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
PS> Invoke-M365SecurityAudit
|
||||||
|
Performs a security audit using default parameters.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E3
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
||||||
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E5
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -IncludeIG1
|
||||||
|
|
||||||
Performs an audit including all tests where IG1 is true.
|
Performs an audit including all tests where IG1 is true.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E3
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
||||||
|
|
||||||
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E3
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com"
|
||||||
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
||||||
|
|
||||||
Captures the audit results into a variable and exports them to a CSV file.
|
Captures the audit results into a variable and exports them to a CSV file.
|
||||||
|
Output:
|
||||||
|
CISAuditResult[]
|
||||||
|
auditResults.csv
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -WhatIf
|
||||||
|
Displays what would happen if the cmdlet is run without actually performing the audit.
|
||||||
|
Output:
|
||||||
|
What if: Performing the operation "Invoke-M365SecurityAudit" on target "Microsoft 365 environment".
|
||||||
.INPUTS
|
.INPUTS
|
||||||
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
||||||
.OUTPUTS
|
.OUTPUTS
|
||||||
CISAuditResult[]
|
CISAuditResult[]
|
||||||
The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
||||||
.NOTES
|
.NOTES
|
||||||
- This module is based on CIS benchmarks.
|
- This module is based on CIS benchmarks.
|
||||||
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
|
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
|
||||||
- Modifications and sharing are allowed under the same license.
|
- 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
|
- 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
|
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
|
||||||
.LINK
|
.LINK
|
||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||||
#>
|
#>
|
||||||
@@ -63,7 +118,7 @@ function Invoke-M365SecurityAudit {
|
|||||||
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
||||||
[OutputType([CISAuditResult[]])]
|
[OutputType([CISAuditResult[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'.")]
|
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
|
||||||
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
|
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
|
||||||
[string]$TenantAdminUrl,
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
@@ -94,12 +149,12 @@ function Invoke-M365SecurityAudit {
|
|||||||
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
||||||
[ValidateSet(
|
[ValidateSet(
|
||||||
'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', `
|
'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', `
|
'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', `
|
'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', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
||||||
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
||||||
'8.5.7', '8.6.1'
|
'8.5.7', '8.6.1'
|
||||||
)]
|
)]
|
||||||
[string[]]$IncludeRecommendation,
|
[string[]]$IncludeRecommendation,
|
||||||
|
|
||||||
@@ -107,12 +162,12 @@ function Invoke-M365SecurityAudit {
|
|||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
||||||
[ValidateSet(
|
[ValidateSet(
|
||||||
'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', `
|
'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', `
|
'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', `
|
'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', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
||||||
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
||||||
'8.5.7', '8.6.1'
|
'8.5.7', '8.6.1'
|
||||||
)]
|
)]
|
||||||
[string[]]$SkipRecommendation,
|
[string[]]$SkipRecommendation,
|
||||||
|
|
||||||
@@ -127,12 +182,18 @@ function Invoke-M365SecurityAudit {
|
|||||||
$script:MaximumFunctionCount = 8192
|
$script:MaximumFunctionCount = 8192
|
||||||
}
|
}
|
||||||
# Ensure required modules are installed
|
# Ensure required modules are installed
|
||||||
if (!($NoModuleCheck)) {
|
$requiredModules = Get-RequiredModule -AuditFunction
|
||||||
$requiredModules = Get-RequiredModule -AuditFunction
|
|
||||||
|
# Format the required modules list
|
||||||
|
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
|
||||||
|
|
||||||
|
# Check and install required modules if necessary
|
||||||
|
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules: $requiredModulesFormatted", "Check")) {
|
||||||
foreach ($module in $requiredModules) {
|
foreach ($module in $requiredModules) {
|
||||||
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load test definitions from CSV
|
# Load test definitions from CSV
|
||||||
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
||||||
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
@@ -151,9 +212,14 @@ function Invoke-M365SecurityAudit {
|
|||||||
$testDefinitions = Get-TestDefinitionsObject @params
|
$testDefinitions = Get-TestDefinitionsObject @params
|
||||||
# Extract unique connections needed
|
# Extract unique connections needed
|
||||||
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
||||||
# Establishing connections if required
|
if ($requiredConnections -contains 'SPO') {
|
||||||
if (!($DoNotConnect)) {
|
if (-not $TenantAdminUrl) {
|
||||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
$requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' }
|
||||||
|
$testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' }
|
||||||
|
if ($null -eq $testDefinitions) {
|
||||||
|
throw "No tests to run as no SharePoint Online tests are available."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
# Determine which test files to load based on filtering
|
# Determine which test files to load based on filtering
|
||||||
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
||||||
@@ -162,6 +228,7 @@ function Invoke-M365SecurityAudit {
|
|||||||
# Initialize a collection to hold failed test details
|
# Initialize a collection to hold failed test details
|
||||||
$script:FailedTests = [System.Collections.ArrayList]::new()
|
$script:FailedTests = [System.Collections.ArrayList]::new()
|
||||||
} # End Begin
|
} # End Begin
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
$allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
|
$allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
|
||||||
# Dynamically dot-source the test scripts
|
# Dynamically dot-source the test scripts
|
||||||
@@ -172,6 +239,15 @@ function Invoke-M365SecurityAudit {
|
|||||||
$totalTests = $testFiles.Count
|
$totalTests = $testFiles.Count
|
||||||
$currentTestIndex = 0
|
$currentTestIndex = 0
|
||||||
|
|
||||||
|
# Establishing connections if required
|
||||||
|
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
||||||
|
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
|
||||||
|
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" -InformationAction Continue
|
||||||
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Write-Information "A total of $($totalTests) tests were selected to run..." -InformationAction Continue
|
||||||
# Import the test functions
|
# Import the test functions
|
||||||
$testFiles | ForEach-Object {
|
$testFiles | ForEach-Object {
|
||||||
$currentTestIndex++
|
$currentTestIndex++
|
||||||
@@ -202,14 +278,16 @@ function Invoke-M365SecurityAudit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
End {
|
End {
|
||||||
if (!($DoNotDisconnect)) {
|
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Disconnect")) {
|
||||||
# Clean up sessions
|
# Clean up sessions
|
||||||
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
||||||
}
|
}
|
||||||
# Call the private function to calculate and display results
|
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
|
||||||
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
# Call the private function to calculate and display results
|
||||||
# Return all collected audit results
|
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
||||||
return $allAuditResults.ToArray() | Sort-Object -Property Rec
|
# Return all collected audit results
|
||||||
|
return $allAuditResults.ToArray() | Sort-Object -Property Rec
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,6 +44,7 @@ If the SkipUpdate switch is used, the function returns an array of custom object
|
|||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
||||||
#>
|
#>
|
||||||
function Sync-CISExcelAndCsvData {
|
function Sync-CISExcelAndCsvData {
|
||||||
|
[OutputType([void], [PSCustomObject[]])]
|
||||||
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
|
@@ -42,7 +42,7 @@ function Test-PasswordNeverExpirePolicy {
|
|||||||
$failureReasons = if ($isCompliant) {
|
$failureReasons = if ($isCompliant) {
|
||||||
"N/A"
|
"N/A"
|
||||||
} else {
|
} else {
|
||||||
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30"
|
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30`n"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = "$domainName|$passwordPolicy days|$isDefault"
|
$details = "$domainName|$passwordPolicy days|$isDefault"
|
||||||
|
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user