Skip to content

Commit

Permalink
Added new features for Adding Teams and Channels
Browse files Browse the repository at this point in the history
  • Loading branch information
DevClate committed Dec 16, 2023
1 parent 1c3c1eb commit 5c86060
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 119 deletions.
5 changes: 3 additions & 2 deletions 365AutomatedLab.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = '365AutomatedLab.psm1'

# Version number of this module.
ModuleVersion = '2.0.0'
ModuleVersion = '2.2.0'

# Supported PSEditions
CompatiblePSEditions = 'Core'
Expand Down Expand Up @@ -96,7 +96,8 @@ FunctionsToExport = @(
'Set-CT365SPDistinctNumber',
'Remove-CT365AllDeletedM365Groups',
'Export-CT365ProdGroupToExcel',
'Export-CT365ProdTeamsToExcel'
'Export-CT365ProdTeamsToExcel',
'Verify-CT365TeamsCreation'

)

Expand Down
16 changes: 16 additions & 0 deletions Functions/Private/Verify-CT365TeamsCreation.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function Verify-CT365TeamsCreation {
param(
[string]$teamName,
[int]$retryCount = 5,
[int]$delayInSeconds = 10
)

for ($i = 0; $i -lt $retryCount; $i++) {
$existingTeam = Get-PnPTeamsTeam | Where-Object { $_.DisplayName -eq $teamName }
if ($existingTeam) {
return $true
}
Start-Sleep -Seconds $delayInSeconds
}
return $false
}
222 changes: 105 additions & 117 deletions Functions/Public/New-CT365Teams.ps1
Original file line number Diff line number Diff line change
@@ -1,51 +1,36 @@
<#
.SYNOPSIS
Creates new Microsoft 365 Teams and channels based on data from an Excel file.
Creates new Microsoft Teams and associated channels based on data from an Excel file.
.DESCRIPTION
The New-CT365Teams function connects to SharePoint Online and creates new Microsoft 365 Teams and channels using the PnP PowerShell Module.
The teams and channels are defined in an Excel file provided by the user.
The New-CT365Teams function connects to Microsoft Teams via PnP PowerShell, reads team and channel information from an Excel file, and creates new Teams and channels as specified. It supports retry logic for team and channel creation and allows specifying a default owner. The function requires the PnP.PowerShell, ImportExcel, and PSFramework modules.
.PARAMETER FilePath
Specifies the path to the Excel file that contains the teams and channels information.
The Excel file should contain a worksheet named "Teams".
This parameter is mandatory and can be passed through the pipeline.
Specifies the path to the Excel file containing the Teams and channel data. The file must be in .xlsx format.
.PARAMETER AdminUrl
Specifies the SharePoint Online admin URL.
If not provided, the function will attempt to connect to SharePoint Online interactively.
Specifies the SharePoint admin URL for the tenant. The URL must match the format 'tenant.sharepoint.com'.
.PARAMETER ChannelColumns
Specifies the columns in the Excel file that contain the channel names.
By default, it looks for columns named "Channel1Name" and "Channel2Name".
You can specify other column names if your Excel file is structured differently.
.PARAMETER DefaultOwnerUPN
Specifies the default owner's User Principal Name (UPN) for the Teams and channels.
.EXAMPLE
New-CT365Teams -FilePath "C:\path\to\teams.xlsx" -AdminUrl "https://contoso-admin.sharepoint.com"
PS> New-CT365Teams -FilePath "C:\TeamsData.xlsx" -AdminUrl "contoso.sharepoint.com" -DefaultOwnerUPN "admin@contoso.com"
This example connects to the specified SharePoint Online admin URL, reads the teams and channels from the provided Excel file, and then creates the teams and channels in Microsoft 365.
.EXAMPLE
$filePath = "C:\path\to\teams.xlsx"
$filePath | New-CT365Teams
This example uses pipeline input to provide the file path to the New-365Teams function.
This example creates Teams and channels based on the data in 'C:\TeamsData.xlsx', using '[email protected]' as the default owner if none is specified in the Excel file.
.NOTES
Please submit any feedback and/or recommendations
Prerequisite : PnP.PowerShell, ImportExcel, PSFramework, Microsoft.Identity.Client modules should be installed.
- Requires the PnP.PowerShell, ImportExcel, and PSFramework modules.
- The Excel file should have a worksheet named 'teams' with appropriate columns for team and channel data.
- The function includes error handling and logging using PSFramework.
#>
function New-CT365Teams {
[CmdletBinding()]
param (
# Validate the Excel file path.
param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateScript({
switch ($psitem){
{-not([System.IO.File]::Exists($psitem))}{
throw "Invalid file path: '$PSitem'."
}
{-not(([System.IO.Path]::GetExtension($psitem)) -match "(.xlsx)")}{
"Invalid file format: '$PSitem'. Use .xlsx"
}
Expand All @@ -56,116 +41,119 @@ function New-CT365Teams {
})]
[string]$FilePath,

[Parameter(Mandatory=$false)]
[Parameter(Mandatory)]
[ValidateScript({
if ($_ -match '^[a-zA-Z0-9]+\.sharepoint\.[a-zA-Z0-9]+$') {
$true
} else {
throw "The URL $_ does not match the required format."
}
})]
if ($_ -match '^[a-zA-Z0-9]+\.sharepoint\.[a-zA-Z0-9]+$') {
$true
}
else {
throw "The URL $_ does not match the required format."
}
})]
[string]$AdminUrl,
[Parameter(Mandatory=$false)]
[string[]]$ChannelColumns = @("Channel1Name", "Channel2Name")

