Public/FlyProjectApi.ps1
<#
.SYNOPSIS Create a new migration project .DESCRIPTION Create a new migration project .PARAMETER Name Specify the name of project .PARAMETER SourceConnection Specify the name of source connection .PARAMETER DestinationConnection Specify the name of destination connection .PARAMETER Policy Specify the name of policy which applied to the project .PARAMETER Tags Specify a list of tags to the project .OUTPUTS ProjectModel<PSCustomObject> #> function New-FlyMigrationProject { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [String] ${Name}, [Parameter(Mandatory = $true)] [String] ${SourceConnection}, [Parameter(Mandatory = $true)] [String] ${DestinationConnection}, [Parameter(Mandatory = $true)] [String] ${Policy}, [Parameter(Mandatory = $false)] [String[]] ${Tags} ) Process { 'Calling method: New-FlyMigrationProject' | Write-Debug $PSBoundParameters | Out-DebugParameter | Write-Debug Try { Resolve-FlyProjectName -ProjectName $Name $source = Get-FlyConnectionByName -ConnectionName $SourceConnection $destination = Get-FlyConnectionByName -ConnectionName $DestinationConnection $migrationModuleType = $source.ConnectionType if ($source.ConnectionType -eq [PlatformType]::SharePoint -and $destination.ConnectionType -eq [PlatformType]::GoogleDrive) { $migrationModuleType = 21 } elseif ($source.ConnectionType -eq [PlatformType]::OneDrive -and $destination.ConnectionType -eq [PlatformType]::GoogleDrive) { $migrationModuleType = 22 } $targetPolicy = Get-FlyPolicyByName -PolicyName $Policy -PlatformType $migrationModuleType #Construct the project creation model $projectModel = [PSCustomObject]@{ "name" = $Name "sourcePlatform" = $source.ConnectionType "sourceConnectionId" = $source.Id "destinationPlatform" = $destination.ConnectionType "destinationConnectionId" = $destination.id "policyId" = $targetPolicy.Id "tagIds" = @() } if ($Tags -and $Tags.Count -gt 0) { $tagIds = $Tags | ForEach-Object { Get-FlyTagByName $PSItem } | Select-Object -Property Id | ForEach-Object { "$($_.Id)" } $projectModel.tagIds = @($tagIds) } #Create a new project $result = New-FlyProject -ProjectCreationModel $projectModel if ($result) { Write-Host 'Successfully created the project.' -ForegroundColor Green $result.sourcePlatform = [PlatformType].GetEnumName($result.sourcePlatform) $result.destinationPlatform = [PlatformType].GetEnumName($result.destinationPlatform) $result.createTime = [DateTime]$result.createTime $result.lastModifyTime = [DateTime]$result.lastModifyTime } return $result } Catch { Write-Host 'Failed to create the project.' -ForegroundColor Red ErrorDetail -Error $_ throw } } } <# .SYNOPSIS Get the project by name .DESCRIPTION Get the project by name .PARAMETER Name The name of the project .PARAMETER Platform The platform of the project .OUTPUTS ProjectSummaryModel, the object model of the project #> function Get-FlyMigrationProject { [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [String] ${Name} ) Process { 'Calling method: Get-FlyMigrationProject' | Write-Debug Try { $project = Get-FlyProjectByName -ProjectName $Name $project.sourcePlatform = [PlatformType].GetEnumName($project.sourcePlatform) $project.destinationPlatform = [PlatformType].GetEnumName($project.destinationPlatform) $project.createTime = [DateTime]$project.createTime $project.lastModifyTime = [DateTime]$project.lastModifyTime return $project } Catch { $_.Exception | Write-Debug } return $null } } <# .SYNOPSIS Import migration projects from csv file. .DESCRIPTION Import migration projects from csv file. .PARAMETER Path Specify the csv file path of the projects to import. .PARAMETER OutFile Specify the csv file path of the import report. If you do not specify the report path,the report file will be generated in the same directory as the import file. .OUTPUTS None #> function Import-FlyMigrationProjects { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [String] ${Path}, [Parameter(Mandatory = $false)] [String] ${OutFile} ) Process { 'Calling method: Import-FlyMigrationProjects' | Write-Debug $PSBoundParameters | Out-DebugParameter | Write-Debug $importProjects = Get-FlyMappingsFromCsv -Path $Path if ($OutFile) { $isCsv = Confirm-FileExtension -Path $OutFile -AllowedExtensions 'csv' if (-not $isCsv) { throw 'The report file is not a CSV file.' } } $connectionCache = @{} $policyCache = @{} $tagCache = @{} $reports = @() foreach ($project in $importProjects) { $obj = [PSCustomObject]@{ 'Project name' = $project.'Project name' 'Source connection' = $project.'Source connection' 'Destination connection' = $project.'Destination connection' 'Migration policy' = $project.'Migration policy' 'Tags' = $project.'Tags' 'Status' = '' 'Comment' = '' } while ($true) { Try { $validateProperties = @('Project name', 'Source connection', 'Destination connection', 'Migration policy') Confirm-PSCustomObjectProperties -Object $obj -Properties $validateProperties $srcConnection = $connectionCache[$obj.'Source connection'] $destConnection = $connectionCache[$obj.'Destination connection'] $targetPolicy = $policyCache[$obj.'Migration policy'] $tagIds = @() if ($null -eq $srcConnection) { $srcConnection = Get-FlyConnectionByName -ConnectionName $obj.'Source connection' $connectionCache[$obj.'Source connection'] = $srcConnection } if ($null -eq $destConnection) { $destConnection = Get-FlyConnectionByName -ConnectionName $obj.'Destination connection' $connectionCache[$obj.'Destination connection'] = $destConnection } if ($null -eq $targetPolicy) { $targetPolicy = Get-FlyPolicyByName -PolicyName $obj.'Migration policy' -PlatformType $srcConnection.ConnectionType $policyCache[$obj.'Migration policy'] = $targetPolicy } if ($obj.Tags) { $tagNames = $obj.Tags.Split(';') foreach ($tagName in $tagNames) { $tagId = $tagCache[$tagName] if ($null -eq $tagId) { $tagId = (Get-FlyTagByName -TagName $tagName).Id $tagCache[$tagName] = $tagId } if ($tagId) { $tagIds += $tagId } } } #Construct the project creation model $projectModel = [PSCustomObject]@{ "name" = $obj.'Project name' "sourcePlatform" = $srcConnection.ConnectionType "sourceConnectionId" = $srcConnection.Id "destinationPlatform" = $destConnection.ConnectionType "destinationConnectionId" = $destConnection.id "policyId" = $targetPolicy.Id "tagIds" = $tagIds } #Create a new project $result = New-FlyProject -ProjectCreationModel $projectModel if ($result) { Write-Host ('Successfully created the project: {0}' -f $obj.'Project name') -ForegroundColor Green $obj.Status = 'Successful' $reports += $obj } break } Catch { if ($null -ne $_.ErrorDetails) { Write-Host ('Failed to create the project: {0}. Details: {1}' -f $obj.'Project name', $_.ErrorDetails.Message) -ForegroundColor Red Try { $errorMessage = ConvertFrom-Json $_.ErrorDetails.Message if ($errorMessage.statusCode -eq 429) { $obj.Status = '' $obj.Comment = '' Write-Host ('Will try to create the project: {0} again after 60 seconds.' -f $obj.'Project name') Start-Sleep -Seconds 60 } else { $obj.Status = 'Failed' $obj.Comment = $errorMessage.errorMessage $reports += $obj break } } Catch { break } } elseIf ($null -ne $_.Exception) { Write-Host ('Failed to create the project: {0}. Details: {1}' -f $obj.'Project name', $_.Exception) -ForegroundColor Red if ($null -ne $_.Exception.Response -and $_.Exception.Response.StatusCode -eq 429) { $obj.Status = '' $obj.Comment = '' Write-Host ('Will try to create the project: {0} again after 60 seconds.' -f $obj.'Project name') Start-Sleep -Seconds 60 } else { $obj.Status = 'Failed' $obj.Comment = $_.Exception.Message $reports += $obj break } } } } } if (-not $OutFile) { $file = -Join ('Import_Projects_Report_', (Get-Date -Format "yyyyMMddHHmmss"), '.csv') $folder = Split-Path -Path $Path $OutFile = Join-Path $folder -ChildPath $file } $reports | Export-Csv -Path $OutFile -NoTypeInformation Write-Host ('Report path: {0}' -f $OutFile) } } <# .SYNOPSIS Restructure migration projects from csv file. .DESCRIPTION Restructure migration projects from csv file. .PARAMETER Path Specify the csv file path of the project mappings. .PARAMETER OutFile Specify the csv file path of the restructure result. If you do not specify the report path,the report file will be generated in the same directory as the csv file. .OUTPUTS None #> function Move-FlyMigrationMappings { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [String] ${Path}, [Parameter(Mandatory = $false)] [String] ${OutFile} ) Process { 'Calling method: Move-FlyMigrationMappings' | Write-Debug $PSBoundParameters | Out-DebugParameter | Write-Debug $moveMappings = Get-FlyMappingsFromCsv -Path $Path if ($OutFile) { $isCsv = Confirm-FileExtension -Path $OutFile -AllowedExtensions 'csv' if (-not $isCsv) { throw 'The report file is not a CSV file.' } } $projectCache = @{} $mappingsCache = @{} $moveSettings = @{} $reports = @() foreach ($moveItem in $moveMappings) { $mapping = $moveItem.PSObject.Copy() $mapping | Add-Member -MemberType NoteProperty -Name 'Mapping ID' -Value $null $mapping | Add-Member -MemberType NoteProperty -Name 'Status' -Value '' $mapping | Add-Member -MemberType NoteProperty -Name 'Comment' -Value '' $reports += $mapping Try { $validateProperties = @('Source', 'Destination', 'Source project', 'Destination project') Confirm-PSCustomObjectProperties -Object $mapping -Properties $validateProperties if ($mapping.'Source project' -eq $mapping.'Destination project') { $mapping.Status = 'Verification failed' $mapping.Comment = 'The source and destination projects must be different.' continue; } if (!$projectCache.ContainsKey($mapping.'Source project')) { $projectCache[$mapping.'Source project'] = Get-FlyProjectByName -ProjectName $mapping.'Source project' } $oldProject = $projectCache[$mapping.'Source project'] if ($null -eq $oldProject) { $mapping.Status = 'Verification failed' $mapping.Comment = 'Failed to retrieve the project: {0}.' -f $mapping.'Source project' continue; } if (!$projectCache.ContainsKey($mapping.'Destination project')) { $projectCache[$mapping.'Destination project'] = Get-FlyProjectByName -ProjectName $mapping.'Destination project' } $newProject = $projectCache[$mapping.'Destination project'] if ($null -eq $newProject) { $mapping.Status = 'Verification failed' $mapping.Comment = 'Failed to retrieve the project: {0}.' -f $mapping.'Destination project' continue; } $allMappings = $mappingsCache[$oldProject.Id] if ($null -eq $allMappings) { $allMappings = Get-FlyAllProjectMappings -ProjectId $oldProject.Id $mappingsCache[$oldProject.Id] = $allMappings } foreach ($item in $allMappings) { $sourceIdentity = [System.Web.HttpUtility]::UrlDecode($mapping.'Source') $destinationIdentity = [System.Web.HttpUtility]::UrlDecode($mapping.'Destination') if ($item.SourceIdentity -eq $sourceIdentity -and $item.DestinationIdentity -eq $destinationIdentity) { $mapping.'Mapping ID' = $item.Id if (!$moveSettings.ContainsKey($oldProject.Id + ';' + $newProject.Id)) { $moveSettings[$oldProject.Id + ';' + $newProject.Id] = @() } if ($moveSettings[$oldProject.Id + ';' + $newProject.Id] -contains $item.Id) { $mapping.Status = 'Verification failed' $mapping.Comment = 'The mapping already exists in the file.' } else { $moveSettings[$oldProject.Id + ';' + $newProject.Id] += $item.Id } break; } } if ($null -eq $mapping.'Mapping ID') { $mapping.Status = 'Verification failed' $mapping.Comment = 'The mapping does not exist in the project: {0}.' -f $oldProject.Name } } Catch { if ($null -ne $_.ErrorDetails) { Try { $errorMessage = ConvertFrom-Json $_.ErrorDetails.Message $mapping.Status = 'Verification failed' $mapping.Comment = $errorMessage.errorMessage } Catch { } } elseIf ($null -ne $_.Exception) { $mapping.Status = 'Verification failed' $mapping.Comment = $_.Exception.Message } } } foreach ($settingKey in $moveSettings.Keys) { if ($moveSettings[$settingKey].Length -eq 0) { continue; } $projectIds = $settingKey -split ';' $oldProjectId = $projectIds[0] $oldProject = $projectCache.Values | Where-Object { $_.Id -eq $oldProjectId } | Select-Object -First 1 $oldProjectName = $oldProject.Name $newProjectId = $projectIds[1] $newProject = $projectCache.Values | Where-Object { $_.Id -eq $newProjectId } | Select-Object -First 1 $newProjectName = $newProject.Name $mappingIds = $moveSettings[$settingKey] Try { $setting = [PSCustomObject]@{ 'projectId' = $newProjectId 'mappingIds' = $mappingIds } switch ($oldProject.SourcePlatform) { 0 { [void](Move-FlyExchangeMappings -ProjectId $oldProjectId -MoveMappingSettingsModel $setting) } 1 { [void](Move-FlyTeamsMappings -ProjectId $oldProjectId -MoveMappingSettingsModel $setting) } 2 { [void](Move-FlySharePointMappings -ProjectId $oldProjectId -MoveMappingSettingsModel $setting) } 3 { [void](Move-FlyOneDriveMappings -ProjectId $oldProjectId -MoveMappingSettingsModel $setting) } 4 { [void](Move-FlyM365GroupMappings -ProjectId $oldProjectId -MoveMappingSettingsModel $setting) } 5 { [void](Move-FlyTeamChatMappings -ProjectId $oldProjectId -MoveMappingSettingsModel $setting) } Default { throw 'This workspace is not supported.' } } Write-Host ('Successfully moved {0} mappings from the project: {1} to the project: {2}' -f $mappingIds.Length, $oldProjectName, $newProjectName) -ForegroundColor Green foreach ($mappingId in $mappingIds) { $target = $reports | Where-Object { $_.'Mapping ID' -eq $mappingId } | Select-Object -First 1 if ($null -ne $target) { $target.Status = 'Successful' $target.Comment = '' } } } Catch { if ($null -ne $_.ErrorDetails) { Write-Host ('Failed to moved mappings from the project: {0} to the project: {1}. Details: {2}' -f $oldProjectName, $newProjectName, $_.ErrorDetails.Message) -ForegroundColor Red Try { $errorMessage = ConvertFrom-Json $_.ErrorDetails.Message foreach ($mappingId in $mappingIds) { $target = $reports | Where-Object { $_.'Mapping ID' -eq $mappingId } | Select-Object -First 1 if ($null -ne $target) { $target.Status = 'Failed' $target.Comment = $errorMessage.errorMessage } } } Catch { } } elseIf ($null -ne $_.Exception) { Write-Host ('Failed to moved mappings from the project: {0} to the project: {1}. Details: {2}' -f $oldProjectName, $newProjectName, $_.Exception) -ForegroundColor Red if ($null -ne $_.Exception.Response -and $_.Exception.Response.StatusCode -eq 429) { foreach ($mappingId in $mappingIds) { $target = $reports | Where-Object { $_.'Mapping ID' -eq $mappingId } | Select-Object -First 1 if ($null -ne $target) { $target.Status = '' $target.Comment = '' } } Write-Host ('Will try again after 60 seconds.') Start-Sleep -Seconds 60 } else { foreach ($mappingId in $mappingIds) { $target = $reports | Where-Object { $_.'Mapping ID' -eq $mappingId } | Select-Object -First 1 if ($null -ne $target) { $target.Status = 'Failed' $target.Comment = $_.Exception.Message } } } } } } if (-not $OutFile) { $file = 'Fly_Move_Mappings_Report_{0}.csv' -f (Get-Date -Format "yyyyMMddHHmmss") $folder = Split-Path -Path $Path $OutFile = Join-Path $folder -ChildPath $file } $reports | Export-Csv -Path $OutFile -NoTypeInformation Write-Host ('Report path: {0}' -f $OutFile) } } <# .SYNOPSIS Generate error report for the specified projects .DESCRIPTION Generate error report for the specified projects .PARAMETER Projects Specify a list of project names to generate their error report. .PARAMETER FileType Specify the format of the generated report file, CSV(default) or Excel .PARAMETER OutFolder Specify the folder path of error report file to download .PARAMETER TimeZoneOffset Specify the UTC time offset of current browser. This value will be used to adjust time values when generating the report file. .OUTPUTS None #> function Export-FlyErrorReport { [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [String[]] ${Projects}, [Parameter(Mandatory = $true)] [String] ${OutFolder}, [Parameter(Mandatory = $false)] [ValidateSet('CSV', 'Excel')] [String] ${FileType} = [ReportFileType]::CSV, [Parameter(Mandatory = $false)] [Int32] ${TimeZoneOffset} ) Process { 'Calling method: Export-FlyErrorReport' | Write-Debug $PSBoundParameters | Out-DebugParameter | Write-Debug Try { $targetProjects = $Projects | ForEach-Object { Get-FlyProjectByName $PSItem } $projectIds = $targetProjects | Select-Object -Property Id | ForEach-Object { "$($_.Id)" } #Construct the settings of the error report $reportSetting = [PSCustomObject]@{ "reportFileType" = $FileType "projectIds" = @($projectIds) "timeZone" = $TimeZoneOffset } #Trigger the error report job and get the job id $jobId = Start-FlyErrorReportJob -GenerateProjectErrorReportSettingsModel $reportSetting #Monitor the job status and download the report file when job is finished while ($true) { Write-Host 'The report generation job is running.' -ForegroundColor Green Start-Sleep -Seconds 60 $job = Get-FlyReportJobs -RequestBody @($jobId) $jobStatus = $job.data[0].Status if ($jobStatus -eq [MappingJobStatus]::Finished -or $jobStatus -eq [MappingJobStatus]::FinishedWithException) { Write-Host 'The report generation job is finished.' -ForegroundColor Green $result = Get-FlyReportUrl -JobId $jobId if ($null -ne $result.ReportUrl) { $fileName = Split-Path $([uri]$result.ReportUrl).AbsolutePath -Leaf $filePath = Join-Path -Path $OutFolder -ChildPath $fileName If (-not (Test-Path $OutFolder)) { # Folder does not exist, create it [void](New-Item -Path $OutFolder -ItemType Directory) } Invoke-WebRequest -URI $result.ReportUrl -OutFile $filePath -UseBasicParsing Write-Host 'Successfully downloaded the job report. Report path:' $filePath -ForegroundColor Green } break; } elseif ($jobStatus -eq [MappingJobStatus]::Failed -or $jobStatus -eq [MappingJobStatus]::Stopped) { throw ('The report generation job is {0} with id {1}' -f [MappingJobStatus].GetEnumName($jobStatus), $job.data[0].JobName) } } } Catch { Write-Host 'Failed to generate the error report.' -ForegroundColor Red ErrorDetail $_ throw } } } |