Skip to content

Commit 26e32a9

Browse files
authored
Merge pull request #971 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 2bbc55d + e563aea commit 26e32a9

8 files changed

Lines changed: 562 additions & 57 deletions

File tree

Config/standards.json

Lines changed: 82 additions & 25 deletions
Large diffs are not rendered by default.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
function Get-CIPPSPOSite {
2+
<#
3+
.SYNOPSIS
4+
Get SharePoint Site properties via CSOM
5+
6+
.DESCRIPTION
7+
Retrieves all SharePoint site properties from the tenant using the CSOM GetSitePropertiesFromSharePoint method.
8+
Returns all site properties including version policy settings.
9+
10+
.PARAMETER TenantFilter
11+
Tenant to query
12+
13+
.EXAMPLE
14+
Get-CIPPSPOSite -TenantFilter 'contoso.onmicrosoft.com'
15+
16+
.FUNCTIONALITY
17+
Internal
18+
19+
#>
20+
[CmdletBinding()]
21+
param(
22+
[Parameter(Mandatory = $true)]
23+
[string]$TenantFilter
24+
)
25+
26+
$SharePointInfo = Get-SharePointAdminLink -Public $false -tenantFilter $TenantFilter
27+
$AdminUrl = $SharePointInfo.AdminUrl
28+
29+
$XML = @'
30+
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SharePoint Online PowerShell (16.0.24908.0)" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="172" ObjectPathId="171" /><ObjectPath Id="174" ObjectPathId="173" /><Query Id="175" ObjectPathId="173"><Query SelectAllProperties="true"><Properties><Property Name="NextStartIndexFromSharePoint" ScalarProperty="true" /></Properties></Query><ChildItemQuery SelectAllProperties="true"><Properties /></ChildItemQuery></Query></Actions><ObjectPaths><Constructor Id="171" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /><Method Id="173" ParentId="171" Name="GetSitePropertiesFromSharePoint"><Parameters><Parameter Type="Null" /><Parameter Type="Boolean">false</Parameter></Parameters></Method></ObjectPaths></Request>
31+
'@
32+
33+
$AdditionalHeaders = @{
34+
'Accept' = 'application/json;odata=verbose'
35+
}
36+
37+
$AllSites = [System.Collections.Generic.List[object]]::new()
38+
$StartIndex = $null
39+
40+
do {
41+
if ($null -ne $StartIndex) {
42+
$XML = @"
43+
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SharePoint Online PowerShell (16.0.24908.0)" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="172" ObjectPathId="171" /><ObjectPath Id="174" ObjectPathId="173" /><Query Id="175" ObjectPathId="173"><Query SelectAllProperties="true"><Properties><Property Name="NextStartIndexFromSharePoint" ScalarProperty="true" /></Properties></Query><ChildItemQuery SelectAllProperties="true"><Properties /></ChildItemQuery></Query></Actions><ObjectPaths><Constructor Id="171" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /><Method Id="173" ParentId="171" Name="GetSitePropertiesFromSharePoint"><Parameters><Parameter Type="String">$([System.Security.SecurityElement]::Escape($StartIndex))</Parameter><Parameter Type="Boolean">false</Parameter></Parameters></Method></ObjectPaths></Request>
44+
"@
45+
}
46+
47+
$Results = New-GraphPostRequest -scope "$AdminUrl/.default" -tenantid $TenantFilter -Uri "$AdminUrl/_vti_bin/client.svc/ProcessQuery" -Type POST -Body $XML -ContentType 'text/xml' -AddedHeaders $AdditionalHeaders
48+
49+
# The response contains multiple objects; find the one with _Child_Items_ (site list) and NextStartIndexFromSharePoint
50+
$SiteCollection = $Results | Where-Object { $_._Child_Items_ }
51+
if ($SiteCollection) {
52+
foreach ($Site in $SiteCollection._Child_Items_) {
53+
[void]$AllSites.Add($Site)
54+
}
55+
$StartIndex = $SiteCollection.NextStartIndexFromSharePoint
56+
} else {
57+
$StartIndex = $null
58+
}
59+
} while ($StartIndex)
60+
61+
return $AllSites
62+
}

