Build-PorteoSiteListFromTemplateInParallel.ps1

<#
    .Synopsis
        Build-PorteoSiteListFromTemplateOnParallel -TenantUrl "https://mysite-admin.sharepoint.com" -Credentials (Get-Credential) `
                     -SiteList @(@("site1", "Site 1 Title"), @("site2", "Site 2 Title"))
                     -Owner "name@mysite.onmicrosoft.com" -Type "TeamSite" -ThrottleLimit 5`
                     -SrcConnection $srcConnection

    .Description
        This function builds a *Porteo* site for each item in the site list, but does it in parallel. A *Porteo* site is a ***SimpleSharepoint*** base site, based on
        either a **TeamSite** or **ComunicationSite** template type and has the following ***SimpleSharepoint*** lists:

        * Site Assets (SiteAssets)
        * Documents (Shared Documents)
        * Journal
        * Requests
        * SiteCollectionList
        * Links
        * SubsiteLists

        At this point, you would create this base site, then add contents to the above lists. You may
        also add some other lists, add to the _SiteAssets_ and any other *Pages* with content and
        branding. This site will be used to provision the specified sites on specified tenant.

        To provision a list of new sites, you first extract a template from the desired site using
        Get-SSPSiteTemplate with a file name (.pnp extension). You then use the `-Template` argument to specify
        this file and use the `-TenantUrl` and `-Credentials` arguments to create the site on the target tenant. See
        the series of Examples below to see the process and sequence of operations.

        Then the target sites are provisioned from the site associated with the template file.

        The site Pages are copied as well as the Pages' contents. Furthermore, the *Porteo* standard lists,
        which are listed above, have their structure and contents copied.

        The examples provide in sequence the process by which you may create a base *Porteo* site, modify it,
        and then use the result to provision another site.

        ***Note***: If a site already exists, this function will notify and skip its creation.
        ***Note***: The site type must match the site type of the source template.

    .Parameter Template
        This paramter holds the template file. It must be a ".pnp" file name.
    .Parameter TenantUrl
        This parameter holds the tenant adminstrative site url, e.g. https://mysite-admin.sharepoint.com
    .Parameter Credentials
        This parameter holds the credentials for the tenant adminstrator that can create sites.
    .Parameter SiteList
        This parameter holds the urls and titles of the sites to be created. It is a list of site specs.
        A site spec is a list of url and site title.
    .Parameter Type
        This paramter holds the type of site to be created, e.g. TeamSite or CommunicationSite.
        This parameter MUST be the same type as the template.
    .Parameter Owner
        This paramter holds the owner of the new site, which is an email address.
    .Parameter Lcid
        This paramter holds the language for the new sites. Default is "1033" English.
    .Parameter Runs
        This parameter contains a number of runs to use in applying the template to the site.
        It is found that probably due to temporal issues for Sharepoint, that multiple runs
        are necessary. Default is 2.
    .Parameter ColorPaletteUrl
        This parameter holds the color palette url for the theme, which should be in the template file.
    .Parameter AddInstalledApps
        This parameter adds any installed apps on the tenant to the site.
    .Parameter ThrottleLimit
        This parameter tells the parallel executor to use a max number of threads to execute jobs.

    .Example
    $dstCreds = Get-Credential

    Gets the tenant credentials to to build the new site.
    .Example
    $srcCreds = Get-Credential

    Gets the tenant credentials for the source site template.

    .Example
    $srcConn = Connect-PnPOnline -Url https://company.sharepoint.com/sites/site-template -Credentials $srcCreds -ReturnConnection

    Gets the authenticated connection for the template site.
    .Example
    Get-SSPSiteTemplate -FileName /tmp/template.pnp -SrcConnection $srcConn

    Gets and writes the template for the source site to the file /tmp/template.pnp.

    .Example
    $colorPaletteUrl = Add-SSPThemeToTemplate -Path /tmp/template.pnp

    Adds the current Theme to the template file. (THIS IS A WORKAROUND FOR BROKEN Theme Handler)
    .Example
    $newSiteUrl = Build-PorteoSiteListFromTemplateInParallel -Template /tmp/template.pnp
                 -TenantUrl "https://client-admin.sharepoint.com" -Credentials $dstCreds `
                 -SiteList @(@("site1", "Site 1 Title"), @("site2", "Site 2 Title")) -Type "TeamSite" `
                 -Owner "admin@company.com" -ColorPaletteUrl $colorPaletteUrl -AddInstalledApps -ThrottleLimit 5

    Builds the new sites "Site 1 Title" and "Site 2 Title" at https://client.sharepoint.com/sites/site1 and
    https://client.sharepoint.com/sites/site2 respectively, and provisions them from the
    template file /tmp/template.pnp.
#>


function Build-PorteoSiteListFromTemplateInParallel
{
    param(
        [parameter(Mandatory = $true)]
        [string]
        $template,
        [parameter(Mandatory = $true)]
        [string]
        $tenantUrl,
        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $credentials,
        [parameter(Mandatory = $true)]
        [Collections.Generic.List[string[]]]
        $siteList,
        [parameter(Mandatory = $true)]
        [string]
        $owner,
        [parameter(Mandatory = $false, HelpMessage = "Default is TeamSite")]
        [string]
        $type = "TeamSite",
        [parameter(Mandatory=$false)]
        [string]
        $lcid = "1033",
        [parameter(Mandatory=$false)]
        $runs = 2,
        [string]
        $colorPaletteUrl,
        [Boolean]
        $addInstalledApps = $false,
        [parameter(Mandatory = $false)]
        $throttleLimit = 5
    )
    # Reason for $using: Powershell has parallel execution but does not know how to build closures. WTF?

    $siteList | ForEach-Object -ThrottleLimit $throttleLimit -Parallel {
        Write-Host "Build-PorteoSiteListFromTemplateInParallel: Building site $($_[0]) from Template $using:template using $($using:runs) runs"
        Import-Module SSP.PorteoLibrary
        if (-not $using:colorPaletteUrl)
        {
            Build-PorteoSiteFromTemplate -Template $using:template -TenantUrl $using:tenantUrl `
             -Credentials $using:credentials -SiteName $_[0] -Title $_[1] -Owner $using:owner `
             -Type $using:type -Lcid $using:lcid -Runs $using:runs -AddInstalledApps $using:addInstalledApps
        }
        else
        {
            Build-PorteoSiteFromTemplate -Template $using:template -TenantUrl $using:tenantUrl `
             -Credentials $using:credentials -SiteName $_[0] -Title $_[1] -Owner $using:owner `
             -Type $using:type -Lcid $using:lcid -Runs $using:runs -AddInstalledApps $using:addInstalledApps `
             -ColorPaletteUrl $using:colorPaletteUrl
        }
    }
}