Public/Acronis/Start-AcronisAutomatedSetup.ps1
$ApiBase = "https://dk01-cloud.acronis.com" function Invoke-WebJson { param( [Parameter(Mandatory)] [string]$Method, [Parameter(Mandatory)] [string]$Uri, [Microsoft.PowerShell.Commands.WebRequestSession]$Session, [hashtable]$Headers, [string]$Body, [string]$ContentType = "application/json; charset=UTF-8" ) $hdr = @{'Accept' = 'application/json' } if ($Headers) { $hdr += $Headers } $params = @{ Method = $Method; Uri = $Uri; UseBasicParsing = $true; Headers = $hdr } if ($Session) { $params.WebSession = $Session } if ($PSBoundParameters.ContainsKey('Body')) { $params.Body = $Body; $params.ContentType = $ContentType } $resp = Invoke-WebRequest @params if ($resp -and $resp.Content) { try { return ($resp.Content | ConvertFrom-Json) } catch { return $null } } return $null } function Ensure-DynamicUserGroup { param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestSession]$Session, [Parameter(Mandatory)] [string]$ApiBase, [Parameter(Mandatory)] [object[]]$AcronisGroups, [Parameter(Mandatory)] [string]$AzureBackupGroupId ) $usersGroup = $AcronisGroups | Where-Object { $_.name -eq 'All users' } if (-not $usersGroup) { throw "'All users' group not found in Acronis." } $dynamic = $AcronisGroups | Where-Object { $_.name -eq 'AcronisBackup' -and $_.type -eq 'Dynamic' } if ($dynamic) { return $dynamic } Write-ModuleLog -Message "Creating 'AcronisBackup' dynamic group in Acronis" -Level Info -Component "AcronisAutomatedSetup" $createBody = @{ name = 'AcronisBackup'; type = 'Dynamic'; parentId = "$($usersGroup.parentId)"; groupKind = 'USER'; query_azure_group_id = "$AzureBackupGroupId" } | ConvertTo-Json $null = Invoke-WebJson -Method 'POST' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups?parentId=$($usersGroup.parentId)" -Session $Session -Body $createBody # re-fetch groups to get the new ID $groups = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups" -Session $Session).data $dynamic = $groups | Where-Object { $_.name -eq 'AcronisBackup' -and $_.type -eq 'Dynamic' } if (-not $dynamic) { throw "Failed to create 'AcronisBackup' dynamic group." } return $dynamic } function Get-ExistingPlansState { param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestSession]$Session, [Parameter(Mandatory)] [string]$ApiBase, [Parameter(Mandatory)] [string]$UserGroupId, [Parameter(Mandatory)] [string]$TeamsGroupId, [Parameter(Mandatory)] [string]$SitesGroupId ) $plansUser = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/policy_manager/v2/o365/applied_policies?embed=policy&group=$UserGroupId%3AUSER" -Session $Session).Policies $plansTeam = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/policy_manager/v2/o365/applied_policies?embed=policy&group=$TeamsGroupId%3ATEAM" -Session $Session).Policies $plansSite = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/policy_manager/v2/o365/applied_policies?embed=policy&group=$SitesGroupId%3ASITE" -Session $Session).Policies $plans = @() if ($plansUser) { $plans += $plansUser } if ($plansTeam) { $plans += $plansTeam } if ($plansSite) { $plans += $plansSite } $state = [ordered]@{ HasMailbox = $false HasOneDrive = $false HasTeams = $false HasSharePoint = $false Plans = $plans } foreach ($p in $plans) { if ($p.KIND -eq 'MAILBOX') { $state.HasMailbox = $true if ($p.name -ne 'Microsoft 365 mailboxes to Cloud storage') { $state.MailboxRename = $true $state.MailboxRenamePolicyId = $p.id } } elseif ($p.KIND -eq 'DRIVE') { $state.HasOneDrive = $true if ($p.name -ne 'OneDrive to Cloud storage') { $state.OneDriveRename = $true $state.OneDriveRenamePolicyId = $p.id } } elseif ($p.KIND -eq 'TEAM') { $state.HasTeams = $true if ($p.name -ne 'Microsoft Teams to Cloud storage') { $state.TeamsRename = $true $state.TeamsRenamePolicyId = $p.id } } elseif ($p.KIND -eq 'SITE') { $state.HasSharePoint = $true if ($p.name -ne 'SharePoint sites to Cloud storage') { $state.SharePointRename = $true $state.SharePointRenamePolicyId = $p.id } } } return $state } function New-And-Apply-Policy { param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestSession]$Session, [Parameter(Mandatory)] [string]$ApiBase, [Parameter(Mandatory)] [ValidateSet('MAILBOX', 'DRIVE', 'TEAM', 'SITE')] [string]$Kind, [Parameter(Mandatory)] [string]$SelectionGroupId, # group to select resources from [Parameter(Mandatory)] [ValidateSet('USER', 'TEAM', 'SITE')] [string]$SelectionGroupKind, [Parameter(Mandatory)] [string]$ApplicableGroupId, # group used for applicable_policies and apply [Parameter(Mandatory)] [ValidateSet('USER', 'TEAM', 'SITE')] [string]$ApplicableSuffix, [Parameter(Mandatory)] [string]$PolicyDisplayName ) $uuid = [guid]::NewGuid().ToString() $draftBody = @{ id = $uuid; kind = $Kind; name = 'AcronisBackup'; selectedResources = @(@{ id = "$SelectionGroupId"; kind = $SelectionGroupKind; isGroup = $true }) } | ConvertTo-Json -Depth 6 $draft = Invoke-WebJson -Method 'POST' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts" -Session $Session -Body $draftBody $draftId = $draft.policy.Id # Set names $null = Invoke-WebJson -Method 'PUT' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts/$draftId/default_name" -Session $Session -Body (@{ name = $PolicyDisplayName } | ConvertTo-Json) $null = Invoke-WebJson -Method 'PUT' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts/$draftId/name" -Session $Session -Body (@{ name = $PolicyDisplayName } | ConvertTo-Json) # Scheduling every 6 hours $null = Invoke-WebJson -Method 'PUT' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts/$draftId/scheduling" -Session $Session -Body (@{ backupFrequency = 6 } | ConvertTo-Json) # Retention: cleanupByTime, 5 years $retention = @{ retentionRules = @{ type = 'cleanupByTime'; backupCount = 1; archiveSize = 1099511627776; backupSets = 'all'; startCleanup = 'after'; time = @(@{ backupSet = 'all'; period = @{ value = 5; type = 'years' } }) } } $null = Invoke-WebJson -Method 'PUT' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts/$draftId/retention" -Session $Session -Body ($retention | ConvertTo-Json -Depth 10) # Remove encryption $null = Invoke-WebJson -Method 'DELETE' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts/$draftId/password" -Session $Session -Body '{}' # Publish $null = Invoke-WebJson -Method 'POST' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/policies" -Session $Session -Body (@{ draftId = "$draftId" } | ConvertTo-Json) # Get applicable and apply $appl = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/applicable_policies?group=$ApplicableGroupId%3A$ApplicableSuffix" -Session $Session).data $null = Invoke-WebJson -Method 'POST' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/policies/$($appl.id)/apply" -Session $Session -Body (@{ resources = @(@{ id = "$ApplicableGroupId"; type = 'group' }) } | ConvertTo-Json) } function Rename-Policy { param( [Parameter(Mandatory)] [Microsoft.PowerShell.Commands.WebRequestSession]$Session, [Parameter(Mandatory)] [string]$ApiBase, [Parameter(Mandatory)] [string]$PolicyKind ) $Policy = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/policies" -Session $Session).data | Where-Object { $_.kind -eq $PolicyKind } $groupsData = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups" -Session $session).data $teamsGroup = $groupsData | Where-Object { $_.name -eq 'All teams' } $sitesGroup = $groupsData | Where-Object { $_.name -eq 'All sites' } $backupAzureGroup = $aadGroups | Where-Object { $_.name -eq 'AcronisBackup' } $dynamicGroup = Ensure-DynamicUserGroup -Session $session -ApiBase $ApiBase -AcronisGroups $groupsData -AzureBackupGroupId $backupAzureGroup.id switch ($PolicyKind) { 'MAILBOX' { $selectedResources = @(@{ id = "$($dynamicGroup.id)"; kind = 'USER'; isGroup = $true }) $NewName = "Microsoft 365 mailboxes to Cloud storage" } 'DRIVE' { $selectedResources = @(@{ id = "$($dynamicGroup.id)"; kind = 'USER'; isGroup = $true }) $NewName = "OneDrive to Cloud storage" } 'TEAM' { $selectedResources = @(@{ id = "$($teamsGroup.id)"; kind = 'USER'; isGroup = $true }) $NewName = "Microsoft Teams to Cloud storage" } 'SITE' { $selectedResources = @(@{ id = "$($sitesGroup.id)"; kind = 'USER'; isGroup = $true }) $NewName = "SharePoint sites to Cloud storage" } } $DraftResponse = Invoke-WebJson -Method 'POST' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts" -Session $Session -Body (@{ planId = $Policy.id; kind = $PolicyKind; selectedResources = $selectedResources } | ConvertTo-Json) if ($DraftResponse.policy.id) { $null = Invoke-WebJson -Method 'PUT' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/drafts/$($DraftResponse.policy.id)/name" -Session $Session -Body (@{ name = $NewName } | ConvertTo-Json) $null = Invoke-WebJson -Method 'POST' -Uri "$ApiBase/bc/api/policy_manager/v1/o365/policies" -Session $Session -Body (@{ draftId = $DraftResponse.policy.id } | ConvertTo-Json) } } function Start-AcronisAutomatedSetup { [CmdletBinding()] param ( [Parameter()] $CloudFactoryCustomer, [Parameter()] [string]$TenantId ) begin { # Initialize results collection $results = [System.Collections.Generic.List[pscustomobject]]::new() # Determine if $CloudFactoryCustomer or $TenantId is provided if (-not $CloudFactoryCustomer -and -not $TenantId) { Write-Error "You must provide either CloudFactoryCustomer or TenantId." return } if ($CloudFactoryCustomer) { $TenantId = $CloudFactoryCustomer.externalServices.MICROSOFT if (-not $TenantId) { Write-ModuleLog -Message "CloudFactoryCustomer does not have a Microsoft TenantId." -Level Error -Component "AcronisAutomatedSetup" throw "CloudFactoryCustomer does not have a Microsoft TenantId." } } Write-ModuleLog -Message "Finding CloudFactory Customer for TenantId: $TenantId - This could take a while...." -Level Info -Component "AcronisAutomatedSetup" $AllCloudFactoryCustomers = Get-CFCustomers -All $CloudFactoryCustomer = $AllCloudFactoryCustomers | Where-Object { $_.externalServices.MICROSOFT -eq $TenantId } if (-not $CloudFactoryCustomer) { Write-ModuleLog -Message "No CloudFactory Customer found for TenantId: $TenantId" -Level Error -Component "AcronisAutomatedSetup" throw "No CloudFactory Customer found for TenantId: $TenantId" } Write-ModuleLog -Message "Verified CloudFactory Customer: $($CloudFactoryCustomer.name)" -Level Info -Component "AcronisAutomatedSetup" } process { if ($CloudFactoryCustomer.externalServices.ACRONIS) { Write-ModuleLog -Message "Acronis is already attached to CloudFactory Customer: $($CloudFactoryCustomer.name) -Verifying existing Acronis Customer" -Level Info -Component "AcronisAutomatedSetup" try { $AcronisCustomer = Get-AcronisCustomerInfo -CustomerId $CloudFactoryCustomer.externalServices.ACRONIS Write-ModuleLog -Message "Acronis Customer found: $($AcronisCustomer.name)" -Level Info -Component "AcronisAutomatedSetup" } catch { Write-ModuleLog -Message "Failed to retrieve Acronis Customer information for ID: $($CloudFactoryCustomer.externalServices.ACRONIS)" -Level Error -Component "AcronisAutomatedSetup" throw "Failed to retrieve Acronis Customer information for ID: $($CloudFactoryCustomer.externalServices.ACRONIS)" } } else { Write-ModuleLog -Message "Acronis is not enabled for CloudFactory Customer: $($CloudFactoryCustomer.name) - Enabling Acronis" -Level Info -Component "AcronisAutomatedSetup" try { $AcronisTenantName = "$($CloudFactoryCustomer.customerReference) - $($CloudFactoryCustomer.name)" $AcronisCustomer = New-AcronisCustomer -CustomerName $AcronisTenantName -CustomerReference $CloudFactoryCustomer.customerReference Write-ModuleLog -Message "Acronis Customer created: $($AcronisCustomer.name)" -Level Info -Component "AcronisAutomatedSetup" $AddCfExternalService = Add-CFExternalService -CustomerObject $CloudFactoryCustomer -ServiceName "ACRONIS" -Uuid $AcronisCustomer.id if (-not $AddCfExternalService) { Write-ModuleLog -Message "Failed to add Acronis external service to CloudFactory Customer: $($CloudFactoryCustomer.name)" -Level Error -Component "AcronisAutomatedSetup" throw "Failed to add Acronis external service to CloudFactory Customer: $($CloudFactoryCustomer.name)" } Write-ModuleLog -Message "Acronis Customer ID: $($AcronisCustomer.id) added to CloudFactory Customer: $($CloudFactoryCustomer.name)" -Level Info -Component "AcronisAutomatedSetup" } catch { Write-ModuleLog -Message "Failed to create Acronis Customer for CloudFactory Customer: $($CloudFactoryCustomer.name)" -Level Error -Component "AcronisAutomatedSetup" throw "Failed to create Acronis Customer for CloudFactory Customer: $($CloudFactoryCustomer.name)" } } try { # Enable all offerings for the customer Write-ModuleLog -Message "Enabling all Acronis offerings for customer: $($CloudFactoryCustomer.name)" -Level Info -Component "AcronisAutomatedSetup" $enabledOfferings = Enable-AcronisAllOfferings -CustomerId $AcronisCustomer.id if ($enabledOfferings) { Write-ModuleLog -Message "Successfully enabled all Acronis offerings for customer: $($AcronisCustomer.name)" -Level Info -Component "AcronisAutomatedSetup" } else { Write-ModuleLog -Message "No offerings were enabled for customer: $($AcronisCustomer.name)" -Level Warning -Component "AcronisAutomatedSetup" } } catch { Write-ModuleLog -Message "Failed to enable all Acronis offerings for customer: $($AcronisCustomer.name)" -Level Error -Component "AcronisAutomatedSetup" -ErrorRecord $_ throw "Failed to enable all Acronis offerings for customer: $($AcronisCustomer.name)" } try { # Login to Acronis $session = Get-AcronisSession } catch { Write-ModuleLog -Message "Failed to establish Acronis session for customer: $($AcronisCustomer.name)" -Level Error -Component "AcronisAutomatedSetup" -ErrorRecord $_ throw "Failed to establish Acronis session for customer: $($AcronisCustomer.name)" } try { # Refetch the Cloud factory customer to ensure we have the latest data $CFCustomer = Get-CFCustomers -CustomerId $CloudFactoryCustomer.id $acronisTenant = $CloudFactoryCustomer.externalServices.ACRONIS $msTenant = $CloudFactoryCustomer.externalServices.MICROSOFT if (-not $acronisTenant -or -not $msTenant) { throw "CloudFactory Customer is missing required external services. ACRONIS: $acronisTenant, MICROSOFT: $msTenant" } $row = [ordered]@{ CustomerName = $CFCustomer.Name AcronisTenantId = $acronisTenant MicrosoftTenantId = $msTenant SwitchedTenant = $false HasMailboxPlanBefore = $false CreatedMailboxPlan = $false HasOneDrivePlanBefore = $false CreatedOneDrivePlan = $false HasTeamsPlanBefore = $false CreatedTeamsPlan = $false HasSharePointPlanBefore = $false CreatedSharePointPlan = $false Success = $false Errors = '' } $errors = @() Write-ModuleLog -Message "Switching to Acronis tenant: $($acronisTenant) - CF Customer: $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" try { $switchUrl = "$ApiBase/bc/api/gateway/session?tenant_id=$acronisTenant" $null = Invoke-WebJson -Method 'PUT' -Uri $switchUrl -Session $session -Body '' -ContentType 'application/json' Start-Sleep -Seconds 2 $confirm = Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/gateway/session" -Session $session $currentTenant = $confirm.current_tenant_uuid if ($currentTenant -ne $acronisTenant) { throw "Failed to switch to tenant $acronisTenant (current: $currentTenant)" } $row.SwitchedTenant = $true } catch { $msg = "Tenant switch error for $($CFCustomer.Name): $($_.Exception.Message)" Write-ModuleLog -Message $msg -Level Error -Component "AcronisAutomatedSetup" $errors += $msg $results.Add([pscustomobject]$row) | Out-Null continue } try { # Fetch groups $groupsData = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups" -Session $session).data # Check if accountId exists if (-not $groupsData -or -not $groupsData.accountId) { # This means the acronis tenant has not correctly linked to the ms tenant - attempt onboarding $onboardUrl = "https://dk01-cloud.acronis.com/api/resource_manager/v1/o365/accounts/auth?admin_consent=True&tenant=$($msTenant)&state=eyJkY0lkIjoibzM2NXdvcmxkd2lkZSJ9" $onboard = Invoke-WebJson -Method 'GET' -Uri $onboardUrl -Session $session # Sleep for 5 seconds to let the acronis tenant catch up Write-ModuleLog -Message "Waiting for Acronis tenant to catch up..." -Level Info -Component "AcronisAutomatedSetup" Start-Sleep -Seconds 5 $groupsData = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups" -Session $session).data } $accountId = ($groupsData | Select-Object -First 1 -ExpandProperty accountId) $aadGroups = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/accounts/$accountId/azure_ad_groups?limit=1000" -Session $session).items $usersGroup = $groupsData | Where-Object { $_.name -eq 'All users' } $teamsGroup = $groupsData | Where-Object { $_.name -eq 'All teams' } $sitesGroup = $groupsData | Where-Object { $_.name -eq 'All sites' } $backupAzureGroup = $aadGroups | Where-Object { $_.name -eq 'AcronisBackup' } if (-not $backupAzureGroup) { throw "Azure AD group 'AcronisBackup' not found" } if (-not $usersGroup) { throw "Acronis 'All users' group not found" } if (-not $teamsGroup) { throw "Acronis 'All teams' group not found" } if (-not $sitesGroup) { throw "Acronis 'All sites' group not found" } $dynamicGroup = Ensure-DynamicUserGroup -Session $session -ApiBase $ApiBase -AcronisGroups $groupsData -AzureBackupGroupId $backupAzureGroup.id $state = Get-ExistingPlansState -Session $session -ApiBase $ApiBase -UserGroupId $dynamicGroup.id -TeamsGroupId $teamsGroup.id -SitesGroupId $sitesGroup.id $row.HasMailboxPlanBefore = [bool]$state.HasMailbox $row.HasOneDrivePlanBefore = [bool]$state.HasOneDrive $row.HasTeamsPlanBefore = [bool]$state.HasTeams $row.HasSharePointPlanBefore = [bool]$state.HasSharePoint if ($state.Plans -and $state.Plans.Count -gt 0) { $plansText = ($state.Plans | ForEach-Object { "[$($_.name) $($_.KIND)]" } | Sort-Object) Write-ModuleLog -Message ("Existing backup plans found: " + ($plansText -join ', ')) -Level Debug -Component "AcronisAutomatedSetup" } # Create missing policies if (-not $state.HasMailbox) { try { New-And-Apply-Policy -Session $session -ApiBase $ApiBase -Kind 'MAILBOX' -SelectionGroupId $dynamicGroup.id -SelectionGroupKind 'USER' -ApplicableGroupId $dynamicGroup.id -ApplicableSuffix 'USER' -PolicyDisplayName 'Microsoft 365 mailboxes to Cloud storage' $row.CreatedMailboxPlan = $true Write-ModuleLog -Message "Mailbox policy created and applied for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } catch { $msg = "Mailbox policy error: $($_.Exception.Message)"; Write-Log $msg 'ERROR'; $errors += $msg } } elseif ($state.MailboxRename) { try { Rename-Policy -Session $session -ApiBase $ApiBase -PolicyKind 'MAILBOX' $row.RenamedMailboxPlan = $true Write-ModuleLog -Message "Mailbox policy renamed for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } catch { $msg = "Mailbox policy error: $($_.Exception.Message)"; Write-Log $msg 'ERROR'; $errors += $msg } } if (-not $state.HasOneDrive) { try { New-And-Apply-Policy -Session $session -ApiBase $ApiBase -Kind 'DRIVE' -SelectionGroupId $dynamicGroup.id -SelectionGroupKind 'USER' -ApplicableGroupId $dynamicGroup.id -ApplicableSuffix 'USER' -PolicyDisplayName 'OneDrive to Cloud storage' $row.CreatedOneDrivePlan = $true Write-ModuleLog -Message "OneDrive policy created and applied for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } catch { $msg = "OneDrive policy error: $($_.Exception.Message)"; Write-Log $msg 'ERROR'; $errors += $msg } } elseif ($state.OneDriveRename) { try { Rename-Policy -Session $session -ApiBase $ApiBase -PolicyKind 'DRIVE' $row.RenamedOneDrivePlan = $true Write-ModuleLog -Message "OneDrive policy renamed for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } catch { $msg = "OneDrive policy error: $($_.Exception.Message)"; Write-Log $msg 'ERROR'; $errors += $msg } } if (-not $state.HasTeams) { try { # Check if the team group is empty $teamsGroupContent = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups/$($teamsGroup.id)/resources?limit=30" -Session $session) if ($teamsGroupContent.items -and $teamsGroupContent.items.Count -gt 0) { New-And-Apply-Policy -Session $session -ApiBase $ApiBase -Kind 'TEAM' -SelectionGroupId $teamsGroup.id -SelectionGroupKind 'TEAM' -ApplicableGroupId $teamsGroup.id -ApplicableSuffix 'TEAM' -PolicyDisplayName 'Microsoft Teams to Cloud storage' $row.CreatedTeamsPlan = $true Write-ModuleLog -Message "Teams policy created and applied for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } else { Write-ModuleLog -Message "Nothing to backup in teams, all is good.." -Level Info -Component "AcronisAutomatedSetup" } } catch { $msg = "Teams policy error: $($_.Exception.Message)"; Write-ModuleLog -Message $msg -Level Error -Component "AcronisAutomatedSetup"; $errors += $msg } } elseif ($state.TeamsRename) { try { Rename-Policy -Session $session -ApiBase $ApiBase -PolicyKind 'TEAM' $row.RenamedTeamsPlan = $true Write-ModuleLog -Message "Teams policy renamed for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } catch { $msg = "Teams policy error: $($_.Exception.Message)"; Write-Log $msg 'ERROR'; $errors += $msg } } if (-not $state.HasSharePoint) { try { # Check if the site group is empty $sitesGroupContent = (Invoke-WebJson -Method 'GET' -Uri "$ApiBase/bc/api/resource_manager/v1/o365/groups/$($sitesGroup.id)/resources?limit=30" -Session $session) if ($sitesGroupContent.items -and $sitesGroupContent.items.Count -gt 0) { New-And-Apply-Policy -Session $session -ApiBase $ApiBase -Kind 'SITE' -SelectionGroupId $sitesGroup.id -SelectionGroupKind 'SITE' -ApplicableGroupId $sitesGroup.id -ApplicableSuffix 'SITE' -PolicyDisplayName 'SharePoint sites to Cloud storage' $row.CreatedSharePointPlan = $true Write-ModuleLog -Message "SharePoint policy created and applied for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } else { Write-ModuleLog -Message "Nothing to backup in sites, all is good.." -Level Info -Component "AcronisAutomatedSetup" } } catch { $msg = "SharePoint policy error: $($_.Exception.Message)"; Write-ModuleLog -Message $msg -Level Error -Component "AcronisAutomatedSetup"; $errors += $msg } } elseif ($state.SharePointRename) { try { Rename-Policy -Session $session -ApiBase $ApiBase -PolicyKind 'SITE' $row.RenamedSharePointPlan = $true Write-ModuleLog -Message "SharePoint policy renamed for $($CFCustomer.Name)" -Level Info -Component "AcronisAutomatedSetup" } catch { $msg = "SharePoint policy rename error: $($_.Exception.Message)"; Write-ModuleLog -Message $msg -Level Error -Component "AcronisAutomatedSetup"; $errors += $msg } } # Final verification (optional lightweight re-check) try { $final = Get-ExistingPlansState -Session $session -ApiBase $ApiBase -UserGroupId $dynamicGroup.id -TeamsGroupId $teamsGroup.id -SitesGroupId $sitesGroup.id $row.Success = [bool]($final.HasMailbox -and $final.HasOneDrive -and $final.HasTeams -and $final.HasSharePoint) } catch { $msg = "Final verification error: $($_.Exception.Message)"; Write-ModuleLog -Message $msg -Level Error -Component "AcronisAutomatedSetup"; $errors += $msg } } catch { $msg = "Unhandled error for customer $($CFCustomer.Name): $($_.Exception.Message)" Write-ModuleLog -Message $msg -Level Error -Component "AcronisAutomatedSetup" $errors += $msg } if ($errors.Count -gt 0) { $row.Errors = ($errors -join '; ') } $results.Add([pscustomobject]$row) | Out-Null } catch { Write-ModuleLog -Message "Critical error processing customer: $($_.Exception.Message)" -Level Error -Component "AcronisAutomatedSetup" -ErrorRecord $_ # Add a basic error result if we haven't already added one if (-not $row) { $row = [ordered]@{ CustomerName = "Unknown" AcronisTenantId = "Unknown" MicrosoftTenantId = $TenantId SwitchedTenant = $false HasMailboxPlanBefore = $false CreatedMailboxPlan = $false HasOneDrivePlanBefore = $false CreatedOneDrivePlan = $false HasTeamsPlanBefore = $false CreatedTeamsPlan = $false HasSharePointPlanBefore = $false CreatedSharePointPlan = $false Success = $false Errors = "Critical error: $($_.Exception.Message)" } } else { $row.Success = $false if ([string]::IsNullOrEmpty($row.Errors)) { $row.Errors = "Critical error: $($_.Exception.Message)" } else { $row.Errors += "; Critical error: $($_.Exception.Message)" } } $results.Add([pscustomobject]$row) | Out-Null } } end { # Display single customer results if ($results.Count -gt 0) { $result = $results[0] # Since this is for a single customer, get the first (and only) result Write-ModuleLog -Message "Acronis Automated Setup completed for: $($result.CustomerName)" -Level Info -Component "AcronisAutomatedSetup" if ($result.Success) { Write-ModuleLog -Message "Setup completed successfully!" -Level Info -Component "AcronisAutomatedSetup" } else { Write-ModuleLog -Message "Setup completed with errors." -Level Warning -Component "AcronisAutomatedSetup" } # Display what was accomplished Write-Host "" Write-Host "Setup Results:" -ForegroundColor Cyan Write-Host "Customer: $($result.CustomerName)" -ForegroundColor White Write-Host "Acronis Tenant ID: $($result.AcronisTenantId)" -ForegroundColor White Write-Host "Microsoft Tenant ID: $($result.MicrosoftTenantId)" -ForegroundColor White Write-Host "" Write-Host "Backup Plans:" -ForegroundColor Cyan if ($result.CreatedMailboxPlan) { Write-Host "✓ Mailbox backup plan created" -ForegroundColor Green } elseif ($result.HasMailboxPlanBefore) { Write-Host "✓ Mailbox backup plan already existed" -ForegroundColor Yellow } else { Write-Host "✗ Mailbox backup plan not created" -ForegroundColor Red } if ($result.CreatedOneDrivePlan) { Write-Host "✓ OneDrive backup plan created" -ForegroundColor Green } elseif ($result.HasOneDrivePlanBefore) { Write-Host "✓ OneDrive backup plan already existed" -ForegroundColor Yellow } else { Write-Host "✗ OneDrive backup plan not created" -ForegroundColor Red } if ($result.CreatedTeamsPlan) { Write-Host "✓ Teams backup plan created" -ForegroundColor Green } elseif ($result.HasTeamsPlanBefore) { Write-Host "✓ Teams backup plan already existed" -ForegroundColor Yellow } else { Write-Host "✗ Teams backup plan not created" -ForegroundColor Red } if ($result.CreatedSharePointPlan) { Write-Host "✓ SharePoint backup plan created" -ForegroundColor Green } elseif ($result.HasSharePointPlanBefore) { Write-Host "✓ SharePoint backup plan already existed" -ForegroundColor Yellow } else { Write-Host "✗ SharePoint backup plan not created" -ForegroundColor Red } # Show errors if any if (-not [string]::IsNullOrEmpty($result.Errors)) { Write-Host "" Write-Host "Errors encountered:" -ForegroundColor Red Write-Host $result.Errors -ForegroundColor Red } Write-Host "" } else { Write-ModuleLog -Message "No customer was processed." -Level Warning -Component "AcronisAutomatedSetup" } } } |