Modules/CIPPCore/Public/New-CIPPTAP.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function New-CIPPTAP {
4949
# Create parameter string for logging
5050
$paramString = ' with ' + ($logParts -join ', ')
5151

52-
Write-LogMessage -headers $Headers -API $APIName -message "Created Temporary Access Password (TAP) for $UserID$paramString" -Sev 'Info' -tenant $TenantFilter
52+
Write-LogMessage -headers $Headers -API $APIName -message "Created Temporary Access Pass (TAP) for $UserID$paramString" -Sev 'Info' -tenant $TenantFilter
5353

5454
# Build result text with parameters
5555
$resultText = "The TAP for $UserID is $($GraphRequest.temporaryAccessPass) - This TAP is usable for the next $($GraphRequest.LifetimeInMinutes) minutes"
@@ -69,7 +69,7 @@ function New-CIPPTAP {
6969

7070
} catch {
7171
$ErrorMessage = Get-CippException -Exception $_
72-
$Result = "Failed to create Temporary Access Password (TAP) for $($UserID): $($ErrorMessage.NormalizedError)"
72+
$Result = "Failed to create Temporary Access Pass (TAP) for $($UserID): $($ErrorMessage.NormalizedError)"
7373
Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage
7474
throw $Result
7575
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
function Set-CIPPSPOSite {
2+
<#
3+
.SYNOPSIS
4+
Set SharePoint Site properties via CSOM
5+
6+
.DESCRIPTION
7+
Sets properties on an individual SharePoint site using the CSOM GetSitePropertiesByUrl + SetProperty + Update pattern.
8+
9+
.PARAMETER TenantFilter
10+
Tenant to apply settings to
11+
12+
.PARAMETER SiteUrl
13+
Full URL of the SharePoint site to modify
14+
15+
.PARAMETER Properties
16+
Hashtable of site properties to change. Supported value types: Boolean, String, Int32.
17+
18+
.EXAMPLE
19+
$Properties = @{
20+
InheritVersionPolicyFromTenant = $false
21+
EnableAutoExpirationVersionTrim = $false
22+
ApplyToNewDocumentLibraries = $true
23+
ApplyToExistingDocumentLibraries = $true
24+
MajorVersionLimit = 50
25+
ExpireVersionsAfterDays = 365
26+
}
27+
Set-CIPPSPOSite -TenantFilter 'contoso.onmicrosoft.com' -SiteUrl 'https://contoso.sharepoint.com/sites/MySite' -Properties $Properties
28+
29+
.FUNCTIONALITY
30+
Internal
31+
32+
#>
33+
[CmdletBinding(SupportsShouldProcess = $true)]
34+
param(
35+
[Parameter(Mandatory = $true)]
36+
[string]$TenantFilter,
37+
[Parameter(Mandatory = $true)]
38+
[string]$SiteUrl,
39+
[Parameter(Mandatory = $true)]
40+
[hashtable]$Properties
41+
)
42+
43+
$SharePointInfo = Get-SharePointAdminLink -Public $false -tenantFilter $TenantFilter
44+
$AdminUrl = $SharePointInfo.AdminUrl
45+
46+
$AllowedTypes = @('Boolean', 'String', 'Int32')
47+
$SetProperty = [System.Collections.Generic.List[string]]::new()
48+
$x = 106
49+
foreach ($Property in $Properties.Keys) {
50+
$PropertyType = $Properties[$Property].GetType().Name
51+
if ($PropertyType -in $AllowedTypes) {
52+
$PropertyToSet = if ($PropertyType -eq 'Boolean') { $Properties[$Property].ToString().ToLower() } else { $Properties[$Property] }
53+
$SetProperty.Add("<SetProperty Id=`"$x`" ObjectPathId=`"104`" Name=`"$Property`"><Parameter Type=`"$PropertyType`">$PropertyToSet</Parameter></SetProperty>")
54+
$x++
55+
}
56+
}
57+
58+
if ($SetProperty.Count -eq 0) {
59+
Write-Error 'No valid properties found'
60+
return
61+
}
62+
63+
# CSOM pattern: Tenant Constructor → GetSitePropertiesByUrl → SetProperty(s) → Update
64+
$XML = @"
65+
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SharePoint Online PowerShell (16.0.24908.0)" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions>$($SetProperty -join '')<ObjectPath Id="$x" ObjectPathId="113" /><ObjectIdentityQuery Id="$($x + 1)" ObjectPathId="104" /></Actions><ObjectPaths><Method Id="104" ParentId="102" Name="GetSitePropertiesByUrl"><Parameters><Parameter Type="String">$([System.Security.SecurityElement]::Escape($SiteUrl))</Parameter><Parameter Type="Boolean">false</Parameter></Parameters></Method><Method Id="113" ParentId="104" Name="Update" /><Constructor Id="102" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /></ObjectPaths></Request>
66+
"@
67+
68+
$AdditionalHeaders = @{
69+
'Accept' = 'application/json;odata=verbose'
70+
}
71+
72+
if ($PSCmdlet.ShouldProcess($SiteUrl, 'Set Site Properties')) {
73+
New-GraphPostRequest -scope "$AdminUrl/.default" -tenantid $TenantFilter -Uri "$AdminUrl/_vti_bin/client.svc/ProcessQuery" -Type POST -Body $XML -ContentType 'text/xml' -AddedHeaders $AdditionalHeaders
74+
}
75+
}

Modules/CIPPCore/Public/Set-CIPPSPOTenant.ps1

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
function Set-CIPPSPOTenant {
22
<#
33
.SYNOPSIS
4-
Set SharePoint Tenant properties
4+
Set SharePoint Tenant properties or invoke methods
55
66
.DESCRIPTION
7-
Set SharePoint Tenant properties via SPO API
7+
Set SharePoint Tenant properties via SPO CSOM API, or invoke a CSOM method on the Tenant object.
88
99
.PARAMETER TenantFilter
1010
Tenant to apply settings to
@@ -13,7 +13,14 @@ function Set-CIPPSPOTenant {
1313
Tenant Identity (Get from Get-CIPPSPOTenant)
1414
1515
.PARAMETER Properties
16-
Hashtable of tenant properties to change
16+
Hashtable of tenant properties to change (uses SetProperty actions)
17+
18+
.PARAMETER MethodName
19+
Name of the CSOM method to invoke on the Tenant object
20+
21+
.PARAMETER MethodParameters
22+
Ordered array of parameter hashtables for the method call. Each entry must have 'Type' and 'Value' keys.
23+
Supported types: Boolean, String, Int32, Int64.
1724
1825
.PARAMETER SharepointPrefix
1926
Prefix for the sharepoint tenant
@@ -24,20 +31,35 @@ function Set-CIPPSPOTenant {
2431
}
2532
Get-CippSPOTenant -TenantFilter 'contoso.onmicrosoft.com' | Set-CIPPSPOTenant -Properties $Properties
2633
34+
.EXAMPLE
35+
$MethodParams = @(
36+
@{ Type = 'Boolean'; Value = $false }
37+
@{ Type = 'Int32'; Value = 50 }
38+
@{ Type = 'Int32'; Value = 365 }
39+
)
40+
Get-CIPPSPOTenant -TenantFilter 'contoso.onmicrosoft.com' | Set-CIPPSPOTenant -MethodName 'SetFileVersionPolicy' -MethodParameters $MethodParams
41+
2742
.FUNCTIONALITY
2843
Internal
2944
3045
#>
31-
[CmdletBinding(SupportsShouldProcess = $true)]
46+
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Properties')]
3247
param(
33-
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
48+
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Properties')]
49+
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Method')]
3450
[string]$TenantFilter,
35-
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
51+
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Properties')]
52+
[Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Method')]
3653
[Alias('_ObjectIdentity_')]
3754
[string]$Identity,
38-
[Parameter(Mandatory = $true)]
55+
[Parameter(Mandatory = $true, ParameterSetName = 'Properties')]
3956
[hashtable]$Properties,
40-
[Parameter(ValueFromPipelineByPropertyName = $true)]
57+
[Parameter(Mandatory = $true, ParameterSetName = 'Method')]
58+
[string]$MethodName,
59+
[Parameter(Mandatory = $true, ParameterSetName = 'Method')]
60+
[array]$MethodParameters,
61+
[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Properties')]
62+
[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Method')]
4163
[string]$SharepointPrefix
4264
)
4365

@@ -51,42 +73,56 @@ function Set-CIPPSPOTenant {
5173
$AdminUrl = "https://$($tenantName)-admin.sharepoint.com"
5274
}
5375
$Identity = $Identity -replace "`n", '&#xA;'
54-
$AllowedTypes = @('Boolean', 'String', 'Int32')
55-
$SetProperty = [System.Collections.Generic.List[string]]::new()
56-
$x = 114
57-
foreach ($Property in $Properties.Keys) {
58-
# Get property type
59-
$PropertyType = $Properties[$Property].GetType().Name
60-
if ($PropertyType -in $AllowedTypes) {
61-
if ($PropertyType -eq 'Boolean') {
62-
$PropertyToSet = $Properties[$Property].ToString().ToLower()
63-
} else {
64-
$PropertyToSet = $Properties[$Property]
65-
}
66-
$xml = @"
76+
77+
if ($PSCmdlet.ParameterSetName -eq 'Properties') {
78+
$AllowedTypes = @('Boolean', 'String', 'Int32')
79+
$SetProperty = [System.Collections.Generic.List[string]]::new()
80+
$x = 114
81+
foreach ($Property in $Properties.Keys) {
82+
# Get property type
83+
$PropertyType = $Properties[$Property].GetType().Name
84+
if ($PropertyType -in $AllowedTypes) {
85+
if ($PropertyType -eq 'Boolean') {
86+
$PropertyToSet = $Properties[$Property].ToString().ToLower()
87+
} else {
88+
$PropertyToSet = $Properties[$Property]
89+
}
90+
$xml = @"
6791
<SetProperty Id="$x" ObjectPathId="110" Name="$Property">
6892
<Parameter Type="$PropertyType">$($PropertyToSet)</Parameter>
6993
</SetProperty>
7094
"@
71-
$SetProperty.Add($xml)
72-
$x++
95+
$SetProperty.Add($xml)
96+
$x++
97+
}
98+
}
99+
100+
if (($SetProperty | Measure-Object).Count -eq 0) {
101+
Write-Error 'No valid properties found'
102+
return
73103
}
74-
}
75104

76-
if (($SetProperty | Measure-Object).Count -eq 0) {
77-
Write-Error 'No valid properties found'
78-
return
105+
$ActionsXml = $SetProperty -join ''
106+
$Description = $Properties.Keys -join ', '
107+
} else {
108+
# Method call
109+
$Params = foreach ($Param in $MethodParameters) {
110+
$ParamValue = if ($Param.Type -eq 'Boolean') { $Param.Value.ToString().ToLower() } else { $Param.Value }
111+
"<Parameter Type=`"$($Param.Type)`">$ParamValue</Parameter>"
112+
}
113+
$ActionsXml = "<Method Name=`"$MethodName`" Id=`"114`" ObjectPathId=`"110`"><Parameters>$($Params -join '')</Parameters></Method>"
114+
$Description = $MethodName
79115
}
80116

81-
# Query tenant settings
117+
# Build CSOM request
82118
$XML = @"
83-
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SharePoint Online PowerShell (16.0.24908.0)" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions>$($SetProperty -join '')</Actions><ObjectPaths><Identity Id="110" Name="$Identity" /></ObjectPaths></Request>
119+
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SharePoint Online PowerShell (16.0.24908.0)" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions>$ActionsXml</Actions><ObjectPaths><Identity Id="110" Name="$Identity" /></ObjectPaths></Request>
84120
"@
85121
$AdditionalHeaders = @{
86122
'Accept' = 'application/json;odata=verbose'
87123
}
88124

89-
if ($PSCmdlet.ShouldProcess(($Properties.Keys -join ', '), 'Set Tenant Properties')) {
125+
if ($PSCmdlet.ShouldProcess($Description, 'Set Tenant Properties')) {
90126
New-GraphPostRequest -scope "$AdminURL/.default" -tenantid $TenantFilter -Uri "$AdminURL/_vti_bin/client.svc/ProcessQuery" -Type POST -Body $XML -ContentType 'text/xml' -AddedHeaders $AdditionalHeaders
91127

92128
# Invalidate cached tenant data so subsequent reads reflect the change
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
function Start-CIPPSiteVersionCleanup {
2+
<#
3+
.SYNOPSIS
4+
Start a file version batch delete job for a SharePoint site
5+
6+
.DESCRIPTION
7+
Creates a new file version batch delete job via the CSOM NewFileVersionBatchDeleteJob method.
8+
This triggers cleanup of old file versions on a SharePoint site based on the specified parameters.
9+
10+
.PARAMETER TenantFilter
11+
Tenant to run the cleanup on
12+
13+
.PARAMETER SiteUrl
14+
Full URL of the SharePoint site to clean up
15+
16+
.PARAMETER BatchDeleteMode
17+
Cleanup mode as an enum value:
18+
0 = DeleteOlderThanDays
19+
1 = CountLimits
20+
2 = SyncPolicy (apply the site's current version policy)
21+
22+
.PARAMETER DeleteOlderThanDays
23+
Delete versions older than this many days. Use -1 to skip (when using SyncPolicy mode).
24+
25+
.PARAMETER MajorVersionLimit
26+
Maximum major versions to keep. Use -1 to skip (when using SyncPolicy mode).
27+
28+
.PARAMETER MajorWithMinorVersionsLimit
29+
Maximum major versions that retain minor versions. Use -1 to skip (when using SyncPolicy mode).
30+
31+
.PARAMETER SyncListPolicy
32+
Whether to sync the list-level policy. Defaults to $false.
33+
34+
.EXAMPLE
35+
# Sync site policy (apply current version settings to all existing versions)
36+
Start-CIPPSiteVersionCleanup -TenantFilter 'contoso.onmicrosoft.com' -SiteUrl 'https://contoso.sharepoint.com/sites/MySite' -BatchDeleteMode 2
37+
38+
.EXAMPLE
39+
# Delete versions older than 365 days
40+
Start-CIPPSiteVersionCleanup -TenantFilter 'contoso.onmicrosoft.com' -SiteUrl 'https://contoso.sharepoint.com/sites/MySite' -BatchDeleteMode 0 -DeleteOlderThanDays 365
41+
42+
.FUNCTIONALITY
43+
Internal
44+
45+
#>
46+
[CmdletBinding(SupportsShouldProcess = $true)]
47+
param(
48+
[Parameter(Mandatory = $true)]
49+
[string]$TenantFilter,
50+
[Parameter(Mandatory = $true)]
51+
[string]$SiteUrl,
52+
[Parameter(Mandatory = $false)]
53+
[int]$BatchDeleteMode = 2,
54+
[Parameter(Mandatory = $false)]
55+
[int]$DeleteOlderThanDays = -1,
56+
[Parameter(Mandatory = $false)]
57+
[int]$MajorVersionLimit = -1,
58+
[Parameter(Mandatory = $false)]
59+
[int]$MajorWithMinorVersionsLimit = -1,
60+
[Parameter(Mandatory = $false)]
61+
[bool]$SyncListPolicy = $false
62+
)
63+
64+
$SharePointInfo = Get-SharePointAdminLink -Public $false -tenantFilter $TenantFilter
65+
$AdminUrl = $SharePointInfo.AdminUrl
66+
$EscapedSiteUrl = [System.Security.SecurityElement]::Escape($SiteUrl)
67+
$SyncListPolicyValue = $SyncListPolicy.ToString().ToLower()
68+
69+
$XML = @"
70+
<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="SharePoint Online PowerShell (16.0.24908.0)" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="199" ObjectPathId="198" /><ObjectPath Id="201" ObjectPathId="200" /><Query Id="202" ObjectPathId="200"><Query SelectAllProperties="true"><Properties /></Query></Query></Actions><ObjectPaths><Constructor Id="198" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /><Method Id="200" ParentId="198" Name="NewFileVersionBatchDeleteJob"><Parameters><Parameter Type="String">$EscapedSiteUrl</Parameter><Parameter TypeId="{d1fd43d3-dba9-4d1c-bf13-d3894db255c7}"><Property Name="BatchDeleteMode" Type="Enum">$BatchDeleteMode</Property><Property Name="DeleteOlderThanDays" Type="Int32">$DeleteOlderThanDays</Property><Property Name="FileTypeSelections" Type="Null" /><Property Name="MajorVersionLimit" Type="Int32">$MajorVersionLimit</Property><Property Name="MajorWithMinorVersionsLimit" Type="Int32">$MajorWithMinorVersionsLimit</Property><Property Name="SyncListPolicy" Type="Boolean">$SyncListPolicyValue</Property></Parameter></Parameters></Method></ObjectPaths></Request>
71+
"@
72+
73+
$AdditionalHeaders = @{
74+
'Accept' = 'application/json;odata=verbose'
75+
}
76+
77+
if ($PSCmdlet.ShouldProcess($SiteUrl, 'Start file version batch delete job')) {
78+
return New-GraphPostRequest -scope "$AdminUrl/.default" -tenantid $TenantFilter -Uri "$AdminUrl/_vti_bin/client.svc/ProcessQuery" -Type POST -Body $XML -ContentType 'text/xml' -AddedHeaders $AdditionalHeaders
79+
}
80+
}

0 commit comments

Comments
 (0)