Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
The format is based on and uses the types of changes according to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.1.2] - 2026-07-15

### Changed

- SPSCleanVersions.ps1
- The HTML report is now **local-only**. In Azure Automation it is no longer emitted
into the job output stream (dumping the full HTML made the runbook log unreadable);
only the run summary line is printed. Local execution is unchanged — the report is
still written to `Results/`

## [3.1.1] - 2026-07-15

### Fixed
Expand Down
22 changes: 6 additions & 16 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
# SPSCleanVersions - Release Notes

## [3.1.1] - 2026-07-15
## [3.1.2] - 2026-07-15

### Fixed
### Changed

- SPSCleanVersions.ps1
- **Azure Automation runbook compatibility:** remove the parameter sets introduced
with `-ConfigFile`. Azure Automation rejects runbooks that use parameter sets
(*"Parameter sets in runbooks are not supported in this release"*), which prevented
the script from starting as a runbook. `-InputJson` and `-ConfigFile` are now plain
optional parameters whose mutual exclusivity is validated in the body (exactly one
is required)
- **Azure Automation runbook compatibility:** explicitly `Import-Module PnP.PowerShell`,
because in a runbook the `#Requires -Modules` directive does not import the module and
command auto-loading is unreliable in the sandbox (`Connect-PnPOnline` was *"not
recognized"*). Harmless locally
- Wiki Documentation
- Add a PowerShell / PnP.PowerShell version compatibility matrix (PnP 3.x needs
PowerShell 7.4; use a 7.4 Runtime Environment in Azure Automation, or PnP 2.12.x
on a 7.2 runbook)
- The HTML report is now **local-only**. In Azure Automation it is no longer emitted
into the job output stream (dumping the full HTML made the runbook log unreadable);
only the run summary line is printed. Local execution is unchanged — the report is
still written to `Results/`

A full list of changes in each version can be found in the [change log](CHANGELOG.md)
36 changes: 15 additions & 21 deletions scripts/SPSCleanVersions.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<#PSScriptInfo
.VERSION 3.1.1
.VERSION 3.1.2

.GUID 7ecf4acd-17c4-4c50-be79-1fcf2b6611fe

Expand Down Expand Up @@ -66,8 +66,8 @@
SiteScope is 'All' (e.g. https://contoso-admin.sharepoint.com).
- SiteFilter (string, optional) — server-side -Filter passed to Get-PnPTenantSite
to narrow the enumeration when SiteScope is 'All'.
- EnableReport (boolean, optional, default: true) — generate an HTML report.
Local: written to Results/. Azure Automation: emitted to the output stream.
- EnableReport (boolean, optional, default: true) — write a local HTML report
to Results/ (local execution only; not produced in Azure Automation).
- LogRetentionDays (integer, optional, default: 180) — prune Logs/ and Results/ files
older than this many days (local only). 0 disables pruning.

Expand Down Expand Up @@ -97,7 +97,7 @@
FileName: SPSCleanVersions.ps1
Author: Jean-Cyril DROUHIN
Date: July 15, 2026
Version: 3.1.1
Version: 3.1.2

.LINK
https://spjc.fr/
Expand Down Expand Up @@ -437,7 +437,7 @@ function Clear-OldRunFiles {
# Run context: local writes transcript + report files; Azure Automation emits the report
# into the output stream (no persistent filesystem).
$script:IsAzureAutomationRun = Test-IsAzureAutomation
$script:ScriptVersion = '3.1.1'
$script:ScriptVersion = '3.1.2'
$script:RunTimestamp = Get-Date -Format 'yyyy-MM-dd_HHmmss'
$script:LogsFolder = $null
$script:ResultsFolder = $null
Expand Down Expand Up @@ -806,25 +806,19 @@ Skipping New-PnPSiteFileVersionBatchDeleteJob for site: $SiteUrl
}

#region --- Report output ---
if ($EnableReport -and $script:RunResults.Count -gt 0) {
# The HTML report is a local artifact only. In Azure Automation there is no persistent
# filesystem and dumping the HTML into the job output stream makes the log unreadable, so
# the report is simply not produced there (the run summary below is still printed).
if ($EnableReport -and -not $script:IsAzureAutomationRun -and $script:RunResults.Count -gt 0) {
$reportHtml = Export-SPSCleanVersionsReport -Results $script:RunResults `
-Title 'SPSCleanVersions' -Version $script:ScriptVersion -DryRunMode:$WhatIfPreference

if ($script:IsAzureAutomationRun) {
# No persistent filesystem in Azure Automation: emit the HTML into the output stream.
Write-Output '--- BEGIN SPSCleanVersions HTML report ---'
Write-Output $reportHtml
Write-Output '--- END SPSCleanVersions HTML report ---'
try {
$reportPath = Join-Path -Path $script:ResultsFolder -ChildPath ("SPSCleanVersions-$($script:RunTimestamp).html")
Set-Content -Path $reportPath -Value $reportHtml -Encoding UTF8 -Force
Write-Output "HTML report written to: $reportPath"
}
else {
try {
$reportPath = Join-Path -Path $script:ResultsFolder -ChildPath ("SPSCleanVersions-$($script:RunTimestamp).html")
Set-Content -Path $reportPath -Value $reportHtml -Encoding UTF8 -Force
Write-Output "HTML report written to: $reportPath"
}
catch {
Write-Warning "Unable to write HTML report: $($_.Exception.Message)"
}
catch {
Write-Warning "Unable to write HTML report: $($_.Exception.Message)"
}
}

Expand Down
5 changes: 3 additions & 2 deletions tests/SPSCleanVersions.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,9 @@ Describe 'SPSCleanVersions Script' {
$scriptContent | Should -Match 'Stop-Transcript'
}

It 'Should emit the HTML report to the output stream in Azure Automation' {
$scriptContent | Should -Match 'BEGIN SPSCleanVersions HTML report'
It 'Should keep the HTML report local-only (not dumped in Azure Automation)' {
$scriptContent | Should -Not -Match 'BEGIN SPSCleanVersions HTML report'
$scriptContent | Should -Match '-not \$script:IsAzureAutomationRun'
}

It 'Should default EnableReport to true' {
Expand Down
4 changes: 2 additions & 2 deletions wiki/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Both sources are parsed with `ConvertFrom-Json` and share the exact same schema,
| `SiteScope` | string | No | `Selected` | `Selected` processes `SiteUrls`. `All` enumerates **every** site collection in the tenant via `Get-PnPTenantSite`. Site version policy modes only (not `Legacy`). See [Tenant-wide scope](#tenant-wide-scope-sitescope-all). |
| `TenantAdminUrl` | string | Conditional | — | SharePoint admin center URL (e.g. `https://contoso-admin.sharepoint.com`). **Required** when `SiteScope` is `All`. |
| `SiteFilter` | string | No | — | Optional server-side `-Filter` passed to `Get-PnPTenantSite` to narrow the enumeration when `SiteScope` is `All` (e.g. `"Url -like 'sales'"`). |
| `EnableReport` | boolean | No | `true` | Generate an HTML report of the run. Local: written to `Results/`. Azure Automation: emitted to the output stream. |
| `EnableReport` | boolean | No | `true` | Write a local HTML report of the run to `Results/`. **Local execution only** — no report is produced when running in Azure Automation. |
| `LogRetentionDays` | integer | No | `180` | Prune `Logs/` and `Results/` files older than this many days (local only). `0` disables pruning. |

## Version policy modes
Expand Down Expand Up @@ -91,7 +91,7 @@ By default (`SiteScope: Selected`) the script only processes the sites listed in
Every run produces a summary of what happened per site (**Applied** / **Skipped** / **Compliant** / **Failed**).

- **Local execution:** a transcript is written to a `Logs/` folder and a self-contained HTML report (summary cards + a filterable table) to a `Results/` folder, both next to the script. Files older than `LogRetentionDays` (default 180) are pruned automatically. Set `"EnableReport": false` to skip the HTML report.
- **Azure Automation:** there is no persistent filesystem, so the HTML report is emitted into the job **output stream** (between `--- BEGIN SPSCleanVersions HTML report ---` and `--- END ... ---`) instead of being written to disk.
- **Azure Automation:** there is no persistent filesystem, so the **HTML report is not produced**. The per-site actions are visible in the job output (`Write-Output`/`Write-Warning`) and the run ends with a summary line (`--- SPSCleanVersions finished: ... ---`).

The report values are HTML-encoded, and a `DryRun` badge is shown when the run is a simulation.

Expand Down