Convert-ADOPipeline.ps1
function Convert-ADOPipeline { <# .Synopsis Converts builds to Azure DevOps Pipelines .Description Converts builds TFS or "Classic" builds to Azure DevOps YAML Pipelines. .Link New-ADOPipeline .Link Get-ADOTask .Example $taskList = (Get-ADOTask -Server $tfsRootUrl -Org $projectCollectionName) Get-ADOBuild -Definition -Server $tfsRootUrl -Org $projectCollection | Convert-ADOPipeline -TaskList $taskList #> [OutputType([string],[PSObject])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidAssignmentToAutomaticVariable", "", Justification="Functionality is Desired")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("Test-ForUnusedVariable", "", Justification="Functionality is Desired")] param( # A list of build steps. # This will be automatically populated when piping in a TFS Build definition. [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [Alias('Build')] [PSObject[]] $BuildStep, # An object containing build variables. # This will be automatically populated when piping in a TFS build definition. [Parameter(ValueFromPipelineByPropertyName)] [Alias('Variable','Variables')] [PSObject] $BuildVariable, # A list of task definitions. This will normally be the output from Get-ADOTask. [Parameter(Mandatory)] [PSObject[]] $TaskList, # A dictionary of conditional transformations. [Alias('WhereForeach','WhereFor')] [ValidateScript({ foreach ($k in $_.Keys) { if ($k -isnot [string] -and $k -isnot [ScriptBlock]) { throw "Key must be a string or ScriptBlock" } } foreach ($v in $_.Values) { if ($v -isnot [ScriptBlock]) { throw "Values must be script blocks" } } return $true })] [Collections.IDictionary] $WhereFore, # If set, will output the dictionary used to create each pipeline. # If not set, will output the pipeline YAML. [switch] $Passthru ) begin { $q = [Collections.Queue]::new() } process { #region Enqueue Input $in = $_ if (-not $BuildStep.Task) { Write-Error "Build Step must contain tasks" return } $q.Enqueue( @{psParameterSet= $PSCmdlet.ParameterSetName;InputObject= $in} + $PSBoundParameters ) #endregion Enqueue Input } end { $c, $t, $id = 0, $q.Count, [Random]::new().Next() while ($q.Count) { $dequeued = $q.Dequeue() $BuildVariable = $null foreach ($kv in $dequeued.GetEnumerator()) { $ExecutionContext.SessionState.PSVariable.Set($kv.Key, $kv.Value) } $C++ Write-Progress "Converting Builds" "[$c of $t]" -PercentComplete ($c * 100 / $t) -Id $id $buildSteps = @(foreach ($bs in $BuildStep) { $matchingTask = $TaskList | Where-Object ID -eq $bs.task.id if (-not $matchingTask) { Write-Warning "Could not find task $($bs.task.id)" } else { $taskName= $matchingTask | Select-Object -ExpandProperty Name -Unique $newTask = [Ordered]@{ task = $taskName + '@' + ($bs.task.versionSpec -replace '\.\*') inputs = [Ordered]@{} } foreach ($prop in $bs.inputs.psobject.properties) { if (-not [String]::IsNullOrWhiteSpace($prop.Value)) { $newTask.inputs[$prop.Name] = $prop.Value } } if (-not $newTask.inputs.count) { $newTask.Remove('inputs') } if (-not $WhereFore.Count) { $newTask } else { :outputted do { foreach ($wf in $WhereFore.GetEnumerator()) { $this = $_ = $newTask if ( $wf.Key -is [string] -and $newTask.task -like $wf.Key -or $wf.Key -is [ScriptBlock] -and (. $wf.Key) ) { $this = $_ = $newTask . $wf.Value break outputted } } $newTask } while ($false) } } }) $buildVariables = @( if ($BuildVariable) { foreach ($bv in $BuildVariable.psobject.properties) { [Ordered]@{name=$bv.Name;value=$bv.value.value} } } ) if ($PassThru) { $out = ([Ordered]@{variables=$BuildVariables;steps=$buildSteps}) if ($inputObject) { Add-Member -InputObject $out -MemberType NoteProperty -Name InputObject -Value $inputObject -Force } $out } else { $newPipeline = New-ADOPipeline -InputObject ([Ordered]@{variables=$BuildVariables;steps=$buildSteps}) if ($inputObject) { $newPipeline | Add-Member NoteProperty InputObject $inputObject -Force } $newPipeline } } $C++ Write-Progress "Converting Builds" "[$c of $t]" -Completed -Id $id } } |