From 9dc85c8057734aa46bc77251ee0b79153003ed81 Mon Sep 17 00:00:00 2001 From: Kawa Date: Mon, 16 Mar 2026 13:45:00 +0100 Subject: [PATCH] Added sample CSV files for user/sites importation. Fixed a few bugs. --- Sharepoint_ToolBox.ps1 | 141 +++++++++++++++++++++------------ examples/bulk_add_members.csv | 8 ++ examples/bulk_create_sites.csv | 6 ++ 3 files changed, 104 insertions(+), 51 deletions(-) create mode 100644 examples/bulk_add_members.csv create mode 100644 examples/bulk_create_sites.csv diff --git a/Sharepoint_ToolBox.ps1 b/Sharepoint_ToolBox.ps1 index 659ad7c..f641f8b 100644 --- a/Sharepoint_ToolBox.ps1 +++ b/Sharepoint_ToolBox.ps1 @@ -5229,30 +5229,41 @@ $btnBulkCsv.Add_Click({ $ofd = New-Object System.Windows.Forms.OpenFileDialog $ofd.Filter = "CSV (*.csv)|*.csv|All (*.*)|*.*" if ($ofd.ShowDialog($form) -ne "OK") { return } - $rows = Import-Csv $ofd.FileName + # Try semicolon first (handles commas inside fields), fall back to comma + $content = Get-Content $ofd.FileName -Raw + if ($content -match ';') { + $rows = Import-Csv $ofd.FileName -Delimiter ';' + } else { + $rows = Import-Csv $ofd.FileName + } $count = 0 foreach ($r in $rows) { - # Accepted column names (case-insensitive via PSObject) - $name = if ($r.Name) { $r.Name } elseif ($r.name) { $r.name } - elseif ($r.Title) { $r.Title } elseif ($r.title) { $r.title } else { "" } - $alias = if ($r.Alias) { $r.Alias } elseif ($r.alias) { $r.alias } - elseif ($r.URL) { $r.URL } elseif ($r.url) { $r.url } else { "" } - $type = if ($r.Type) { $r.Type } elseif ($r.type) { $r.type } else { "Team" } - $tpl = if ($r.Template) { $r.Template } elseif ($r.template) { $r.template } else { "" } - $own = if ($r.Owners) { $r.Owners } elseif ($r.owners) { $r.owners } - elseif ($r.Owner) { $r.Owner } elseif ($r.owner) { $r.owner } else { "" } - $mem = if ($r.Members) { $r.Members } elseif ($r.members) { $r.members } else { "" } + # Read columns via PSObject properties (case-insensitive) + $props = @{} + foreach ($p in $r.PSObject.Properties) { $props[$p.Name.ToLower()] = "$($p.Value)".Trim() } - if (-not $name -or -not $alias) { continue } + $name = if ($props['name']) { $props['name'] } elseif ($props['title']) { $props['title'] } else { "" } + $alias = if ($props['alias']) { $props['alias'] } elseif ($props['url']) { $props['url'] } else { "" } + $type = if ($props['type']) { $props['type'] } else { "Team" } + $tpl = if ($props['template']) { $props['template'] } else { "" } + $own = if ($props['owners']) { $props['owners'] } elseif ($props['owner']) { $props['owner'] } else { "" } + $mem = if ($props['members']) { $props['members'] } else { "" } + + # Name is required; skip empty rows + if (-not $name) { continue } + # Auto-generate alias from name if not provided + if (-not $alias) { + $alias = $name.ToLower() -replace '[^a-z0-9\-]', '-' -replace '-+', '-' -replace '^-|-$', '' + } # Normalize type if ($type -match '^[Cc]omm') { $type = "Communication" } else { $type = "Team" } Add-BulkListItem @{ - Name = $name.Trim() - Alias = $alias.Trim() + Name = $name + Alias = $alias Type = $type - Template = $tpl.Trim() - Owners = $own.Trim() - Members = $mem.Trim() + Template = $tpl + Owners = $own + Members = $mem } $count++ } @@ -5340,32 +5351,83 @@ $btnBulkCreate.Add_Click({ $name = $entry.Name $alias = $entry.Alias $isTeam = $entry.Type -ne "Communication" - $owners = @($entry.Owners -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ }) - $members = @($entry.Members -split '[,;]' | ForEach-Object { $_.Trim() } | Where-Object { $_ }) + $ownerRaw = "$($entry.Owners)" + $memberRaw = "$($entry.Members)" + $owners = [string[]]@($ownerRaw -split '[,;\s]+' | Where-Object { $_ -match '\S' }) + $members = [string[]]@($memberRaw -split '[,;\s]+' | Where-Object { $_ -match '\S' }) $tplName = $entry.Template BgLog "[$idx/$total] Creating '$name' (alias: $alias, type: $($entry.Type))..." "White" + BgLog " DEBUG owners raw='$ownerRaw' parsed=[$($owners -join '|')] count=$($owners.Count)" "Gray" + BgLog " DEBUG members raw='$memberRaw' parsed=[$($members -join '|')] count=$($members.Count)" "Gray" + + # TeamSite requires at least one owner + if ($isTeam -and $owners.Count -eq 0) { + BgLog " ERREUR : TeamSite requires at least one owner — skipping '$name'" "Red" + $Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "Error: no owner" }) + $Sync.ErrCount++ + continue + } # Update status $Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "Creating..." }) try { - # Create the site + # Create the site WITHOUT owners/members (PnP bug: odata.bind empty array) + # Current user becomes default owner; we add owners/members after creation Connect-PnPOnline -Url $adminUrl -Interactive -ClientId $Params.ClientId - $newUrl = if ($isTeam) { - if ($owners.Count -gt 0) { - New-PnPSite -Type TeamSite -Title $name -Alias $alias -Owners $owners -Wait - } else { - New-PnPSite -Type TeamSite -Title $name -Alias $alias -Wait - } + if ($isTeam) { + BgLog " Creating TeamSite '$alias' (owners/members added after)..." "DarkGray" + $newUrl = New-PnPSite -Type TeamSite -Title $name -Alias $alias -Wait } else { - New-PnPSite -Type CommunicationSite -Title $name -Url "$base/sites/$alias" -Wait + BgLog " Creating CommunicationSite '$alias'..." "DarkGray" + $newUrl = New-PnPSite -Type CommunicationSite -Title $name -Url "$base/sites/$alias" -Wait } BgLog " Site cree : $newUrl" "LightGreen" - # Connect to the new site for template + members + # Connect to the new site for owners/members/template Connect-PnPOnline -Url $newUrl -Interactive -ClientId $Params.ClientId + # Assign owners & members post-creation + if ($isTeam) { + $groupId = $null + try { $groupId = (Get-PnPSite -Includes GroupId).GroupId.Guid } catch {} + if ($groupId) { + foreach ($o in $owners) { + try { + Add-PnPMicrosoft365GroupOwner -Identity $groupId -Users $o -ErrorAction Stop + BgLog " Owner added: $o" "Cyan" + } catch { BgLog " Warn owner '$o': $($_.Exception.Message)" "DarkYellow" } + } + foreach ($m in $members) { + try { + Add-PnPMicrosoft365GroupMember -Identity $groupId -Users $m -ErrorAction Stop + BgLog " Member added: $m" "Cyan" + } catch { BgLog " Warn member '$m': $($_.Exception.Message)" "DarkYellow" } + } + } else { + BgLog " Could not get M365 GroupId — owners/members not assigned" "DarkYellow" + } + } else { + # CommunicationSite — classic SharePoint groups + if ($owners.Count -gt 0) { + $ownerGrp = Get-PnPGroup | Where-Object { $_.Title -like "*Propri*" -or $_.Title -like "*Owner*" } | Select-Object -First 1 + if ($ownerGrp) { + foreach ($o in $owners) { + try { Add-PnPGroupMember -LoginName $o -Group $ownerGrp.Title -ErrorAction SilentlyContinue } catch {} + } + } + } + if ($members.Count -gt 0) { + $memberGrp = Get-PnPGroup | Where-Object { $_.Title -like "*Membre*" -or $_.Title -like "*Member*" } | Select-Object -First 1 + if ($memberGrp) { + foreach ($m in $members) { + try { Add-PnPGroupMember -LoginName $m -Group $memberGrp.Title -ErrorAction SilentlyContinue } catch {} + } + } + } + } + # Apply template if specified if ($tplName -and $Params.Templates.ContainsKey($tplName)) { $tpl = $Params.Templates[$tplName] @@ -5406,30 +5468,7 @@ $btnBulkCreate.Add_Click({ } } - # Add members - if ($members.Count -gt 0) { - $memberGroup = Get-PnPGroup | Where-Object { $_.Title -like "*Membres*" -or $_.Title -like "*Members*" } | Select-Object -First 1 - if ($memberGroup) { - foreach ($m in $members) { - try { - Add-PnPGroupMember -LoginName $m -Group $memberGroup.Title -ErrorAction SilentlyContinue - } catch {} - } - BgLog " $($members.Count) member(s) added." "Cyan" - } - } - # Add owners for Communication sites (TeamSite owners set at creation) - if (-not $isTeam -and $owners.Count -gt 0) { - $ownerGroup = Get-PnPGroup | Where-Object { $_.Title -like "*Propri*" -or $_.Title -like "*Owner*" } | Select-Object -First 1 - if ($ownerGroup) { - foreach ($o in $owners) { - try { - Add-PnPGroupMember -LoginName $o -Group $ownerGroup.Title -ErrorAction SilentlyContinue - } catch {} - } - } - } $Sync.Queue.Enqueue(@{ Text = "##STATUS##"; Index = ($idx - 1); Value = "OK" }) $Sync.CreatedSites.Add([PSCustomObject]@{ diff --git a/examples/bulk_add_members.csv b/examples/bulk_add_members.csv new file mode 100644 index 0000000..fa49e16 --- /dev/null +++ b/examples/bulk_add_members.csv @@ -0,0 +1,8 @@ +Email +user1@contoso.com +user2@contoso.com +user3@contoso.com +manager1@contoso.com +designer@contoso.com +analyste@contoso.com +stagiaire@contoso.com diff --git a/examples/bulk_create_sites.csv b/examples/bulk_create_sites.csv new file mode 100644 index 0000000..39e19d0 --- /dev/null +++ b/examples/bulk_create_sites.csv @@ -0,0 +1,6 @@ +Name;Alias;Type;Template;Owners;Members +Projet Alpha;projet-alpha;Team;;admin@contoso.com;user1@contoso.com, user2@contoso.com +Projet Beta;;Team;;admin@contoso.com;user3@contoso.com, user4@contoso.com +Communication RH;;Communication;;rh-admin@contoso.com;manager1@contoso.com, manager2@contoso.com +Equipe Marketing;equipe-marketing;Team;;marketing-lead@contoso.com;designer@contoso.com, redacteur@contoso.com +Portail Intranet;;Communication;;it-admin@contoso.com;