Office 365 グループに接続するConnect to an Office 365 group

サイトをモダン化する場合、Office 365 グループを既存の SharePoint サイトに接続できることが重要になります。Being able to connect an Office 365 group to an existing SharePoint site is important if you want to modernize that site. サイトが Office 365 グループに接続されると、Microsoft Teams、Planner などグループに接続済みのその他のすべてのサービスの利点を活用することができます。After your site is connected to an Office 365 group, it can benefit from all other group-connected services such as Microsoft Teams and Planner. この接続により従来のサイトは、既定で Office 365 グループに接続済みの、現行のモダン チーム サイトのようなものに一歩近づきます。This connection also brings your classic site a step closer to being like the current modern team site, which by default is connected to an Office 365 group.

ユーザー インターフェイスから、サイトを 1 つずつ新しい Office 365 グループに接続することができます。この方法は小規模な環境にお勧めです。You can connect your site to a new Office 365 group from the user interface site-by-site, which might be good for smaller environments. 一方、大規模な顧客では、ユーザーに一貫したエクスペリエンスを提供するために、サイトの一括操作を行うことがよくあります。However, larger customers often want to offer a consistent experience to their users, and therefore want to perform a bulk operation of their sites.

この記事では、このような関連サイトの新しい Office 365 グループへの一括操作の準備方法と、実際の操作方法を説明します。In this article, you'll learn how to prepare for such a bulk operation for associating sites to new Office 365 groups and how to actually make it happen.

重要

