Loading...

Follow Jerry Yasir SharePoint Blog on Feedspot

Continue with Google
Continue with Facebook
or

Valid

There are many examples available that shows how to bulk migrate or upload files to SharePoint but none of those are providing a method to migrate with metadata.  The script below can be used to migrate complicated folder hierarchy to be uploaded to SharePoint Online or On-Premises.  The script is based on the the effort on Technet example https://gallery.technet.microsoft.com/PowerShell-Bulk-Upload-b9e9d600.  The same script can be modified to upload large amount of metadata.  You just need to export the files list to a CSV and then read values after the upload.  I will upload the other example later.

[CmdletBinding()]
param(
[Parameter(Mandatory=$false,Position=1)]
[String]$Credentials,
[Parameter(Mandatory=$True, Position=3)]
[String]$SiteURL,
[Parameter(Mandatory=$True, Position=4)]
[String]$DocLibName,
[Parameter(Mandatory=$True, Position=5)]
[String]$Folder,
[Parameter(Mandatory=$False, Position=6)]
[Switch]$Checkin,
[Parameter(Mandatory=$False, Position=7)]
[Switch]$O365
)

<#
    Add references to SharePoint client assemblies and authenticate to Office 365 site – required for CSOM

Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll”
Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll”
#>

Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll” -ErrorAction SilentlyContinue
Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll” -ErrorAction SilentlyContinue

Add-Type -Path ([System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint.Client”).location)
Add-Type -Path ([System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint.Client.runtime”).location)

function GetUserLookupString{
    [CmdletBinding()]
    param($context, $userString)
   
    try{
        $user = $context.Web.EnsureUser($userString)
        $context.Load($user)
        $context.ExecuteQuery()
       
        # The “proper” way would seem to be to set the user field to the user value object
        # but that does not work, so we use the formatted user lookup string instead
        #$userValue = New-Object Microsoft.SharePoint.Client.FieldUserValue
        #$userValue.LookupId = $user.Id
        $userLookupString = “{0};#{1}” -f $user.Id, $user.LoginName
    }
    catch{
        Write-Host “Unable to ensure user ‘$($userString)’.” -ErrorAction SilentlyContinue
        $userLookupString = $null
    }
   
    return $userLookupString
}

<#
    Define Functions
#>

<#
    Upload File – This function performs the actual file upload
