diff --git a/.github/workflows/Build Module.yml b/.github/workflows/Build Module.yml index f502168..b2b59ee 100644 --- a/.github/workflows/Build Module.yml +++ b/.github/workflows/Build Module.yml @@ -19,10 +19,13 @@ on: workflow_dispatch: +permissions: + contents: read + jobs: test: name: ๐Ÿงช Run Tests - runs-on: ubuntu-latest + runs-on: windows-latest strategy: fail-fast: false @@ -31,11 +34,6 @@ jobs: - name: โœ… Checkout Repository uses: actions/checkout@v4 - # Uncomment below to explore what modules/variables/env variables are available in the build image - - name: ๐Ÿ“ฆ Modules and Variables Display - shell: pwsh - run: Get-Module -ListAvailable; (Get-Variable).GetEnumerator() | Sort-Object Name | Out-String; (Get-ChildItem env:*).GetEnumerator() | Sort-Object Name | Out-String - - name: ๐Ÿฅพ Bootstrap shell: pwsh run: ./actions_bootstrap.ps1 @@ -57,9 +55,3 @@ jobs: name: zip-archive path: .\src\Archive if-no-files-found: warn - # git-auto-commit-action only runs on Linux-based platforms. - - - name: ๐Ÿ’พ Commit Changes - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: 'Commit Build' diff --git a/docs/Clear-OldIISLog.md b/docs/Clear-OldIISLog.md index 36696ef..3f7ae3f 100644 --- a/docs/Clear-OldIISLog.md +++ b/docs/Clear-OldIISLog.md @@ -1,65 +1,97 @@ ---- -external help file: TheCleaners-help.xml -Module Name: TheCleaners -online version: -schema: 2.0.0 ---- - -# Clear-OldIISLog - -## SYNOPSIS -A script to clean out old IIS log files. - -## SYNTAX - -``` -Clear-OldIISLog [[-Days] ] [] -``` - -## DESCRIPTION -This script will clean out IIS log files older than x days. - -## EXAMPLES - -### EXAMPLE 1 -``` -Clear-OldIISLogFile -Days 60 -``` - -Removes all IIS log files that are older than 60 days. - -## PARAMETERS - -### -Days -The number of days to keep log files. -The default is 30 days. - -```yaml -Type: Int16 -Parameter Sets: (All) -Aliases: - -Required: False -Position: 1 -Default value: 60 -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. -For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES -If the WebAdministration module is available, it will use that to check the specific log file locations for -each web site. -Otherwise, it checks the assumed default log folder location and the registry for the IIS -log file location. - -To Do: Add a summary of which blocks were run and possibly a count of log files removed. - +--- +external help file: TheCleaners-help.xml +Module Name: TheCleaners +online version: +schema: 2.0.0 +--- + +# Clear-OldIISLog + +## SYNOPSIS +A script to clean out old IIS log files. + +## SYNTAX + +``` +Clear-OldIISLog [[-Days] ] [-WhatIf] [-Confirm] + [] +``` + +## DESCRIPTION +This script will clean out IIS log files older than x days. + +## EXAMPLES + +### EXAMPLE 1 +``` +Clear-OldIISLog -Days 60 +``` + +Removes all IIS log files that are older than 60 days. + +## PARAMETERS + +### -Days +The number of days to keep log files. +The default is 60 days. + +```yaml +Type: Int16 +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: 60 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. +For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +If the WebAdministration module is available, it will use that to check the specific log file locations for +each web site. +Otherwise, it checks the assumed default log folder location and the registry for the IIS +log file location. + +To Do: Add a summary of which blocks were run and possibly a count of log files removed. + ## RELATED LINKS diff --git a/docs/Start-Cleaning.md b/docs/Start-Cleaning.md index 03d6b57..90d676a 100644 --- a/docs/Start-Cleaning.md +++ b/docs/Start-Cleaning.md @@ -1,58 +1,58 @@ ---- -external help file: TheCleaners-help.xml -Module Name: TheCleaners -online version: -schema: 2.0.0 ---- - -# Start-Cleaning - -## SYNOPSIS -Show the commands you can give The Cleaners. - -## SYNTAX - -``` -Start-Cleaning [-Dedication] [] -``` - -## DESCRIPTION -Get started with a menu of services The Cleaners can offer. - -## EXAMPLES - -### EXAMPLE 1 -``` -Start-Cleaning -``` - -View the menu of services that TheCleaners provide. - -## PARAMETERS - -### -Dedication -Show dedication - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. -For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - +--- +external help file: TheCleaners-help.xml +Module Name: TheCleaners +online version: +schema: 2.0.0 +--- + +# Start-Cleaning + +## SYNOPSIS +Show the commands you can give The Cleaners. + +## SYNTAX + +``` +Start-Cleaning [-Dedication] [] +``` + +## DESCRIPTION +Get started with a menu of services The Cleaners can offer. + +## EXAMPLES + +### EXAMPLE 1 +``` +Start-Cleaning +``` + +View the menu of services that TheCleaners provide. + +## PARAMETERS + +### -Dedication +Show a short dedication before the command menu. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. +For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + ## RELATED LINKS diff --git a/src/Tests/SkipUnit/ExportedFunctions.Tests.ps1 b/src/Tests/SkipUnit/ExportedFunctions.Tests.ps1 deleted file mode 100644 index e1ad8a0..0000000 --- a/src/Tests/SkipUnit/ExportedFunctions.Tests.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -#------------------------------------------------------------------------- -Set-Location -Path $PSScriptRoot -#------------------------------------------------------------------------- -$ModuleName = 'TheCleaners' -$PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") -#------------------------------------------------------------------------- -if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { - #if the module is already in memory, remove it - Remove-Module -Name $ModuleName -Force -} -Import-Module $PathToManifest -Force -#------------------------------------------------------------------------- - -BeforeAll { - Set-Location -Path $PSScriptRoot - $ModuleName = 'TheCleaners' - $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") - $manifestContent = Test-ModuleManifest -Path $PathToManifest - $moduleExported = Get-Command -Module $ModuleName | Select-Object -ExpandProperty Name - $manifestExported = ($manifestContent.ExportedFunctions).Keys -} -Describe $ModuleName { - - Context 'Exported Commands' -Fixture { - - Context 'Number of commands' -Fixture { - It -Name 'Exports the same number of public functions as what is listed in the Module Manifest' -Test { - $manifestExported.Count | Should -BeExactly $moduleExported.Count - } - } - - Context 'Explicitly exported commands' -ForEach $moduleExported { - foreach ($command in $moduleExported) { - BeforeAll { - $command = $_ - } - It -Name "Includes the $command in the Module Manifest ExportedFunctions" -Test { - $manifestExported -contains $command | Should -BeTrue - } - } - } - } - - Context 'Command Help' -ForEach $moduleExported { - foreach ($command in $moduleExported) { - BeforeAll { - $help = Get-Help -Name $_ -Full - } - Context $command -Fixture { - $help = Get-Help -Name $command -Full - - It -Name 'Includes a Synopsis' -Test { - $help.Synopsis | Should -Not -BeNullOrEmpty - } - - It -Name 'Includes a Description' -Test { - $help.description.Text | Should -Not -BeNullOrEmpty - } - - It -Name 'Includes an Example' -Test { - $help.examples.example | Should -Not -BeNullOrEmpty - } - } - } - } -} diff --git a/src/Tests/Unit/CleanupBehavior.Tests.ps1 b/src/Tests/Unit/CleanupBehavior.Tests.ps1 new file mode 100644 index 0000000..594ad24 --- /dev/null +++ b/src/Tests/Unit/CleanupBehavior.Tests.ps1 @@ -0,0 +1,102 @@ +BeforeAll { + $ModuleRoot = (Resolve-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '..\..\TheCleaners')).Path + . (Join-Path -Path $ModuleRoot -ChildPath 'Private\Remove-OldFiles.ps1') + . (Join-Path -Path $ModuleRoot -ChildPath 'Public\Clear-CurrentUserTemp.ps1') + . (Join-Path -Path $ModuleRoot -ChildPath 'Public\Clear-OldIISLog.ps1') + . (Join-Path -Path $ModuleRoot -ChildPath 'Public\Clear-OldExchangeLog.ps1') +} + +Describe 'Remove-OldFiles' -Tag Unit { + BeforeEach { + $TestRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([guid]::NewGuid().Guid) + New-Item -Path $TestRoot -ItemType Directory -Force | Out-Null + $OldFile = New-Item -Path (Join-Path -Path $TestRoot -ChildPath 'old.log') -ItemType File + $NewFile = New-Item -Path (Join-Path -Path $TestRoot -ChildPath 'new.log') -ItemType File + $OldFile.LastWriteTime = (Get-Date).AddDays(-31) + $NewFile.LastWriteTime = Get-Date + } + + AfterEach { + Remove-Item -LiteralPath $TestRoot -Recurse -Force -ErrorAction SilentlyContinue + } + + It 'removes files older than the retention window by LastWriteTime' { + Remove-OldFiles -Path $TestRoot -Days 30 -Confirm:$false + + $OldFile.FullName | Should -Not -Exist + $NewFile.FullName | Should -Exist + } + + It 'does not remove matching files when WhatIf is used' { + Remove-OldFiles -Path $TestRoot -Days 30 -WhatIf + + $OldFile.FullName | Should -Exist + $NewFile.FullName | Should -Exist + } +} + +Describe 'Clear-CurrentUserTemp' -Tag Unit { + BeforeEach { + $PreviousTemp = $env:TEMP + $TestRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([guid]::NewGuid().Guid) + $NestedDirectory = Join-Path -Path $TestRoot -ChildPath 'empty\child' + New-Item -Path $NestedDirectory -ItemType Directory -Force | Out-Null + $OldFile = New-Item -Path (Join-Path -Path $NestedDirectory -ChildPath 'old.tmp') -ItemType File + $OldFile.LastWriteTime = (Get-Date).AddDays(-31) + $env:TEMP = $TestRoot + } + + AfterEach { + $env:TEMP = $PreviousTemp + Remove-Item -LiteralPath $TestRoot -Recurse -Force -ErrorAction SilentlyContinue + } + + It 'removes empty directories after removing old files' -Skip:$IsLinux { + Clear-CurrentUserTemp -Days 30 -TimeOut 5 -Confirm:$false | Out-Null + + (Join-Path -Path $TestRoot -ChildPath 'empty') | Should -Not -Exist + } +} + +Describe 'Clear-OldExchangeLog' -Tag Unit { + BeforeEach { + $TestRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([guid]::NewGuid().Guid) + $ExchangeLoggingPath = Join-Path -Path $TestRoot -ChildPath 'Logging' + New-Item -Path $ExchangeLoggingPath -ItemType Directory -Force | Out-Null + New-Item -Path (Join-Path -Path $TestRoot -ChildPath 'Bin\Search\Ceres\Diagnostics\ETLTraces') -ItemType Directory -Force | Out-Null + New-Item -Path (Join-Path -Path $TestRoot -ChildPath 'Bin\Search\Ceres\Diagnostics\Logs') -ItemType Directory -Force | Out-Null + New-Item -Path (Join-Path -Path $TestRoot -ChildPath 'TransportRoles\Logs\MessageTracking') -ItemType Directory -Force | Out-Null + $OldLog = New-Item -Path (Join-Path -Path $ExchangeLoggingPath -ChildPath 'old.log') -ItemType File + $NewLog = New-Item -Path (Join-Path -Path $ExchangeLoggingPath -ChildPath 'new.log') -ItemType File + $IgnoredFile = New-Item -Path (Join-Path -Path $ExchangeLoggingPath -ChildPath 'old.txt') -ItemType File + $OldLog.LastWriteTime = (Get-Date).AddDays(-31) + $IgnoredFile.LastWriteTime = (Get-Date).AddDays(-31) + $NewLog.LastWriteTime = Get-Date + + Mock Get-ItemProperty { + [pscustomobject]@{ + MsiInstallPath = $TestRoot + } + } + Mock Clear-OldIISLog {} + } + + AfterEach { + Remove-Item -LiteralPath $TestRoot -Recurse -Force -ErrorAction SilentlyContinue + } + + It 'calls the existing IIS cleanup command' { + Clear-OldExchangeLog -Days 30 -WhatIf + + Should -Invoke Clear-OldIISLog -Exactly 1 -ParameterFilter { $Days -eq 30 } + } + + It 'removes only old Exchange log files' { + Clear-OldExchangeLog -Days 30 -Confirm:$false + + $OldLog.FullName | Should -Not -Exist + $NewLog.FullName | Should -Exist + $IgnoredFile.FullName | Should -Exist + } +} + diff --git a/src/Tests/Unit/ExportedFunctions.Tests.ps1 b/src/Tests/Unit/ExportedFunctions.Tests.ps1 new file mode 100644 index 0000000..6ce2572 --- /dev/null +++ b/src/Tests/Unit/ExportedFunctions.Tests.ps1 @@ -0,0 +1,43 @@ +#------------------------------------------------------------------------- +BeforeAll { + Set-Location -Path $PSScriptRoot + $ModuleName = 'TheCleaners' + $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") + if (Get-Module -Name $ModuleName -ErrorAction 'SilentlyContinue') { + Remove-Module -Name $ModuleName -Force + } + Import-Module $PathToManifest -Force + $manifestContent = Test-ModuleManifest -Path $PathToManifest + # Limit the runtime query to functions so it matches the manifest's ExportedFunctions collection. + $moduleExported = Get-Command -Module $ModuleName -CommandType Function | Select-Object -ExpandProperty Name + $manifestExported = ($manifestContent.ExportedFunctions).Keys +} + +Describe $ModuleName { + + Context 'Exported Commands' -Fixture { + + Context 'Number of commands' -Fixture { + It -Name 'Exports the same number of public functions as what is listed in the Module Manifest' -Test { + $manifestExported.Count | Should -BeExactly $moduleExported.Count + } + } + + Context 'Explicitly exported commands' { + It -Name 'Includes <_> in the Module Manifest ExportedFunctions' -ForEach $moduleExported -Test { + $manifestExported | Should -Contain $_ + } + } + } + + Context 'Command Help' { + It -Name '<_> includes complete help' -ForEach $moduleExported -Test { + $help = Get-Help -Name $_ -Full + + $help.Synopsis | Should -Not -BeNullOrEmpty + $help.description.Text | Should -Not -BeNullOrEmpty + $help.examples.example | Should -Not -BeNullOrEmpty + } + } +} + diff --git a/src/Tests/SkipUnit/TheCleaners-Module.Tests.ps1 b/src/Tests/Unit/TheCleaners-Module.Tests.ps1 similarity index 92% rename from src/Tests/SkipUnit/TheCleaners-Module.Tests.ps1 rename to src/Tests/Unit/TheCleaners-Module.Tests.ps1 index 7fde39a..2858d4d 100644 --- a/src/Tests/SkipUnit/TheCleaners-Module.Tests.ps1 +++ b/src/Tests/Unit/TheCleaners-Module.Tests.ps1 @@ -7,43 +7,57 @@ BeforeAll { $PathToModule = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psm1") #------------------------------------------------------------------------- } + Describe 'Module Tests' -Tag Unit { - Context "Module Tests" { - $script:manifestEval = $null + Context 'Module Tests' { + BeforeAll { + $script:manifestEval = $null + } + It 'Passes Test-ModuleManifest' { { $script:manifestEval = Test-ModuleManifest -Path $PathToManifest } | Should -Not -Throw $? | Should -BeTrue } #manifestTest + It 'root module TheCleaners.psm1 should exist' { $PathToModule | Should -Exist $? | Should -BeTrue } #psm1Exists + It 'manifest should contain TheCleaners.psm1' { $PathToManifest | - Should -FileContentMatchExactly "TheCleaners.psm1" + Should -FileContentMatchExactly 'TheCleaners.psm1' } #validPSM1 + It 'should have a matching module name in the manifest' { $script:manifestEval.Name | Should -BeExactly $ModuleName } #name + It 'should have a valid description in the manifest' { $script:manifestEval.Description | Should -Not -BeNullOrEmpty } #description + It 'should have a valid author in the manifest' { $script:manifestEval.Author | Should -Not -BeNullOrEmpty } #author + It 'should have a valid version in the manifest' { $script:manifestEval.Version -as [Version] | Should -Not -BeNullOrEmpty } #version + It 'should have a valid guid in the manifest' { { [guid]::Parse($script:manifestEval.Guid) } | Should -Not -Throw } #guid + It 'should not have any spaces in the tags' { foreach ($tag in $script:manifestEval.Tags) { $tag | Should -Not -Match '\s' } } #tagSpaces + It 'should have a valid project Uri' { $script:manifestEval.ProjectUri | Should -Not -BeNullOrEmpty } #uri } #context_ModuleTests } #describe_ModuleTests + diff --git a/src/TheCleaners.build.ps1 b/src/TheCleaners.build.ps1 index 934b069..e5b3c6f 100644 --- a/src/TheCleaners.build.ps1 +++ b/src/TheCleaners.build.ps1 @@ -249,7 +249,10 @@ Add-BuildTask Test { $pesterConfiguration.Run.PassThru = $true $pesterConfiguration.Run.Exit = $false $pesterConfiguration.CodeCoverage.Enabled = $true - $pesterConfiguration.CodeCoverage.Path = "..\..\..\$ModuleName\*\*.ps1" + $pesterConfiguration.CodeCoverage.Path = @( + Join-Path -Path $script:ModuleSourcePath -ChildPath '*.ps1' + Join-Path -Path $script:ModuleSourcePath -ChildPath '*\*.ps1' + ) $pesterConfiguration.CodeCoverage.CoveragePercentTarget = $script:coverageThreshold $pesterConfiguration.CodeCoverage.OutputPath = "$codeCovPath\CodeCoverage.xml" $pesterConfiguration.CodeCoverage.OutputFormat = 'JaCoCo' @@ -303,7 +306,10 @@ Add-BuildTask DevCC { $pesterConfiguration = New-PesterConfiguration $pesterConfiguration.run.Path = $script:UnitTestsPath $pesterConfiguration.CodeCoverage.Enabled = $true - $pesterConfiguration.CodeCoverage.Path = "$PSScriptRoot\$ModuleName\*\*.ps1" + $pesterConfiguration.CodeCoverage.Path = @( + Join-Path -Path $script:ModuleSourcePath -ChildPath '*.ps1' + Join-Path -Path $script:ModuleSourcePath -ChildPath '*\*.ps1' + ) $pesterConfiguration.CodeCoverage.CoveragePercentTarget = $script:coverageThreshold $pesterConfiguration.CodeCoverage.OutputPath = '..\..\..\cov.xml' $pesterConfiguration.CodeCoverage.OutputFormat = 'CoverageGutters' diff --git a/src/TheCleaners/Private/Remove-OldFiles.ps1 b/src/TheCleaners/Private/Remove-OldFiles.ps1 index 2d3abff..6567cfc 100644 --- a/src/TheCleaners/Private/Remove-OldFiles.ps1 +++ b/src/TheCleaners/Private/Remove-OldFiles.ps1 @@ -6,19 +6,24 @@ Remove files in a path that are older than the specified number of days. This function is used by other functions within the module when removing old files. .EXAMPLE - Remove-OldFiles -Path "C:\Windows\Temp" -Days 60 -Recurse + Remove-OldFiles -Path "C:\Windows\Temp" -Days 60 - Removes all files older than 60 does in C:\Windows\Temp with recursion to clean subfolders. + Removes all files older than 60 days in C:\Windows\Temp, including subfolders. #> function Remove-OldFiles { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns')] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] param ( # The path containing files to remove + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })] [string] $Path, # How many days worth of logs to retain (how far back to filter) + [Parameter()] + [ValidateRange(1, [int16]::MaxValue)] [int16] $Days = 60 ) @@ -29,9 +34,15 @@ function Remove-OldFiles { process { Write-Verbose -Message "Finding and removing files older than $Days." - Get-ChildItem -Path $Path -Recurse | Where-Object { - $_.CreationTime -le ([datetime]::Now.AddDays( -$Days )) - } | Remove-Item + $OldFiles = Get-ChildItem -LiteralPath $Path -File -Recurse -ErrorAction Stop | Where-Object { + $_.LastWriteTime -le ([datetime]::Now.AddDays(-$Days)) + } + + foreach ($File in $OldFiles) { + if ($PSCmdlet.ShouldProcess($File.FullName, 'Remove old file')) { + Remove-Item -LiteralPath $File.FullName -ErrorAction Stop + } + } } end { diff --git a/src/TheCleaners/Public/Clear-CurrentUserTemp.ps1 b/src/TheCleaners/Public/Clear-CurrentUserTemp.ps1 index 263304b..1131621 100644 --- a/src/TheCleaners/Public/Clear-CurrentUserTemp.ps1 +++ b/src/TheCleaners/Public/Clear-CurrentUserTemp.ps1 @@ -85,9 +85,23 @@ function Clear-CurrentUserTemp { break } # Get directories that have 0 files in them. - $EmptyDirectories = Get-ChildItem -Path $UserTempPath -Directory -Recurse | Where-Object { $_.GetFileSystemInfos().Count -eq 0 } | Out-Null + $EmptyDirectories = @(Get-ChildItem -Path $UserTempPath -Directory -Recurse | Where-Object { $_.GetFileSystemInfos().Count -eq 0 }) Write-Verbose "$($EmptyDirectories.Count) empty directories found." - $EmptyDirectories | Remove-Item + $RemovedDirectory = $false + foreach ($Directory in $EmptyDirectories) { + if ($PSCmdlet.ShouldProcess("Removing $($Directory.FullName)", $Directory.FullName, 'Remove-Item')) { + try { + Remove-Item -LiteralPath $Directory.FullName -ErrorAction Stop + $RemovedDirectory = $true + } catch { + Write-Warning -Message "Failed to remove directory '$($Directory.FullName)': $($_.Exception.Message)" + } + } + } + + if ($EmptyDirectories.Count -gt 0 -and -not $RemovedDirectory) { + break + } } until ( $EmptyDirectories.Count -eq 0 ) diff --git a/src/TheCleaners/Public/Clear-OldExchangeLog.ps1 b/src/TheCleaners/Public/Clear-OldExchangeLog.ps1 index 8dece43..d374e36 100644 --- a/src/TheCleaners/Public/Clear-OldExchangeLog.ps1 +++ b/src/TheCleaners/Public/Clear-OldExchangeLog.ps1 @@ -28,7 +28,8 @@ function Clear-OldExchangeLog { # Logs older than this number of days will be removed. param ( [Parameter()] - [int] + [ValidateRange(1, [int16]::MaxValue)] + [int16] $Days = 60 ) @@ -45,8 +46,8 @@ function Clear-OldExchangeLog { $LogLocations = @{ ExchangeLoggingPath = Join-Path -Path $ExchangeInstallPath -ChildPath 'Logging\' -ErrorAction Ignore ETLTracesPath = Join-Path -Path $ExchangeInstallPath -ChildPath 'Bin\Search\Ceres\Diagnostics\ETLTraces\' -ErrorAction Ignore - DiagnosticLogsPath = Join-Path -Path $ExchangeInstallPath -ChildPath '\Bin\Search\Ceres\Diagnostics\Logs' -ErrorAction Ignore - MessageTrackingLogsPath = Join-Path -Path $ExchangeInstallPath -ChildPath '\TransportRoles\Logs\MessageTracking\' -ErrorAction Ignore + DiagnosticLogsPath = Join-Path -Path $ExchangeInstallPath -ChildPath 'Bin\Search\Ceres\Diagnostics\Logs' -ErrorAction Ignore + MessageTrackingLogsPath = Join-Path -Path $ExchangeInstallPath -ChildPath 'TransportRoles\Logs\MessageTracking\' -ErrorAction Ignore } $LastWriteDate = (Get-Date).AddDays(-$Days) @@ -54,7 +55,7 @@ function Clear-OldExchangeLog { process { # Clean up the IIS log files - Clear-OldIisLogFiles -Days $Days + Clear-OldIISLog -Days $Days -WhatIf:$WhatIfPreference foreach ($LogLocation in $LogLocations.GetEnumerator()) { if (-not (Test-Path -Path $LogLocation.Value)) { @@ -62,12 +63,14 @@ function Clear-OldExchangeLog { continue } - $OldFiles = Get-ChildItem -Path $($LogLocation.Value) -Recurse | - Where-Object { ($_.Name -like '*.log') -and ($_.lastWriteTime -le "$LastWriteDate") } | Select-Object FullName + $OldFiles = Get-ChildItem -Path $($LogLocation.Value) -File -Recurse | + Where-Object { ($_.Name -like '*.log') -and ($_.LastWriteTime -le $LastWriteDate) } - foreach ($file in $OldFiles) { - if ( $PSCmdlet.ShouldProcess($file.Name) ) { - $file.Delete() + foreach ($File in $OldFiles) { + if ($PSCmdlet.ShouldProcess($File.FullName, 'Remove old Exchange log file')) { + # Confirmation is handled by the outer ShouldProcess check, so suppress + # nested Remove-Item confirmation to avoid duplicate prompts. + Remove-Item -LiteralPath $File.FullName -Confirm:$false -ErrorAction Stop } } # end foreach $file diff --git a/src/TheCleaners/Public/Clear-OldIISLog.ps1 b/src/TheCleaners/Public/Clear-OldIISLog.ps1 index 6e3ca8d..a5808a7 100644 --- a/src/TheCleaners/Public/Clear-OldIISLog.ps1 +++ b/src/TheCleaners/Public/Clear-OldIISLog.ps1 @@ -22,7 +22,7 @@ function Clear-OldIISLog { To Do: Add a summary of which blocks were run and possibly a count of log files removed. #> - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [Alias('Clean-IISLog')] #[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns')] param ( @@ -40,7 +40,9 @@ function Clear-OldIISLog { $SiteLogFileDirectory = ("$($Site.logFile.directory)\W3SVC$($Site.id)").Replace( '%SystemDrive%', $env:SystemDrive ) Write-Information -MessageData "Removing old IIS log files from $($Site.name) at $SiteLogFileDirectory." -InformationAction Continue try { - Remove-OldFiles -Path $SiteLogFileDirectory -Days $Days -WhatIf + if ($PSCmdlet.ShouldProcess($SiteLogFileDirectory, "Remove IIS log files older than $Days days")) { + Remove-OldFiles -Path $SiteLogFileDirectory -Days $Days -Confirm:$false + } } catch { Write-Error -Message $_.Exception.Message -ErrorAction Continue Write-Warning "Failed to remove old IIS log files from $($Site.name) at $SiteLogFileDirectory." -WarningAction Continue @@ -52,7 +54,9 @@ function Clear-OldIISLog { Write-Information "The WebAdministration module is not installed. We will check the default IIS log file location at '$DefaultIISLogLocation'." -InformationAction Continue if (Test-Path -Path $DefaultIISLogLocation -ErrorAction SilentlyContinue) { try { - Remove-OldFiles -Path $DefaultIISLogLocation -Days $Days + if ($PSCmdlet.ShouldProcess($DefaultIISLogLocation, "Remove IIS log files older than $Days days")) { + Remove-OldFiles -Path $DefaultIISLogLocation -Days $Days -Confirm:$false + } } catch { Write-Error -Message $_.Exception.Message -ErrorAction Continue Write-Warning "Failed to remove old log files from the default IIS log file location at '$DefaultIISLogLocation'." -WarningAction Continue @@ -65,7 +69,9 @@ function Clear-OldIISLog { $LogDir = Get-ItemProperty -Path 'HKLM:\\System\CurrentControlSet\Services\W3SVC\Parameters' -Name 'LogDir' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty LogDir if ($LogDir -and (Test-Path -Path $LogDir)) { try { - Remove-OldFiles -Path $LogDir -Days $Days + if ($PSCmdlet.ShouldProcess($LogDir, "Remove IIS log files older than $Days days")) { + Remove-OldFiles -Path $LogDir -Days $Days -Confirm:$false + } } catch { Write-Error -Message $_.Exception.Message -ErrorAction Continue Write-Warning "Failed to remove old IIS log files from the location specified in the directory ($LogDir)." -WarningAction Continue diff --git a/src/TheCleaners/Public/Get-StaleUserProfile.ps1 b/src/TheCleaners/Public/Get-StaleUserProfile.ps1 index e09bfa7..b9ac789 100644 --- a/src/TheCleaners/Public/Get-StaleUserProfile.ps1 +++ b/src/TheCleaners/Public/Get-StaleUserProfile.ps1 @@ -37,7 +37,7 @@ function Get-StaleUserProfile { [array]$StaleUserProfiles = Get-CimInstance -Class Win32_UserProfile | Where-Object { ($_.LastUseTime -lt (Get-Date).AddDays(-$Days)) -and (!$_.Special) -and (!$_.Loaded) } # Might need to check last modified date using NTFS: foreach ($profile in $StaleUserProfiles) { Get-Item -Path $($_.LocalPath).LastWriteTime } - if ($StaleUserProfiles.Count -lt 1 -or !(StaleUserProfiles)) { + if ($StaleUserProfiles.Count -lt 1 -or -not $StaleUserProfiles) { Write-Information 'No stale user profiles were found.' -InformationAction Continue } else { if ($ShowSummary) { diff --git a/src/TheCleaners/Public/Start-Cleaning.ps1 b/src/TheCleaners/Public/Start-Cleaning.ps1 index 5f208e9..11cba1a 100644 --- a/src/TheCleaners/Public/Start-Cleaning.ps1 +++ b/src/TheCleaners/Public/Start-Cleaning.ps1 @@ -6,8 +6,8 @@ .DESCRIPTION Get started with a menu of services The Cleaners can offer. - .PARAMETER NoLogo - Do not display the logo. + .PARAMETER Dedication + Show a short dedication before the command menu. .EXAMPLE Start-Cleaning diff --git a/src/TheCleaners/TheCleaners.psd1 b/src/TheCleaners/TheCleaners.psd1 index 3e2553f..7fa35c3 100644 --- a/src/TheCleaners/TheCleaners.psd1 +++ b/src/TheCleaners/TheCleaners.psd1 @@ -107,9 +107,7 @@ # RequireLicenseAcceptance = $false # External dependent modules of this module - ExternalModuleDependencies = @( - 'WebAdministration' - ) + ExternalModuleDependencies = @() } # End of PSData hashtable