[Parameter(Mandatory)]
[string]$DefaultOwnerUPN
)

begin {
# Import required modules.
$ModulesToImport = "ImportExcel","PnP.PowerShell","PSFramework","Microsoft.Identity.Client"
Import-Module $ModulesToImport

# Check and import required modules
$requiredModules = @('PnP.PowerShell', 'ImportExcel', 'PSFramework')
foreach ($module in $requiredModules) {
try {
# Connect to SharePoint Online.
$connectPnPOnlineSplat = @{
Url = $AdminUrl
Interactive = $true
ErrorAction = 'Stop'
if (-not (Get-Module -ListAvailable -Name $module)) {
throw "Module $module is not installed."
}
Connect-PnPOnline @connectPnPOnlineSplat
}
catch {
# Log an error and exit if the connection fails.
Write-PSFMessage -Message "Failed to connect to SharePoint Online" -Level Error
return
}

try {
# Import site data from Excel.
$SiteData = Import-Excel -Path $FilePath -WorksheetName "Teams"
}
catch {
# Log an error and exit if importing site data fails.
Write-PSFMessage -Message "Failed to import SharePoint Site data from Excel file." -Level Error
Import-Module $module
} catch {
Write-PSFMessage -Level Warning -Message "[$(Get-Date -Format 'u')] $_.Exception.Message"
return
}
}

process {
foreach ($team in $SiteData) {
Write-PSFMessage -Message "Processing team: $($team.TeamName)" -Level Host

$existingTeam = Get-PnPTeamsTeam | Where-Object { $_.DisplayName -eq $team.TeamName }

# If the team does not exist, create it.
if (-not $existingTeam) {
try {
Connect-PnPOnline -Url $AdminUrl -Interactive
} catch {
Write-PSFMessage -Level Error -Message "[$(Get-Date -Format 'u')] Failed to connect to PnP Online: $($_.Exception.Message)"
return
}

try {
$teamsData = Import-Excel -Path $FilePath -WorksheetName "teams"
$existingTeams = Get-PnPTeamsTeam
} catch {
Write-PSFMessage -Level Error -Message "[$(Get-Date -Format 'u')] Failed to import data from Excel or retrieve existing teams: $($_.Exception.Message)"
return
}

foreach ($teamRow in $teamsData) {
try {
$teamOwnerUPN = if ($teamRow.TeamOwnerUPN) { $teamRow.TeamOwnerUPN } else { $DefaultOwnerUPN }
$existingTeam = $existingTeams | Where-Object { $_.DisplayName -eq $teamRow.TeamName }

if ($existingTeam) {
Write-PSFMessage -Level Host -Message "[$(Get-Date -Format 'u')] Team $($teamRow.TeamName) already exists. Skipping creation."
continue
}

$retryCount = 0
$teamCreationSuccess = $false
do {
try {
$newPnPTeamsTeamSplat = @{
DisplayName = $team.TeamName
Description = $team.TeamDescription
Visibility = 'Private'
ErrorAction = 'Stop'
$teamId = New-PnPTeamsTeam -DisplayName $teamRow.TeamName -Description $teamRow.TeamDescription -Visibility $teamRow.TeamType -Owners $teamOwnerUPN
if (Verify-CT365TeamsCreation -teamName $teamRow.TeamName) {
Write-PSFMessage -Level Host -Message "[$(Get-Date -Format 'u')] Verified creation of Team: $($teamRow.TeamName)"
$teamCreationSuccess = $true
break
} else {
Write-PSFMessage -Level Warning -Message "[$(Get-Date -Format 'u')] Team $($teamRow.TeamName) creation reported but not verified. Retrying..."
}

New-PnPTeamsTeam @newPnPTeamsTeamSplat
Write-PSFMessage -Message "Successfully created Team: $($team.TeamName)" -Level Host
} catch {
Write-PSFMessage -Level Warning -Message "[$(Get-Date -Format 'u')] Attempt $retryCount to create team $($teamRow.TeamName) failed: $($_.Exception.Message)"
}
catch {
Write-PSFMessage -Message "Failed to create team $($team.TeamName): $_" -Level Error
continue # Skip to the next team in case of error.
}
}

# If the team already exists or was just created, log a message.
Write-PSFMessage -Message "Team $($team.TeamName) exists or was just created. Proceeding to create channels..." -Level Host

# Retry mechanism to fetch team details up to 3 times.
$retryCount = 0
$maxRetries = 3
$teamResult = $existingTeam ?? $null

while ($retryCount -lt $maxRetries -and (-not $teamResult)) {
Start-Sleep -Seconds 15 # Wait before fetching the team details.
$teamResult = Get-PnPTeamsTeam | Where-Object { $_.DisplayName -eq $team.TeamName }
$retryCount++
}

# If the team wasn't found after all retry attempts, log a warning and skip to the next team.
if (-not $teamResult) {
Write-PSFMessage -Message "Team $($team.TeamName) was not found after $maxRetries attempts." -Level Warning
Start-Sleep -Seconds 5
} while ($retryCount -lt 5)

if (-not $teamCreationSuccess) {
Write-PSFMessage -Level Error -Message "[$(Get-Date -Format 'u')] Failed to create and verify Team: $($teamRow.TeamName) after multiple retries."
continue
}

# Create channels based on the provided column names.
foreach ($column in $ChannelColumns) {
$channelName = $team.$column
if (-not $channelName) { continue } # Skip to the next channel if the name is not found.

Write-PSFMessage -Message "Creating channel: $channelName for team: $($team.TeamName)" -Level Host
try {
Add-PnPTeamsChannel -Team $teamresult.GroupId -DisplayName $channelName -Description "Channel named $channelName for $($team.TeamName)"
Write-PSFMessage -Message "Successfully created channel: $channelName for team: $($team.TeamName)" -Level Host
}
catch {
Write-PSFMessage -Message "Failed to create channel $channelName for team $($team.TeamName): $_" -Level Error

for ($i = 1; $i -le 4; $i++) {
$channelName = $teamRow."Channel${i}Name"
$channelType = $teamRow."Channel${i}Type"
$channelDescription = $teamRow."Channel${i}Description"
$channelOwnerUPN = if ($teamRow."Channel${i}OwnerUPN") { $teamRow."Channel${i}OwnerUPN" } else { $DefaultOwnerUPN }

if ($channelName -and $channelType) {
$retryCount = 1
$channelCreationSuccess = $false
do {
try {
Add-PnPTeamsChannel -Team $teamId -DisplayName $channelName -Description $channelDescription -ChannelType $channelType -OwnerUPN $channelOwnerUPN
Write-PSFMessage -Level Host -Message "[$(Get-Date -Format 'u')] Created Channel: $channelName in Team: $($teamRow.TeamName) with Type: $channelType and Description: $channelDescription"
$channelCreationSuccess = $true
break
} catch {
Write-PSFMessage -Level Warning -Message "[$(Get-Date -Format 'u')] Attempt $retryCount to create channel $channelName in Team: $($teamRow.TeamName) failed: $($_.Exception.Message)"
$retryCount++
Start-Sleep -Seconds 10
}
} while ($retryCount -lt 5)

if (-not $channelCreationSuccess) {
Write-PSFMessage -Level Error -Message "[$(Get-Date -Format 'u')] Failed to create Channel: $channelName in Team: $($teamRow.TeamName) after multiple retries."
}
}
}
} catch {
Write-PSFMessage -Level Error -Message "[$(Get-Date -Format 'u')] Error processing team $($teamRow.TeamName): $($_.Exception.Message)"
}
}

end {
# Disconnect from PnP
try {
Disconnect-PnPOnline
Write-PSFMessage "Teams and Channels creation completed."
} catch {
Write-PSFMessage -Level Error -Message "[$(Get-Date -Format 'u')] Error disconnecting PnP Online: $($_.Exception.Message)"
}
}
}
Binary file modified LabSources/365DataEnvironment.xlsx
Binary file not shown.

0 comments on commit 5c86060

Please sign in to comment.