diff --git a/VB365-SpTeamsCatchAllLeftJob/VB365-SpTeamsCatchAllLeftJob.ps1 b/VB365-SpTeamsCatchAllLeftJob/VB365-SpTeamsCatchAllLeftJob.ps1 new file mode 100644 index 0000000..b72c9fd --- /dev/null +++ b/VB365-SpTeamsCatchAllLeftJob/VB365-SpTeamsCatchAllLeftJob.ps1 @@ -0,0 +1,133 @@ +<# + .SYNOPSIS + Creating a VB365 Backup Job that includes the whole organization for SharePoint and Teams but exclude everything already in a different job + + .DESCRIPTION + This script will create or update a job with the goal to catch all SharePoint Sites and Teams which are not already in any other job. + It is using job exclusions to be able to use the entire organisation for the job selection and protect every new SharePoint or Teams object. + It can be run once or multiple times to update the exclusions if needed. + + .NOTES + Version: 0.1 + Author: David Bewernick (david.bewernick@veeam.com) + Creation Date: 21.02.2024 + Purpose/Change: Initial script development + + .CHANGELOG + v0.1 21.02.2024 Script created + + #> + +$timestampFileName = get-date -Format "yyyy-mm-dd_HH-mm-ss" +$jobSPExcludeItems = New-Object System.Collections.ArrayList +$jobTeamsExcludeItems = New-Object System.Collections.ArrayList +$jobAllExcludeItems = New-Object System.Collections.ArrayList +$unprotectesSites = New-Object System.Collections.ArrayList +$unprotectesTeams = New-Object System.Collections.ArrayList + +# Set some variables to your need +[string]$Script:LogFile = "vb365_SpTeams_ChatchLeftJob.log" #logfile name, can be a full path +[string]$orgName = "dabew.onmicrosoft.com" #for which organization should this be processed? +[string]$catchallJobName = "SpTeamsCatchAll" #the catch all job name +[string]$repoName = "repo04-minio" #the backup repository name +[switch]$enableSP = $true #should SharePoint Sites be processed? +[switch]$enableTeams = $true #should SharePoint Sites be processed? +[switch]$excludePersonalSites = $true #should Personal SharePoint Sites be excluded? + +function Write-Log($Info, $Status){ + $timestamp = get-date -Format "yyyy-mm-dd HH:mm:ss" + switch($Status){ + Info {Write-Host "$timestamp $Info" -ForegroundColor Green ; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + Status {Write-Host "$timestamp $Info" -ForegroundColor Yellow ; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + Warning {Write-Host "$timestamp $Info" -ForegroundColor Yellow ; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + Error {Write-Host "$timestamp $Info" -ForegroundColor Red -BackgroundColor White; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + default {Write-Host "$timestamp $Info" -ForegroundColor white "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + } +} +Write-Log -Info "-----------------------" -Status Info +Write-Log -Info "Script started" -Status Info +Write-Log -Info "-----------------------" -Status Info + +$org = Get-VBOOrganization -Name $orgName +$repo = Get-VBORepository -Name $repoName + +#make sure the arraylists are empty +$jobSPExcludeItems.Clear() +$jobTeamsExcludeItems.clear() +$unprotectesSites.Clear() +$unprotectesTeams.Clear() + +### get all protected SP and Teams items in VB365 Jobs + +# get all jobs for the organization +$jobs = Get-VBOJob -Organization $org +foreach ($job in $jobs){ + + #get all SharePoint items in the job + if ($enableSP){ + $jobItems = Get-VBOBackupItem -Job $job -Sites + if ($jobItems) { + foreach ($item in $jobItems){ + $jobSPExcludeItems.Add($item) + } + } + else { + Write-Log -Info "$job has no SharePoint items" -Status Info + } + + } + + #get all Teams items in the job + if ($enableTeams){ + $jobItems = Get-VBOBackupItem -Job $job -Teams + if ($jobItems) { + foreach ($item in $jobItems){ + $jobTeamsExcludeItems.Add($item) + } + } + else { + Write-Log -Info "$job has no Teams items" -Status Info + } + } +} + +#create a list with all the Site names to exclude and write it to the log +$jobSPList = ($jobSPExcludeItems.Site.Name -join ", ") +Write-Log -Info "All SharePoint Sites found in Jobs: $jobSPList" -Status Info + +#create a list with all the Teams names to exclude and write it to the log +$jobTeamsList = ($jobTeamsExcludeItems.Team.DisplayName -join ", ") +Write-Log -Info "All Teams found in Jobs: $jobTeamsList" -Status Info + +### get all unprotected SP Sites +#$unprotectesSites = Get-VBOOrganizationSite -Organization $org -NotInJob +#$unprotectesTeams = Get-VBOOrganizationTeam -Organization $org -NotInJob + +#merge all SharePoint Sites, Teams and the personal sites (if enabled) to exclude into one ArrayList +if ($excludePersonalSites){ + $excludedItemPersonalSites = New-VBOBackupItem -PersonalSites + $jobAllExcludeItems = $jobSPExcludeItems + $jobTeamsExcludeItems + $excludedItemPersonalSites +} +else{ + $jobAllExcludeItems = $jobSPExcludeItems + $jobTeamsExcludeItems +} + +#check if the catch all job exists +$catchallJob = get-vbojob -Organization $org -Name $catchallJobName + +Write-Log -Info "-----------------------" -Status Info +#if the job exists, add the excluded items +if ($catchallJob){ + Write-Log -Info "$catchallJob found" -Status Info + Set-VBOJob -Job $catchallJob -ExcludedItems $jobAllExcludeItems + Write-Log -Info "$catchallJob has been updated" -Status Info +} + +#if the job does not exist, create it and add the excluded items +else{ + Write-Log -Info "$catchallJob not found" -Status Info + $selectedItems = New-VBOBackupItem -Organization $org -Sites -Teams + Add-VBOJob -Organization $org -Name $catchallJobName -Repository $repo -SelectedItems $selectedItems -ExcludedItems $jobAllExcludeItems + Write-Log -Info "$catchallJob has been created" -Status Info +} +Write-Log -Info "-----------------------" -Status Info \ No newline at end of file diff --git a/VB365-SpTeamsCatchAllLeftJob/readme.md b/VB365-SpTeamsCatchAllLeftJob/readme.md new file mode 100644 index 0000000..34ec2df --- /dev/null +++ b/VB365-SpTeamsCatchAllLeftJob/readme.md @@ -0,0 +1,23 @@ +# Create a catch-all-the-rest job for SharePoint and Teams + +## Version: +1.0 + +## Author + +* David Bewernick (@d-works42) + +## Function + +This script reads all the SharePoint sites and Teams present in any backup job for the defined organization. +It then creates or updates a SharePoint and Teams backup job for the Organization and adds all the previously found objects as exclusions. +The idea is to run this script only once and have a dynamic job that captures all new SharePoint Sites and Teams. If changes to other jobs have been made (adding or revoming objects to backup), this here can be run again to re-populate the exclusion list with an up-to-date staus. + +## Requirements + +* Veeam Backup for Microsoft 365 v7 + * *Other versions are untested* + +## Usage + +Adjust the variables within the script before running it. \ No newline at end of file diff --git a/VB365-UserJobsDynamicGroups/README.md b/VB365-UserJobsDynamicGroups/README.md new file mode 100644 index 0000000..7d1321c --- /dev/null +++ b/VB365-UserJobsDynamicGroups/README.md @@ -0,0 +1,27 @@ +# Create Dynamic Groups in Azure AD and the related backup jobs in VB365 + +## Version: +1.1 + +## Author + +* David Bewernick (@d-works42) + +## Function + +This script creates Azure AD dynamic groups to split up the users of a whole tenant based on the first two characters of their ObjectID. +The number of groups beeing created is depending on the array of first and second character. +Per default, this script will create 64 groups, since the first charakter will be from "0" to "f" and the second character will be grouped in 4 expression ranges. +VB365 backup jobs will be created for every dynamic group and separatly for Exchange Online and OneDrive. + +## Requirements + +* Veeam Backup for Microsoft 365 v7 + * *Other versions are untested* +* AzureADPreview Module and a Microsoft subscription which includes at least Azure AD Premium P1 features. + You need to use the Microsoft AzureADPreview module since the parameter "MembershipRule" is only available in the beta of GraphAPI. + -> Install-Module AzureADPreview -Scope CurrentUser -AllowClobber + +## Usage + +Adjust the variables within the script before running it. diff --git a/VB365-UserJobsDynamicGroups/VB365-UserJobsDynamicGroups.ps1 b/VB365-UserJobsDynamicGroups/VB365-UserJobsDynamicGroups.ps1 new file mode 100644 index 0000000..b92a560 --- /dev/null +++ b/VB365-UserJobsDynamicGroups/VB365-UserJobsDynamicGroups.ps1 @@ -0,0 +1,226 @@ +<# + .SYNOPSIb + Creating Azure AD dynamic groups with user membership based on regex and creating VB365 backup jobs for every group. + + .DESCRIPTION + !!! You need to use the Microsoft AzureADPreview module since the parameter "MembershipRule" is only available in the beta of GraphAPI. + -> Install-Module AzureADPreview -Scope CurrentUser -AllowClobber !!! + + Requires: Microsoft subscription which includes at least Azure AD Premium P1 features. + + This script creates Azure AD dynamic groups to split up the users of a whole tenant based on the first two characters of their ObjectID. + The number of groups beeing created is depending on the array of first and second character. + Per default, this script will create 64 groups, since the first charakter will be from "0" to "f" and the second character will be grouped in 4 expression ranges. + VB365 backup jobs will be created for every dynamic group and separatly for Exchange Online and OneDrive. + + .NOTES + Version: 1.1 + Author: David Bewernick (david.bewernick@veeam.com) + Creation Date: 03.04.2023 + + .CHANGELOG + 1.0 31.03.2023 Script created + 1.1 03.04.2023 Description and dynamic membership rules extention + + #> + +# Enable (1) or disable (0) debug messages +$debug = 0 + +# Install-Module AzureADPreview -Scope CurrentUser -AllowClobber #uncomment this to install the AureADPreview module + +$timestampFileName = get-date -Format "yyyy-mm-dd_HH-mm-ss" +[string[]]$script:arrFirstChar = @("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f") #array of characters for the regex +[string[]]$Script:arrScndChar = @('0-3','4-7','8-9a-b','c-f') + +# Variables to fit your needs +$OrgName = "XYZ.onmicrosoft.com" # The name of your M365 organization +$RepoName = "XYZ-Repo" # Define the target repository +$LogFile = "VBO-CreateDynamicGroups.log" #logfile name +$GroupNameFile = "DynamicGroupsList_$timestampFileName.log" #file to export group names +$strGroupNameStart = "VB365-UserBackup_" #start of the Azure AD dynamic group names +$strJobNameStartExch = "Exch_" #start of the Exchange Online job names +$strJobNameStartOD = "OD_" #start of the OneDrive job names + +# Get the organizsation in an object +$vbOrgObject = Get-VBOOrganization -Name $OrgName +# Get the VB365 target repository object +$vbRepoObject = Get-VBORepository -Name $RepoName + +# Define the backup schedule for new jobs (Options Info: https://helpcenter.veeam.com/archive/vbo365/40/powershell/new-vbojobschedulepolicy.html) +$vbSchedType = "daily" +$vbSchedDailyType = "Everyday" +$vbSchedStartHr = "22" +$vbSchedStartMin = "00" + +# Time to delay in minutes for the next created job +$global:vbSchedDelayMin = "2" + +# Create the initial start time value +$global:vbDailyTime = "$vbSchedStartHr" + ":" + "$vbSchedStartMin" + ":00" + +#Function for logging and console output +function Write-Log($Info, $Status){ + $timestamp = get-date -Format "yyyy-mm-dd HH:mm:ss" + switch($Status){ + Info {Write-Host "$timestamp $Info" -ForegroundColor Green ; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + Status {Write-Host "$timestamp $Info" -ForegroundColor Yellow ; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + Warning {Write-Host "$timestamp $Info" -ForegroundColor Yellow ; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + Error {Write-Host "$timestamp $Info" -ForegroundColor Red -BackgroundColor White; "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + default {Write-Host "$timestamp $Info" -ForegroundColor white "$timestamp $Info" | Out-File -FilePath $LogFile -Append} + } +} + +# Create a time object for later modification +$global:vbSchedTimeObj = New-Object DateTime 2022, 1, 1, $vbSchedStartHr, $vbSchedStartMin, 0, ([DateTimeKind]::Utc) +if($debug="1"){Write-Host "Initial vbSchedTimeObj: $vbSchedTimeObj"} + +# Build the initial backup job schedule +$global:vbSchedule = New-VBOJobSchedulePolicy -Type $vbSchedType -DailyType $vbSchedDailyType -DailyTime $vbDailyTime +Write-Log -Info "New jobs will be created with this initual schedule:`n`t`t`t`t Type: $($vbSchedule.Type) `n`t`t`t`t DailyType: $($vbSchedule.DailyType) `n`t`t`t`t DailyTime: $($vbSchedule.DailyTime) `n`t`t`t`t Delay for the next job: $vbSchedDelayMin" -Status Info + +function AddTimeDelay(){ + + if($debug="1"){Write-Host "vbSchedTimeObj before delay: $vbSchedTimeObj"} + + # Add the delay minutes + $global:vbSchedTimeObj = $vbSchedTimeObj.AddMinutes($vbSchedDelayMin) + if($debug="1"){Write-Host "vbSchedTimeObj after delay: $vbSchedTimeObj"} + + # Recreate the DailyTime value + $global:vbDailyTime = $vbSchedTimeObj.ToString("HH:mm:ss") + if($debug="1"){Write-Host "vbDailyTime: $vbDailyTime"} + + # Recreate the schedule object with the new start time + $global:vbSchedule = New-VBOJobSchedulePolicy -Type $vbSchedType -DailyType $vbSchedDailyType -DailyTime $vbDailyTime + if($debug="1"){Write-Host "vbSchedule: $vbSchedule"} +} + +#Function to create the Azure AD dynamic groups and backup jobs in VB365 +function create-groups(){ + $i=0 + while($i -lt $arrFirstChar.length){ #go through the array for the first character + $j=0 + while($j -lt $arrScndChar.length){ #go through the array for the second character + $strRegex = '^' + $arrFirstChar[$i] + '[' + $arrScndChar[$j] + ']' #building the regex based on the array strings + $strGroupName = $strGroupNameStart + $arrFirstChar[$i] + $arrScndChar[$j] #create the group name + $strMembershipRule = '(user.objectID -match "' + $strRegex + '") and (user.mail -ne "$null") and (user.accountEnabled -eq true) and (user.assignedPlans -ne $null) and (user.assignedPlans -any (assignedPlan.capabilityStatus -eq "Enabled"))' #define the Membership rule based on the regex and additional properties" + #Write-Output $strGroupName + #Write-Output $strRegex + #Write-Output $strMembershipRule + + if((Get-AzureADMSGroup | where{$_.DisplayName -eq $strGroupName}) -eq $null) { + try { + New-AzureADMSGroup -DisplayName "$strGroupName" -MailNickname "$strGroupName" -Description "Group for VBO backup with rule $strRegex" -MailEnabled $false -GroupTypes {DynamicMembership} -SecurityEnabled $true -MembershipRule "$strMembershipRule" -MembershipRuleProcessingState 'on' #this is finally creating the dynamic group in AzureAD + Write-Log -Info "Group $strGroupName created with MembershipRule $strMembershipRule" -Status Info + $strGroupName | Out-File -FilePath $GroupNameFile -Append # write groupname to CSV file + } + catch{ + Write-Log -Info "$_" -Status Error + Write-Log -Info "Group $strGroupName could not be created" -Status Error + exit 99 + } + } + else { + Write-Log -Info "Group $strGroupName is already existing" -Status Status + $strGroupName | Out-File -FilePath $GroupNameFile -Append # write groupname to CSV file + } + + # Call the function to create or modify the backup job + create-jobs($strGroupName) + + $j++ + } + $i++ + } + +} + + + +# Function to create the backup jobs based on the group name +function create-jobs ($strGroupName){ + + # get the organization group in an object + $orggroup = Get-VBOOrganizationGroup -Organization $vbOrgObject -DisplayName $strGroupName + # create backup job item objects for Exchange and OneDrive + $ExchJobItem = New-VBOBackupItem -Group $orggroup -Mailbox -ArchiveMailbox + $ODJobItem = New-VBOBackupItem -Group $orggroup -OneDrive + + $ExchJobName = $strJobNameStartExch + $strGroupName + $ODJobName = $strJobNameStartOD + $strGroupName + + ### Modify or create Exchange backup job ### + + # Check if job exist and add group + + if ($VbJob=Get-VBOJob | ? {$_.Name -like $ExchJobName}){ + Write-Log -Info "$ExchJobName exists, adding $strGroupName" -Status Info + Add-VBOBackupItem -Job $VbJob -BackupItem $ExchJobItem + + } + + # If job does not exist create it including the site + else { + Write-Log -Info "Creating $ExchJobName and adding $strGroupName" -Status Info + Add-VBOJob -Organization $vbOrgObject -Name "$ExchJobName" -Repository $vbRepoObject -SchedulePolicy $vbSchedule -SelectedItems $ExchJobItem + + # Increase the time for the next schedule + if($debug="1"){Write-Host "Schedule before delay: $($vbSchedule.DailyTime)"} + AddTimeDelay + if($debug="1"){Write-Host "Schedule after delay: $($vbSchedule.DailyTime)"} + } + + ### Modify or create OneDrive backup job ### + + # Check if job exist and add group + + if ($VbJob=Get-VBOJob | ? {$_.Name -like $ODJobName}){ + Write-Log -Info "$ODJobName exists, adding $strGroupName" -Status Info + Add-VBOBackupItem -Job $VbJob -BackupItem $ODJobItem + + } + + # If job does not exist create it including the site + else { + Write-Log -Info "Creating $ODJobName and adding $strGroupName" -Status Info + Add-VBOJob -Organization $vbOrgObject -Name "$ODJobName" -Repository $vbRepoObject -SchedulePolicy $vbSchedule -SelectedItems $ODJobItem + + # Increase the time for the next schedule + if($debug="1"){Write-Host "Schedule before delay: $($vbSchedule.DailyTime)"} + AddTimeDelay + if($debug="1"){Write-Host "Schedule after delay: $($vbSchedule.DailyTime)"} + } +} + +### MAIN ### + +# Connecting to AzureAD +Write-Log -Info "Trying to connect to AzureAD..." -Status Info + try { + Connect-AzureAD + $ConnectionAccountName = Get-AzureADCurrentSessionInfo | select Account + Write-Log -Info "Connection successful with $ConnectionAccountName" -Status Info + } + catch { + Write-Log -Info "$_" -Status Error + Write-Log -Info "Could not connect with $ConnectionAccountName" -Status Error + exit 99 + } + + +Write-Log -Info "Creating the groups and jobs..." -Status Info + +create-groups + +#Disconnecting from AzureAD +Write-Log -Info "Trying to disconnect from AzureAD..." -Status Info + try { + Disconnect-AzureAD + Write-Log -Info "Successfully disconnected" -Status Info + } + catch { + Write-Log -Info "$_" -Status Error + Write-Log -Info "Could not disconnect from AzureAD" -Status Error + exit 99 + }