From 6646c22f2ea59d8e574c7b9182b26d29693eb85c Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 27 Sep 2024 21:44:15 +0300 Subject: [PATCH] Fix #3832: Refactor New-PnPSite cmdlet to support additional parameters for team sites (#4350) Co-authored-by: Gautam Sheth --- documentation/New-PnPSite.md | 158 ++++++++++++++++++++++++++++++- src/Commands/Admin/NewSite.cs | 169 +++++++++++++++++++++++++++++----- 2 files changed, 300 insertions(+), 27 deletions(-) diff --git a/documentation/New-PnPSite.md b/documentation/New-PnPSite.md index 78eeaa049..1087ef69e 100644 --- a/documentation/New-PnPSite.md +++ b/documentation/New-PnPSite.md @@ -16,7 +16,8 @@ Creates a communication site, Microsoft 365 group-connected team site or Modern ### TeamSite ```powershell -New-PnPSite -Type TeamSite -Title -Alias [-Description ] [-Classification ] [-IsPublic] [-Lcid ] [-Owners ] [-PreferredDataLocation ] [-SensitivityLabel ] [-HubSiteId ] [-SiteAlias ] [-TimeZone ] [-Wait] [-Connection ] +New-PnPSite -Type TeamSite -Title -Alias [-Description ] [-Classification ] [-IsPublic] [-Lcid ] [-Owners ] [-PreferredDataLocation ] [-SensitivityLabel ] [-HubSiteId ] [-SiteAlias ] [-TimeZone ] [-Members ] [-WelcomeEmailDisabled ] [-SubscribeNewGroupMembers ] [-AllowOnlyMembersToPost ] [-CalendarMemberReadOnly ] +[-ConnectorsDisabled ] [-HideGroupInOutlook ] [-SubscribeMembersToCalendarEventsDisabled ] [-SiteDesignId ] [-Wait] [-Connection ] ``` @@ -147,6 +148,13 @@ New-PnPSite -Type TeamSite -TimeZone UTCPLUS0200_HELSINKI_KYIV_RIGA_SOFIA_TALLIN This will create a new Modern team site collection connected to a Microsoft 365 Group with the title 'Contoso' and the url 'https://tenant.sharepoint.com/sites/contoso' and sets the timezone to UTC + 2 which is the Eastern European time zone. +### EXAMPLE 17 +```powershell +New-PnPSite -Type TeamSite -TimeZone UTCPLUS0200_HELSINKI_KYIV_RIGA_SOFIA_TALLINN_VILNIUS -Title "Contoso" -Alias "Contoso" -WelcomeEmailDisabled -SubscribeNewGroupMembers -AllowOnlyMembersToPost -CalendarMemberReadOnly -ConnectorsDisabled -HideGroupInOutlook -SubscribeMembersToCalendarEventsDisabled +``` + +This will create a new Modern team site collection connected to a Microsoft 365 Group with the title 'Contoso' and the url 'https://tenant.sharepoint.com/sites/contoso' and sets the timezone to UTC + 2 which is the Eastern European time zone. In addition to that, **if application permissions are used** , it will also set resource behavior options to disable welcome mails, make calendar read only , hide the group visibility in outlook and other options + ## PARAMETERS ### -Alias @@ -219,7 +227,8 @@ Accept wildcard characters: False ``` ### -HubSiteId -If specified the site will be associated to the hubsite as identified by this id +If specified the site will be associated to the hubsite as identified by this id. +**Note: Only applicable when delegated permissions are used.** ```yaml Type: Guid @@ -367,7 +376,7 @@ Allows to specify a custom site design ```yaml Type: Guid -Parameter Sets: CommunicationSite, TeamSiteWithoutMicrosoft365Group +Parameter Sets: CommunicationSite, TeamSiteWithoutMicrosoft365Group, TeamSite Required: False Position: Named @@ -405,6 +414,149 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Members + +Set the members of the team site connected group. Specify the UPN values in a string array. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: String[] +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WelcomeEmailDisabled + +If true, welcome emails are not sent to new members. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SubscribeNewGroupMembers + +If true, group members are subscribed to receive group conversations. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SubscribeMembersToCalendarEventsDisabled + +If true, members are not subscribed to the group's calendar events in Outlook. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -HideGroupInOutlook + +If true, members are not subscribed to the group's calendar events in Outlook. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ConnectorsDisabled + +If true, changes made to the group in Exchange Online are not synced back to on-premises Active Directory. **Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CalendarMemberReadOnly + +If true, members can view the group calendar in Outlook but cannot make changes. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AllowOnlyMembersToPost + +If true, only group members can post conversations to the group. +**Note: Only applicable when application permissions are used.** + +```yaml +Type: SwitchParameter +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SiteDesignId + +The ID of the Site Design to apply. +**Note: Only applicable when delegated permissions are used.** + +```yaml +Type: GUID +Parameter Sets: TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Type Specifies with type of site to create. diff --git a/src/Commands/Admin/NewSite.cs b/src/Commands/Admin/NewSite.cs index a7fdd9573..331d5bbca 100644 --- a/src/Commands/Admin/NewSite.cs +++ b/src/Commands/Admin/NewSite.cs @@ -4,7 +4,11 @@ using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Enums; using PnP.PowerShell.Commands.Attributes; -using PnP.PowerShell.Commands.Base; +using PnP.Core.Admin.Model.SharePoint; +using PnP.Core.Services; +using PnP.PowerShell.Commands.Utilities; +using PnP.Core.Admin.Model.Microsoft365; +using System.Text.Json; namespace PnP.PowerShell.Commands { @@ -66,7 +70,7 @@ protected override void ExecuteCmdlet() creationInformation.Title = _communicationSiteParameters.Title; creationInformation.Url = _communicationSiteParameters.Url; creationInformation.Description = _communicationSiteParameters.Description; - creationInformation.Classification = _communicationSiteParameters.Classification; + creationInformation.Classification = _communicationSiteParameters.Classification; creationInformation.ShareByEmailEnabled = _communicationSiteParameters.ShareByEmailEnabled; creationInformation.Lcid = _communicationSiteParameters.Lcid; if (ParameterSpecified(nameof(HubSiteId))) @@ -109,40 +113,84 @@ protected override void ExecuteCmdlet() _teamSiteParameters.Lcid = ClientContext.Web.Language; } var creationInformation = new Framework.Sites.TeamSiteCollectionCreationInformation(); - creationInformation.DisplayName = _teamSiteParameters.Title; - creationInformation.Alias = _teamSiteParameters.Alias; - creationInformation.Classification = _teamSiteParameters.Classification; - creationInformation.Description = _teamSiteParameters.Description; - creationInformation.IsPublic = _teamSiteParameters.IsPublic; - creationInformation.Lcid = _teamSiteParameters.Lcid; + + var groupVisibility = Core.Model.Security.GroupVisibility.Private; + if (_teamSiteParameters.IsPublic == true) + { + groupVisibility = Core.Model.Security.GroupVisibility.Public; + } + + var teamSiteOptions = new TeamSiteOptions(_teamSiteParameters.Alias, _teamSiteParameters.Title) + { + Classification = _teamSiteParameters.Classification, + Description = _teamSiteParameters.Description, + Visibility = groupVisibility, + Owners = _teamSiteParameters.Owners, + Language = (Core.Admin.Model.SharePoint.Language)_teamSiteParameters.Lcid, + Members = _teamSiteParameters.Members, + WelcomeEmailDisabled = _teamSiteParameters.WelcomeEmailDisabled, + SubscribeNewGroupMembers = _teamSiteParameters.SubscribeNewGroupMembers, + AllowOnlyMembersToPost = _teamSiteParameters.AllowOnlyMembersToPost, + CalendarMemberReadOnly = _teamSiteParameters.CalendarMemberReadOnly, + ConnectorsDisabled = _teamSiteParameters.ConnectorsDisabled, + HideGroupInOutlook = _teamSiteParameters.HideGroupInOutlook, + SubscribeMembersToCalendarEventsDisabled = _teamSiteParameters.SubscribeMembersToCalendarEventsDisabled, + SensitivityLabelId = GetSensitivityLabelGuid(_teamSiteParameters.SensitivityLabel), + SiteDesignId = _teamSiteParameters.SiteDesignId + }; + if (ParameterSpecified(nameof(HubSiteId))) { - creationInformation.HubSiteId = HubSiteId; + teamSiteOptions.HubSiteId = HubSiteId; } - creationInformation.Owners = _teamSiteParameters.Owners; - creationInformation.PreferredDataLocation = _teamSiteParameters.PreferredDataLocation; - creationInformation.SensitivityLabel = _teamSiteParameters.SensitivityLabel; if (ParameterSpecified("SiteAlias")) { - creationInformation.SiteAlias = _teamSiteParameters.SiteAlias; + teamSiteOptions.SiteAlias = _teamSiteParameters.SiteAlias; + } + + if (ParameterSpecified("PreferredDataLocation") && _teamSiteParameters.PreferredDataLocation.HasValue) + { + teamSiteOptions.PreferredDataLocation = GetGeoLocation(_teamSiteParameters.PreferredDataLocation.Value); } + SiteCreationOptions siteCreationOptions = new() + { + WaitForAsyncProvisioning = Wait + }; + + var tenantUrl = Connection.TenantAdminUrl ?? UrlUtilities.GetTenantAdministrationUrl(ClientContext.Url); + VanityUrlOptions vanityUrlOptions = new() + { + AdminCenterUri = new Uri(tenantUrl) + }; + if (ClientContext.GetContextSettings()?.Type != Framework.Utilities.Context.ClientContextType.SharePointACSAppOnly) { - var returnedContext = Framework.Sites.SiteCollection.Create(ClientContext, creationInformation, noWait: !Wait, graphAccessToken: GraphAccessToken, azureEnvironment: Connection?.AzureEnvironment ?? Framework.AzureEnvironment.Production); - if (ParameterSpecified(nameof(TimeZone))) + try { - returnedContext.Web.EnsureProperties(w => w.RegionalSettings, w => w.RegionalSettings.TimeZones); - returnedContext.Web.RegionalSettings.TimeZone = returnedContext.Web.RegionalSettings.TimeZones.Where(t => t.Id == ((int)TimeZone)).First(); - returnedContext.Web.RegionalSettings.Update(); - returnedContext.ExecuteQueryRetry(); - returnedContext.Site.EnsureProperty(s => s.Url); - WriteObject(returnedContext.Site.Url); + var rc = Connection.PnPContext.GetSiteCollectionManager().CreateSiteCollection(teamSiteOptions, siteCreationOptions, vanityUrlOptions); + + if (ParameterSpecified(nameof(TimeZone))) + { + using (var newSiteContext = ClientContext.Clone(rc.Uri.AbsoluteUri)) + { + newSiteContext.Web.EnsureProperties(w => w.RegionalSettings, w => w.RegionalSettings.TimeZones); + newSiteContext.Web.RegionalSettings.TimeZone = newSiteContext.Web.RegionalSettings.TimeZones.Where(t => t.Id == ((int)TimeZone)).First(); + newSiteContext.Web.RegionalSettings.Update(); + newSiteContext.ExecuteQueryRetry(); + newSiteContext.Site.EnsureProperty(s => s.Url); + WriteObject(rc.Uri.AbsoluteUri); + } + } + else + { + WriteObject(rc.Uri.AbsoluteUri); + } } - else + catch (Exception ex) { - WriteObject(returnedContext.Url); + WriteError(ex, ErrorCategory.WriteError); } } else @@ -162,7 +210,7 @@ protected override void ExecuteCmdlet() creationInformation.Title = _teamSiteWithoutMicrosoft365GroupParameters.Title; creationInformation.Url = _teamSiteWithoutMicrosoft365GroupParameters.Url; creationInformation.Description = _teamSiteWithoutMicrosoft365GroupParameters.Description; - creationInformation.Classification = _teamSiteWithoutMicrosoft365GroupParameters.Classification; + creationInformation.Classification = _teamSiteWithoutMicrosoft365GroupParameters.Classification; creationInformation.ShareByEmailEnabled = _teamSiteWithoutMicrosoft365GroupParameters.ShareByEmailEnabled; creationInformation.Lcid = _teamSiteWithoutMicrosoft365GroupParameters.Lcid; if (ParameterSpecified(nameof(HubSiteId))) @@ -275,6 +323,33 @@ public class TeamSiteParameters [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] public string SiteAlias; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public string[] Members; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter WelcomeEmailDisabled; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter SubscribeNewGroupMembers; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter AllowOnlyMembersToPost; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter CalendarMemberReadOnly; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter ConnectorsDisabled; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter HideGroupInOutlook; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public SwitchParameter SubscribeMembersToCalendarEventsDisabled; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAM)] + public Guid SiteDesignId; } public class TeamSiteWithoutMicrosoft365Group @@ -309,5 +384,51 @@ public class TeamSiteWithoutMicrosoft365Group [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TEAMSITENOGROUP)] public string SensitivityLabel; } + + private GeoLocation GetGeoLocation(PnP.Framework.Enums.Office365Geography office365Geography) + { + switch (office365Geography) + { + case Framework.Enums.Office365Geography.APC: return GeoLocation.APC; + case Framework.Enums.Office365Geography.ARE: return GeoLocation.ARE; + case Framework.Enums.Office365Geography.AUS: return GeoLocation.AUS; + case Framework.Enums.Office365Geography.BRA: return GeoLocation.BRA; + case Framework.Enums.Office365Geography.CAN: return GeoLocation.CAN; + case Framework.Enums.Office365Geography.CHE: return GeoLocation.CHE; + case Framework.Enums.Office365Geography.DEU: return GeoLocation.DEU; + case Framework.Enums.Office365Geography.EUR: return GeoLocation.EUR; + case Framework.Enums.Office365Geography.FRA: return GeoLocation.FRA; + case Framework.Enums.Office365Geography.GBR: return GeoLocation.GBR; + case Framework.Enums.Office365Geography.IND: return GeoLocation.IND; + case Framework.Enums.Office365Geography.JPN: return GeoLocation.JPN; + case Framework.Enums.Office365Geography.KOR: return GeoLocation.KOR; + case Framework.Enums.Office365Geography.NAM: return GeoLocation.NAM; + case Framework.Enums.Office365Geography.NOR: return GeoLocation.NOR; + case Framework.Enums.Office365Geography.QAT: return GeoLocation.QAT; + case Framework.Enums.Office365Geography.SWE: return GeoLocation.SWE; + case Framework.Enums.Office365Geography.ZAF: return GeoLocation.ZAF; + } + return default; + } + + private Guid GetSensitivityLabelGuid(string sensitivityLabel) + { + if (string.IsNullOrEmpty(sensitivityLabel)) + return Guid.Empty; + + var sensitivityLabelsPayload = Utilities.REST.RestHelper.Get(Connection.HttpClient, $"{ClientContext.Url.TrimEnd('/')}/_api/groupsitemanager/GetGroupCreationContext", AccessToken); + var jsonDoc = JsonDocument.Parse(sensitivityLabelsPayload); + + var root = jsonDoc.RootElement; + string sensitivityLabelStringId = ""; + if (root.TryGetProperty("DataClassificationOptionsNew", out var dataClassificationOptions)) + { + JsonElement? val = dataClassificationOptions.EnumerateArray() + .FirstOrDefault(jt => jt.GetProperty("Value").GetString() == sensitivityLabel); + + sensitivityLabelStringId = val?.GetProperty("Key").GetString(); + } + return Guid.Parse(sensitivityLabelStringId); + } } } \ No newline at end of file