発行ポータル (BLANKINTERNET#0、ENTERWIKI#0、SRCHCEN#0、SRCHCENTERLITE#0、BICENTERSITE#0、POINTPUBLISHINGHUB#0、POINTPUBLISHINGTOPIC#0 に基づくサイトまたは “Pages” ライブラリを使用するサイト) の場合、Office 365 グループへの接続またはモダン ページの使用は現在サポートされていません。For publishing portals (sites based upon BLANKINTERNET#0, ENTERWIKI#0, SRCHCEN#0, SRCHCENTERLITE#0, BICENTERSITE#0, POINTPUBLISHINGHUB#0, POINTPUBLISHINGTOPIC#0 or sites using the “Pages” library) it's not currently supported to connect these to an Office 365 group or to use modern pages. 発行ポータルをモダン化する場合、新しいコミュニケーション サイトから開始し、それを適切に構成することをお勧めします。If you want to modernize your publishing portal it's recommended to start from a new communication site and configure that one accordingly.

重要

コミュニケーション サイトの Office 365 グループへの接続はサポートされていませんIt is not supported to connect a Communication site to Office 365 group.

新しい Office 365 グループに接続すると、サイトはどうなりますかWhat connecting to a new Office 365 group does to your site

新しい Office 365 グループにサイトを接続すると、さまざまな用途につながります。When you connect your site to a new Office 365 group, a number of things happen:

  • 新しい Office 365 グループが作成され、使用しているサイト コレクションにそのグループが接続されますA new Office 365 group is created, and that group is connected to your site collection
  • 新しくモダンなホーム ページがサイトに作成され、サイトのホーム ページとして設定されますA new modern home page is created on your site and set as the site's home page
  • グループの所有者がサイト コレクションの管理者になりますThe group's Owners are now the site collection administrators
  • グループの所有者がサイトの所有者グループに追加されますThe group's Owners are added to your site's Owners group
  • グループのメンバーがサイトのメンバー グループに追加されますThe group's Members are added to your site's Members group

サイトは、Office 365 グループにいったん接続されると、チーム サイトに接続されたモダン グループのように動作します。このため、接続された Office 365 グループへのアクセス許可をユーザーに付与すると、そのユーザーに SharePoint サイトへのアクセス許可も付与されるほか、サイトの上に Microsoft Team を作成したり、Planner を統合したり、さまざまなことが実現できるようになります。After your site is connected to an Office 365 group, it behaves like a modern group-connected team site, so granting people permission to the connected Office 365 group now also grants them access to the SharePoint site, a Microsoft Team can be created on top of the site, Planner can be integrated, and so on.

SharePoint ユーザー インターフェイスを使用して Office 365 グループを接続するConnect an Office 365 group using the SharePoint user interface

Office 365 グループをサイトに接続する方法の 1 つは、ユーザー インターフェイスで使用可能なオプションを使用することです。One approach to connect an Office 365 group to your site is to use the option available in the user interface. ナビゲーション バーの歯車アイコンを選択すると、[新しい Office 365 グループへの接続] オプションが選択できます。このオプションを選択すると、以下のスクリーンショットで示すようなウィザードが起動して、グループ接続のプロセスを進めてゆくことができます。By selecting the gear icon in the navigation bar, you can select the Connect to new Office 365 Group option, which launches a wizard that walks you through the group-connection process as shown in the following screenshots.

[サイトの操作] メニュー (歯車アイコン)Site actions menu (gear icon)

サイトの操作


ウィザードWizard

ウィザード

プログラムを使用して Office 365 グループを接続するProgrammatically connect an Office 365 group

プログラムを使用して、Office 365 グループを接続する場合は、3 段階の手順に従うことをお勧めします。To programmatically connect an Office 365 group, we recommend that you follow a three-step process:

  • 学習Learn
  • 分析Analyze
  • モダン化Modernize

手順 1: グループ接続がサイトに与える影響を学習するStep 1: Learn what group-connection does to your site

サイトを接続すると生じる変化について、十分に理解することが重要です。このため、ユーザー インターフェイスのオプションを使用して、いくつかのテスト サイトを手動で接続することをお勧めします。Getting familiar with what group-connection does to your site is important, and therefore we recommend that you do a manual group-connection for some test sites by using the user interface option. 評価するべき重要な面は、新たに作成されるモダン ホーム ページを維持し続ける必要があるかどうかです。An important aspect to evaluate is whether you want to keep the newly created modern home page. スクリプトをモダン化することの一部としてカスタマイズされたホーム ページを作成できますが、既定で作成されるホーム ページでニーズを満たしている場合は、それを使用する方が望ましいと言えます。As part of the modernization script, you'll be able to create a tailored home page, but if the default one serves your needs, that's the preferred option.

手順 2: サイトを分析するStep 2: Analyze your sites

前のセクションで示したユーザー インターフェイスのオプションは、何百もあるサイト コレクションのグループ接続には適していません。The user interface option shown in the previous section is not suitable if you want to group-connect hundreds of site collections. その場合は、API を使用してプログラム的に接続する方が理にかなっています。At that point, using an API to programmatically do this makes a lot of sense. ただし、グループ接続に適さないサイトもあるため、実際に実行する前に、どのサイトが接続の準備ができているかを確認することをお勧めします。But before doing that, it's best to verify which sites are ready to be group-connected because not all sites are suitable for this.

グループ接続をする準備ができているサイトを確認する助けとして、SharePoint モダン化スキャナーを使用して環境を分析できます。To help you understand which sites are ready to be group-connected, you can use the SharePoint Modernization Scanner to analyze your environment. このリンクには、スキャナーの実行に必要なすべての詳細が含まれています。This link contains all the details needed to run the scanner. スキャナーを実行した後、「スキャナーの結果を理解して処理する」の記事を参照して、スキャンの結果を分析してください。After you've run the scanner go the Understand and process the scanner results article to analyze the scan results.

手順 3: サイトをモダン化するStep 3: Modernize your sites

一括グループ接続のプロセスは、2 つの手順で構成されます。The bulk group-connect process consists of two steps:

  • 一括グループ接続のプロセスを実行するために使用する入力ファイルを準備し検証します。Prepare and validate an input file that you'll use to drive the bulk group-connect process.
  • 一括グループ接続のプロセスを実行する。Run the bulk group-connect process.

一括グループ接続用の入力ファイルを作成し検証するCreate an input file for bulk group-connection and validate it

スキャナーを実行し、結果を処理すると、グループ接続に適したサイトを特定できます。次の手順として、一括グループ接続プロセスを実行するための CSV ファイルを準備します。After running the scanner and processing the results, you have identified which sites are ready to group-connect.The next step is to prepare a CSV file to drive the bulk group-connection process. CSV ファイル形式は次の簡単なものです。The CSV file format is simple:

  • URL 列には、グループ接続するサイト コレクションの URL を格納します。URL column contains the URL to the site collection to group-connect.
  • Alias には、使用する Office 365 グループのエイリアスを格納します。Alias contains the Office 365 group alias that you want to use. このエイリアスにスペースを含めることはできません。また、以前に使用されていないものである必要があります。Note that this alias cannot contain spaces, and it should not have been used before.
  • IsPublic は、サイトの公開または非公開を指定します。IsPublic indicates whether you want the site to be a public or private site.
  • Classification には、グループ接続後にサイトに設定するサイトの分類を格納します。Classification contains the site classification that you want to set to the site after you group-connect. これが必要になるのは、グループへの接続後、この分類が Office 365 グループ レベルで管理されるためです。This is needed because after being connected to a group, this classification is maintained at the Office 365 group level.

簡単な例を次に示します。Following is a short sample:

Url,Alias,IsPublic,Classification
https://contoso.sharepoint.com/sites/hrteam,hrteam,false,Medium Impact
https://contoso.sharepoint.com/sites/engineering,engineeringteam,true,Low Impact

このファイルの使用前の検証に、このセクションの最後にある PowerShell スクリプトを使用できます。To help you verify this file before using it, you can use the PowerShell script at the end of this section. このスクリプトは、有効なサイトの URL とエイリアスを確認します。This script checks for valid site URLs and aliases. 使用しているテナントの管理センター URL を使用してこのスクリプトを更新し、実行してください。Update this script with your tenant admin center URL and run it. このスクリプトは、CSV ファイルの名前を確認し、自動的にレポートを生成します。The script asks for the CSV file name and generates a report for you.

検証スクリプトの実行時に、次のエラーが表示される場合があります。During the validation script execution, the following errors can appear:

エラーError | 説明Description ---------|---------- AzureAD Naming policy : PrefixSuffix does contain AD attributes that are resolved based on the user running the group-connectionAzureAD Naming policy : PrefixSuffix does contain AD attributes that are resolved based on the user running the group-connection | Azure AD で Office 365 グループの名前付けポリシーを定義できます。In Azure AD, you can define a naming policy for Office 365 groups. このポリシーにユーザー Active Directory 属性が含まれている場合、一括グループ接続では_現在の_ユーザーを使用してすべてのサイトを処理するため、このことが問題になる場合があります。If this policy contains user Active Directory attributes, this might be an issue because bulk group-connect handles all sites using the current user. AzureAD Creation policy : adminUPN is not part of group CanCreateGroupsId that controls Office 365 group creationAzureAD Creation policy : adminUPN is not part of group CanCreateGroupsId that controls Office 365 group creation | Azure AD グループの作成が特定のアカウントに制限されており、_現在の_アカウントがそれに含まれていない場合、Office 365 グループの作成は失敗します。If Azure AD group creation is restricted to certain accounts and the current account is not among those, the Office 365 group creation fails. siteUrl : Alias [siteAlias] contains a space, which is not allowedsiteUrl : Alias [siteAlias] contains a space, which is not allowed | Office 365 グループのエイリアスにスペースを含めることはできません。The alias of an Office 365 group cannot contain a space. siteUrl : Classification [siteClassification] does not comply with available Azure AD classifications [ClassificationListString]siteUrl : Classification [siteClassification] does not comply with available Azure AD classifications [ClassificationListString] | 指定したサイトの分類が、許可されている Office 365 グループのサイト分類のいずれとしても定義されていません。The provided site classification is not defined as one of the allowed site classifications for Office 365 groups. siteUrl : Alias [siteAlias] is in the Azure AD blocked word list [CustomBlockedWordsListString]siteUrl : Alias [siteAlias] is in the Azure AD blocked word list [CustomBlockedWordsListString] | Azure AD で禁止単語のリストが設定されており、指定された Office 365 グループの名前がリスト内の禁止単語を使用している場合は、このエラーが生成されます。If a blocked words list is set up in Azure AD and the provided Office 365 group name uses such a word, this error is generated. siteUrl : Site is already connected to a groupsiteUrl : Site is already connected to a group | サイトが接続できるのは、1 つの Office 365 グループのみです。したがって、いったんサイトを接続すると、それ以上グループ接続を行うことはできません。A site can only be connected to a single Office 365 group, so after a site is connected, it cannot be group-connected anymore. siteUrl : Alias [siteAlias] is already in usesiteUrl : Alias [siteAlias] is already in use | それぞれの Office 365 グループには固有のエイリアスが必要です。仮予約されたエイリアスが他の Office 365 グループで既に使用されている場合、このエラーが生成されます。Each Office 365 group needs a unique alias; an error is generated when the proposed alias was already used by another Office 365 group. siteUrl : Alias [siteAlias] was already marked as approved alias for another site in this filesiteUrl : Alias [siteAlias] was already marked as approved alias for another site in this file | 仮予約されたサイト エイリアスは既に、一括グループ接続の CSV ファイルの先行する入力行で別のサイトのために定義済みです。The proposed site alias was already defined for another site in earlier input lines of the bulk group-connect CSV file. siteUrl : Site does not exist or is not available (status = site.Status)siteUrl : Site does not exist or is not available (status = site.Status) | 指定したサイトの URL は、到達可能なサイト コレクションにはありません。The provided site URL does not represent a reachable site collection.

注意

テナント管理センターの URL (https://contoso-admin.sharepoint.com など) を含むように、以下に示すスクリプト内の $tenantAdminUrl 変数を更新してください。Please update the $tenantAdminUrl variable in the following script to contain your tenant admin center URL (for example, https://contoso-admin.sharepoint.com).

スクリプトの実行中、ログ ファイルが生成されるのと同時にエラー ファイルも生成されます。エラー ファイルにはログ ファイルのサブセット (エラーのみ) が格納されます。During script execution, a log file is generated, combined with an error file that contains a subset of the log file (only the errors).

<#
.SYNOPSIS
Validates the CSV input file for the bulk "Office 365 Group Connects". 

.EXAMPLE
PS C:\> .\ValidateInput.ps1
#>

#region Logging and generic functions
function LogWrite
{
    param([string] $log , [string] $ForegroundColor)

    $global:strmWrtLog.writeLine($log)
    if([string]::IsNullOrEmpty($ForegroundColor))
    {
        Write-Host $log
    }
    else
    {    
        Write-Host $log -ForegroundColor $ForegroundColor
    }
}

function LogError
{
    param([string] $log)
    
    $global:strmWrtError.writeLine($log)
}

function IsGuid
{
    param([string] $owner)

    try
    {
        [GUID]$g = $owner
        $t = $g.GetType()
        return ($t.Name -eq "Guid")
    }
    catch
    {
        return $false
    }
}

function IsGroupConnected
{
    param([string] $owner)

    if (-not [string]::IsNullOrEmpty($owner))
    {
        if ($owner.Length -eq 38)
        {
            
            if ((IsGuid $owner.Substring(0, 36)) -and ($owner.Substring(36, 2) -eq "_o"))
            {
                return $true
            }
        }        
    }

    return $false
}

function ContainsADAttribute
{
    param($PrefixSuffix)

    $ADAttributes = @("[Department]", "[Company]", "[Office]", "[StateOrProvince]", "[CountryOrRegion]", "[Title]")


    foreach($attribute in $ADAttributes)
    {
        if ($PrefixSuffix -like "*$attribute*")
        {
            return $true
        }
    }

    return $false
}

#endregion

#######################################################
# MAIN section                                        #
#######################################################
# Tenant admin url
$tenantAdminUrl = "https://contoso-admin.sharepoint.com"
# If you use credential manager then specify the used credential manager entry, if left blank you'll be asked for a user/pwd
$credentialManagerCredentialToUse = ""

#region Setup Logging
$date = Get-Date
$logfile = ((Get-Item -Path ".\" -Verbose).FullName + "\GroupconnectInputValidation_log_" + $date.ToFileTime() + ".txt")
$global:strmWrtLog=[System.IO.StreamWriter]$logfile
$global:Errorfile = ((Get-Item -Path ".\" -Verbose).FullName + "\GroupconnectInputValidation_error_" + $date.ToFileTime() + ".txt")
$global:strmWrtError=[System.IO.StreamWriter]$Errorfile
#endregion

#region Load needed PowerShell modules
#Ensure PnP PowerShell is loaded
$minimumVersion = New-Object System.Version("2.24.1803.0")
if (-not (Get-InstalledModule -Name SharePointPnPPowerShellOnline -MinimumVersion $minimumVersion -ErrorAction Ignore)) 
{
    Install-Module SharePointPnPPowerShellOnline -MinimumVersion $minimumVersion -Scope CurrentUser -Force
}
Import-Module SharePointPnPPowerShellOnline -DisableNameChecking -MinimumVersion $minimumVersion
#endregion

#region Ensure Azure PowerShell is loaded
$minimumAzurePowerShellVersion = New-Object System.Version("2.0.0.137")
if (-not (Get-InstalledModule -Name AzureADPreview -MinimumVersion $minimumAzurePowerShellVersion -ErrorAction Ignore))
{
    install-module AzureADPreview -MinimumVersion $minimumAzurePowerShellVersion -Scope CurrentUser -Force
}

Import-Module AzureADPreview -MinimumVersion $minimumAzurePowerShellVersion

$siteURLFile = Read-Host -Prompt 'Input name of .CSV file to validate (e.g. sitecollections.csv) ?'

# Get the tenant admin credentials.
$credentials = $null
$adminUPN = $null
if(![String]::IsNullOrEmpty($credentialManagerCredentialToUse) -and (Get-PnPStoredCredential -Name $credentialManagerCredentialToUse) -ne $null)
{
    $adminUPN = (Get-PnPStoredCredential -Name $credentialManagerCredentialToUse).UserName
    $credentials = $credentialManagerCredentialToUse
    $azureADCredentials = Get-PnPStoredCredential -Name $credentialManagerCredentialToUse -Type PSCredential
}
else
{
    # Prompts for credentials, if not found in the Windows Credential Manager.
    $adminUPN = Read-Host -Prompt "Please enter admin UPN"
    $pass = Read-host -AsSecureString "Please enter admin password"
    $credentials = new-object management.automation.pscredential $adminUPN,$pass
    $azureADCredentials = $credentials
}

if($credentials -eq $null) 
{
    Write-Host "Error: No credentials supplied." -ForegroundColor Red
    exit 1
}
#endregion

#region Connect to SharePoint and Azure
# Get a tenant admin connection, will be reused in the remainder of the script
LogWrite "Connect to tenant admin site $tenantAdminUrl"
$tenantContext = Connect-PnPOnline -Url $tenantAdminUrl -Credentials $credentials -Verbose -ReturnConnection

LogWrite "Connect to Azure AD"
$azureUser = Connect-AzureAD -Credential $azureADCredentials
#endregion

#region Read Azure AD group settings
$groupSettings = (Get-AzureADDirectorySetting | Where-Object -Property DisplayName -Value "Group.Unified" -EQ)

$CheckGroupCreation = $false
$CanCreateGroupsId = $null
$CheckClassificationList = $false
$ClassificationList = $null
$CheckPrefixSuffix = $false
$PrefixSuffix = $null
$CheckDefaultClassification = $false
$DefaultClassification = $null
$CheckCustomBlockedWordsList = $false

if (-not ($groupSettings -eq $null))
{
    if (-not($groupSettings["EnableGroupCreation"] -eq $true))
    {
        # Group creation is restricted to a security group...verify if the current user is part of that group
        # See: https://support.office.com/en-us/article/manage-who-can-create-office-365-groups-4c46c8cb-17d0-44b5-9776-005fced8e618?ui=en-US&rs=en-001&ad=US
        $CheckGroupCreation = $true
        $CanCreateGroupsId = $groupSettings["GroupCreationAllowedGroupId"]
    }

    if (-not ($groupSettings["CustomBlockedWordsList"] -eq ""))
    {
        # Check for blocked words in group name
        # See: https://support.office.com/en-us/article/office-365-groups-naming-policy-6ceca4d3-cad1-4532-9f0f-d469dfbbb552?ui=en-US&rs=en-001&ad=US
        $CheckCustomBlockedWordsList = $true
        $option = [System.StringSplitOptions]::RemoveEmptyEntries
        $CustomBlockedWordsListString = $groupSettings["CustomBlockedWordsList"]
        $CustomBlockedWordsList = $groupSettings["CustomBlockedWordsList"].Split(",", $option)
        
        # Trim array elements
        [int] $arraycounter = 0
        foreach($c in $CustomBlockedWordsList)
        {
            $CustomBlockedWordsList[$arraycounter] = $c.Trim(" ")
            $arraycounter++
        }
    }

    if (-not ($groupSettings["PrefixSuffixNamingRequirement"] -eq ""))
    {
        # Check for prefix/suffix naming - any dynamic tokens beside [groupname] can be problematic since all 
        # groups are created using the user running the bulk group connect
        # See: https://support.office.com/en-us/article/office-365-groups-naming-policy-6ceca4d3-cad1-4532-9f0f-d469dfbbb552?ui=en-US&rs=en-001&ad=US
        $CheckPrefixSuffix = $true
        $PrefixSuffix = $groupSettings["PrefixSuffixNamingRequirement"]
    }

    if (-not ($groupSettings["ClassificationList"] -eq ""))
    {
        # Check for valid classification labels
        # See: https://support.office.com/en-us/article/Manage-Office-365-Groups-with-PowerShell-aeb669aa-1770-4537-9de2-a82ac11b0540 
        $CheckClassificationList = $true

        $option = [System.StringSplitOptions]::RemoveEmptyEntries
        $ClassificationListString = $groupSettings["ClassificationList"]
        $ClassificationList = $groupSettings["ClassificationList"].Split(",", $option)
        
        # Trim array elements
        [int] $arraycounter = 0
        foreach($c in $ClassificationList)
        {
            $ClassificationList[$arraycounter] = $c.Trim(" ")
            $arraycounter++
        }

        if (-not ($groupSettings["DefaultClassification"] -eq ""))
        {        
            $CheckDefaultClassification = $true
            $DefaultClassification = $groupSettings["DefaultClassification"].Trim(" ")
        }
    }    
}
#endregion

#region Validate input
LogWrite "General Azure AD validation"
if ($CheckPrefixSuffix -and (ContainsADAttribute $PrefixSuffix))
{
    $message = "[ERROR] AzureAD Naming policy : $PrefixSuffix does contain AD attributes that are resolved based on the user running the group connect"
    LogWrite $message Red
    LogError $message                         
}

if ($CheckGroupCreation)
{
    $groupToCheck = new-object Microsoft.Open.AzureAD.Model.GroupIdsForMembershipCheck
    $groupToCheck.GroupIds = $CanCreateGroupsId
    $accountToCheck = Get-AzureADUser -SearchString $adminUPN
    $groupsUserIsMemberOf = Select-AzureADGroupIdsUserIsMemberOf -ObjectId $accountToCheck.ObjectId -GroupIdsForMembershipCheck $groupToCheck
    if ($groupsUserIsMemberOf -eq $null)
    {
        $message = "[ERROR] AzureAD Creation policy : $adminUPN is not part of group $CanCreateGroupsId which controls Office 365 Group creation"
        LogWrite $message Red
        LogError $message                         
    }
}

# "approved" aliases
$approvedAliases = @{}

LogWrite "Validating rows in $siteURLFile..."
$csvRows = Import-Csv $siteURLFile

foreach($row in $csvRows)
{
    if($row.Url.Trim() -ne "")
    {
        $siteUrl = $row.Url
        $siteAlias = $row.Alias
        $siteClassification = $row.Classification
        if ($siteClassification -ne $null)
        {
            $siteClassification = $siteClassification.Trim(" ")
        }

        LogWrite "[VALIDATING] $siteUrl with alias [$siteAlias] and classification [$siteClassification]"

        try 
        {
            # First perform validations that do not require to load the site
            if ($siteAlias.IndexOf(" ") -gt 0)
            {
                $message = "[ERROR] $siteUrl : Alias [$siteAlias] contains a space, which not allowed"
                LogWrite $message Red
                LogError $message 
            }
            elseif (($CheckClassificationList -eq $true) -and (-not ($ClassificationList -contains $siteClassification)))
            {
                $message = "[ERROR] $siteUrl : Classification [$siteClassification] does not comply with available AzureAD classifications [$ClassificationListString]"
                LogWrite $message Red
                LogError $message                         
            }     
            elseif (($CheckCustomBlockedWordsList -eq $true) -and ($CustomBlockedWordsList -contains $siteAlias))
            {
                $message = "[ERROR] $siteUrl : Alias [$siteAlias] is in the AzureAD blocked word list [$CustomBlockedWordsListString]"
                LogWrite $message Red
                LogError $message                         
            }                       
            else 
            {
                # try getting the site
                $site = Get-PnPTenantSite -Url $siteUrl -Connection $tenantContext -ErrorAction Ignore
                
                if ($site.Status -eq "Active")
                {
                    if (IsGroupConnected $site.Owner)
                    {
                        $message = "[ERROR] $siteUrl : Site is already connected a group"
                        LogWrite $message Red
                        LogError $message 
                    }
                    else
                    {
                        $aliasIsUsed = Test-PnPOffice365GroupAliasIsUsed -Alias $siteAlias -Connection $tenantContext      
                        if ($aliasIsUsed)
                        {
                            $message = "[ERROR] $siteUrl : Alias [$siteAlias] is already in use"
                            LogWrite $message Red
                            LogError $message   
                        }
                        elseif ($approvedAliases.ContainsKey($siteAlias))
                        {
                            $message = "[ERROR] $siteUrl : Alias [$siteAlias] was already marked as approved alias for another site in this file"
                            LogWrite $message Red
                            LogError $message   
                        }
                        else 
                        {
                            $approvedAliases.Add($siteAlias, $siteAlias)
                            LogWrite "[VALIDATED] $siteUrl with alias [$siteAlias] and classification [$siteClassification]" Green
                        }                        
                    }
                }
                else 
                {
                    $message = "[ERROR] $siteUrl : Site does not exist or is not available (status = $($site.Status))"
                    LogWrite $message Red    
                    LogError $message
                }                
            }
        }
        catch [Exception]
        {
            $ErrorMessage = $_.Exception.Message
            LogWrite "Error: $ErrorMessage" Red
            LogError $ErrorMessage    
        }
        
    }
}
#endregion

#region Close log files
if ($global:strmWrtLog -ne $NULL)
{
    $global:strmWrtLog.Close()
    $global:strmWrtLog.Dispose()
}

if ($global:strmWrtError -ne $NULL)
{
    $global:strmWrtError.Close()
    $global:strmWrtError.Dispose()
}
#endregion

一括グループ接続のプロセスを実行するRun the bulk group-connect process

グループ接続する必要があるサイトを定義する入力ファイルが準備できたので、実行に移ることができます。Now that we have an input file that's defining the sites that need to be group-connected, we can finally make it happen. 以下の PowerShell スクリプトは、サンプル スクリプトです。グループ接続の一部として、スクリプトを足したり減らしたり、ニーズに応じて調整してください。The following PowerShell script is a sample script that you can tweak to your needs because you might want more or fewer things as part of the group-connection.

以下に示すサンプル バージョンのスクリプトでは、次の手順を実装します。The shared sample version of the script implements the following steps:

  • 必要に応じて、サイト管理者として現在のテナント管理者を追加します。グループ接続するにはユーザー アカウントが必要です (アプリ単独では不可)。Adds current tenant admin as site admin when needed; group-connection requires a user account (so no app-only).
  • スキャナーのロジックに合わせて、グループ接続を妨げるサイト テンプレートや発行機能が使用されていないかどうかを確認します。Verifies site template / publishing feature use and prevents group-connection; aligns with the logic in the scanner.
  • モダンをブロックする機能が有効になっていないことを確認し、有効になっている場合は修正します。Ensures that no modern blocking features are enabled, and if so, fixes them.
  • モダン ページの機能が有効になっていることを確認します。Ensures that the modern page feature is enabled.
  • オプション: アプリケーション (例: アプリケーション カスタマイザー) を展開します。Optional: Deploys applications (for example, Application Customizer).
  • オプション: モダン ホーム ページを追加します。Optional: Adds your own modern home page.
  • グループ接続の API を呼び出します。Calls the group-connect API.
  • サイト管理者とサイトの所有者をグループの所有者として定義します。Defines site admins and site owners as group owners.
  • サイト メンバーをグループ メンバーとして定義します。Defines site members as group members.
  • SharePoint 管理者から、追加されたテナント管理者とサイトの所有者を削除します。Removes added tenant admin and site owners from SharePoint admins.
  • Office 365 グループから追加されたテナント管理者を削除します。Removes added tenant admin from the Office 365 group.

以下の PowerShell スクリプトを実行するには、テナント管理センターの URL を更新し、実行時に資格情報と CSV 入力ファイルを提供する必要があります。Running the following PowerShell script requires that you update the tenant admin center URL and at run time provide credentials and the CSV input file.

注意

これはサンプル スクリプトであり、オプションの部分を更新または削除したり、さらにモダン化タスクを追加したり (SharePoint サイトのテーマ設定など) して、それぞれのユーザーの必要に合わせたものにする必要があります。This is a sample script that you need to address to your needs by updating/dropping the optional parts or by adding additional modernization tasks (such as setting a SharePoint site theme). テナント管理センターの URL (https://contoso-admin.sharepoint.com など) を含むようにスクリプト内の $tenantAdminUrl 変数を更新してください。Please update the $tenantAdminUrl variable in the script to contain your tenant admin center URL (for example, https://contoso-admin.sharepoint.com).

スクリプトの実行中、ログ ファイルが生成されるのと同時にエラー ファイルも生成されます。エラー ファイルにはログ ファイルのサブセット (エラーのみ) が格納されます。During script execution, a log file is generated, combined with an error file that contains a subset of the log file (only the errors).

<#
.SYNOPSIS
"Office 365 Group Connects" a Classic SharePoint Online team site by attaching it to an Office Group and provisioning the default resources. Also enables the user to add a classification label and alias for the Group and enables Modern User Experience for the site.

Doesn't use parameters, rather asks for the values it needs. Optionally, supports hardcoding the use of Credential Manager (won't ask for credentials) and SharePoint admin site url.

.EXAMPLE
PS C:\> .\O365GroupConnectSite.ps1
#>

#region Logging and generic functions
function LogWrite
{
    param([string] $log , [string] $ForegroundColor)

    $global:strmWrtLog.writeLine($log)
    if([string]::IsNullOrEmpty($ForegroundColor))
    {
        Write-Host $log
    }
    else
    {    
        Write-Host $log -ForegroundColor $ForegroundColor
    }
}

function LogError
{
    param([string] $log)
    
    $global:strmWrtError.writeLine($log)
}

function LoginNameToUPN
{
    param([string] $loginName)

    return $loginName.Replace("i:0#.f|membership|", "")
}

function AddToOffice365GroupOwnersMembers
{
    param($groupUserUpn, $groupId, [bool] $Owners)

    # Apply an incremental backoff strategy as after group creation the group is not immediately available on all Azure AD nodes resulting in resource not found errors
    # It can take up to a minute to get all Azure AD nodes in sync
    $retryCount = 5
    $retryAttempts = 0
    $backOffInterval = 2

    LogWrite "Attempting to add $groupUserUpn to group $groupId"  

    while($retryAttempts -le $retryCount)
    {
        try 
        {
            if ($Owners)
            {
                $azureUserId = Get-AzureADUser -ObjectId $groupUserUpn            
                Add-AzureADGroupOwner -ObjectId $groupId -RefObjectId $azureUserId.ObjectId  
                LogWrite "User $groupUserUpn added as group owner"  
            }
            else 
            {
                $azureUserId = Get-AzureADUser -ObjectId $groupUserUpn           
                Add-AzureADGroupMember -ObjectId $groupId -RefObjectId $azureUserId.ObjectId    
                LogWrite "User $groupUserUpn added as group member"  
            }
            
            $retryAttempts = $retryCount + 1;
        }
        catch 
        {
            if ($retryAttempts -lt $retryCount)
            {
                $retryAttempts = $retryAttempts + 1        
                Write-Host "Retry attempt number: $retryAttempts. Sleeping for $backOffInterval seconds..."
                Start-Sleep $backOffInterval
                $backOffInterval = $backOffInterval * 2
            }
            else
            {
                throw
            }
        }
    }
}

function UsageLog
{
    try 
    {
        $cc = Get-PnPContext
        $cc.Load($cc.Web)
        $cc.ClientTag = "SPDev:GroupifyPS"
        $cc.ExecuteQuery()
    }
    catch [Exception] { }
}
#endregion

function GroupConnectSite
{
    param([string] $siteCollectionUrl, 
          [string] $alias,
          [Boolean] $isPublic,
          [string] $siteClassification,
          $credentials,
          $tenantContext,
          [string] $adminUPN)
    
    
    #region Ensure access to the site collection, if needed promote the calling account to site collection admin
    # Check if we can access the site...if not let's 'promote' ourselves as site admin
    $adminClaim = "i:0#.f|membership|$adminUPN"    
    $adminWasAdded = $false
    $siteOwnersGroup = $null
    $siteContext = $null    
    $siteCollectionUrl = $siteCollectionUrl.TrimEnd("/");

    Try
    {
        LogWrite "User running group connect: $adminUPN"
        LogWrite "Connecting to site $siteCollectionUrl"
        $siteContext = Connect-PnPOnline -Url $siteCollectionUrl -Credentials $credentials -Verbose -ReturnConnection
    }
    Catch [Exception]
    {
        # If Access Denied then use tenant API to add current tenant admin user as site collection admin to the current site
        if ($_.Exception.Response.StatusCode -eq "Unauthorized")
        {
            LogWrite "Temporarily adding user $adminUPN as site collection admin"
            Set-PnPTenantSite -Url $siteCollectionUrl -Owners @($adminUPN) -Connection $tenantContext
            $adminWasAdded = $true
            LogWrite "Second attempt to connect to site $siteCollectionUrl"
            $siteContext = Connect-PnPOnline -Url $siteCollectionUrl -Credentials $credentials -Verbose -ReturnConnection
        }
        else 
        {
            $ErrorMessage = $_.Exception.Message
            LogWrite "Error for site $siteCollectionUrl : $ErrorMessage" Red
            LogError $ErrorMessage
            return              
        }
    }
    #endregion

    Try
    {
        # Group connect steps
        # - [Done] Add current tenant admin as site admin when needed
        # - [Done] Verify site template / publishing feature use and prevent group connect --> align with the logic in the scanner
        # - [Done] Ensure no modern blocking features are enabled...if so fix it
        # - [Done] Ensure the modern page feature is enabled
        # - [Done] Optional: Deploy applications (e.g. application customizer)
        # - [Done] Optional: Add modern home page
        # - [Done] Call group connect API
        # - [Done] Define Site Admins and Site owners as group owners
        # - [Done] Define Site members as group members
        # - []     Have option to "expand" site owners/members if needed
        # - [Done] Remove added tenant admin and site owners from SharePoint admins
        # - [Done] Remove added tenant admin from the Office 365 group

        #region Adding admin
        # Check if current tenant admin is part of the site collection admins, if not add the account        
        $siteAdmins = $null
        if ($adminWasAdded -eq $false)
        {
            try 
            {
                # Eat exceptions here...resulting $siteAdmins variable will be empty which will trigger the needed actions                
                $siteAdmins = Get-PnPSiteCollectionAdmin -Connection $siteContext -ErrorAction Ignore
            }
            catch [Exception] { }
            
            $adminNeedToBeAdded = $true
            foreach($admin in $siteAdmins)
            {
                if ($admin.LoginName -eq $adminClaim)
                {
                    $adminNeedToBeAdded = $false
                    break
                }
            }

            if ($adminNeedToBeAdded)
            {
                LogWrite "Temporarily adding user $adminUPN as site collection admin"
                Set-PnPTenantSite -Url $siteCollectionUrl -Owners @($adminUPN) -Connection $tenantContext
                $adminWasAdded = $true
            }
        }

        UsageLog
        #endregion

        #region Checking for "blockers"
        $publishingSiteFeature = Get-PnPFeature -Identity "F6924D36-2FA8-4F0B-B16D-06B7250180FA" -Scope Site -Connection $siteContext
        $publishingWebFeature = Get-PnPFeature -Identity "94C94CA6-B32F-4DA9-A9E3-1F3D343D7ECB" -Scope Web -Connection $siteContext

        if (($publishingSiteFeature.DefinitionId -ne $null) -or ($publishingWebFeature.DefinitionId -ne $null))
        {
            throw "Publishing feature enabled...can't group connect this site"
        }

        # Grab the web template and verify if it's a group connect blocker
        $web = Get-PnPWeb -Connection $siteContext -Includes WebTemplate,Configuration,Description
        $webTemplate = $web.WebTemplate + $web.Configuration

        if ($webTemplate -eq "BICENTERSITE#0" -or 
            $webTemplate -eq "BLANKINTERNET#0" -or
            $webTemplate -eq "ENTERWIKI#0" -or
            $webTemplate -eq "SRCHCEN#0" -or
            $webTemplate -eq "SRCHCENTERLITE#0" -or
            $webTemplate -eq "POINTPUBLISHINGHUB#0" -or
            $webTemplate -eq "POINTPUBLISHINGTOPIC#0" -or
            $siteCollectionUrl.EndsWith("/sites/contenttypehub"))
        {
            throw "Incompatible web template detected...can't group connect this site"
        }
        #endregion
        
        #region Enable full modern experience by enabling the pages features and disabling "blocking" features
        LogWrite "Enabling modern page feature, disabling modern list UI blocking features"
        # Enable modern page feature
        Enable-PnPFeature -Identity "B6917CB1-93A0-4B97-A84D-7CF49975D4EC" -Scope Web -Force -Connection $siteContext
        # Disable the modern list site level blocking feature
        Disable-PnPFeature -Identity "E3540C7D-6BEA-403C-A224-1A12EAFEE4C4" -Scope Site -Force -Connection $siteContext
        # Disable the modern list web level blocking feature
        Disable-PnPFeature -Identity "52E14B6F-B1BB-4969-B89B-C4FAA56745EF" -Scope Web -Force -Connection $siteContext
        #endregion

        #region Optional: Add SharePoint Framework customizations - sample
        # LogWrite "Deploying SPFX application customizer"
        # Add-PnPCustomAction -Name "Footer" -Title "Footer" -Location "ClientSideExtension.ApplicationCustomizer" -ClientSideComponentId "edbe7925-a83b-4d61-aabf-81219fdc1539" -ClientSideComponentProperties "{}"
        #endregion

        #region Optional: Add custom home page - sample
        # LogWrite "Deploying a custom modern home page"
        # $homePage = Get-PnPHomePage -Connection $siteContext
        # $newHomePageName = $homePage.Substring($homePage.IndexOf("/") + 1).Replace(".aspx", "_new.aspx")
        # $newHomePagePath = $homePage.Substring(0, $homePage.IndexOf("/") + 1)
        # $newHomePage = Add-PnPClientSidePage -Name $newHomePageName -LayoutType Article -CommentsEnabled:$false -Publish:$true -Connection $siteContext

        # Add your additional web parts here!
        # Add-PnPClientSidePageSection -Page $newHomePage -SectionTemplate OneColumn -Order 1 -Connection $siteContext
        # Add-PnPClientSideText -Page $newHomePage -Text "Old home page was <a href=""$siteCollectionUrl/$homePage"">here</a>" -Section 1 -Column 1
        # Set-PnPHomePage -RootFolderRelativeUrl ($newHomePagePath + $newHomePageName) -Connection $siteContext
        #endregion        

        #region Prepare for group permission configuration
        # Get admins again now that we've ensured our access
        $siteAdmins = Get-PnPSiteCollectionAdmin -Connection $siteContext
        # Get owners and members before the group claim gets added
        $siteOwnersGroup = Get-PnPGroup -AssociatedOwnerGroup -Connection $siteContext               
        $siteMembersGroup = Get-PnPGroup -AssociatedMemberGroup -Connection $siteContext               
        #endregion

        #region Call group connect API
        LogWrite "Call group connnect API with following settings: Alias=$alias, IsPublic=$isPublic, Classification=$siteClassification"
        Add-PnPOffice365GroupToSite -Url $siteCollectionUrl -Alias $alias -DisplayName $alias -Description $web.Description -IsPublic:$isPublic -KeepOldHomePage:$false -Classification $siteClassification -Connection $siteContext
        #endregion

        #region Configure group permissions
        LogWrite "Adding site administrators and site owners to the Office 365 group owners"
        $groupOwners = @{}
        foreach($siteAdmin in $siteAdmins)
        {
            if (($siteAdmin.LoginName).StartsWith("i:0#.f|membership|"))
            {
                $siteAdminUPN = (LoginNameToUPN $siteAdmin.LoginName)
                if (-not ($siteAdminUPN -eq $adminUPN))
                {
                    if (-not ($groupOwners.ContainsKey($siteAdminUPN)))
                    {
                        $groupOwners.Add($siteAdminUPN, $siteAdminUPN)
                    }
                }
            }
            else 
            {
                #TODO: group expansion?    
            }
        }
        foreach($siteOwner in $siteOwnersGroup.Users)
        {
            if (($siteOwner.LoginName).StartsWith("i:0#.f|membership|"))
            {
                $siteOwnerUPN = (LoginNameToUPN $siteOwner.LoginName)
                if (-not ($groupOwners.ContainsKey($siteOwnerUPN)))
                {
                    $groupOwners.Add($siteOwnerUPN, $siteOwnerUPN)
                }
            }
            else 
            {
                #TODO: group expansion?    
            }
        }

        $site = Get-PnPSite -Includes GroupId -Connection $siteContext
        foreach($groupOwner in $groupOwners.keys)
        {
            try 
            {
                AddToOffice365GroupOwnersMembers $groupOwner ($site.GroupId) $true
            }
            catch [Exception]
            {
                $ErrorMessage = $_.Exception.Message
                LogWrite "Error adding user $groupOwner to group owners. Error: $ErrorMessage" Red
                LogError $ErrorMessage
            }
        }

        LogWrite "Adding site members to the Office 365 group members"
        $groupMembers = @{}
        foreach($siteMember in $siteMembersGroup.Users)
        {
            if (($siteMember.LoginName).StartsWith("i:0#.f|membership|"))
            {
                $siteMemberUPN = (LoginNameToUPN $siteMember.LoginName)
                if (-not ($groupMembers.ContainsKey($siteMemberUPN)))
                {
                    $groupMembers.Add($siteMemberUPN, $siteMemberUPN)
                }
            }
            else 
            {
                #TODO: group expansion?    
            }
        }

        foreach($groupMember in $groupMembers.keys)
        {
            try 
            {
                AddToOffice365GroupOwnersMembers $groupMember ($site.GroupId) $false                
            }
            catch [Exception]
            {
                $ErrorMessage = $_.Exception.Message
                LogWrite "Error adding user $groupMember to group members. Error: $ErrorMessage" Red
                LogError $ErrorMessage
            }
        }        
        #endregion

        #region Cleanup updated permissions
        LogWrite "Group connect is done, let's cleanup the configured permissions"
    
        # Remove the added site collection admin - obviously this needs to be the final step in the script :-)
        if ($adminWasAdded)
        {
            #Remove the added site admin from the Office 365 Group owners and members
            LogWrite "Remove $adminUPN from the Office 365 group owners and members"            
            $site = Get-PnPSite -Includes GroupId -Connection $siteContext
            $azureAddedAdminId = Get-AzureADUser -ObjectId $adminUPN
            try 
            {
                Remove-AzureADGroupOwner -ObjectId $site.GroupId -OwnerId $azureAddedAdminId.ObjectId -ErrorAction Ignore
                Remove-AzureADGroupMember -ObjectId $site.GroupId -MemberId $azureAddedAdminId.ObjectId -ErrorAction Ignore                    
            }
            catch [Exception] { }

            LogWrite "Remove $adminUPN from site collection administrators"            
            Remove-PnPSiteCollectionAdmin -Owners @($adminUPN) -Connection $siteContext
}
        #endregion

        LogWrite "Group connect done for site collection $siteCollectionUrl" Green
        
        # Disconnect PnP Powershell from site
        Disconnect-PnPOnline
    }
    Catch [Exception]
    {
        $ErrorMessage = $_.Exception.Message
        LogWrite "Error: $ErrorMessage" Red
        LogError $ErrorMessage

        #region Cleanup updated permissions on error
        # Group connect run did not complete...remove the added tenant admin to restore site permissions as final step in the cleanup
        if ($adminWasAdded)
        {
            # Below logic might fail if the error happened before the Group connect API call, but errors are ignored
            $site = Get-PnPSite -Includes GroupId -Connection $siteContext
            $azureAddedAdminId = Get-AzureADUser -ObjectId $adminUPN
            try 
            {
                Remove-AzureADGroupOwner -ObjectId $site.GroupId -OwnerId $azureAddedAdminId.ObjectId -ErrorAction Ignore
                Remove-AzureADGroupMember -ObjectId $site.GroupId -MemberId $azureAddedAdminId.ObjectId -ErrorAction Ignore
                # Final step, remove the added site collection admin
                Remove-PnPSiteCollectionAdmin -Owners @($adminUPN) -Connection $siteContext
            }
            catch [Exception] { }
        }
        #endregion

        LogWrite "Group connect failed for site collection $siteCollectionUrl" Red
    } 

}

#######################################################
# MAIN section                                        #
#######################################################

# OVERRIDES
# If you want to automate the run and make the script ask less questions, feel free to hardcode these 2 values below. Otherwise they'll be asked from the user or parsed from the values they input

# Tenant admin url
$tenantAdminUrl = "" # e.g. "https://contoso-admin.sharepoint.com"
# If you use credential manager then specify the used credential manager entry, if left blank you'll be asked for a user/pwd
$credentialManagerCredentialToUse = ""

#region Setup Logging
$date = Get-Date
$logfile = ((Get-Item -Path ".\" -Verbose).FullName + "\Groupconnect_log_" + $date.ToFileTime() + ".txt")
$global:strmWrtLog=[System.IO.StreamWriter]$logfile
$global:Errorfile = ((Get-Item -Path ".\" -Verbose).FullName + "\Groupconnect_error_" + $date.ToFileTime() + ".txt")
$global:strmWrtError=[System.IO.StreamWriter]$Errorfile
#endregion

#region Load needed PowerShell modules
# Ensure PnP PowerShell is loaded
$minimumVersion = New-Object System.Version("3.4.1812.2")
if (-not (Get-InstalledModule -Name SharePointPnPPowerShellOnline -MinimumVersion $minimumVersion -ErrorAction Ignore)) 
{
    Install-Module SharePointPnPPowerShellOnline -MinimumVersion $minimumVersion -Scope CurrentUser
}
Import-Module SharePointPnPPowerShellOnline -DisableNameChecking -MinimumVersion $minimumVersion

# Ensure Azure PowerShell is loaded
$loadAzurePreview = $false # false to use 2.x stable, true to use the preview versions of cmdlets
if (-not (Get-Module -ListAvailable -Name AzureAD))
{
    # Maybe the preview AzureAD PowerShell is installed?
    if (-not (Get-Module -ListAvailable -Name AzureADPreview))
    {
        install-module azuread
    }
    else 
    {
        $loadAzurePreview = $true
    }
}

if ($loadAzurePreview)
{
    Import-Module AzureADPreview
}
else 
{
    Import-Module AzureAD   
}
#endregion

#region Gather group connect run input
# Url of the site collection to remediate
$siteCollectionUrlToRemediate = ""
$siteAlias = ""
$siteIsPublic = $false

# Get the input information
$siteURLFile = Read-Host -Prompt 'Input either single site collection URL (e.g. https://contoso.sharepoint.com/sites/teamsite1) or name of .CSV file (e.g. sitecollections.csv) ?'
if (-not $siteURLFile.EndsWith(".csv"))
{
    $siteCollectionUrlToRemediate = $siteURLFile
    $siteAlias = Read-Host -Prompt 'Input the alias to be used to group connect this site ?'
    $siteIsPublicString = Read-Host -Prompt 'Will the created Office 365 group be a public group ? Enter True for public, False otherwise'
    $siteClassificationLabel = Read-Host -Prompt 'Classification label to use? Enter label or leave empty if not configured'
    try 
    {
        $siteIsPublic = [System.Convert]::ToBoolean($siteIsPublicString) 
    } 
    catch [FormatException]
    {
        $siteIsPublic = $false
    }
}
# If we are using a CSV, we'll need to get the tenant admin url from the user or use the hardcoded one
else {
    if ($tenantAdminUrl -eq $null -or $tenantAdminUrl.Length -le 0) {
        $tenantAdminUrl = Read-Host -Prompt 'Input the tenant admin site URL (like https://contoso-admin.sharepoint.com): '
    }
}

# We'll parse the tenantAdminUrl from site url (unless it's set already!)
if ($tenantAdminUrl -eq $null -or $tenantAdminUrl.Length -le 0) {
    if ($siteURLFile.IndexOf("/teams") -gt 0) {
        $tenantAdminUrl = $siteURLFile.Substring(0, $siteURLFile.IndexOf("/teams")).Replace(".sharepoint.", "-admin.sharepoint.")
    }
    else {
        $tenantAdminUrl = $siteURLFile.Substring(0, $siteURLFile.IndexOf("/sites")).Replace(".sharepoint.", "-admin.sharepoint.")
    }
}

# Get the tenant admin credentials.
$credentials = $null
$azureADCredentials = $null
$adminUPN = $null
if(![String]::IsNullOrEmpty($credentialManagerCredentialToUse) -and (Get-PnPStoredCredential -Name $credentialManagerCredentialToUse) -ne $null)
{
    $adminUPN = (Get-PnPStoredCredential -Name $credentialManagerCredentialToUse).UserName
    $credentials = $credentialManagerCredentialToUse
    $azureADCredentials = Get-PnPStoredCredential -Name $credentialManagerCredentialToUse -Type PSCredential
}
else
{
    # Prompts for credentials, if not found in the Windows Credential Manager.
    $adminUPN = Read-Host -Prompt "Please enter admin UPN"
    $pass = Read-host -AsSecureString "Please enter admin password"
    $credentials = new-object management.automation.pscredential $adminUPN,$pass
    $azureADCredentials = $credentials
}

if($credentials -eq $null) 
{
    Write-Host "Error: No credentials supplied." -ForegroundColor Red
    exit 1
}
#endregion

#region Connect to SharePoint and Azure
# Get a tenant admin connection, will be reused in the remainder of the script
LogWrite "Connect to tenant admin site $tenantAdminUrl"
$tenantContext = Connect-PnPOnline -Url $tenantAdminUrl -Credentials $credentials -Verbose -ReturnConnection

LogWrite "Connect to Azure AD"
$azureUser = Connect-AzureAD -Credential $azureADCredentials
#endregion

#region Group connect the site(s)
if (-not $siteURLFile.EndsWith(".csv"))
{
    # Remediate the given site collection
    GroupConnectSite $siteCollectionUrlToRemediate $siteAlias $siteIsPublic $siteClassificationLabel $credentials $tenantContext $adminUPN
}
else 
{
    $csvRows = Import-Csv $siteURLFile
    
    foreach($row in $csvRows)
    {
        if($row.Url.Trim() -ne "")
        {
            $siteUrl = $row.Url
            $siteAlias = $row.Alias
            $siteIsPublicString = $row.IsPublic
    
            try 
            {
                $siteIsPublic = [System.Convert]::ToBoolean($siteIsPublicString) 
            } 
            catch [FormatException] 
            {
                $siteIsPublic = $false
            }    

            $siteClassification = $row.Classification
            if ($siteClassification -ne $null)
            {
                $siteClassification = $siteClassification.Trim(" ")
            }

            GroupConnectSite $siteUrl $siteAlias $siteIsPublic $siteClassification $credentials $tenantContext $adminUPN
        }
    }
}
#endregion

#region Close log files
if ($global:strmWrtLog -ne $NULL)
{
    $global:strmWrtLog.Close()
    $global:strmWrtLog.Dispose()
}

if ($global:strmWrtError -ne $NULL)
{
    $global:strmWrtError.Close()
    $global:strmWrtError.Dispose()
}
#endregion

関連項目See also