Using PowerShell to create SharePoint Sites

Very frequently I find myself creating SharePoint sites for clients, normally during migrations to the cloud during holiday periods, this is along drawn out task and can often lead to steps being missed.
Problem Solved!
You can use the below PowerShell script to create SharePoint Sites automatically along with disabling Sharing Links etc to ensure no GDPR data leaks.
clear #------------------------------------------------ #--------------V1 initial release---------------- #----------------DR 15/10/2021------------------- #------------------------------------------------ #------------------Infomation-------------------- #You will be require to login three time for this script to function #as you need to login to different parts of Office 365, Azure and #SharePoint, logins are Interactive to allow 2FA accounts to work also #Once the sites has been created, if you are using Azure AD Connect and #set $ConnectToAD = $true below users will need adding into the correct #groups to get the correct permissions #Disabling sharing links etc will ensure no data is able to be shared with #users who do not alread have access to site files #Giving users Owner permissions will enabled them to be able to create #sharing links outside the the site i.e. people who do not already have access #------------------------------------------------ #--------------Permissions reference------------- #Visitors (Read Access) #Members (Contirubte/Write) #Owners (Full Control of the SharePoint site) #------------------------------------------------ #-------------------Change Log------------------- #18-10-21 - Updated the section that adds AD User groups to SharePoint to use c:0t.c|tenant| as the start of the entry point for group names. #19-10-21 - Set-GPPermission now removed Authenticated Users from having the GPO Applied, the GPOS are now only applied to the AD SharePoint groups #---------------------To Do---------------------- #Find a way for the GPOs to be auto populated with the library names and IDs #------------------------------------------------ #User configurable variables $TenantName = "" # Your Azure tenant name (the bit before -admin.sharepoint.com) $TenantFriendlyName = "" # A friendly name for your tenant, Company name, School name etc $SiteName = "" # The name of the site to be created $OwnerEmail = "" # The email of the user who is to be the new site Owner $StorageQuota = "26214400" # Storage Quota for the site, 26214400 is 25Tb the default $ConnectToAD = $true # Are you using Azure AD connect and want to connect AD Groups to SharePoint if false then perms will need adding manually $ADGroupPath = "" # Use an LDAP Path here thats sync to Office 365 to create the AD Groups #------------------------------------------------ #------------------------------------------------ #Do not alter these unless you know what you are doing! $docLib = 'Documents' $TenantAdminURL = "https://" + $TenantName + "-admin.sharepoint.com" $TenantClientURL = "https://" + $TenantName + ".sharepoint.com" $SiteURL = $TenantClientURL + "/sites/" + $SiteName $ADOwnersGroupName = "SharePoint - " + $TenantFriendlyName + " - " + $SiteName + " Owners" $ADMembersGroupName = "SharePoint - " + $TenantFriendlyName + " - " + $SiteName + " Members" $ADVisitorsGroupName = "SharePoint - " + $TenantFriendlyName + " - " + $SiteName + " Visitors" $GPOName = "SharePoint - " + $TenantFriendlyName + " - " + $SiteName $SPOwnersGroupName = $SiteName + " Owners" $SPMembersGroupName = $SiteName + " Members" $SPVisitorsGroupName = $SiteName + " Visitors" #------------------------------------------------ #------------------------------------------------WORKING #Check we are running as Admin and elevate if not write-host "Info: Checking we are elevated" -foregroundcolor green $ver = $host | select version if ($ver.Version.Major -gt 1) {$Host.Runspace.ThreadOptions = "ReuseThread"} # Verify that user running script is an administrator $IsAdmin=[Security.Principal.WindowsIdentity]::GetCurrent() If ((New-Object Security.Principal.WindowsPrincipal $IsAdmin).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) -eq $FALSE) { "`nERROR: You are NOT a local administrator. Run this script after elevating to Administrator." exit } #------------------------------------------------ #------------------------------------------------WORKING #Uninstall old SharePointPnPPowerShellOnline if it exists Write-Host "Uninstall old SharePointPnPPowerShellOnline if it exists" -foregroundcolor green If (Get-InstalledModule Microsoft.Online.SharePoint.PowerShell) { Write-Host "SharePointPnPPowerShellOnline is installed, uninstalling." -foregroundcolor green Uninstall-Module -Name SharePointPnPPowerShellOnline -AllVersions -Force } #Install dependancies write-host "Info: Install dependancies" -foregroundcolor green If (-not(Get-InstalledModule Microsoft.Online.SharePoint.PowerShell)) { Write-Host "Module Microsoft.Online.SharePoint.PowerShell does not exist, installing." -foregroundcolor green Install-Module -Name Microsoft.Online.SharePoint.PowerShell } else { write-host "Info: Module Microsoft.Online.SharePoint.PowerShell already Installed" -foregroundcolor green } If (-not(Get-InstalledModule PnP.PowerShell)) { Write-Host "Module PnP.PowerShell does not exist, installing." -foregroundcolor green Install-Module -Name PnP.PowerShell } else { write-host "Info: Module PnP.PowerShell already Installed" -foregroundcolor green } If (-not(Get-InstalledModule AzureAD)) { Write-Host "Module AzureAD does not exist, installing." -foregroundcolor green Install-Module -Name AzureAD } else { write-host "Info: Module AzureAD already Installed" -foregroundcolor green } #------------------------------------------------ #------------------------------------------------WORKING #Get latest updates for modules write-host "Info: Updating modules to latest versions" -foregroundcolor green Update-Module -Name Microsoft.Online.SharePoint.PowerShell Update-Module -Name PnP.PowerShell Update-Module -Name AzureAD #------------------------------------------------ #------------------------------------------------WORKING #Connect to to SPOService try{ write-host "Info: connecting to SPOService" -foregroundcolor green Connect-SPOService -Url $TenantAdminURL write-host "Info: Connected succesfully to SPOService" -foregroundcolor green } catch{ write-host "Error: Could not connect to SPOService" -foregroundcolor red exit } #------------------------------------------------ #------------------------------------------------WORKING if ($ConnectToAD -eq $true) { #Create the AD Groups write-host "Info: Create AD Groups" -foregroundcolor green New-ADGroup -Name $ADOwnersGroupName -SamAccountName $ADOwnersGroupName -GroupCategory Security -GroupScope DomainLocal -DisplayName $ADOwnersGroupName -Path $ADGroupPath -Description "Membership of this group gives Owner (Full Control) access to the root of $SiteName" New-ADGroup -Name $ADMembersGroupName -SamAccountName $ADMembersGroupName -GroupCategory Security -GroupScope DomainLocal -DisplayName $ADMembersGroupName -Path $ADGroupPath -Description "Membership of this group gives Member (Read and Write) access to the root of $SiteName" New-ADGroup -Name $ADVisitorsGroupName -SamAccountName $ADVisitorsGroupName -GroupCategory Security -GroupScope DomainLocal -DisplayName $ADVisitorsGroupName -Path $ADGroupPath -Description "Membership of this group gives Visitor (Read) access to the root of $SiteName" #Create Group Policies write-host "Info: Create GPO" -foregroundcolor green New-GPO -Name $GPOName #Remove Authenticated Users from having the GPO Applied Set-GPPermission -Name $GPOName -TargetName "Authenticated Users" -TargetType Group -PermissionLevel GpoRead -Replace #Add the new AD Groups to have the GPO Applied Set-GPPermission -Name $GPOName -TargetName $ADOwnersGroupName -TargetType Group -PermissionLevel GpoApply Set-GPPermission -Name $GPOName -TargetName $ADMembersGroupName -TargetType Group -PermissionLevel GpoApply Set-GPPermission -Name $GPOName -TargetName $ADVisitorsGroupName -TargetType Group -PermissionLevel GpoApply #Run a Delta Sync to Office 365 write-host "Info: Run AD Delta Sync" -foregroundcolor green Start-ADSyncSyncCycle -PolicyType Delta #Wait for 3 minutes to allow 365 to register the sync $Seconds = 180 $EndTime = [datetime]::UtcNow.AddSeconds($Seconds) while (($TimeRemaining = ($EndTime - [datetime]::UtcNow)) -gt 0) { Write-Progress -Activity 'Watiting for...' -SecondsRemaining $TimeRemaining.TotalSeconds Start-Sleep 1 } } #------------------------------------------------ #------------------------------------------------WORKING #verify if site already exists in SharePoint Online write-host "Info: Checking if site already exists" -foregroundcolor green $siteExists = get-SPOSite -Limit ALL | where{$_.url -eq $SiteURL} #verify if site already exists in the recycle bin write-host "Info: Checking if site already exists in the recycle bin" -foregroundcolor green $siteExistsInRecycleBin = get-SPODeletedSite -Limit ALL | where{$_.url -eq $SiteURL} #create site if it doesn't exists if (($siteExists -eq $null) -and ($siteExistsInRecycleBin -eq $null)) { write-host "Info: Creating $($SiteName)" -foregroundcolor green #Create a new site with the Doc Centre template BDR#0 New-SPOSite -Url $SiteURL -Owner $OwnerEmail -StorageQuota $StorageQuota -CompatibilityLevel 15 -LocaleID 1033 -Template "BDR#0" -TimeZoneId 2 -Title $SiteName write-host "Info: Site $($SiteName) Created" -foregroundcolor green } elseif ($siteExists -eq $true){ write-host "Error: $($SiteURL) already exists" -foregroundcolor red exit } else{ write-host "Error: $($SiteURL) still exists" -foregroundcolor red exit } #------------------------------------------------ #------------------------------------------------WORKING #Wait for 3 minutes for Office 365 to acknowledge the new site write-host "Waiting for 3 minutes for Office 365 to acknowledge the new site" -foregroundcolor green $Seconds = 180 $EndTime = [datetime]::UtcNow.AddSeconds($Seconds) while (($TimeRemaining = ($EndTime - [datetime]::UtcNow)) -gt 0) { Write-Progress -Activity 'Watiting for...' -SecondsRemaining $TimeRemaining.TotalSeconds Start-Sleep 1 } #------------------------------------------------ #------------------------------------------------WORKING #Connect to PnPOnline now the site is created try{ write-host "Info: Connecting to PnPOnline" -foregroundcolor green Connect-PnPOnline -Url $SiteURL -Interactive write-host "Info: Connected succesfully to PnPOnline" -foregroundcolor green } catch{ write-host "Error: Could not connect to PnPOnline" -foregroundcolor red exit } #------------------------------------------------ #------------------------------------------------WORKING #Disable check out on the new site write-host "Info: Disabling check out on the new site" -foregroundcolor green #Function to Disable Checkout Option for all libraries in a site Function Disable-PnPRequireCheckout { param ( [parameter(Mandatory = $true, ValueFromPipeline = $True)]$Web ) Try { Write-host "Processing Web:"$Web.URL -foregroundcolor green #Array to exclude system libraries $SystemLibraries = @("Form Templates", "Pages", "Preservation Hold Library","Site Assets", "Site Pages", "Images", "Site Collection Documents", "Site Collection Images","Style Library","Drop Off Library") $Libraries = Get-PnPList -Includes BaseType, Hidden, EnableVersioning -ErrorAction Stop #Get All document libraries $DocumentLibraries = $Libraries | Where {$_.BaseType -eq "DocumentLibrary" -and $_.Hidden -eq $False -and $_.Title -notin $SystemLibraries} #Disable Checkout ForEach($Library in $DocumentLibraries) { If($Library.ForceCheckout) { #Powershell to disable checkout for the library Set-PnPList -Identity $Library -ForceCheckout $false Write-host "`tRequire Check-out disabled on '$($Library.Title)'" -f Green } Else { Write-host "`tRequire Check Out is already set to 'No' at '$($Library.Title)'" -f Yellow } } } Catch { Write-host "`tError:" $_.Exception.Message -foregroundcolor Red } } Get-PnPWeb | Disable-PnPRequireCheckout Get-PnPSubWeb -Recurse | ForEach-Object { Disable-PnPRequireCheckout $_ } #------------------------------------------------ #------------------------------------------------WORKING #Function to disable access request on SharePoint Online write-host "Info: Disabling access request on the new site" -foregroundcolor green Function Disable-PnPAccessRequest { [cmdletbinding()] Param( [parameter(Mandatory = $true, ValueFromPipeline = $True)] $Web ) Try { Write-host "Disabling Access Request on:"$web.Url -foregroundcolor Yellow If($Web.HasUniqueRoleAssignments) { #Disable Access Request $Web.RequestAccessEmail = [string]::Empty $Web.SetUseAccessRequestDefaultAndUpdate($False) $Web.Update() Invoke-PnPQuery Write-host "`tAccess Request has been Disabled!"$web.Url -foregroundcolor Green } else { Write-host "`tWeb inherits permissions from the parent!"$web.Url -foregroundcolor Yellow } } Catch { write-host "`tError Disabling Access Request: $($_.Exception.Message)" -foregroundcolor Red } } #Call the Function for Root Web and all Subwebs Get-PnPWeb -Includes HasUniqueRoleAssignments | Disable-PnPAccessRequest Get-PnPSubWeb -Recurse -Includes HasUniqueRoleAssignments | ForEach-Object { Disable-PnPAccessRequest $_ } #------------------------------------------------ #------------------------------------------------WORKING #Disable Members sharing files folders or the site write-host "Info: Disable Members sharing files folders or the site" -foregroundcolor green function DisableMemberSharing($siteUrl ) { write-host "Info: Disabling Members sharing files folders or the site" -foregroundcolor green $web = Get-PnPWeb -Includes MembersCanShare, AssociatedMemberGroup.AllowMembersEditMembership $web.MembersCanShare=$false $web.AssociatedMemberGroup.AllowMembersEditMembership=$false $web.AssociatedMemberGroup.Update() $web.RequestAccessEmail = $null $web.Update() $web.Context.ExecuteQuery() } write-host "Info: Disabling sharing for none owners" -foregroundcolor green Set-SPOSite -Identity $SiteURL -DisableSharingForNonOwners Set-SPOSite -Identity $SiteURL -SharingCapability Disabled -DefaultLinkToExistingAccess $true -DisableCompanyWideSharingLinks Disabled #------------------------------------------------ #------------------------------------------------WORKING #Connect to PNPOnline and AzureAD write-host "Info: Connect to PnPOnline ManagementShell" -foregroundcolor green try{ write-host "Info: Connecting to PnPManagementShell" -foregroundcolor green Connect-PnPOnline $SiteURL -PnPManagementShell write-host "Info: Connected succesfully to PnPManagementShell" -foregroundcolor green } catch{ write-host "Error: Could not connect to PnPManagementShell" -foregroundcolor red exit } write-host "Info: Connect to AzureAD" -foregroundcolor green try{ write-host "Info: Connecting to AzureAD" -foregroundcolor green Connect-AzureAD write-host "Info: Connected succesfully to AzureAD" -foregroundcolor green } catch{ write-host "Error: Could not connect to AzureAD" -foregroundcolor red exit } #------------------------------------------------WORKING #Add the AD Groups to SharePoint write-host "Info: Add the AD Groups to SharePoint" -foregroundcolor green if ($ConnectToAD -eq $true) { # Get the SharePoint site $SPOSite = Get-SPOSite $SiteURL $ADOwnersGroupID = (Get-AzureADGroup -SearchString $ADOwnersGroupName).ObjectId $OwnersLoginName = "c:0t.c|tenant|" + $ADOwnersGroupID write-host "Info: Adding AD Group $ADOwnersGroupName with Login ID $OwnersLoginName to Owners" -foregroundcolor green Add-SPOUser -Site $SPOSite -LoginName $OwnersLoginName -Group $SPOwnersGroupName $ADMembersGroupID = (Get-AzureADGroup -SearchString $ADMembersGroupName).ObjectId $MembersLoginName = "c:0t.c|tenant|" + $ADMembersGroupID write-host "Info: Adding AD Group $ADMembersGroupName with Login ID $MembersLoginName to Members" -foregroundcolor green Add-SPOUser -Site $SPOSite -LoginName $MembersLoginName -Group $SPMembersGroupName $ADVisitorsGroupID = (Get-AzureADGroup -SearchString $ADVisitorsGroupName).ObjectId $VisitorLoginName = "c:0t.c|tenant|" + $ADVisitorsGroupID write-host "Info: Adding AD Group $ADVisitorsGroupName with Login ID $VisitorLoginName to Visitors" -foregroundcolor green Add-SPOUser -Site $SPOSite -LoginName $VisitorLoginName -Group $SPVisitorsGroupName } #------------------------------------------------ #------------------------------------------------WORKING #Get the library IDs write-host "Info: Get Azure Tenant ID" -foregroundcolor green $tenantId = (Get-AzureADTenantDetail).objectId write-host "Info: Tenant ID: $tenantId" -foregroundcolor green $PnPSite = Get-PnPSite -Includes Id | Select Id $PnPWeb = Get-PnPWeb | Select Id $PnPList = Get-PnPList 'Documents' | Select Id # Convert Tenant ID $tenantId = $tenantId -replace '-','%2D' # Convert Site ID $PnPSite = Get-PnPSite -Includes Id | select id $PnPSite = $PnPSite.Id -replace '-','%2D' $PnPSite = '%7B' + $PnPSite + '%7D' # Convert Web ID $PnPWeb = Get-PnPWeb -Includes Id | select id $PnPWeb = $PnPWeb.Id -replace '-','%2D' $PnPWeb = '%7B' + $PnPWeb + '%7D' # Convert List ID $PnPList = Get-PnPList $docLib -Includes Id | select id $PnPList = $PnPList.Id -replace '-','%2D' $PnPList = '%7B' + $PnPList + '%7D' $PnPList = $PnPList.toUpper() $FULLURL = 'tenantId=' + $tenantId + '&siteId=' + $PnPSite + '&webId=' + $PnPWeb + '&listId=' + $PnPList + '&webUrl=https%3A%2F%2F' + $TenantName + '%2Esharepoint%2Ecom%2Fsites%2F' + $siteName + '&version=1' # Output the FULL URL To Copy and Paste Write-Output 'Library ID: ' $FULLURL Write-Host "You will now need to manually add this library ID to the GPO - $GPOName" -foregroundcolor magenta #------------------------------------------------