Skip to content

Commit

Permalink
Allow user to copy sample (read-only) files packaged with module (#862)
Browse files Browse the repository at this point in the history
* Add copy for samples

* Update SmokeTest to check support commands

* Fix linter errors

* Fix linter issues

* Remove hardcoded file names
  • Loading branch information
crutchfield authored Feb 15, 2024
1 parent fe53215 commit c2ae30d
Show file tree
Hide file tree
Showing 28 changed files with 272 additions and 6 deletions.
113 changes: 109 additions & 4 deletions PowerShell/ScubaGear/Modules/Support/Support.psm1
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
function Copy-ScubaBaselineDocument {
<#
.SYNOPSIS
Execute the SCuBAGear tool security baselines for specified M365 products.
Copy security baselines documents to a user specified location.
.Description
This is the main function that runs the Providers, Rego, and Report creation all in one PowerShell script call.
This function makes copies of the security baseline documents included with the installed ScubaGear module.
.Parameter Destination
Where to copy the baselines. Defaults to <user home>\ScubaGear\baselines
.Example
Expand All @@ -28,14 +28,119 @@ function Copy-ScubaBaselineDocument {
New-Item -ItemType Directory -Path $Destination | Out-Null
}

@("teams", "exo", "defender", "aad", "powerplatform", "sharepoint") | ForEach-Object {
@("teams", "exo", "defender", "aad", "powerbi", "powerplatform", "sharepoint") | ForEach-Object {
$SourceFileName = Join-Path -Path $PSScriptRoot -ChildPath "..\..\baselines\$_.md"
$TargetFileName = Join-Path -Path $Destination -ChildPath "$_.md"
Copy-Item -Path $SourceFileName -Destination $Destination -Force:$Force -ErrorAction Stop 2> $null
Set-ItemProperty -Path $TargetFileName -Name IsReadOnly -Value $true
}
}

function Copy-ScubaSampleReport {
<#
.SYNOPSIS
Copy sample reports to user defined location.
.Description
This function makes copies of the sample reports included with the installed ScubaGear module.
.Parameter Destination
Where to copy the samples. Defaults to <user home>\ScubaGear\samples\reports
.Example
Copy-ScubaSampleReport
.Functionality
Public
.NOTES
SuppressMessage for PSReviewUnusedParameter due to linter bug. Open issue to remove if/when fixed.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
param (
[Parameter(Mandatory = $false)]
[ValidateScript({Test-Path -Path $_ -IsValid})]
[string]
$DestinationDirectory = (Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/reports"),
[Parameter(Mandatory = $false)]
[switch]
$Force
)

$SourceDirectory = Join-Path -Path $PSScriptRoot -ChildPath "..\..\Sample-Reports\"
Copy-ScubaModuleFile -SourceDirectory $SourceDirectory -DestinationDirectory $DestinationDirectory -Force:$Force
}

function Copy-ScubaSampleConfigFile {
<#
.SYNOPSIS
Copy sample configuration files to user defined location.
.Description
This function makes copies of the sample configuration files included with the installed ScubaGear module.
.Parameter Destination
Where to copy the samples. Defaults to <user home>\ScubaGear\samples\config-files
.Example
Copy-ScubaSampleConfigFile
.Functionality
Public
.NOTES
SuppressMessage for PSReviewUnusedParameter due to linter bug. Open issue to remove if/when fixed.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
param (
[Parameter(Mandatory = $false)]
[ValidateScript({Test-Path -Path $_ -IsValid})]
[string]
$DestinationDirectory = (Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/config-files"),
[Parameter(Mandatory = $false)]
[switch]
$Force
)

$SourceDirectory = Join-Path -Path $PSScriptRoot -ChildPath "..\..\Sample-Config-Files\"
Copy-ScubaModuleFile -SourceDirectory $SourceDirectory -DestinationDirectory $DestinationDirectory -Force:$Force
}

function Copy-ScubaModuleFile {
<#
.SYNOPSIS
Copy Scuba module files (read-only) to user defined location.
.Description
This function makes copies of files included with the installed ScubaGear module.
.Parameter Destination
Where to copy the files.
.Example
Copy-ScubaModuleFile =Destination SomeWhere
.Functionality
Private
.NOTES
SuppressMessage for PSReviewUnusedParameter due to linter bug. Open issue to remove if/when fixed.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
param (
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path -Path $_ -PathType Container})]
[string]
$SourceDirectory,
[Parameter(Mandatory = $true)]
[ValidateScript({Test-Path -Path $_ -IsValid})]
[string]
$DestinationDirectory,
[Parameter(Mandatory = $false)]
[switch]
$Force
)

if (-not (Test-Path -Path $DestinationDirectory -PathType Container)){
New-Item -ItemType Directory -Path $DestinationDirectory | Out-Null
}

try {
Get-ChildItem -Path $SourceDirectory | Copy-Item -Destination $DestinationDirectory -Recurse -Container -Force:$Force -ErrorAction Stop 2> $null
Get-ChildItem -Path $DestinationDirectory -File -Recurse | ForEach-Object {$_.IsReadOnly = $true}
}
catch {
throw "Scuba copy module files failed."
}
}

Export-ModuleMember -Function @(
'Copy-ScubaBaselineDocument'
'Copy-ScubaBaselineDocument',
'Copy-ScubaSampleReport',
'Copy-ScubaSampleConfigFile'
)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion PowerShell/ScubaGear/ScubaGear.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ FunctionsToExport = @(
'Invoke-SCuBA',
'Invoke-RunCached',
'Disconnect-SCuBATenant',
'Copy-ScubaBaselineDocument'
'Copy-ScubaBaselineDocument',
'Copy-ScubaSampleReport',
'Copy-ScubaSampleConfigFile'
)

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "../../../../Modules/Support/Support.psm1") -Function 'Copy-ScubaConfigFile' -Force