#>
function UploadFile($DestinationFolder, $File)
{
    #Get the datastream of the file, assign it to a variable
    $FileStream = New-Object IO.FileStream($File.FullName,[System.IO.FileMode]::Open)

    #Create an instance of a FileCreationInformation object
    $FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation

    #Indicate whether or not you would like to overwrite files in the event of a conflict
    $FileCreationInfo.Overwrite = $True

    #Make the datastream of the file you wish to create equal to the datastream of the source file
    $FileCreationInfo.ContentStream = $FileStream

    #Make the URL of the file equal to the $File variable which was passed to the function.  This will be equal to the source file name
    $FileCreationInfo.url = $File

    #Add the file to the destination folder which was passed to the function, using the FileCreationInformation supplied.  Assign this to a variable so that it can be loaded into context.
    $Upload = $DestinationFolder.Files.Add($FileCreationInfo)
    if($Checkin)
    {
        $Context.Load($Upload)
        $Context.ExecuteQuery()
        if($Upload.CheckOutType -ne “none”)
        {
            $Upload.CheckIn(“Checked in by Administrator”, [Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn)
        }
    }
    $Context.Load($Upload)
    $Context.ExecuteQuery()

    Write-Host “Adding Metadata to File Item…$($File.FullName)”
            $listItem = $Upload.ListItemAllFields
            $Context.Load($listItem)
            #$Context.ExecuteQuery()
            $Context.ExecuteQuery()
   
            $Item = $List.GetItemById($listItem.Id);
            $Context.Load($Item)

            $Owner = $File.GetAccessControl().Owner
            $LastModifiedBy =Get-ChildItem $File.FullName | Sort {$_.LastWriteTime} | select -last 1 | foreach {$a=$_;$b=Get-Acl $_.FullName; Add-Member -InputObject $b -Name “LastWriteTime” -MemberType NoteProperty -Value $a.LastWriteTime;$b}
           
            if($Owner -eq “BUILTIN\Administrators”)
            {
                $Author = “hp\spadmin”
            }

            if($LastModifiedBy.Owner -eq “BUILTIN\Administrators”)
            {
                $Editor = “hp\spadmin”
            }
     
            $Author = GetUserLookupString $Context $Author
            if($Author)
            {
                $Item[“Author”] = $Author
            }
            $Editor = GetUserLookupString $Context $Editor
            if($Editor)
            {
                $Item[“Editor”] = $Editor
            }
       
            [System.DateTime]$CreatedOnDate = $File.CreationTime
            [System.DateTime]$ModifiedOnDate = $File.LastWriteTime

            $Item[“Created”] = $CreatedOnDate
            $Item[“Modified”] = $ModifiedOnDate
            try
            {
                $Item.Update()
                $Context.ExecuteQuery()
            }
            catch
            { Write-Host “Exception Occured in reading list item properties… But script will continue” }
           
}

<#
    Create Folder Function.
#>
function PopulateFolder($ListRootFolder, $FolderRelativePath, $FolderFullPath)
{
    #split the FolderRelativePath passed into chunks (between the backslashes) so that we can check if the folder structure exists
    $PathChunks = $FolderRelativePath.substring(1).split(“\”)

    #Make sure we start with a fresh WorkingFolder for every folder passed to the function
    if($WorkingFolder)
    {
        Remove-Variable WorkingFolder
    }

    #Start with the root folder of the list, load this into context
    $WorkingFolder = $ListRootFolder
    $Context.load($WorkingFolder)
    $Context.ExecuteQuery()

    #Load the folders of the current working folder into context
    $Context.load(($WorkingFolder.folders))
    $Context.executeQuery()

    #Set the FileSource folder equal to the absolute path of the folder that passed to the function
    $FileSource = $Folder + $FolderRelativePath
   
    #Loop through the folder chunks, ensuring that the correct folder hierarchy exists in the destination
    foreach($Chunk in $PathChunks)
    {
        #Check to find out if a subfolder exists in the current folder that matches the patch chunk being evaluated
        if($WorkingFolder.folders | ? {$_.name -eq $Chunk})
        {
            #Log the status to the PowerShell host window
            Write-Host “Folder $Chunk Exists in” $WorkingFolder.name -ForegroundColor Green

            #Since we will be evaluating other chunks in the path, set the working folder to the current folder and load this into context.
            $WorkingFolder = $WorkingFolder.folders | ? {$_.name -eq $Chunk}
            $Context.load($WorkingFolder)
            $Context.load($WorkingFolder.folders)
            $Context.ExecuteQuery()

        }
        else
        {
            #If the folder doesn’t exist, Log a message indicating that the folder doesn’t exist, and another message indicating that it is being created
            Write-Host “Folder $Chunk Does Not Exist in” $WorkingFolder.name -ForegroundColor Yellow
            Write-Host “Creating Folder $Chunk in” $WorkingFolder.name -ForegroundColor Green
           
            #Load the working folder into context and create a subfolder with a name equal to the chunk being evaluated, and load this into context
            $Context.load($WorkingFolder)
            $Context.load($WorkingFolder.folders)
            $Context.ExecuteQuery()

            $WorkingFolder= $WorkingFolder.folders.add($Chunk)
            $Context.load($WorkingFolder)
            $Context.load($WorkingFolder.folders)
            $Context.ExecuteQuery()

            #$FolderRelativePath = “C:\Demo\Corporate Docs”
            $LocalFolder = Get-Item -Path $FolderFullPath

            $Web = $Context.Web
            $Context.Load($Web)
            $Context.ExecuteQuery()

            $SPFolder = $Web.GetFolderByServerRelativeUrl($WorkingFolder.ServerRelativeUrl)
            $Context.Load($SPFolder)
            $Context.Load($SPFolder.ListItemAllFields)
            $Context.ExecuteQuery()
            $FolderItemId = $SPFolder.ListItemAllFields.Id
            $FItem = $List.GetItemById($FolderItemId)
            $Context.Load($FItem)
            $Context.ExecuteQuery()

            #$Query = New-Object Microsoft.SharePOint.Client.CamlQuery
            #$Query.ViewXml = “<View Scope=’RecursiveAll’><Query><Where><And><Eq><FieldRef Name=’ContentType’/><Value Type=’Text’>Folder</Value></Eq><Eq><FieldRef Name=’FileLeafRef’/><Value Type=’Text’>” + $Chunk + “</Value></Eq></And></Where></Query></View>”

            $FItem[“Created”] = $LocalFolder.CreationTime.ToString()
            $FItem[“Modified”] = $LocalFolder.LastWriteTime.ToString()
            $CreatedBy = $LocalFolder.GetAccessControl().Owner
            if($CreatedBy -eq “BUILTIN\Administrators”)
            {
                $CreatedBy = “hp\spadmin”
            }

            $Author = GetUserLookupString $Context $CreatedBy
            if($Author)
            {
                $FItem[“Editor”] = $Author
                $FItem[“Author”] = $Author
            }
            $Context.ExecuteQuery()

            try
            {
                $FItem.Update()
                $Context.ExecuteQuery()
            }
            catch
            { Write-Host “Exception Occured in updating folder properties… But script will continue” }

        }

    }

    #Folder is confirmed existing or created – now it’s time to list all files in the source folder, and assign this to a variable
    $FilesInFolder = Get-ChildItem -Path $FileSource | ? {$_.psIsContainer -eq $False}
   
    #For each file in the source folder being evaluated, call the UploadFile function to upload the file to the appropriate location
    Foreach ($File in ($FilesInFolder))
    {

        #Notify the operator that the file is being uploaed to a specific location
        Write-Host “Uploading file ” $file.Name “to” $WorkingFolder.name -ForegroundColor Cyan

        #Upload the file
        UploadFile $WorkingFolder $File

    }
   
   
   
}

<#
    Bind your context to the site collection
#>
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)

<#
    Create a credential object using the username and password supplied
#>

if($O365)
{
    $Creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Credentials.UserName,$Credentials.Password)
}
else
{
    $Creds = [System.Net.CredentialCache]::DefaultCredentials
    #$Creds = New-Object System.Net.NetworkCredential($Credentials.UserName,$Credentials.Password)
}
#if($O365)
#{
#    $Creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,(ConvertTo-SecureString $Password -AsPlainText -Force))
#}
#else
#{
#    #$Creds = New-Object System.Net.NetworkCredential($UserName, (ConvertTo-SecureString $Password -AsPlainText -Force))
#}

