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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions documentation/Set-PnPTenant.md
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,23 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -KnowledgeAgentSelectedSitesList

Specifies a list of site collection URLs that should be selected for the tenant Knowledge Agent. Each entry must be a full site URL (for example: "https://contoso.sharepoint.com/sites/team1"). The cmdlet will resolve each URL to the corresponding site id and configure the tenant Knowledge Agent to target those sites.

Note: Running `Set-PnPTenant` with `-KnowledgeAgentSelectedSitesList` will overwrite any existing configured Knowledge Agent sites in the tenant with the supplied list. To clear the configured list, pass an empty array: `-KnowledgeAgentSelectedSitesList @()`.

```yaml
Type: String[]
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -AllowSensitivityLabelOnRecords
Allows sensitivity label on records.

Expand Down
50 changes: 50 additions & 0 deletions src/Commands/Admin/SetTenant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,9 @@ public class SetTenant : PnPSharePointOnlineAdminCmdlet
[Parameter(Mandatory = false)]
public bool? DelayDenyAddAndCustomizePagesEnforcementOnClassicPublishingSites { private set; get; }

[Parameter(Mandatory = false)]
public string[] KnowledgeAgentSelectedSitesList { private set; get; }

protected override void ExecuteCmdlet()
{
AdminContext.Load(Tenant);
Expand Down Expand Up @@ -1676,6 +1679,53 @@ protected override void ExecuteCmdlet()
Tenant.KnowledgeAgentEnabled = KnowledgeAgentEnabled.Value;
modified = true;
}
if (KnowledgeAgentSelectedSitesList != null)
{
if (KnowledgeAgentSelectedSitesList.Length == 0)
{
// Explicit empty array passed - clear configured Knowledge Agent sites
Tenant.KnowledgeAgentSiteList = [];
modified = true;
}
else
{
// Build a GUID array by resolving each provided site URL to its SiteProperties and extracting the Id
var siteIdList = new List<Guid>();
var tenantForLookup = new Tenant(AdminContext);
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new Tenant instance for each parameter processing is inefficient. Consider reusing the existing Tenant object that's already loaded in the ExecuteCmdlet method at line 539.

Copilot uses AI. Check for mistakes.

foreach (var siteUrl in KnowledgeAgentSelectedSitesList)
{
if (string.IsNullOrWhiteSpace(siteUrl))
{
continue;
}

try
{
// The GetSitePropertiesByUrl call requires the tenant admin context
var siteProps = tenantForLookup.GetSitePropertiesByUrl(siteUrl, includeDetail: false);
tenantForLookup.Context.Load(siteProps, sp => sp.SiteId);
tenantForLookup.Context.ExecuteQueryRetry();
if (siteProps != null && siteProps.SiteId != Guid.Empty)
{
siteIdList.Add(siteProps.SiteId);
}
else
{
LogWarning($"Unable to resolve site URL '{siteUrl}' to a site id. It will be skipped.");
}
}
catch (Exception ex)
{
// Don't fail the whole cmdlet for one bad URL; log warning and continue
LogWarning($"Error resolving site URL '{siteUrl}': {ex.Message}. It will be skipped.");
}
}

Comment on lines +1696 to +1724
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates a separate API call for each site URL. Consider batching these operations or collecting all site properties before executing queries to reduce the number of round trips to SharePoint.

Suggested change
foreach (var siteUrl in KnowledgeAgentSelectedSitesList)
{
if (string.IsNullOrWhiteSpace(siteUrl))
{
continue;
}
try
{
// The GetSitePropertiesByUrl call requires the tenant admin context
var siteProps = tenantForLookup.GetSitePropertiesByUrl(siteUrl, includeDetail: false);
tenantForLookup.Context.Load(siteProps, sp => sp.SiteId);
tenantForLookup.Context.ExecuteQueryRetry();
if (siteProps != null && siteProps.SiteId != Guid.Empty)
{
siteIdList.Add(siteProps.SiteId);
}
else
{
LogWarning($"Unable to resolve site URL '{siteUrl}' to a site id. It will be skipped.");
}
}
catch (Exception ex)
{
// Don't fail the whole cmdlet for one bad URL; log warning and continue
LogWarning($"Error resolving site URL '{siteUrl}': {ex.Message}. It will be skipped.");
}
}
var sitePropsList = new List<SiteProperties>();
var siteUrlList = new List<string>();
for (int i = 0; i < KnowledgeAgentSelectedSitesList.Length; i++)
{
var siteUrl = KnowledgeAgentSelectedSitesList[i];
if (string.IsNullOrWhiteSpace(siteUrl))
{
continue;
}
try
{
var siteProps = tenantForLookup.GetSitePropertiesByUrl(siteUrl, includeDetail: false);
tenantForLookup.Context.Load(siteProps, sp => sp.SiteId);
sitePropsList.Add(siteProps);
siteUrlList.Add(siteUrl);
}
catch (Exception ex)
{
LogWarning($"Error resolving site URL '{siteUrl}': {ex.Message}. It will be skipped.");
}
}
try
{
tenantForLookup.Context.ExecuteQueryRetry();
}
catch (Exception ex)
{
LogWarning($"Error executing batch site property lookup: {ex.Message}");
}
for (int i = 0; i < sitePropsList.Count; i++)
{
var siteProps = sitePropsList[i];
var siteUrl = siteUrlList[i];
if (siteProps != null && siteProps.SiteId != Guid.Empty)
{
siteIdList.Add(siteProps.SiteId);
}
else
{
LogWarning($"Unable to resolve site URL '{siteUrl}' to a site id. It will be skipped.");
}
}
Copilot uses AI. Check for mistakes.
Tenant.KnowledgeAgentSiteList = siteIdList.ToArray();
modified = true;
}
}
if (GuestSharingGroupAllowListInTenantByPrincipalIdentity != null)
{
if (GuestSharingGroupAllowListInTenantByPrincipalIdentity.Length > 0)
Expand Down
Loading