InModuleScope Support {
Describe "Copy sample config files to specified directory" {
$SampleFiles = (Get-ChildItem PowerShell/ScubaGear/Sample-Config-Files).Name
BeforeAll{
$SampleConfigCopyFolder = Join-Path -Path $env:Temp -ChildPath 'samples/config-files'
if (Test-Path -Path $SampleConfigCopyFolder){
Remove-Item -Path $SampleConfigCopyFolder -Recurse -Force
}
}
It "Call Copy-ScubaSampleConfigFile with bad destination" {
{Copy-ScubaSampleConfigFile -Destination "$SampleConfigCopyFolder\`tInvalid-"} |
Should -Throw -Because "directory does not exist."
}
It "Call Copy-ScubaSampleConfigFile with good destination"{
Test-Path -Path $SampleConfigCopyFolder -PathType Container | Should -Not -BeTrue
{Copy-ScubaSampleConfigFile -Destination $SampleConfigCopyFolder} |
Should -Not -Throw
}
It "Top level sample file, <_>, is copied" -ForEach $SampleFiles {
$ItemPath = Join-Path -Path $SampleConfigCopyFolder -ChildPath $_
Test-Path -Path $ItemPath -PathType Leaf | Should -BeTrue
}
It "Sample document, <_>, is read only" -ForEach $SampleFiles{
$ItemPath = Join-Path -Path $SampleConfigCopyFolder -ChildPath $_
Get-Item -Path $ItemPath | Select-Object IsReadyOnly | Should -BeTrue
}
It "Call Copy-ScubaSampleConfigFile already exists - Not Force update"{
$PreviousCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleConfigCopyFolder -ChildPath "aad-config.yaml")).CreationTime
Test-Path -Path $SampleConfigCopyFolder -PathType Container | Should -BeTrue
{Copy-ScubaSampleConfigFile -Destination $SampleConfigCopyFolder} |
Should -Throw -ExpectedMessage "Scuba copy module files failed."
$CurrentCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleConfigCopyFolder -ChildPath "aad-config.yaml")).CreationTime
$PreviousCreateTime -eq $CurrentCreateTime | Should -BeTrue
}
It "Call Copy-ScubaSampleConfigFile already exists - Force Update"{
$PreviousCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleConfigCopyFolder -ChildPath "aad-config.yaml")).CreationTime
Test-Path -Path $SampleConfigCopyFolder -PathType Container | Should -BeTrue
{Copy-ScubaSampleConfigFile -Destination $SampleConfigCopyFolder -Force} |
Should -Not -Throw
$CurrentCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleConfigCopyFolder -ChildPath "aad-config.yaml")).CreationTime
($CurrentCreateTime -ge $PreviousCreateTime) | Should -BeTrue -Because "$($CurrentCreateTime) vs $($PreviousCreateTime)"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "../../../../Modules/Support/Support.psm1") -Function 'Copy-ScubaSampleReport' -Force