<#
    Set the credentials that are used in the context.
#>
$Context.Credentials = $Creds

<#
    Retrieve the library, and load it into the context
#>
$List = $Context.Web.Lists.GetByTitle($DocLibName)
$Context.Load($List)
$Context.ExecuteQuery()

$List.Fields.GetByInternalNameOrTitle(“Author”).ReadOnlyField = $true
$List.Fields.GetByInternalNameOrTitle(“Editor”).ReadOnlyField = $true
$List.Fields.GetByInternalNameOrTitle(“Created”).ReadOnlyField = $true
$List.Fields.GetByInternalNameOrTitle(“Modified”).ReadOnlyField = $true
#$List.EnableVersioning = $True
$List.Update()
$Context.ExecuteQuery()

#Get a recursive list of all folders beneath the folder supplied by the operator
$AllFolders = Get-ChildItem -Recurse -Path $Folder |? {$_.psIsContainer -eq $True}

#Get a list of all files that exist directly at the root of the folder supplied by the operator
$FilesInRoot = Get-ChildItem -Path $Folder | ? {$_.psIsContainer -eq $False}

#Upload all files in the root of the folder supplied by the operator
Foreach ($File in ($FilesInRoot))
{

    #Notify the operator that the file is being uploaded to a specific location
    Write-Host “Uploading file ” $File.Name “to” $DocLibName -ForegroundColor Cyan

    #Upload the file
    UploadFile($list.RootFolder) $File
   

}

#Loop through all folders (recursive) that exist within the folder supplied by the operator
foreach($CurrentFolder in $AllFolders)
{
    #Set the FolderRelativePath by removing the path of the folder supplied by the operator from the fullname of the folder
    $FolderRelativePath = ($CurrentFolder.FullName).Substring($Folder.Length)
   
    #Call the PopulateFolder function for the current folder, which will ensure that the folder exists and upload all files in the folder to the appropriate location
    PopulateFolder ($list.RootFolder) $FolderRelativePath $CurrentFolder.FullName
}

#Calling the script for SharePoint on-premises

$Creds = Get-Credential
.\BulkUploadSharePointCSOM.ps1 -SiteURL “https://portal.hp.com” -DocLibName “Metadata” -Folder “C:\Demo”

#Calling the script for SharePoint online

$Creds = Get-Credential
.\BulkUploadSharePointCSOM.ps1 -SiteURL “https://portal.hp.com” -DocLibName “Metadata” -Folder “C:\Demo” -Credentials $Creds -Checkin -O365

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

In the previous script I provide a way to browse and export Microsoft Teams Information using Graph but this time I have taken that to next level.  The script below will allow you to create a Team, Channel, Members, Owners, Tabs and Message, all using Microsoft Graph.  The script is created for someone who do not have great knowledge of Graph and may act as starting point.  It is not a complete solution but can be use to provision team with basic to medium complexities.  Creating Team from Graph and PowerShell may look a bit complex but once you read the graph documentation, It become very easy.  The script uses both v1.0 and beta endpoints based on their availability. 

The CSV format is as following

ID,TeamName,MailNickName,TeamType,Classification,DeleteExistingTeam,Description,Channels,Owners,Members,Tabs,
1,TeamMXC,TeamMXC,Private,Internal,Yes,Demo Team will provide support,Channel1#Channel2#Channel3,GarthF@tenant.onmicrosoft.com#jerryyasir@tenant.onmicrosoft.com,GarretV@tenant.onmicrosoft.com#abrown@tenant.onmicrosoft.com,C:\temp\CreateTeam-Members.csv,Channel1;Tab1;Tab2#Channel2;Tab3;Tab4

Script

#$scopes = @(“Group.Read.All”,”Group.ReadWrite.All”,”User.ReadWrite.All”, “Directory.Read.All”,”Reports.Read.All”)
$scopes = $null

$ApplicationID = “”
$Password = ”
$appaaddomain = ‘tenant.onmicrosoft.com’
$CurrentUser = “jerryyasir@tenant.onmicrosoft.com”
 
$GraphURL = “https://graph.microsoft.com/beta”
$graphV1Endpoint = “https://graph.microsoft.com/v1.0″

#Establish connection
If($scopes.Length -gt 0){
    Connect-PnPOnline -Scopes $scopes
} elseif($ApplicationID.Length -gt 0) {
    #Connect-PnPOnline -AppId $ApplicationID -AppSecret $Password -AADDomain $appaaddomain
    Connect-PnPMicrosoftGraph -AppId $ApplicationID -AppSecret $Password -AADDomain $appaaddomain
} else {
    write-host ‘Connection issue’ -ForegroundColor Red
    exit
}

$token = Get-PnPAccessToken

$CSVPath = “C:\temp\CreateTeam-Basic-Graph.csv”

$Data = Import-Csv -Path $CSVPath