InModuleScope Support {
Describe "Copy Sample Reports to specified directory" {
$SampleFiles = @(
"BaselineReports.html",
"ProviderSettingsExport.json",
"TestResults.csv",
"TestResults.json"
)
$SampleFolders = @(
"individualReports"
"individualReports/images"
)
BeforeAll{
$SampleReportsCopyFolder = Join-Path -Path $env:Temp -ChildPath 'samples/reports'
if (Test-Path -Path $SampleReportsCopyFolder){
Remove-Item -Path $SampleReportsCopyFolder -Recurse -Force
}
}
It "Call Copy-ScubaSampleReports with bad destination" {
{Copy-ScubaSampleReport -Destination "$SampleReportsCopyFolder\`tInvalid-"} |
Should -Throw -Because "directory does not exist."
}
It "Call Copy-ScubaSampleReports with good destination"{
Test-Path -Path $SampleReportsCopyFolder -PathType Container | Should -Not -BeTrue
{Copy-ScubaSampleReport -Destination $SampleReportsCopyFolder} |
Should -Not -Throw
}
It "Top level sample file, <_>, is copied" -ForEach $SampleFiles {
$ItemPath = Join-Path -Path $SampleReportsCopyFolder -ChildPath $_
Test-Path -Path $ItemPath -PathType Leaf | Should -BeTrue
}
It "Sample sub directory, <_>, is copied" -ForEach $SampleFolders {
$ItemPath = Join-Path -Path $SampleReportsCopyFolder -ChildPath $_
Test-Path -Path $ItemPath -PathType Container | Should -BeTrue
}
It "Sample document, <_>, is read only" -ForEach $SampleFiles{
$ItemPath = Join-Path -Path $SampleReportsCopyFolder -ChildPath "$_"
Get-Item -Path $ItemPath | Select-Object IsReadyOnly | Should -BeTrue
}
It "Sample folder, <_>, is read only" -ForEach $SampleFolders{
$ItemPath = Join-Path -Path $SampleReportsCopyFolder -ChildPath "$_"
Get-Item -Path $ItemPath | Select-Object IsReadyOnly | Should -BeTrue
}
It "Call Copy-ScubaSampleReports already exists - Not Force update"{
$PreviousCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleReportsCopyFolder -ChildPath "BaselineReports.html")).CreationTime
Test-Path -Path $SampleReportsCopyFolder -PathType Container | Should -BeTrue
{Copy-ScubaSampleReport -Destination $SampleReportsCopyFolder} |
Should -Throw -ExpectedMessage "Scuba copy module files failed."
$CurrentCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleReportsCopyFolder -ChildPath "BaselineReports.html")).CreationTime
$PreviousCreateTime -eq $CurrentCreateTime | Should -BeTrue
}
It "Call Copy-ScubaSampleReports already exists - Force Update"{
$PreviousCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleReportsCopyFolder -ChildPath "BaselineReports.html")).CreationTime
Test-Path -Path $SampleReportsCopyFolder -PathType Container | Should -BeTrue
{Copy-ScubaSampleReport -Destination $SampleReportsCopyFolder -Force} |
Should -Not -Throw
$CurrentCreateTime = [System.DateTime](Get-Item -Path (Join-Path -Path $SampleReportsCopyFolder -ChildPath "BaselineReports.html")).CreationTime
($CurrentCreateTime -ge $PreviousCreateTime) | Should -BeTrue -Because "$($CurrentCreateTime) vs $($PreviousCreateTime)"
}
}
}
50 changes: 49 additions & 1 deletion Testing/Functional/SmokeTest/SmokeTest001.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,53 @@ Describe "Smoke Test: Generate Output" {
){
Test-Path -Path "./$OutputFolder/$Item" -PathType $ItemType |
Should -Be $true
} }
}
}
Context "Verify exported functions for ScubaGear module" {
BeforeAll{
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'ScubaGearExportedFunctions',
Justification = 'Variable is used in another scope')]
$ScubaGearExportedFunctions = @(
'Disconnect-SCuBATenant',
'Invoke-RunCached',
'Invoke-SCuBA',
'Copy-ScubaBaselineDocument',
'Copy-ScubaSampleConfigFile',
'Copy-ScubaSampleReport'
)
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'ExportedCommands',
Justification = 'Variable is used in another scope')]
$ExportedCommands = (Get-Module -Name ScubaGear).ExportedCommands
}
It "Is <_> exported?" -ForEach $ScubaGearExportedFunctions {
$ExportedCommands | Should -Contain $_
}
}
Context "Verify Copy* exported commands" -ForEach @(
@{Command='Copy-ScubaBaselineDocument'; CopiedFiles=@(
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/aad.md"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/defender.md"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/exo.md"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/powerbi.md"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/powerplatform.md"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/sharepoint.md"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/teams.md")
)},
@{Command='Copy-ScubaSampleConfigFile'; CopiedFiles=@(
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/config-files/aad-config.yaml"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/config-files/defender-config.yaml"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/config-files/sample-config.json"),
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/config-files/sample-config.yaml")
)},
@{Command='Copy-ScubaSampleReport'; CopiedFiles=@(
(Join-Path -Path $env:USERPROFILE -ChildPath "ScubaGear/samples/reports/BaselineReports.html")
)}
){
It "Validate call to <Command>" {
{& $Command -Force} | Should -Not -Throw
}
It "Validate <Command> copied file <_>" -ForEach $CopiedFiles {
Test-Path -Path $_ -PathType Leaf | Should -BeTrue
}
}
}
1 change: 1 addition & 0 deletions sample-config-files/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The sample configuration files have been moved into the ScubaGear PowerShell module for easier distribution. The configuration files can now be found [here](../PowerShell/ScubaGear/Sample-Config-Files)
1 change: 1 addition & 0 deletions sample-report/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The sample reports have been moved into the ScubaGear PowerShell module for easier distribution. The reports can now be found [here](../PowerShell/ScubaGear/Sample-Reports)

0 comments on commit c2ae30d

Please sign in to comment.