foreach($team in $Data)
{
    Write-Host “Team Creation Started…” $team.TeamsName -ForegroundColor Yellow
    $TeamName       = $team.TeamName
    $displayname    = $team.TeamName
    $MailNickName    = $team.MailNickName
    $AccessType     = $team.TeamType
    $Description    = $team.Description
    $Classification = $team.Classification
    $TeamChannels   = $team.Channels
    $TeamMembers    = $team.Members
    $TeamOwners     = $team.Owners
    $DeleteExistingTeam    = $team.DeleteExistingTeam
 
    #$url = “$GraphURL/groups?`$filter=displayName eq ‘TeamABC'”
    $getTeamFromGraphUrl = “$GraphURL/groups?`$filter=displayName eq ‘” + $TeamName + “‘”
    $teamAlreadyExistResponse = Invoke-RestMethod -Uri $getTeamFromGraphUrl -Headers @{Authorization = “Bearer $token”}
    if($teamAlreadyExistResponse.value.Count -gt 0){
        foreach($r in $teamAlreadyExistResponse.value){
            if($r.resourceProvisioningOptions -eq ‘Team’){
       
                $GroupCreatedDateTime = $r.createdDateTime
               
                $TeamID = $r.id
                $TeamsOwnerUrl =  “$GraphURL/groups/$TeamID/owners”
                $teamsOwnersResponse = Invoke-RestMethod -Uri $TeamsOwnerUrl -Headers @{Authorization = “Bearer $token”}
                $OwnerName = “”
                if($teamsOwnersResponse)
                {
                    Write-Host ”    Team Owners:”
                    foreach($owner in $teamsOwnersResponse.value)
                    {
                        $OwnerName += $owner.displayName + “;”
                    }
                    $teamsOwnersResponse = $null
                }
                write-host “The Team $($r.displayname) Created On $GroupCreatedDateTime Owned by $OwnerName already exist on the tenant.” -ForegroundColor Yellow
               
                if($DeleteExistingTeam)
                {
                   
                }
           
            }
            else {
                write-host $r.displayname “is an O365 Group.” -ForegroundColor Green
            }
        }
    }
   
    else
    {
        $arrayMembers = New-Object System.Collections.ArrayList
        try
        {
            $arrTeamMembers = $TeamMembers -split “#”
            if($arrTeamMembers)
            {
                for($i =0; $i -le ($arrTeamMembers.count – 1) ; $i++)
                {
                    $MemberUrl = “$graphV1Endpoint/users”
                    $UserUserPrincipalName = $arrTeamMembers[$i]
                    if($UserUserPrincipalName -ne $CurrentUser)
                    {
                        #$arrayMembers.Add(“$MemberUrl/$UserUserPrincipalName”)
                        $arrayMembers.Add($UserUserPrincipalName)
                    }
                }
            }
        }
        Catch
        {
            Write-Host “There is issue with Channel settings in CSV, Check and Fix:. $teamchannels”
        }
               
        $arrayOwners = New-Object System.Collections.ArrayList
               
        try
        {
            $arrTeamOwners = $TeamOwners -split “#”
            if($arrTeamOwners)
            {
                for($i =0; $i -le ($arrTeamOwners.count – 1) ; $i++)
                {
                    $OwnerUserPrincipalName = $arrTeamOwners[$i]
                    $OwnerUrl = “$graphV1Endpoint/users”
                    $arrayOwners.Add($OwnerUserPrincipalName)
                }
            }
        }
        Catch
        {
            Write-Host “There is issue with Channel settings in CSV, Check and Fix:. $teamchannels”
        }

        $arryGroupType = @(“Unified”)
       
        $arrayOwnersInREST = New-Object System.Collections.ArrayList
        $arrayMembersInREST = New-Object System.Collections.ArrayList
        foreach($Member in $arrayMembers)
        {
            $FindMemberUrl = “https://graph.microsoft.com/v1.0/users/” + $Member + “?`$Select=Id”
            $Response = Invoke-RestMethod -Uri $FindMemberUrl -Headers @{Authorization = “Bearer $token”} -Method Get -ContentType “application/json” -Verbose
            if($Response)
            {
                $Response.id
                $MembersUrl = “https://graph.microsoft.com/v1.0/Users/$($Response.id)”
                $arrayMembersInREST.Add($MembersUrl)
            }
        }
        foreach($owner in $arrayOwners)
        {
            $FindOwnerUrl = “https://graph.microsoft.com/v1.0/users/” + $owner + “?`$Select=Id”
            $Response = Invoke-RestMethod -Uri $FindOwnerUrl -Headers @{Authorization = “Bearer $token”} -Method Get -ContentType “application/json” -Verbose
            if($Response)
            {$Response.id
                $OwnerUrl = “https://graph.microsoft.com/v1.0/Users/$($Response.id)”
                $arrayOwnersInREST.Add($OwnerUrl)
            }
        }
       
        $FindOwnerUrl = “https://graph.microsoft.com/v1.0/users/” + $CurrentUser + “?`$Select=Id”
        $Response = Invoke-RestMethod -Uri $FindOwnerUrl -Headers @{Authorization = “Bearer $token”} -Method Get -ContentType “application/json” -Verbose
        if($Response)
        {
            $Response.id
            $CurrentUserAsMemberUrl = “https://graph.microsoft.com/v1.0/directoryobjects/$($Response.id)”
                   
            $CurrentUserAsMember = “$graphV1Endpoint/groups/$TeamID/members/`$ref”
            $body = [ordered]@{
                “@odata.id” = $CurrentUserAsMemberUrl
            }
            $bodyJSON = $body | ConvertTo-Json 
            Invoke-RestMethod -Uri $CurrentUserAsMember -Headers @{Authorization = “Bearer $token”} -Body $bodyJSON -Method Post -ContentType “application/json” -Verbose
                   
            $PlannerUri = “$GraphURL/planner/plans”
            $body = [ordered]@{
                owner = $TeamID;
                title = $TeamName;
            }
            $bodyJSON = $body | ConvertTo-Json 
            Invoke-RestMethod -Uri $PlannerUri -Headers @{Authorization = “Bearer $token”} -Body $bodyJSON -Method Post -ContentType “application/json” -Verbose
            Write-Host ”      A planner plan $TeamName is now added to the Team.” -ForegroundColor Green
        }
               
        $GroupUrl = “$graphV1Endpoint/groups”
        $body = [ordered]@{
            displayName = $TeamName;
            description = $Description;
            groupTypes = $arryGroupType;
            mailEnabled= $true;
            mailnickname = $MailNickName;
            securityEnabled=$false;
            “members@odata.bind” = $arrayMembersInREST;
            “owners@odata.bind” = $arrayOwnersInREST;
        }
       
        $bodyJSON = $body | ConvertTo-Json 
        $Response = Invoke-RestMethod -Uri $GroupUrl -Headers @{Authorization = “Bearer $token”} -Body $bodyJSON -Method Post -ContentType “application/json” -Verbose
       
        $GroupCreationResponse = $null
        $Stoploop = $false
        $GroupId = $null
        do {
            $GroupQueryUrl=$GroupUrl + “/?`$filter=displayName eq ‘” + $TeamName + “‘”
            $GroupCreationResponse = Invoke-RestMethod -Uri $GroupQueryUrl -Headers @{Authorization = “Bearer $token”} -Method Get -Verbose 
            if($GroupCreationResponse.value.Count -gt 0){
                $Stoploop = $true
                foreach($val in $GroupCreationResponse.value)
                {
                    $GroupId = $val.Id
                }
               
            }
        }
        While ($Stoploop -eq $false)
       
        $memberSettings = @{}
        $memberSettings.Add(“allowCreateUpdateChannels”,$true)
       
        $messagingSettings = @{}
        $messagingSettings.Add(“allowUserEditMessages”,$true)
        $messagingSettings.Add(“allowUserDeleteMessages”,$true)

        $funSettings = @{}
        $funSettings.Add(“allowGiphy”,$true)
        $funSettings.Add(“giphyContentRating”,$true)
       
        $TeamCreationUrl = “$GraphURL/groups/$GroupId/team”
        $body = [ordered]@{
        }
        $bodyJSON = $body | ConvertTo-Json
       
        $TeamCreationResponse = $null
        $TeamCreationResponse=Invoke-RestMethod -Uri $TeamCreationUrl -Headers @{Authorization = “Bearer $token”} -Body $bodyJSON -Method Put -ContentType “application/json” -Verbose
        if($TeamCreationResponse -eq $null)
        {
            $Stoploop = $false
            do {
                $TeamCreationResponse = Invoke-RestMethod -Uri $getTeamFromGraphUrl -Headers @{Authorization = “Bearer $token”} -Method Get -Verbose 
                if($TeamCreationResponse){
                    $Stoploop = $true
                }
            }
            While ($Stoploop -eq $false)
        }
       
     
        if($TeamCreationResponse){
            foreach($t in $TeamCreationResponse){
                $newTeamDisplayName = $t.displayName
                $TeamId = $t.Id
                Write-Host “Team $newTeamDisplayName has been created successfully…” -ForegroundColor Green

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

As we already know that Microsoft Teams PowerShell does not provide cmdlets to get detailed information from Microsoft Teams.  It may evolve over next few months but we do have Microsoft Graph API that provides new has endpoints that you can use to export information about Team, Channels, Messages, Members and owners etc.  The sample below does some of the basic functions.  It all the teams and then try to get some information to an array of objects.  This is no way a complete sample but a good starting point.   It might help you get started with Teams.

I am using PNPOnline Powershell Module to get connect to Micorosft Graph and Get the Access token.  There couple of different ways to do that but PNP makes it lot easier.  Make sure you install the SharePointPnPPowerShellOnline module.

$scopes = @(“Group.Read.All”,”User.ReadWrite.All”, “Directory.Read.All”,”Reports.Read.All”)
$scopes = $null

$ApplicationID = “”
$Password = ”
$appaaddomain = ‘tenant.onmicrosoft.com’
 
$GraphURL = “https://graph.microsoft.com/beta”
$url = “$GraphURL/groups?`$filter=resourceProvisioningOptions/Any(x:x eq ‘Team’)”

#Establish connection
If($scopes.Length -gt 0){
    Connect-PnPOnline -Scopes $scopes
} elseif($ApplicationID.Length -gt 0) {
    Write-Host “Connecting using Application” -ForegroundColor Yellow
    Connect-PnPOnline -AppId $ApplicationID -AppSecret $Password -AADDomain $appaaddomain
} else {
    write-host ‘Connection issue’ -ForegroundColor Red
    exit
}
 
#Get token
$token = Get-PnPAccessToken
 
#Call graph
if($token){
    $response = Invoke-RestMethod -Uri $url -Headers @{Authorization = “Bearer $token”}
} else {
    write-host ‘Token issue’ -ForegroundColor Red
    exit
}

$TeamsData = $null
$TeamsData = @()

#Parse data
if($response){
    foreach($r in $response.value){
        if($r.resourceProvisioningOptions -eq ‘Team’){
           
            $teamDataObj = New-Object -TypeName PSObject
           
            $GroupClassification = $r.classification
            $GroupCreatedDateTime = $r.createdDateTime
            $visibility = $r.visibility
            $TeamName = $r.displayname
            write-host “Team Name:$TeamName Classification:$GroupClassification CreatedOn:$GroupCreatedDateTime” -ForegroundColor Yellow
            $TeamID = $r.id
            $TeamUrl =  “$GraphURL/groups/$TeamID/members”
            $TeamsOwnerUrl =  “$GraphURL/groups/$TeamID/owners”
            $ChannelsUrl =  “$GraphURL/groups/$TeamID/channels”
           
            $teamDataObj | Add-Member -Type NoteProperty -Name TeamName -Value $TeamName
            $teamDataObj | Add-Member -Type NoteProperty -Name Createdon -Value $GroupCreatedDateTime
            $teamDataObj | Add-Member -Type NoteProperty -Name Classification -Value $GroupClassification
            $teamDataObj | Add-Member -Type NoteProperty -Name Visibility -Value $visibility
           
            if($token){
                $rTeam = Invoke-RestMethod -Uri $TeamUrl -Headers @{Authorization = “Bearer $token”}
                $rTeamsOwners = Invoke-RestMethod -Uri $TeamsOwnerUrl -Headers @{Authorization = “Bearer $token”}
                $rTeamChanel = Invoke-RestMethod -Uri $ChannelsUrl -Headers @{Authorization = “Bearer $token”}
            } else {
                write-host ‘Token issue’ -ForegroundColor Red
                exit
            }
            $TeamOwnersAll = “”
            if($rTeamsOwners)
            {
                Write-Host ”    Team Owners:”
                foreach($owner in $rTeamsOwners.value)
                {
                    $OwnerName = $owner.displayName
                    $OwnerEmail = $owner.mail
                    Write-Host ”        $OwnerName”
                    $TeamOwnersAll +=”$OwnerEmail#”
                }
         
            }
            $teamDataObj | Add-Member -Type NoteProperty -Name Owners -Value $OwnerEmail
            if($rTeam)
            {
                $TeamMembersAll = “”
                Write-Host ”    Team Members:”
                foreach($member in $rTeam.value)
                {
                    $TeamMember = $member.displayName
                    $TeamMemberEmail = $member.mail
                    $TeamMemberRole = $member.assignedLicenses
                    Write-Host ”        $TeamMember”
                    $TeamMembersAll +=”$TeamMemberEmail#”
                }
         
            }
            $teamDataObj | Add-Member -Type NoteProperty -Name TeamMembers -Value $TeamMembersAll
            if($rTeamChanel)
            {
                Write-Host ”    Channels”
                $AllChannels = “”
                foreach($channel in $rTeamChanel.value)
                {
                    $ChannelName = $channel.displayName
                    $ChannelId = $channel.id
                    Write-Host ”        Channels: $ChannelName”
                    $AllChannels +=”$ChannelName#”
                   
           
                    #/teams/{id}/channels/{id}/messages
           
                    $ChannelsMessageUrls =  “$GraphURL/teams/$TeamID/channels/$ChannelId/messages”
           

                    if($token){
                        $rMessages = Invoke-RestMethod -Uri $ChannelsMessageUrls -Headers @{Authorization = “Bearer $token”}
                    } else {
                        write-host ‘Token issue’ -ForegroundColor Red
                        exit
                    }
                   
                    $MessageCount = 0
                    $Replies = 0
           
                    if($rMessages)
                    {
                        foreach($Message in $rMessages.value)
                        {
                            $MessageId = $Message.id
                            $messageBody = $Message.body
                            $MessagecreatedDateTime = $message.createdDateTime
                            $MessageCount++;
                            Write-Host ”        Message: $messageBody $MessageId $MessagecreatedDateTime”
                            $ChannelsRepliesUrls =  “$GraphURL/teams/$TeamID/channels/$ChannelId/messages/$MessageId/replies”
                            if($token){
                                $rReplies = Invoke-RestMethod -Uri $ChannelsRepliesUrls -Headers @{Authorization = “Bearer $token”}
                                if($rReplies)
                                {
                                    foreach($rReplie in $rReplies.Value)
                                    {
                                        $rreplyId = $rReplie.body
                                        $rcreatedDateTime = $rReplie.createdDateTime
                                        $Replies++;
                                        Write-Host ”            Reply:$rcreatedDateTime”
                                    }
                                }
                            } else {
                                write-host ‘Token issue’ -ForegroundColor Red
                                exit
                            }
                        }
                    }
       
                }
                $teamDataObj | Add-Member -Type NoteProperty -Name Channels -Value $AllChannels
                $teamDataObj | Add-Member -Type NoteProperty -Name MessageCount -Value $MessageCount
                $teamDataObj | Add-Member -Type NoteProperty -Name RepliesCount -Value $Replies
            }
           
            $TeamsData += $teamDataObj
       
            #Do fancy stuff in here
        } else {
            write-host $r.displayname “is a regular O365 Group” -ForegroundColor Green
        }
    }
} else {
    write-host ‘Response issue’ -ForegroundColor Red
}

$TeamsData | Out-GridView
$TeamsData | Export-Csv -Path C:\temp\TeamsInfo.csv -NoTypeInformation

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The script uses a list of all sites from SharePoint Online.  Check my earlier post Get all Sites and Subsites from SharePoint Online Sites to get the list of all sites.

$User = “admin@M365x086769.onmicrosoft.com”
#$User = Read-host “Please enter Office365 Admin User name username@domain.onmicrosoft.com. “
$File = “C:\365Creds\AllSites.csv”
$Creds = Get-Credentials

$SiteModernPages = @()
$CSVPath = “C:\temp\AllSites.csv”
$Data = Import-Csv -Path $CSVPath
if($Data)
{
    foreach($Site in $Data)
    {
        try
        {
            Connect-PnPOnline -Url $Site.SiteUrl -Credentials $Creds
            Write-Host “Working on $($Site.SiteUrl).”
            $items = $null
            $items = Get-PnPListItem -List “SitePages” -Fields ID,Title,BannerImageUrl,FileRef -ErrorAction SilentlyContinue
            if($items)
            {
                foreach($ModernPage in $items)
                {
                    Write-Host “.” -NoNewline
                    if($ModernPage[“BannerImageUrl”].Url -ne $null)
                    {
                        Write-Host “.” -NoNewline
                        $FileName = $ModernPage[“FileLeafRef”]
                        $PageUrl = “$($Site.SiteUrl)/SitePages/$FileName”
                        $PageObject = [PSCustomObject]@{Site = $Site.SiteUrl; PageTitle = $ModernPage[“Title”]; PageUrl = $PageUrl  }
                        $SiteModernPages +=$PageObject
                        Write-Host “Modern Page Found.  Added to List” -ForegroundColor Green
                    }
                }
            }
       
            Disconnect-PnPOnline

        }
        catch
        {
            “Error was $_”
            $line = $_.InvocationInfo.ScriptLineNumber
            “Error was in Line $line”
        }

    }
}

#Export All Pages to CSV
$SiteModernPages | Export-Csv -Path C:\temp\SitesModernPages.csv -NoTypeInformation -Force
#Export Page Count by Site to CSV
$SiteModernPages | group Site | Select Name, Count | Export-Csv -Path C:\temp\SitesModernPagesCount.csv -NoTypeInformation –Force

Technet Link

https://gallery.technet.microsoft.com/Get-List-of-Modern-Pages-9ae35436

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

$User = “admin@M365x086769.onmicrosoft.com”
$Creds = Get-Credentials

$AllSites = @()
function Get-SPOSubWebs{
    Param(
        [Microsoft.SharePoint.Client.ClientContext]$Context,
        [Microsoft.SharePoint.Client.Web]$RootWeb
    )
 
 
    $Webs = $RootWeb.Webs
    $Context.Load($Webs)
    $Context.ExecuteQuery()
 
    ForEach ($sWeb in $Webs)
    {
        Write-Output $sWeb
        $SiteObject = [PSCustomObject]@{Site = $sWeb.Url }
        $AllSites +=$SiteObject
        Get-SPOSubWebs -RootWeb $sWeb -Context $Context
    }
}

Connect-SPOService -Url “https://M365x086769-admin.sharepoint.com” -Credential $Creds
$Sites = Get-SPOSite -Limit All | Select Url
foreach($Site in $Sites)
{
   
    Set-SPOUser -Site $Site.Url -LoginName $Creds.UserName -IsSiteCollectionAdmin $true -Verbose
    $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Creds.UserName, $Creds.Password)
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($Site.Url)
    $ctx.Credentials = $credentials
    $Web = $ctx.Web
    $ctx.Load($Web)
    $ctx.ExecuteQuery()
    $SiteObject = [PSCustomObject]@{SiteUrl = $Web.Url }
    $AllSites +=$SiteObject
    Get-SPOSubWebs -RootWeb $Web -Context $ctx   
}

$AllSites

$AllSites | Export-Csv -Path C:\temp\AllSites.csv -NoTypeInformation –Force

Link on Technet

https://gallery.technet.microsoft.com/Get-all-Sites-and-Subsites-a0972bdb

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The script below uses SharePoint Online PowerShell and Client Side Object Model to collect and store site collection information and export it to SQL.  To use this script you must install the SharePoint Online Management Shell and ensure that SharePoint Online Client components are also installed on the machine.  You would need to provide credentials.  The user specified as UserName and possibly the credentials will be added to every site as Site collection Admin.  That is a required step because the user may or may not have detailed information available.

Script is available below.

https://gallery.technet.microsoft.com/Export-SharePoint-Online-7e20c575

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The script below provides the ability to create a Microsoft Team using Microsoft Teams PowerShell Module.  You must install the latest version of the module from PowerShell Gallery.  The script uses an Excel file as input.  For Authentication you have the option to use Azure Active Directory App (Preview) with certificate authentication or simply cloud based authentication using local credentials.  

You must login to Azure Active Directory to create the Azure Active Directory Application inside preview.  Then create a self signed certificate and upload that to Azure AD App.  You can then use the thumbprint to connect to Microsoft Teams Module.  You must also specify the Current user as Owner as App based auth requires at least one Team Owner.

You can install both the Modules using the script below.

Install-Module ImportExcel -Force -Verbose
Install-Module MicrosoftTeams -Force –Verbose

You can test the ExcelImport module using the basic script below.

$ImportPath = “C:\temp\CreateTeam-Basic.xlsx”
$Teams = Import-Excel -Path $ImportPath -WorksheetName “Teams”
$Owers = Import-Excel -Path $ImportPath -WorksheetName “Owners”
$Members = Import-Excel -Path $ImportPath -WorksheetName “Members”
$Channels = Import-Excel -Path $ImportPath -WorksheetName “Channels”
$Tabs = Import-Excel -Path $ImportPath -WorksheetName “Tabs”

https://gallery.technet.microsoft.com/Provision-Multiple-Teams-4ef05794

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The script below provides the ability to create a Microsoft Team using Microsoft Teams PowerShell Module.  You must install the latest version of the module from PowerShell Gallery.  The script uses a CSV File as input.  For Authentication you have the option to use Azure Active Directory App (Preview) with certificate authentication or simply cloud based authentication using local credentials.  

You must login to Azure Active Directory to create the Azure Active Directory Application inside preview.  Then create a self signed certificate and upload that to Azure AD App.  You can then use the thumbprint to connect to Microsoft Teams Module.  You must also specify the Current user as Owner as App based authentication requires at least one Team Owner.

The script can be downloaded from

https://gallery.technet.microsoft.com/Create-using-CSV-d8294bc9

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

There is easy way to collection reports when you have large number of Site collections on SharePoint Online.  The new Admin center will help but sometime customers have external system where they want to review and control site collection information.  The script below will iterate site collections, sites, lists and permissions and store them to a SQL Server Database.  It uses SharePoint Online Client components so you must downloaded and install the latest version of client Components from PowerShell Gallary using the method below

https://www.nuget.org/packages/Microsoft.SharePointOnline.CSOM

Download nuget command line (I saved it to c:\nuget)

    Command Prompt: 

    1. cd C:\nuget
    2. nuget.exe install Microsoft.SharePointOnline.CSOM

    Powershell (elevated)

    1. cd C:\nuget
    2. Install-Package -Name ‘Microsoft.SharePointOnline.CSOM’ -Source .\Microsoft.SharePointOnline.CSOM.16.1.8412.1200
    3. Import-Module ‘C:\nuget\Microsoft.SharePointOnline.CSOM.16.1.8412.1200\lib\net45\Microsoft.SharePoint.Client.dll

    https://gallery.technet.microsoft.com/Export-SharePoint-Online-27920e67

    • Show original
    • .
    • Share
    • .
    • Favorite
    • .
    • Email
    • .
    • Add Tags 

    Few days ago, I got a question if it is possible to add a banner message to a SharePoint Online or On-premises site without customizing the master page.  They were looking for a way to enable and disable this using the Feature framework so I had to write a feature event receiver to upload the customized master page.  The customer was happy but I was not. 

    I did some research and ended up finding something that makes it lot easier to do such things.  You can do it using Code using the event receiver but I wanted to make sure you do not deploy any server side solution.  The script below using CSOM to get things done.  All you need is the jquery file and the custom js file uploaded to a CDN or SharePoint assets library.  If you are running off the server then you need to deploy SharePoint client side assemblies from Microsoft download center.

    Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll”
    Add-Type -Path “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll”

    function Add-SPBanner($SiteUrl, $Credentials)
    {
      $context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
      $context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Credentials.UserName,$Credentials.Password)
      $site = $context.Web
      $context.Load($site)
      $context.ExecuteQuery()

      $UserCustomActions = $site.UserCustomActions
      $context.Load($UserCustomActions)
      $context.ExecuteQuery()

      $newAction = $UserCustomActions.Add()
      $newAction.Location = “ScriptLink”
      $newAction.scriptSrc = “~SiteCollection/Style Library/jquery.min.js”
      $newAction.Sequence = 30000
      $newAction.Title= “BannerJquery”
      $newAction.Update()
      $context.ExecuteQuery()

      #add custom js injection action
      $customJSAction = $UserCustomActions.Add()
      $customJSAction.Location = “ScriptLink”
      #reference to JS file
      $customJSAction.ScriptSrc = “~SiteCollection/Style Library/test.js”
      #load it last
      $customJSAction.Title= “BannerJS”
      $customJSAction.Sequence = 30001
      #make the changes
      $customJSAction.Update()
      $context.ExecuteQuery()
     
      Write-Host “Banner has been Added…” -ForegroundColor Green
    }

    function Remove-SPBanner($SiteUrl, $Credentials)

      $context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
      $context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Credentials.UserName,$Credentials.Password)
      $site = $context.Web
      $context.Load($site)
      $context.ExecuteQuery()

      $UserCustomActions = $site.UserCustomActions
      $context.Load($UserCustomActions)
      $context.ExecuteQuery()

      $UserCustomActions | ? Title -Like “Banner*” | Select Title, Sequence
      if($UserCustomActions.Count -gt 0)
      {
        $CA = $UserCustomActions | ? Title -eq “BannerJquery”
        $CA.DeleteObject()

        $CA = $UserCustomActions | ? Title -eq “BannerJS”
        $CA.DeleteObject()
        $context.ExecuteQuery()
        Write-Host “Banner has been Removed…” -ForegroundColor Green
      } 
    }

    $Creds = Get-Credential
    $SiteUrl = “https://sharepointmvp.sharepoint.com”

    Add-SPBanner -SiteUrl $SiteUrl -Credentials $Creds
    Remove-SPBanner -SiteUrl $SiteUrl -Credentials $Creds

    Read for later

    Articles marked as Favorite are saved for later viewing.
    close
    • Show original
    • .
    • Share
    • .
    • Favorite
    • .
    • Email
    • .
    • Add Tags 

    Separate tags by commas
    To access this feature, please upgrade your account.
    Start your free month
    Free Preview