diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7c1de0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ + +BuildOutput/ + +Config/config.json + +Manual_test_script.ps1 + +Test-results.xml + +testscriptdefinitions.ps1 + +snippets.ps1 + +.vscode/settings.json diff --git a/AzDoAPITools.PSDeploy.ps1 b/AzDoAPITools.PSDeploy.ps1 new file mode 100644 index 0000000..5a9e397 --- /dev/null +++ b/AzDoAPITools.PSDeploy.ps1 @@ -0,0 +1,10 @@ +Deploy Module { + By PSGalleryModule { + FromSource AzDoAPITools + To PSGallery + WithOptions @{ + + ApiKey = $ENV:PSGalleryKey + } + } +} \ No newline at end of file diff --git a/AzdoAPITools.build.ps1 b/AzdoAPITools.build.ps1 new file mode 100644 index 0000000..0af2c47 --- /dev/null +++ b/AzdoAPITools.build.ps1 @@ -0,0 +1,200 @@ +#requires -Modules InvokeBuild, Buildhelpers, PSScriptAnalyzer, Pester, PSDeploy, PlatyPS + +$script:ModuleName = 'AzdoAPITools' +$Script:Author = 'Tobi Steenbakkers' +$Script:CompanyName = 'Continuous Data' +$script:Source = Join-Path $BuildRoot Source +$script:Output = Join-Path $BuildRoot BuildOutput +$script:DocPath = Join-Path $BuildRoot "docs\functions" +$script:TestRoot = Join-Path $BuildRoot 'Tests\Unit' +$script:Destination = Join-Path $Output $ModuleName +$script:ModulePath = "$Destination\$ModuleName.psm1" +$script:ManifestPath = "$Destination\$ModuleName.psd1" +$script:Imports = ( 'Private','Public' ) + +task Default Clean, Build, AnalyzeErrors, Pester +task Build ModuleBuild, DocBuild +task Pester {ImportModule}, Test, {uninstall} +Task UpdateDocs {ImportModule}, CreateUpdateDocs, {uninstall} + +task Local Default, UpdateSource, UpdateDocs +task CICD Default, UpdateVersion, {Uninstall} + +Task Clean { + If(Get-Module $moduleName){ + Remove-Module $moduleName + } + If(Test-Path $Output){ + $null = Remove-Item $Output -Recurse -ErrorAction Ignore + } +} + +task AnalyzeErrors { + $scriptAnalyzerParams = @{ + Path = $Destination + Severity = @('Error') + Recurse = $true + Verbose = $false + #ExcludeRule = 'PSUseDeclaredVarsMoreThanAssignments' + } + + $saResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams + + if ($saResults) { + $saResults | Format-Table + throw "One or more PSScriptAnalyzer errors/warnings where found." + } +} + +task Analyze { + $scriptAnalyzerParams = @{ + Path = $Destination + Severity = @('Warning','Error') + Recurse = $true + Verbose = $false + #ExcludeRule = 'PSUseDeclaredVarsMoreThanAssignments' + } + + $saResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams + + if ($saResults) { + $saResults | Format-Table + throw "One or more PSScriptAnalyzer errors/warnings where found." + } +} + +task Test { + + $invokePesterParams = @{ + Passthru = $true + Verbose = $false + EnableExit = $true + OutputFile = 'Test-results.xml' + OutputFormat = 'NunitXML' + Path = $script:TestRoot + } + + $testResults = Invoke-Pester @invokePesterParams + + $numberFails = $testResults.FailedCount + assert($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) +} + +task UpdateVersion { + try + { + #$moduleManifestFile = ((($ManifestPath -split '\\')[-1] -split '\.')[0]+'.psd1') + $manifestContent = Get-Content $ManifestPath -Raw + [version]$version = [regex]::matches($manifestContent,"ModuleVersion\s=\s\'(?(\d+\.)?(\d+\.)?(\*|\d+))") | ForEach-Object {$_.groups['version'].value} + $newVersion = "{0}.{1}.{2}" -f $version.Major, $version.Minor, $ENV:Build_BuildID + + $replacements = @{ + "ModuleVersion = '.*'" = "ModuleVersion = '$newVersion'" + } + + $replacements.GetEnumerator() | ForEach-Object { + $manifestContent = $manifestContent -replace $_.Key,$_.Value + } + + $manifestContent | Set-Content -Path "$ManifestPath" + } + catch + { + Write-Error -Message $_.Exception.Message + $host.SetShouldExit($LastExitCode) + } +} + +Task UpdateSource { + Copy-Item $ManifestPath -Destination "$source\$ModuleName.psd1" +} + +Function ImportModule { + if ( -Not ( Test-Path $ManifestPath ) ) + { + " Modue [$ModuleName] is not built, cannot find [$ManifestPath]" + Write-Error "Could not find module manifest [$ManifestPath]. You may need to build the module first" + } + else + { + if (Get-Module $ModuleName) + { + " Unloading Module [$ModuleName] from previous import" + Remove-Module $ModuleName + } + " Importing Module [$ModuleName] from [$ManifestPath]" + Import-Module $ManifestPath -Force + } +} + +function Uninstall { + 'Unloading Modules...' + Get-Module -Name $ModuleName -ErrorAction 'Ignore' | Remove-Module + + 'Uninstalling Module packages...' + $modules = Get-Module $ModuleName -ErrorAction 'Ignore' -ListAvailable + foreach ($module in $modules) + { + Uninstall-Module -Name $module.Name -RequiredVersion $module.Version -ErrorAction 'Ignore' + } + + 'Cleaning up manually installed Modules...' + $path = $env:PSModulePath.Split(';').Where( { + $_ -like 'C:\Users\*' + }, 'First', 1) + + $path = Join-Path -Path $path -ChildPath $ModuleName + if ($path -and (Test-Path -Path $path)) + { + 'Removing files... (This may fail if any DLLs are in use.)' + Get-ChildItem -Path $path -File -Recurse | + Remove-Item -Force | ForEach-Object 'FullName' + + 'Removing folders... (This may fail if any DLLs are in use.)' + Remove-Item $path -Recurse -Force + } +} + +task Publish { + Invoke-PSDeploy -Path $PSScriptRoot -Force +} + +Task DocBuild { + New-ExternalHelp $DocPath -OutputPath "$destination\EN-US" +} + +Task CreateUpdateDocs { + + If(-not (Test-Path $DocPath)){ + "Creating Documents path: $DocPath" + $null = New-Item -Type Directory -Path $DocPath -ErrorAction Ignore + } + + "Creating new markdown files if any" + New-MarkdownHelp -Module $modulename -OutputFolder $docpath -ErrorAction SilentlyContinue + "Updating existing markdown files" + Update-MarkdownHelp $docpath + +} + +task ModuleBuild { + $pubFiles = Get-ChildItem "$Source\public" -Filter *.ps1 -File + $privFiles = Get-ChildItem "$Source\private" -Filter *.ps1 -File + If(-not(Test-Path $Destination)){ + New-Item $destination -ItemType Directory + } + ForEach($file in ($pubFiles + $privFiles)) { + Get-Content $file.FullName | Out-File "$destination\$moduleName.psm1" -Append -Encoding utf8 + } + Copy-Item "$Source\$moduleName.psd1" -Destination $destination + + $moduleManifestData = @{ + Author = $author + Copyright = "(c) $((get-date).Year) $companyname. All rights reserved." + Path = "$destination\$moduleName.psd1" + FunctionsToExport = $pubFiles.BaseName + RootModule = "$moduleName.psm1" + ProjectUri = "https://github.com/Continuous-Data/$modulename" + } + Update-ModuleManifest @moduleManifestData +} \ No newline at end of file diff --git a/Config/config-example.json b/Config/config-example.json new file mode 100644 index 0000000..34beb82 --- /dev/null +++ b/Config/config-example.json @@ -0,0 +1,9 @@ +{ + "profiles":[ + { + "profilename": "", + "Organization": "", + "pat": "" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 501bfb5..71bba72 100644 --- a/README.md +++ b/README.md @@ -1 +1,64 @@ -AzDoAPITools +# AzDoAPITools + +[![Build Status](https://dev.azure.com/ContinuousData/cdtestproject/_apis/build/status/tsteenbakkers.AzDoAPITools?branchName=master)](https://dev.azure.com/ContinuousData/cdtestproject/_build/latest?definitionId=4&branchName=master) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Continuous-Data/AzDoAPITools/blob/master/LICENSE) +[![Documentation - AzDoAPITools](https://img.shields.io/badge/Documentation-AzDoAPITools-blue.svg)](https://github.com/Continuous-Data/AzDoAPITools/blob/master/docs/readme.md) +[![PowerShell Gallery - AzDoAPITools](https://img.shields.io/badge/PowerShell%20Gallery-AzDoAPITools-blue.svg)](https://www.powershellgallery.com/packages/AzDoAPITools) +[![Minimum Supported PowerShell Version](https://img.shields.io/badge/PowerShell-5.1-blue.svg)](https://github.com/PowerShell/PowerShell) + +## Introduction + +AzDoAPITools is a project which was born when doing a migration from classical pipelines to YAML pipelines for a customer. Which is the current function of the published module. The module will convert Task Groups and classical build pipelines to usable all in one YAML pipelines / step templates. + +In the future you can expect other automations which i have done for customers such as automatic branching / mass policy application etc. to be bundled in this module. + +## Requirements + +- Powershell 5.1 + +## Module Dependancies + +[Powershell-YAML](https://www.powershellgallery.com/packages/powershell-yaml) is required if you want to create \*.yml files with this Module. The module is capable of delivering a PSObject with all YAML components inside found in your Task Group / Definition if you wish to use a different convert to YAML tool. + +## installation + +Install this module from the [Powershell Gallery](https://www.powershellgallery.com/packages/AzdoAPITools) or by performing `Install-Module -Name AzdoAPITools` + +## First Time use + +Run Set-AzDOAPIToolsConfig to create a config.json file which is stored inside the %APPData%\AzDoAPITools folder. Don't sweat if you forget this step. The module will prompt you if it does not find a configfile. + +## Documentation + +You can find generic documentation [here](/docs/README.md) or check specific functionality documentation below. + +## Functionality + +- [Convert Classical (GUI) Pipelines to YAML Pipelines](/docs/classic-to-yaml-conversion.md) +- [Convert Task Groups to YAML Templates](/docs/classic-to-yaml-conversion.md) +- Retrieve a list of names of Build / Release Definitions & Task Groups +- Retrieve details of Build / Release Definitions & Task Groups based of (a list of) names +- Filter Task Groups API to return highest / draft / preview of a Task Group + +## How to Build local + +- Download Source code / Clone repo +- Run Invoke-Build from the modules root directory + - You will need the following modules in order to use Invoke-Build: + - InvokeBuild + - Buildhelpers + - PSScriptAnalyzer + - Pester + - PSDeploy + - PlatyPS +- Load the module from the BuildOutput folder + +## Pester Tests + +Currently only placeholders have been made for each function. Tests were done on my personal Azure DevOps Instance and verified by using the actual converted pipelines to see how they work. + +I need to gain more knowledge on Pester Tests and especially on how to mock the REST API calls. Tests will be added when I have this knowledged and time to create tests. + +## License + +This project is licensed under the [MIT License](https://github.com/tsteenbakkers/AzDoAPITools/blob/master/LICENSE.md) diff --git a/Source/AzdoAPITools.psd1 b/Source/AzdoAPITools.psd1 new file mode 100644 index 0000000..07289f8 --- /dev/null +++ b/Source/AzdoAPITools.psd1 @@ -0,0 +1,135 @@ +# +# Module manifest for module 'PSGet_AzdoAPITools' +# +# Generated by: Tobi Steenbakkers +# +# Generated on: 15/09/2020 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'AzdoAPITools.psm1' + +# Version number of this module. +ModuleVersion = '1.0.0' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '679e7dd5-6298-48bd-90cb-3c426c061850' + +# Author of this module +Author = 'Tobi Steenbakkers' + +# Company or vendor of this module +CompanyName = 'Continuous Data' + +# Copyright statement for this module +Copyright = '(c) 2020 Continuous Data. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'Set of Powershell functions to converting classical to YAML pipelines / Task Group to YAML templates on Azure DevOps' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '5.1' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# CLRVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @('powershell-yaml') + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Convert-AzDoAPIToolsYamlObjectToYAMLFile', + 'Get-AzDoAPIToolsDefinitionAsYAMLPrepped', + 'Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped', + 'Get-AzDoAPIToolsDefinitionsTaskGroupsByID', + 'Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList', + 'Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped', + 'Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped', + 'Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped', + 'Get-AzDoAPIToolsDefinitonsTaskGroupNames', + 'Get-AzDoAPIToolsTaskGroupAsYAMLPrepped', 'Set-AzdoAPIToolsConfig' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +# VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'Powershell','AzurePipelines','YAML','VSTS','AzureDevops','Repository','BuildDefinition','ReleaseDefinition','TaskGroup' + + # A URL to the license for this module. + LicenseUri = 'https://github.com/Continuous-Data/AzDoAPITools/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/Continuous-Data/AzdoAPITools' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # External dependent modules of this module + # ExternalModuleDependencies = '' + + } # End of PSData hashtable + + } # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/Source/Private/Convert-TGInputsToYamlTemplateInputs.ps1 b/Source/Private/Convert-TGInputsToYamlTemplateInputs.ps1 new file mode 100644 index 0000000..12a6edb --- /dev/null +++ b/Source/Private/Convert-TGInputsToYamlTemplateInputs.ps1 @@ -0,0 +1,64 @@ +function Convert-TGInputsToYamlTemplateInputs { + param ( + # input array + [Parameter()] + [Array] + $InputArray, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Projectname + [Parameter(mandatory=$false)] + [string] + $profilename, + # future switch for expanding nested taskgroups + [Parameter(mandatory=$false)] + [switch] + $ExpandNestedTaskGroups + ) + + foreach ($taskgroup in $InputArray) { + + $InputsArray = @() + + $taskgroup = $taskgroup.value + + if($taskgroup.inputs.count -ge 1){ + $taskgroup.inputs | ForEach-Object { + $yamlparam = [ordered]@{} + $yamlparam.Add('name', $_.name) + $yamlparam.Add('type', 'string') + $InputsArray += $yamlparam + } + + + }else{ + Write-Verbose "No inputs found for task group. skipping yaml generation" + } + + if($ExpandNestedTaskGroups.IsPresent){ + $taskgrouptasks = $taskgroup.tasks | Where-Object {$_.task.definitionType -eq 'metaTask'} + $taskgrouptaskscount = $taskgrouptasks | Measure-Object | Select-Object -ExpandProperty count + if($taskgrouptaskscount -ge 1){ + foreach ($nestedtaskgrouptask in $taskgrouptasks) { + $nestedtaskgroupid = $nestedtaskgrouptask.task.id + $nestedtaskgroupversion = $nestedtaskgrouptask.task.versionspec.split(".`*")[0] -as [int] + + $nestedtaskgroup = Get-AzDoAPIToolsDefinitionsTaskGroupsByID -apitype 'Taskgroup' -projectname $Projectname -profilename $profilename -id $nestedtaskgroupid -TGVersion $nestedtaskgroupversion + $nestedtaskgroupinputs = Convert-TGInputsToYamlTemplateInputs -profilename $profilename -Projectname $Projectname -InputArray $nestedtaskgroup -ExpandNestedTaskGroups + + foreach ($nestedinput in $nestedtaskgroupinputs) { + if ($nestedinput.name -notin $InputsArray.name) { + $InputsArray += $nestedinput + } + } + } + } + } + + return $InputsArray + + } +} + \ No newline at end of file diff --git a/Source/Private/Convert-TaskIDToYAMLTaskIdentifier.ps1 b/Source/Private/Convert-TaskIDToYAMLTaskIdentifier.ps1 new file mode 100644 index 0000000..cb6e1df --- /dev/null +++ b/Source/Private/Convert-TaskIDToYAMLTaskIdentifier.ps1 @@ -0,0 +1,34 @@ +function Convert-TaskIDToYAMLTaskIdentifier { + param ( + # TaskID + [Parameter(Mandatory=$true)] + [guid] + $InputTaskID, + [Parameter(mandatory=$false)] + [string] + $profilename, + # TaskVersion + [Parameter(Mandatory=$true)] + [int] + $InputTaskVersion + ) + + $task = Use-AzDoAPI -method 'Get' -area 'distributedtask' -resource 'tasks' -profilename $profilename -version '5.1' -id $InputTaskID + + $task = $task.value | Where-Object {$_.version.major -eq $InputTaskVersion} + + + if($task -and $task.count -le 1){ + $taskcontributionIdentifier = $task.contributionIdentifier + + $taskname = $task.name + if ($taskcontributionIdentifier) { + $yamltaskid = "$taskcontributionIdentifier.$taskname@$InputTaskVersion" + }else{ + $yamltaskid = "$taskname@$InputTaskVersion" + } + } + + Return $yamltaskid + +} \ No newline at end of file diff --git a/Source/Private/Convert-TaskStepsToYAMLSteps.ps1 b/Source/Private/Convert-TaskStepsToYAMLSteps.ps1 new file mode 100644 index 0000000..170d822 --- /dev/null +++ b/Source/Private/Convert-TaskStepsToYAMLSteps.ps1 @@ -0,0 +1,113 @@ +function Convert-TaskStepsToYAMLSteps { + param ( + # input array + [Parameter()] + [Array] + $InputArray, + # inputtype + [Parameter(mandatory=$true)] + [string] + $inputtype, + # inputtype + [Parameter(mandatory=$false)] + [string] + $parentinputtype, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Projectname + [Parameter(mandatory=$false)] + [string] + $profilename, + # future switch for expanding nested taskgroups + [Parameter(mandatory=$false)] + [switch] + $ExpandNestedTaskGroups + ) + + + foreach ($input in $InputArray) { + + if ($inputtype -eq 'BuildDefinition') { + $steps = $input.steps + }elseif ($inputtype -eq 'TaskGroup') { + $steps = $input.value.tasks + } + + if($steps.count -ge 1){ + $taskscount = $input.count + $convertedsteps = @() + $convertedcount = 0 + Write-verbose "Found $taskscount tasks" + foreach ($step in $steps) { + + $yamlstep = [ordered]@{} + + $stepid = $step.task.id + $stepversion = $step.task.versionspec.split(".`*")[0] -as [int] + + if($step.task.definitionType -eq 'task'){ + #####converting TaskID to YAML taskidentifier + + Write-verbose "version $stepversion found in $inputtype for task $stepid" + + $yamltaskid = Convert-TaskIDToYAMLTaskIdentifier -InputTaskID $stepid -InputTaskVersion $stepversion -profilename $profilename + + $yamlstep.add('task',$yamltaskid) + + ### Adding Other Task Properties to Step Object + $taskproperties = Get-TaskProperties -InputTaskObject $step -propertiestoskip @('environment','inputs','task','AlwaysRun','refName') + + $taskproperties.PSObject.Properties | ForEach-Object{ + $yamlstep.add($_.name,$_.value) + } + + #### Adding Inputs to the task + $YamlInputs = Get-TaskInputs -InputTaskObject $step -profilename $profilename -inputType $inputtype -parentinputtype $parentinputtype + + if ($YamlInputs.count -ge 1) { + + $yamlstep.add('inputs', $YamlInputs) + + } + + #### Adding Step to Steps + $convertedsteps += $yamlstep + + }elseif($step.task.definitionType -eq 'metaTask' -and $step.enabled -eq 'true'){ + + $TGtemplate = Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $stepid -TGVersion $stepversion -ApiType 'TaskGroup' -Projectname $projectname -profilename $profilename + + if ($ExpandNestedTaskGroups.IsPresent) { + $nestedtaskgrouptasks = Convert-TaskStepsToYAMLSteps -profilename $profilename -Projectname $projectname -InputArray $TGTemplate -ExpandNestedTaskGroups -inputType $tgtemplate.type -parentinputtype $inputtype + $convertedsteps += $nestedtaskgrouptasks + }else { + + $TGTemplateName = "$($TGTemplate.name).yml" + $yamlstep.add('template',$TGTemplateName) + + $TGTemplateparameters = $YamlInputs = Get-TaskInputs -InputTaskObject $step -profilename $profilename -inputType $inputtype + + if ($TGTemplateparameters.count -ge 1) { + + $yamlstep.add('parameters', $TGTemplateparameters) + + } + + $convertedsteps += $yamlstep + } + + } + + Write-Verbose "added taskids for $convertedcount steps" + } + + }else{ + Write-Verbose 'No Tasks found. skipping steps.' + } + + return $convertedsteps + } + +} \ No newline at end of file diff --git a/Source/Private/Get-AzDoAPIToolsAgentPool.ps1 b/Source/Private/Get-AzDoAPIToolsAgentPool.ps1 new file mode 100644 index 0000000..3a85d48 --- /dev/null +++ b/Source/Private/Get-AzDoAPIToolsAgentPool.ps1 @@ -0,0 +1,31 @@ +function Get-AzDoAPIToolsAgentPool { + [CmdletBinding()] + param ( + # PoolURL + [Parameter(Mandatory = $true)] + [String] + $PoolURL, + # agentidentifier + [Parameter(Mandatory = $false)] + [String] + $agentidentifier + ) + + + process { + $returnedpool = @{} + $pool = Use-AzDoAPI -url $PoolURL -method 'get' + + if ($pool.pool.isHosted -eq 'true') { + $returnedpool.Add('vmImage',$agentidentifier) + }else{ + $returnedpool.add('name',$pool.name) + } + } + + end { + if ($returnedpool.Count -ge 1) { + return $returnedpool + } + } +} \ No newline at end of file diff --git a/Source/Private/Get-AzdoAPIToolsConfig.ps1 b/Source/Private/Get-AzdoAPIToolsConfig.ps1 new file mode 100644 index 0000000..86ad976 --- /dev/null +++ b/Source/Private/Get-AzdoAPIToolsConfig.ps1 @@ -0,0 +1,34 @@ +function Get-AzdoAPIToolsConfig { + [CmdletBinding()] + param ( + + [Parameter(Mandatory = $false)] $configfilepath + + ) + + if(!$configfilepath){ + Write-verbose "No `$configfilepath parameter supplier. setting to default path." + $configfilepath = "{0}\AzDoAPITools\config.json" -f $env:appdata + # $configfilepath = "$((Get-Item $PSScriptRoot).Parent.FullName)\config\config.json" + } + + if (Test-Path $configfilepath) { + $configJSON = Import-JSON -JSONFile $configfilepath + + return $configJSON + }else{ + + Write-Host "No config file found at $configfilepath" + + if (Get-Confirmation "Would you like to create a new config file in $configfilepath ?") { + Set-AzDoAPIToolsConfig -configfilepath $configfilepath + + $configJSON = Import-JSON -JSONFile $configfilepath + return $configJSON + + }else{ + Write-Error "no configfile found at $configfilepath. please run Set-AzDoAPIToolsConfig to create a profile" + } + + } +} \ No newline at end of file diff --git a/Source/Private/Get-AzdoAPIToolsConfigDetails.ps1 b/Source/Private/Get-AzdoAPIToolsConfigDetails.ps1 new file mode 100644 index 0000000..3055b4b --- /dev/null +++ b/Source/Private/Get-AzdoAPIToolsConfigDetails.ps1 @@ -0,0 +1,42 @@ +function Get-AzdoAPIToolsConfigDetails { + param ( + # New Switch + [Parameter(mandatory=$false)] + [switch] + $new + ) + + $profilename = Read-Host -prompt "Please provide an name / alias for the organization you want to add" + $organization = Read-Host -prompt "Please provide the organization name for the Azure DevOps instance you want to connect to (https://dev.azure.com/)" + $pat = Read-Host -prompt "Please provide a valid PAT string you want to add for $organization" -AsSecureString + $pat = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pat)) + $encodedpat = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("basic:$pat")) + + $JSONDetailConstruct = @" +{ + "profilename": "$profilename", + "Organization": "$organization", + "pat": "$encodedpat" + } +"@ + $JSONFULL = @" +{ + "profiles":[ + $JSONDetailConstruct + ] +} +"@ + + if ($profilename -and $organization -and $pat) { + if ($new.IsPresent) { + $return = $JSONFULL + }else{ + $return = $JSONDetailConstruct + } + }else{ + Write-error "one or more questions were not answered. please retry" + } + + Return $return + +} \ No newline at end of file diff --git a/Source/Private/Get-AzdoAPIToolsProfile.ps1 b/Source/Private/Get-AzdoAPIToolsProfile.ps1 new file mode 100644 index 0000000..8383514 --- /dev/null +++ b/Source/Private/Get-AzdoAPIToolsProfile.ps1 @@ -0,0 +1,49 @@ +function Get-AzdoAPIToolsProfile { + [cmdletbinding()] + param ( + + [Parameter(Mandatory = $false)] + [psobject] + $configfile, + # profilename + [Parameter(mandatory = $false)] + [string] + $profilename + + ) + process{ + + if(!$configfile){ + Write-verbose "No `$configfile parameter supplied. attempting to grab it from config" + + $configfile = Get-AzdoAPIToolsConfig + } + + if ($configfile.profiles) { + if ($profilename) { + Write-verbose "Searching profiles for $profilename" + $configprofile = $configfile.profiles | Where-Object {$_.profilename -eq $profilename} + + + }else{ + Write-Verbose "Returning first profile found" + $configprofile = $configfile.profiles | Select-Object -First 1 + } + + if ($configprofile) { + return $configprofile + }else{ + Write-Error "Unable to find $profilename in configfile provided" + throw; + } + + }else{ + Write-Error 'Unable to find profiles section in configfile' + throw; + } + + } + + + +} \ No newline at end of file diff --git a/Source/Private/Get-AzdoAPIURL.ps1 b/Source/Private/Get-AzdoAPIURL.ps1 new file mode 100644 index 0000000..2d69537 --- /dev/null +++ b/Source/Private/Get-AzdoAPIURL.ps1 @@ -0,0 +1,52 @@ +function Get-AzdoAPIURL { + [CmdletBinding()] + param( + + [string]$profilename, + [string]$area, + [string]$resource, + [string]$id, + [string]$subdomain, + [string]$projectname, + [string]$version + ) + + process { + + $profile = Get-AzdoAPIToolsProfile -profilename $profilename + $subdomain = $profile.Organization + + $sb = New-Object System.Text.StringBuilder + + $sb.Append('Https://') | Out-Null + if($area -eq 'Release'){ + $sb.Append('vsrm.') | Out-Null + } + $sb.Append('dev.azure.com') | Out-Null + $sb.Append("/$subdomain") | Out-Null + if ($projectname) { + $sb.Append("/$projectname") | Out-Null + } + $sb.Append("/_apis") | Out-Null + + if ($area) { + $sb.Append("/$area") | Out-Null + } + + if ($resource) { + $sb.Append("/$resource") | Out-Null + } + + if ($id) { + $sb.Append("/$id") | Out-Null + } + + if ($version) { + $sb.Append("?api-version=$version") | Out-Null + } + + $url = $sb.ToString() + + return $url + } + } \ No newline at end of file diff --git a/Source/Private/Get-Confirmation.ps1 b/Source/Private/Get-Confirmation.ps1 new file mode 100644 index 0000000..d180e62 --- /dev/null +++ b/Source/Private/Get-Confirmation.ps1 @@ -0,0 +1,9 @@ +function Get-Confirmation ( [string] $message ) +{ + do + { + $input = Read-Host -prompt "$message (Y/N)? " + if ($input -Like 'Y') { return $true } + elseif ($input -Like 'N') { return $false } + } while (true); +} \ No newline at end of file diff --git a/Source/Private/Get-CronFromSchedule.ps1 b/Source/Private/Get-CronFromSchedule.ps1 new file mode 100644 index 0000000..b1c7cfc --- /dev/null +++ b/Source/Private/Get-CronFromSchedule.ps1 @@ -0,0 +1,69 @@ +function Get-CronFromSchedule { + [CmdletBinding()] + param ( + # InputSchedule + [Parameter(mandatory=$true)] + [array] + $InputSchedules + ) + + begin { + $weekdays =[ordered]@{ + 'Sunday' = 64 + 'Monday' = 1 + 'Tuesday' = 2 + 'Wednesday' = 4 + 'Thursday' = 8 + 'Friday' = 16 + 'Saturday' = 32 + } + + $schedulehour = $null + $scheduleminute = $null + $scheduledaysofweek = @() + } + + process { + foreach ($schedule in $InputSchedules) { + if ($schedule.daysToBuild -eq 'all') { + $bitvalue = ($weekdays.Values | Measure-Object -Sum).Sum + }elseif ($schedule.daysToBuild -in $weekdays.Keys) { + $bitvalue = $weekdays[$schedule.daysToBuild] + }else{ + $bitvalue = $schedule.daysToBuild + } + + if ($bitvalue -gt 0) { + $weekdays.getenumerator() | ForEach-Object{ + if($_.value -band $bitvalue){ + + $targetdayofweekint = $($weekdays.keys).indexOf($_.key) + $currentdayofweekint = (Get-date).DayOfWeek.value__ + + $datetimetoconvert = Get-Date -Date (Get-Date -Hour $schedule.startHours -Minute $schedule.startMinutes -Second 00).AddDays($targetdayofweekint - $currentdayofweekint).ToString('yyyy-MM-dd HH:mm:ss') + + $SourceTimezone = [System.TimeZoneInfo]::FindSystemTimeZoneById($schedule.timezoneid) + + if($SourceTimezone.SupportsDaylightSavingTime -eq $true){ + $datetimetoconvert = $datetimetoconvert.AddHours(1) + } + + $utcdatetimeobject = [System.TimeZoneInfo]::ConvertTimeToUtc($datetimetoconvert, $SourceTimezone) + + + $schedulehour = $utcdatetimeobject.Hour + $scheduleminute = $utcdatetimeobject.Minute + $scheduledaysofweek += $utcdatetimeobject.DayOfWeek.value__ + } + } + + $CRON = "$scheduleminute $schedulehour * * $($scheduledaysofweek -join ',')" + return $CRON + } + } + } + + end{ + + } +} \ No newline at end of file diff --git a/Source/Private/Get-DefinitionInputIncludeExclude.ps1 b/Source/Private/Get-DefinitionInputIncludeExclude.ps1 new file mode 100644 index 0000000..000a9e3 --- /dev/null +++ b/Source/Private/Get-DefinitionInputIncludeExclude.ps1 @@ -0,0 +1,49 @@ +function Get-DefinitionInputIncludeExclude { + [CmdletBinding()] + param ( + # inputs + [Parameter(mandatory=$true)] + [array] + $inputs + ) + begin{ + $included = @() + $excluded = @() + $return = @() + $regex = '^([\+\-])[\\\/]?(.+)' + } + + process { + + foreach ($input in $inputs) { + if ($input.startswith("+")){ + $input = $input -replace $regex, '$2' + $included += $input + }elseif ($input.startswith("-")) { + $input = $input -replace $regex, '$2' + $excluded += $input + } + } + + } + + end{ + + if ($included.count -ge 1) { + $includedinput = @{ + 'include' = $included + } + $return += $includedinput + } + + if ($excluded.count -ge 1) { + $excludedinput = @{ + 'exclude' = $excluded + } + $return += $excludedinput + } + + return $return + } + +} \ No newline at end of file diff --git a/Source/Private/Get-HighestTaskGroupVersion.ps1 b/Source/Private/Get-HighestTaskGroupVersion.ps1 new file mode 100644 index 0000000..8f98464 --- /dev/null +++ b/Source/Private/Get-HighestTaskGroupVersion.ps1 @@ -0,0 +1,29 @@ +function Get-HighestTaskGroupVersion { + param ( + $TaskGroupObject, + $Taskgroupid, + # also return previews + [Parameter(mandatory=$false)] + [switch] + $includeTGpreview + ) + + $versionnumber = 0 + + if (-not $includeTGpreview.IsPresent) { + $TaskGroupObject = $TaskGroupObject | Where-Object {!$_.Preview} + } + + $TaskGroupObject | ForEach-Object { + + #figure out a way to easily include preview + if ($_.id -eq $Taskgroupid){ + if($_.version.major -gt $versionnumber){ + $versionnumber = $_.version.major + } + } + + } + # Write-Output "Highest version for [$($Taskgroupid)] was [$($versionnumber)]" + return $versionnumber + } \ No newline at end of file diff --git a/Source/Private/Get-TaskInputs.ps1 b/Source/Private/Get-TaskInputs.ps1 new file mode 100644 index 0000000..428e757 --- /dev/null +++ b/Source/Private/Get-TaskInputs.ps1 @@ -0,0 +1,75 @@ +function Get-TaskInputs { + param ( + # InputTaskObject + [Parameter(Mandatory=$true)] + [PSObject] + $InputTaskObject, + # inputtype + [Parameter(mandatory=$true)] + [string] + $inputtype, + # parentinputtype + [Parameter(mandatory=$false)] + [string] + $parentinputtype, + # profilename + [Parameter(mandatory=$false)] + [string] + $profilename + ) + # $ReturnedYAMLInputs = [PSCustomObject]@{} + $TaskInputProperties = @{} + if(!$InputTaskObject.inputs){ + + }else{ + if($InputTaskObject.task.definitionType -eq 'task'){ + $InputTaskid = $InputTaskObject.task.id + $InputTaskVersion = $InputTaskObject.task.versionspec.split(".`*")[0] -as [int] + + $task = Use-AzDoAPI -method 'Get' -area 'distributedtask' -resource 'tasks' -profilename $profilename -version '5.1' -id $InputTaskID + + + $task = $task.value | Where-Object {$_.version.major -eq $InputTaskVersion} + $taskdefaultinputs = $task.inputs + } + + $InputTaskObject.Inputs.PSObject.Properties | ForEach-Object{ + $regexpatternSingle = '(?\$\()(?(?\w+))(?\))' + $regexpatternDouble = '(?\$\()(?(?(?\w+)\.+(?\w+)))(?\))' + + $inputname = $_.name + $inputvalue = $_.value + + if($InputTaskObject.task.definitionType -eq 'task'){ + $defaultinput = $taskdefaultinputs | Where-Object {$_.name -eq $inputname} + $defaultinputvalue = $defaultinput.defaultValue + } + + if(( ($defaultinputvalue -ne $inputvalue) -or ($InputTaskObject.task.definitionType -ne 'task') ) ){ + if ($inputtype -eq 'TaskGroup' -and $parentinputtype -ne 'BuildDefinition') { + switch -regex ($inputvalue) { + $regexpatternSingle { + $inputvalue = $inputvalue -replace $regexpatternSingle, '${{parameters.$2}}' + + } + $regexpatternDouble { + $predefinedvariableprefixes = @('Build', 'Agent', 'System', 'Pipeline', 'Environment', 'Release') + if ($matches.DoubleFirstPart -notin $predefinedvariableprefixes) { + $inputvalue = $inputvalue -replace $regexpatternDouble, '${{parameters.$2}}' + + } + } + Default {} + } + } + $TaskInputProperties.Add($inputname,$inputvalue) + }else{ + write-verbose "Skipping input since it matches defaultvalue" + } + + } + + # $ReturnedYAMLInputs | Add-Member -NotePropertyName 'inputs' -NotePropertyValue $TaskInputProperties + } + return $TaskInputProperties +} \ No newline at end of file diff --git a/Source/Private/Get-TaskProperties.ps1 b/Source/Private/Get-TaskProperties.ps1 new file mode 100644 index 0000000..29099ac --- /dev/null +++ b/Source/Private/Get-TaskProperties.ps1 @@ -0,0 +1,48 @@ +function Get-TaskProperties { + param ( + # InputTaskObject + [Parameter(Mandatory=$true)] + [PSObject] + $InputTaskObject, + # properties to skip + [Parameter(Mandatory=$false)] + [array] + $propertiestoskip + ) + $FilteredTaskProperties = [PSCustomObject]@{} + # $propertiestoskip = ('environment','inputs','task','AlwaysRun') + $InputTaskObject.PSObject.Properties | ForEach-Object{ + $propertyname = $_.Name + $propertyvalue = $_.value + ### processing skipped properties + if($propertyname -notin $propertiestoskip){ + ### Skipping Default values to keep output clean + switch ($propertyname) { + continueOnError { + if($propertyvalue -ne $false){ + $FilteredTaskProperties | Add-Member -NotePropertyName $propertyname -NotePropertyValue $propertyvalue + } + } + condition { + if($propertyvalue -ne 'succeeded()'){ + $FilteredTaskProperties | Add-Member -NotePropertyName $propertyname -NotePropertyValue $propertyvalue + } + } + enabled { + if($propertyvalue -ne $true){ + $FilteredTaskProperties | Add-Member -NotePropertyName $propertyname -NotePropertyValue $propertyvalue + } + } + timeoutInMinutes { + if($propertyvalue -ne 0){ + $FilteredTaskProperties | Add-Member -NotePropertyName $propertyname -NotePropertyValue $propertyvalue + } + } + Default { + $FilteredTaskProperties | Add-Member -NotePropertyName $propertyname -NotePropertyValue $propertyvalue + } + } + } + } + return $FilteredTaskProperties +} diff --git a/Source/Private/Import-JSON.ps1 b/Source/Private/Import-JSON.ps1 new file mode 100644 index 0000000..93ef5ee --- /dev/null +++ b/Source/Private/Import-JSON.ps1 @@ -0,0 +1,20 @@ +function Import-JSON { + param ( + # Parameter help description + [Parameter(Mandatory = $true)] + [string] + $JSONFile + ) + try { + $JSONObject = Get-Content $JSONFile -Raw | ConvertFrom-Json + + return $JSONObject + } + catch { + + Write-Error "Invalid JSON File or unable to import" + $Error + exit 1 + + } +} \ No newline at end of file diff --git a/Source/Private/Use-AzDoAPI.ps1 b/Source/Private/Use-AzDoAPI.ps1 new file mode 100644 index 0000000..c7b9616 --- /dev/null +++ b/Source/Private/Use-AzDoAPI.ps1 @@ -0,0 +1,66 @@ +function Use-AzDoAPI { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline = $true)] + # [Object]$configprofile, + [string]$profilename, + [string]$area, + [string]$resource, + [string]$id, + [ValidateSet('Get', 'Post', 'Patch', 'Delete', 'Options', 'Put', 'Default', 'Head', 'Merge', 'Trace')] + [string]$method, + [Parameter(ValueFromPipeline = $true)] + [object]$body, + [string]$Url, + [string]$ContentType, + [string]$subdomain, + [string]$projectname, + [string]$version, + [string]$pat + + ) + process { + + # If the caller did not provide a Url build it. + + if (-not $Url) { + $buildUriParams = @{ } + $PSBoundParameters; + $extra = 'method', 'body','ContentType','config','profile','pat' + foreach ($x in $extra) { $buildUriParams.Remove($x) | Out-Null } + $Url = Get-AzdoAPIURL @buildUriParams + } + + $configprofile = Get-AzdoAPIToolsProfile -profilename $profilename + $pat = $configprofile.pat + + if ($body) { + Write-Verbose "Body $body" + } + + $params = $PSBoundParameters + $params.Add('Uri', $Url) + + $params.Add('TimeoutSec', 30) + + $params.Add('Headers', @{Authorization = "Basic $pat" }) + + $extra = 'profile', 'profilename', 'Area', 'Id', 'Url', 'Resource','config','profile','subdomain','projectname','version','pat' + foreach ($e in $extra) { $params.Remove($e) | Out-Null } + + try { + $resp = Invoke-RestMethod @params + + if ($resp) { + Write-Verbose "return type: $($resp.gettype())" + Write-Verbose $resp + } + + return $resp + } + catch { + Write-Error "$_" + + throw + } + } + } \ No newline at end of file diff --git a/Source/Public/Convert-AzDoAPIToolsYamlObjectToYAMLFile.ps1 b/Source/Public/Convert-AzDoAPIToolsYamlObjectToYAMLFile.ps1 new file mode 100644 index 0000000..45d2eb0 --- /dev/null +++ b/Source/Public/Convert-AzDoAPIToolsYamlObjectToYAMLFile.ps1 @@ -0,0 +1,30 @@ +function Convert-AzDoAPIToolsYamlObjectToYAMLFile { + [CmdletBinding()] + param ( + # Parameter help description + [Parameter(Mandatory=$true)] + [Object] + $InputObject, + # Parameter help description + [Parameter(Mandatory=$true)] + [String] + $outputpath, + # Parameter help description + [Parameter(Mandatory=$true)] + [String] + $Outputfilename + + ) + + process { + + if (!(Test-Path $outputpath)) { + if(Get-Confirmation "$outputpath not detected. Do you want to create it"){ + New-Item -path $OutputPath -ItemType 'Directory' | Out-Null + } + } + + $InputObject | ConvertTo-Yaml | Out-File "$outputpath\$Outputfilename" -encoding utf8 + } + +} \ No newline at end of file diff --git a/Source/Public/Get-AzDOAPIToolsDefinitionsTaskGroupsByID.ps1 b/Source/Public/Get-AzDOAPIToolsDefinitionsTaskGroupsByID.ps1 new file mode 100644 index 0000000..b8f440b --- /dev/null +++ b/Source/Public/Get-AzDOAPIToolsDefinitionsTaskGroupsByID.ps1 @@ -0,0 +1,139 @@ +function Get-AzDoAPIToolsDefinitionsTaskGroupsByID { + param ( + # Nameslist + [Parameter(mandatory=$true)] + [String] + $ID, + # Nameslist + [Parameter(mandatory=$false)] + [int] + $TGVersion, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Profilename + [Parameter(mandatory=$false)] + [string] + $profilename, + # type + [Parameter(mandatory=$true)] + [ValidateSet('BuildDefinition','ReleaseDefinition','TaskGroup')] + [string] + $ApiType, + # also return drafts + [Parameter(mandatory=$false)] + [switch] + $includeTGdrafts, + # also return previews + [Parameter(mandatory=$false)] + [switch] + $includeTGpreview, + # Return all versions + [Parameter(mandatory=$false)] + [switch] + $AllTGVersions + ) + + + + $return = @() + + + switch ($apitype) { + BuildDefinition { + + + $definitions = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'build' -resource 'definitions' -profilename $profilename -version '5.1' -id $id + + $definitions | ForEach-Object{ + $hash = @{} + $hash.Add('id', $($_.id)) + $hash.Add('name', $($_.name)) + $hash.Add('url', $($_.url)) + $hash.Add('type', $apitype) + $hash.add('value',$_) + + $return += [PSCustomObject]$hash + } + } + ReleaseDefinition { + + $definitions = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'release' -resource 'definitions' -profilename $profilename -version '5.1-preview' -id $id + + $filtereddefinitions = $definitions.value | Where-Object {$_.name -in $NamesList} + + $filtereddefinitions | ForEach-Object{ + $hash = @{} + $hash.Add('id', $($_.id)) + $hash.Add('name', $($_.name)) + $hash.Add('url', $($_.url)) + $hash.Add('type', $apitype) + $hash.Add('value',$_) + + $return += [PSCustomObject]$hash + } + } + TaskGroup { + $taskgroups = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'distributedtask' -resource 'taskgroups' -profilename $profilename -version '5.1-preview' -id $id + + $filteredtaskgroups = $taskgroups.value + $filteredproperties = $filteredtaskgroups + $filteredproperties | ForEach-Object { + $hash = @{} + $hash.Add('id', $($_.id)) + $hash.Add('name', $($_.name)) + $hash.Add('url', (Get-AzdoAPIURL -projectname $Projectname -area 'distributedtask' -resource 'taskgroups' -profilename $profilename -version '5.1-preview' -id $id)) + $hash.Add('type', $apitype) + $hash.Add('version', $($_.version.major)) + if($_.version.istest){ + $hash.Add('Draft','True') + }else{ + $hash.Add('Draft','False') + } + if ($_.preview) { + $hash.Add('Preview','True') + }else{ + $hash.Add('Preview','False') + } + if (!$TGVersion) { + $hash.Add('highestversion',(Get-HighestTaskGroupVersion -TaskGroupObject $filteredproperties -Taskgroupid $_.id -includeTGPreview:$includeTGpreview.IsPresent)) + } + + $hash.Add('value',$_) + + $return += [PSCustomObject]$hash + } + + if ($TGVersion) { + + $return = $return | Where-Object {$_.version -eq $TGVersion} + + if (($return | Measure-Object | Select-Object -ExpandProperty count) -ne 1) { + + Write-Error "version supplied $TGVersion not found for Task Group ID $ID" + + throw; + } + + }else{ + + if (-not $includeTGdrafts.IsPresent) { + $return = $return | Where-Object {$_.Draft -eq $False} + } + if (-not $includeTGpreview.IsPresent) { + $return = $return | Where-Object {$_.Preview -eq $False} + } + if (-not $AllTGVersions.IsPresent) { + $return = $return | Where-Object {$_.version -eq $_.highestversion} + } + + } + + } + Default {Write-Error "unaccepted type found. Accepted values are BuildDefintion, ReleaseDefinition, TaskGroup"} + } + + return $return + + } \ No newline at end of file diff --git a/Source/Public/Get-AzDOAPIToolsTaskGroupAsYAMLPrepped.ps1 b/Source/Public/Get-AzDOAPIToolsTaskGroupAsYAMLPrepped.ps1 new file mode 100644 index 0000000..4d91bf1 --- /dev/null +++ b/Source/Public/Get-AzDOAPIToolsTaskGroupAsYAMLPrepped.ps1 @@ -0,0 +1,54 @@ +function Get-AzDoAPIToolsTaskGroupAsYAMLPrepped { + param ( + # Parameter help description + [Parameter(mandatory=$true)] + [Array] + $TaskGroupsToConvert, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Profilename + [Parameter(mandatory=$false)] + [string] + $profilename, + # OutputPath + [Parameter(mandatory=$true)] + [string] + $OutputPath, + # future switch for expanding nested taskgroups + [Parameter(mandatory=$false)] + [switch] + $ExpandNestedTaskGroups, + # Switch to output file instead of retruning an object + [Parameter(mandatory=$false)] + [switch] + $Outputasfile + ) + + Import-Module powershell-yaml + foreach ($TaskGroup in $TaskGroupsToConvert) { + + $yamlTemplate = [PSCustomObject]@{} + + $inputs = Convert-TGInputsToYamlTemplateInputs -InputArray $TaskGroup -Projectname $Projectname -profilename $profilename -ExpandNestedTaskGroups:$ExpandNestedTaskGroups.IsPresent + $steps = Convert-TaskStepsToYAMLSteps -InputArray $TaskGroup -projectname $projectname -profilename $profilename -ExpandNestedTaskGroups:$ExpandNestedTaskGroups.IsPresent -inputtype $TaskGroup.type + + + $yamlTemplate | Add-Member -NotePropertyName 'parameters' -NotePropertyValue $inputs + $yamlTemplate | Add-Member -NotePropertyName 'steps' -NotePropertyValue $steps + + if ($outputasfile.IsPresent) { + if (!$outputpath) { + Write-Error "You have used the -Outputfile switch without mentioning OutputPath" + }else{ + Convert-AzDoAPIToolsYamlObjectToYAMLFile -InputObject $yamlTemplate -outputpath $OutputPath -Outputfilename "$($taskgroup.name).yml" + } + + }else { + return $yamlTemplate + } + } + + +} \ No newline at end of file diff --git a/Source/Public/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.ps1 new file mode 100644 index 0000000..6adf25f --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.ps1 @@ -0,0 +1,91 @@ +function Get-AzDoAPIToolsDefinitionAsYAMLPrepped { + param ( + # Parameter help description + [Parameter(mandatory=$true)] + [Array] + $DefinitionsToConvert, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Profilename + [Parameter(mandatory=$false)] + [string] + $profilename, + # OutputPath + [Parameter(mandatory=$false)] + [string] + $OutputPath, + # switch for expanding nested taskgroups + [Parameter(mandatory=$false)] + [switch] + $ExpandNestedTaskGroups, + # Switch to output file instead of retruning an object + [Parameter(mandatory=$false)] + [switch] + $Outputasfile + ) + + Import-Module powershell-yaml + foreach ($Definition in $DefinitionsToConvert) { + + $yamlTemplate = New-Object PSObject + $yamlarray = @() + + if ($definition.value.buildNumberFormat) { + $name = @{'name' = $definition.value.buildNumberFormat} + }else { + $name = @{'name' = '$(buildid)'} + } + + $inputs = Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped -InputDefinitions $Definition + $triggers = Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped -InputDefinitions $Definition + $schedules = Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped -InputDefinitions $Definition + $pool = Get-AzDoAPIToolsAgentPool -PoolURL $Definition.value.queue._links.self.href -agentidentifier $Definition.value.process.target.agentSpecification.identifier + $pooltoadd = @{} + $pooltoadd.add('pool',$pool) + $steps = Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped -InputDefinitions $Definition -projectname $projectname -profilename $profilename -ExpandNestedTaskGroups:$ExpandNestedTaskGroups.IsPresent + + if ($name) { + $yamlarray += $name + } + + if ($pooltoadd) { + $yamlarray += $pooltoadd + } + + if ($inputs) { + $yamlarray += $inputs + } + + if ($triggers) { + $yamlarray += $triggers + } + + if ($schedules) { + $yamlarray += $schedules + } + + if ($steps) { + $yamlarray += $steps + } + + foreach ($yamlobject in $yamlarray) { + $yamlobject.getEnumerator() | ForEach-Object{ + + $yamlTemplate | Add-Member -NotePropertyName $_.name -NotePropertyValue $_.value + } + } + + if ($outputasfile.IsPresent) { + if (!$outputpath) { + Write-Error "You have used the -Outputfile switch without mentioning OutputPath" + }else{ + Convert-AzDoAPIToolsYamlObjectToYAMLFile -InputObject $yamlTemplate -outputpath $OutputPath -Outputfilename "$($Definition.name).yml" + } + + }else { + return $yamlTemplate + } + } +} \ No newline at end of file diff --git a/Source/Public/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.ps1 new file mode 100644 index 0000000..a19092e --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.ps1 @@ -0,0 +1,59 @@ +function Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped { + [CmdletBinding()] + param ( + # InputObjects + [Parameter(Mandatory=$true)] + [Array] + $InputDefinitions + ) + + process { + foreach ($definition in $InputDefinitions) { + $definition = $definition.value + + if ($definition.triggers) { + + $citriggers = $definition.triggers | Where-Object {$_.triggerType -eq 'schedule'} + + if ($citriggers.schedules) { + $schedules = @() + foreach ($schedule in $citriggers.schedules) { + $yamlschedule = [ordered]@{} + + $cron = Get-CronFromSchedule -InputSchedules $schedule + $yamlschedule.add('cron', $cron) + + if($schedule.branchFilters){ + $branchtriggers = Get-DefinitionInputIncludeExclude -inputs $schedule.branchFilters + + if ($branchtriggers.count -ge 1) { + $yamlschedule.add('branches', $branchtriggers) + } + } + + if ($schedule.scheduleOnlyWithChanges -eq $false) { + + $yamlschedule.add('always','true') + } + + $schedules += $yamlschedule + } + + + if ($schedules.Count -ge 1) { + $returnedtriggerobject = @{ + 'schedules' = $schedules + } + } + + + } + } + + return $returnedtriggerobject + } + } + +} + + diff --git a/Source/Public/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.ps1 new file mode 100644 index 0000000..3ba852e --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.ps1 @@ -0,0 +1,88 @@ +function Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped { + [CmdletBinding()] + param ( + # InputObjects + [Parameter(Mandatory=$true)] + [Array] + $InputDefinitions, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Projectname + [Parameter(mandatory=$false)] + [string] + $profilename, + # future switch for expanding nested taskgroups + [Parameter(mandatory=$false)] + [switch] + $ExpandNestedTaskGroups + ) + + process { + foreach ($definition in $inputdefinitions) { + $definitiontype = $definition.type + $definition = $definition.value + $jobs = $definition.process.phases + $jobcount = $jobs.count + $definitionjobs = @() + $retunreddefinitionjobs = [ordered]@{} + $phaserefs = @{} + + foreach ($job in $jobs) { + $definitionsteps = [ordered]@{} + $definitionjob = [ordered]@{} + $steps = Convert-TaskStepsToYAMLSteps -InputArray $job -Projectname $projectname -profilename $profilename -inputtype $definitiontype -ExpandNestedTaskGroups:$ExpandNestedTaskGroups.isPresent + + [bool]$custompool = ($job.target.PSobject.Properties.name.contains('queue')) + + [bool]$dependencies = ($job.PSobject.Properties.name.contains('dependencies')) + + $phaserefs.Add($job.refName,$job.name) + + if ($jobcount -gt 1 -or $custompool) { + + $definitionjob.add('job',$job.name.replace(" ","_")) + ### add job pool properties + if ($custompool) { + + $poolToAdd = Get-AzDoAPIToolsAgentPool -poolURL $job.target.queue._links.self.href -AgentIdentifier $job.target.agentSpecification.identifier + if ($pooltoAdd.count -ge 1) { + $definitionjob.add('pool',$poolToAdd) + } + } + + $jobproperties = Get-TaskProperties -InputTaskObject $job -propertiestoskip @('steps','target','name','refname','jobAuthorizationScope','dependencies') + + $jobproperties.PSObject.Properties | ForEach-Object{ + $definitionjob.add($_.name,$_.value) + } + + #add section for dependancies + if ($dependencies) { + $dependancy = $phaserefs.$($job.dependencies.scope) + $definitionjob.add('dependsOn',$dependancy.replace(" ","_")) + } + + $definitionjob.add('steps',$steps) + + $definitionjobs += $definitionjob + }else{ + $definitionsteps.add('steps',$steps) + } + } + + if ($jobcount -gt 1 -or $custompool) { + $retunreddefinitionjobs.add('jobs',$definitionjobs) + + return $retunreddefinitionjobs + + }else{ + + return $definitionsteps + } + + + } + } +} \ No newline at end of file diff --git a/Source/Public/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.ps1 new file mode 100644 index 0000000..66cd649 --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.ps1 @@ -0,0 +1,59 @@ +function Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped { + [CmdletBinding()] + param ( + # InputObjects + [Parameter(Mandatory=$true)] + [Array] + $InputDefinitions + ) + + process { + foreach ($definition in $InputDefinitions) { + $definition = $definition.value + + if ($definition.triggers) { + + $citriggers = $definition.triggers | Where-Object {$_.triggerType -eq 'continuousIntegration'} + + if ($citriggers) { + $triggers = [ordered]@{} + if($citriggers.branchFilters){ + $branchtriggers = Get-DefinitionInputIncludeExclude -inputs $citriggers.branchFilters + + if ($branchtriggers.count -ge 1) { + $triggers.add('branches', $branchtriggers) + } + } + + if($citriggers.pathFilters){ + $pathtriggers = Get-DefinitionInputIncludeExclude -inputs $citriggers.pathFilters + + if ($pathtriggers.count -ge 1) { + $triggers.add('paths' , $pathtriggers) + } + + } + + + if ($citriggers.batchChanges -eq 'true') { + + $triggers.add('batch',$citriggers.batchChanges) + } + + if ($triggers.Count -ge 1) { + $returnedtriggerobject = @{ + 'trigger' = $triggers + } + } + + + } + } + + return $returnedtriggerobject + } + } + +} + + diff --git a/Source/Public/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.ps1 new file mode 100644 index 0000000..54dc999 --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.ps1 @@ -0,0 +1,58 @@ +function Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped { + [CmdletBinding()] + param ( + # InputObjects + [Parameter(Mandatory=$true)] + [Array] + $InputDefinitions + ) + + process { + foreach ($definition in $InputDefinitions) { + $definition = $definition.value + $variables = @() + $parameters = @() + + if ($definition.variables) { + foreach ($var in $definition.variables.psobject.properties){ + if (!$var.value.issecret) { + if ($var.value.allowOverride) { + $parameter = [ordered]@{} + $parameter.Add('name',$var.name) + $parameter.Add('type','string') + $parameter.Add('default',$var.value.value) + $parameters += $parameter + }else{ + $variable = [ordered]@{} + $variable.Add('name',$var.name) + $variable.Add('value',$var.value.value) + $variables += $variable + } + + } + else{ + Write-Verbose "variable $($var.name) is secret. not exporting to yaml. Please bear in mind to add this variable yourself" + } + } + } + + if($definition.variableGroups){ + foreach($vargroup in $definition.variableGroups){ + $variable = @{} + $variable.Add('group',$vargroup.name) + $variables += $variable + } + } + $returnvar = [ordered]@{} + if ($parameters.count -ge 1) { + $returnvar.Add('parameters',$parameters) + } + if ($variables.count -ge 1) { + $returnvar.Add('variables',$variables) + + } + return $returnvar + } + } + +} \ No newline at end of file diff --git a/Source/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.ps1 new file mode 100644 index 0000000..b03fd9f --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.ps1 @@ -0,0 +1,120 @@ +function Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList { + param ( + # Nameslist + [Parameter(mandatory=$true)] + [array] + $NamesList, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Profilename + [Parameter(mandatory=$false)] + [string] + $profilename, + # type + [Parameter(mandatory=$true)] + [ValidateSet('BuildDefinition','ReleaseDefinition','TaskGroup')] + [string] + $ApiType, + # also return drafts + [Parameter(mandatory=$false)] + [switch] + $includeTGdrafts, + # also return previews + [Parameter(mandatory=$false)] + [switch] + $includeTGpreview, + # Return all versions + [Parameter(mandatory=$false)] + [switch] + $AllTGVersions + ) + + $return = @() + + + switch ($apitype) { + BuildDefinition { + + $definitions = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'build' -resource 'definitions' -profilename $profilename -version '5.1-preview' + + $filtereddefinitions = $definitions.value | Where-Object {$_.name -in $NamesList} + $filtereddefinitions | ForEach-Object{ + $filtereddefinitiondetails = Use-AzDoAPI -url $_.url -method 'Get' -projectname $Projectname -profilename $profilename -version '5.1-preview' + # $filtereddefinitiondetails + $hash = @{} + $hash.Add('id', $($_.id)) + $hash.Add('name', $($_.name)) + $hash.Add('url', $($_.url)) + $hash.Add('type', $apitype) + $hash.Add('value',$filtereddefinitiondetails) + + $return += [PSCustomObject]$hash + } + } + ReleaseDefinition { + + $definitions = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'release' -resource 'definitions' -profilename $profilename -version '5.1-preview' + + $filtereddefinitions = $definitions.value | Where-Object {$_.name -in $NamesList} + + $filtereddefinitions | ForEach-Object{ + $filtereddefinitiondetails = Use-AzDoAPI -url $_.url -method 'Get' -projectname $Projectname -profilename $profilename -version '5.1-preview' + $hash = @{} + $hash.Add('id', $($_.id)) + $hash.Add('name', $($_.name)) + $hash.Add('url', $($_.url)) + $hash.Add('type', $apitype) + $hash.Add('value',$filtereddefinitiondetails) + + $return += [PSCustomObject]$hash + } + } + TaskGroup { + + $taskgroups = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'distributedtask' -resource 'taskgroups' -profilename $profilename -version '5.1-preview' + + $filteredtaskgroups = $taskgroups.value | Where-Object {$_.name -in $NamesList} + + $filteredproperties = $filteredtaskgroups + $filteredproperties | ForEach-Object { + $hash = @{} + $hash.Add('id', $($_.id)) + $hash.Add('name', $($_.name)) + $hash.Add('url', (Get-AzdoAPIURL -projectname $Projectname -area 'distributedtask' -resource 'taskgroups' -profilename $profilename -version '5.1-preview' -id $_.id)) + $hash.Add('type', $apitype) + $hash.Add('version', $($_.version.major)) + if($_.version.istest){ + $hash.Add('Draft','True') + }else{ + $hash.Add('Draft','False') + } + if ($_.preview) { + $hash.Add('Preview','True') + }else{ + $hash.Add('Preview','False') + } + $hash.Add('highestversion',(Get-HighestTaskGroupVersion -TaskGroupObject $filteredproperties -Taskgroupid $_.id -includeTGPreview:$includeTGpreview.IsPresent)) + + $hash.Add('value',$_) + + $return += [PSCustomObject]$hash + } + + if (-not $includeTGdrafts.IsPresent) { + $return = $return | Where-Object {$_.Draft -eq $False} + } + if (-not $includeTGpreview.IsPresent) { + $return = $return | Where-Object {$_.Preview -eq $False} + } + if (-not $AllTGVersions.IsPresent) { + $return = $return | Where-Object {$_.version -eq $_.highestversion} + } + } + Default {Write-Error "unaccepted type found. Accepted values are BuildDefintion, ReleaseDefinition, TaskGroup"} + } + + return $return + + } \ No newline at end of file diff --git a/Source/Public/Get-AzDoAPIToolsDefinitonsTaskGroupNames.ps1 b/Source/Public/Get-AzDoAPIToolsDefinitonsTaskGroupNames.ps1 new file mode 100644 index 0000000..5704815 --- /dev/null +++ b/Source/Public/Get-AzDoAPIToolsDefinitonsTaskGroupNames.ps1 @@ -0,0 +1,40 @@ +function Get-AzDoAPIToolsDefinitonsTaskGroupNames { + param ( + # type + [Parameter(mandatory=$true)] + [ValidateSet('BuildDefinition','ReleaseDefinition','TaskGroup')] + [string] + $ApiType, + # Projectname + [Parameter(mandatory=$true)] + [string] + $Projectname, + # Profilename + [Parameter(mandatory=$false)] + [string] + $profilename + ) + + + switch ($apitype) { + BuildDefinition { + $response = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'build' -resource 'definitions' -profilename $profilename -version '5.1-preview' + + + } + ReleaseDefinition { + $response = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'release' -resource 'definitions' -profilename $profilename -version '5.1-preview' + + } + TaskGroup { + $response = Use-AzDoAPI -method 'Get' -projectname $Projectname -area 'distributedtask' -resource 'taskgroups' -profilename $profilename -version '5.1-preview' + + } + Default {Write-Error "unaccepted type found. Accepted values are BuildDefintion, ReleaseDefinition, TaskGroup"} + } + + $response = $response.value | Select-Object -ExpandProperty Name | Get-Unique + + return $response + +} \ No newline at end of file diff --git a/Source/Public/Set-AzdoAPIToolsConfig.ps1 b/Source/Public/Set-AzdoAPIToolsConfig.ps1 new file mode 100644 index 0000000..88586c0 --- /dev/null +++ b/Source/Public/Set-AzdoAPIToolsConfig.ps1 @@ -0,0 +1,52 @@ +function Set-AzdoAPIToolsConfig { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] $configfilepath + ) + + process { + + if(!$configfilepath){ + Write-verbose "No `$configfilepath parameter supplier. setting to default path." + $configfilepath = "{0}\AzDoAPITools\config.json" -f $env:appdata + } + + if (Test-Path $configfilepath) { + Write-Verbose "Found an existing configfile in $configfilepath. loading it" + $existingconfig = Get-AzdoAPIToolsConfig -configfilepath $configfilepath + + if (Get-Confirmation "Do you want to overwrite the existing config in [$configfilepath] (Y) or add to / replace in existing config (N)?") { + $OutConfig = Get-AzDoAPIToolsConfigDetails -new + }else{ + $config = Get-AzDoAPIToolsConfigDetails | ConvertFrom-Json + + $matchingobject = $existingconfig.profiles | Where-Object {$_.profilename -eq $config.profilename} + + if( $matchingobject ){ + Write-Host "Found [$($config.profilename)] in existing configfile. OverWriting existing entry" + + $matchingobject.Organization = $config.Organization + $matchingobject.PAT = $config.PAT + + $OutConfig = $existingconfig | ConvertTo-Json -Depth 3 + + }else{ + Write-Verbose "found no profile with current details. Adding the new config" + + $existingconfig.profiles += $config + + $OutConfig = $existingconfig | ConvertTo-Json -Depth 3 + } + } + }else{ + Write-verbose "no configfile found at $configfilepath. Continuing new file setup" + $OutConfig = Get-AzDoAPIToolsConfigDetails -new + } + + if ($OutConfig) { + $OutConfig | Out-File $configfilepath + } + + + } +} \ No newline at end of file diff --git a/Tests/Unit/Private/Convert-TGInputsToYamlTemplateInputs.Tests.ps1 b/Tests/Unit/Private/Convert-TGInputsToYamlTemplateInputs.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Convert-TaskIDToYAMLTaskIdentifier.Tests.ps1 b/Tests/Unit/Private/Convert-TaskIDToYAMLTaskIdentifier.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Convert-TaskStepsToYAMLSteps.Tests.ps1 b/Tests/Unit/Private/Convert-TaskStepsToYAMLSteps.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-AzDoAPIToolsAgentPool.Tests.ps1 b/Tests/Unit/Private/Get-AzDoAPIToolsAgentPool.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-AzdoAPIToolsConfig.Tests.ps1 b/Tests/Unit/Private/Get-AzdoAPIToolsConfig.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-AzdoAPIToolsConfigDetails.Tests.ps1 b/Tests/Unit/Private/Get-AzdoAPIToolsConfigDetails.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-AzdoAPIToolsProfile.Tests.ps1 b/Tests/Unit/Private/Get-AzdoAPIToolsProfile.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-AzdoAPIURL.Tests.ps1 b/Tests/Unit/Private/Get-AzdoAPIURL.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-Confirmation.Tests.ps1 b/Tests/Unit/Private/Get-Confirmation.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-CronFromSchedule.Tests.ps1 b/Tests/Unit/Private/Get-CronFromSchedule.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-DefinitionInputIncludeExclude.Tests.ps1 b/Tests/Unit/Private/Get-DefinitionInputIncludeExclude.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-HighestTaskGroupVersion.Tests.ps1 b/Tests/Unit/Private/Get-HighestTaskGroupVersion.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-TaskInputs.Tests.ps1 b/Tests/Unit/Private/Get-TaskInputs.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Get-TaskProperties.Tests.ps1 b/Tests/Unit/Private/Get-TaskProperties.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Import-JSON.Tests.ps1 b/Tests/Unit/Private/Import-JSON.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Private/Use-AzDoAPI.Tests.ps1 b/Tests/Unit/Private/Use-AzDoAPI.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Convert-AzDoAPIToolsYamlObjectToYAMLFile.Tests.ps1 b/Tests/Unit/Public/Convert-AzDoAPIToolsYamlObjectToYAMLFile.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByID.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByID.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsDefinitonsTaskGroupNames.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsDefinitonsTaskGroupNames.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Get-AzDoAPIToolsTaskGroupAsYAMLPrepped.Tests.ps1 b/Tests/Unit/Public/Get-AzDoAPIToolsTaskGroupAsYAMLPrepped.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Unit/Public/Set-AzdoAPIToolsConfig.Tests.ps1 b/Tests/Unit/Public/Set-AzdoAPIToolsConfig.Tests.ps1 new file mode 100644 index 0000000..e69de29 diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..f694c56 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,58 @@ +pool: + vmImage: 'vs2017-win2016' +variables: + Modulename: AzDoAPITools +trigger: + batch: true + paths: + include: + - Source/* + - Tests/* + exclude: + - docs/* + - README.MD + - LICENSE.md + - Azure-pipelines.yml + - AzDoAPITools.*.ps1 +stages: + - stage: Build_test + displayName: 'Build & Test' + jobs: + - job: Build + displayName: 'Build & Test' + steps: + - checkout: self + - task: PowerShell@2 + inputs: + targetType: 'FilePath' + filePath: $(System.DefaultWorkingDirectory)/build.ps1 + arguments: -task CICD + - task: CopyFiles@2 + inputs: + Contents: | + $(modulename)*.* + build.ps1 + TargetFolder: '$(System.DefaultWorkingDirectory)/BuildOutput' + - publish: $(System.DefaultWorkingDirectory)/BuildOutput + artifact: $(ModuleName) + - stage: Publish_to_PSGallery + displayName: 'Publish to PS Gallery' + dependsOn: Build_test + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) + jobs: + - job: Publish + displayName: 'Publish To PS Gallery' + steps: + - checkout: none + - download: current + artifact: $(ModuleName) + - task: PowerShell@2 + inputs: + targetType: 'FilePath' + filePath: $(pipeline.workspace)/$(modulename)/build.ps1 + arguments: -task Publish + env: + PSGalleryKey : $(PSGalleryKey) + + + \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..bb87348 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,14 @@ +[cmdletbinding()] +param( + [string[]]$Task = 'CICD' +) + +$DependentModules = @('InvokeBuild', 'Buildhelpers', 'PSScriptAnalyzer', 'Pester', 'PSDeploy', 'PlatyPS','Powershell-YAML') # add pester when pester tests are added +Foreach ($Module in $DependentModules){ + If (-not (Get-Module $module -ListAvailable)){ + Install-Module -name $Module -Scope CurrentUser -Force + } + Import-Module $module -ErrorAction Stop +} +# Builds the module by invoking psake on the build.psake.ps1 script. +Invoke-Build "$PSScriptRoot\AzDoAPITools.build.ps1" -Task $Task \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..ee87b36 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,63 @@ +# AzDoAPITools Documentation + +Welcome to the AzDoAPITools Documentation page. This page will help you get started or will answer specific questions about individual functions. + +## Installation and setup + +- Install from the [Powershell Gallery](https://www.powershellgallery.com/packages/AzdoAPITools) or [build local](.././README.md#how-to-build-local) +- Run Set-AzDoAPIToolsConfig to create a config.json file in %AppData%\AzDoAPITools +- Run separate functions or check the areas below + +## Areas of Functionality + +- [Convert Classical (GUI) Pipelines to YAML Pipelines](/docs/classic-to-yaml-conversion.md) +- [Convert Task Groups to YAML Templates](/docs/classic-to-yaml-conversion.md) +- Retrieve a list of names of Build / Release Definitions & Task Groups +- Retrieve details of Build / Release Definitions & Task Groups based of (a list of) names +- Filter Task Groups API to return highest / draft / preview of a Task Group + +## Individual Functions + +### [Convert-AzDoAPIToolsYamlObjectToYAMLFile](/docs/functions/Convert-AzDoAPIToolsYamlObjectToYAMLFile.md) + +Converts a YAML PSOBject to YAML using [Powershell-YAML](https://www.powershellgallery.com/packages/powershell-yaml) and outs to a UTF-8 \*.yml file + +### [Get-AzDoAPIToolsDefinitionAsYAMLPrepped](/docs/functions/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.md) + +Returns a YAML Prepped PSObject with all the elements of a pipeline (Build Definition) based on a(n Array) of Build Definitions + +### [Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped](/docs/functions/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.md) + +Returns a YAML Prepped PSObject with the Schedules of a pipeline (Build Definition) based on a(n Array) of Build Definitions + +### [Get-AzDoAPIToolsDefinitionsTaskGroupsByID](/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByID.md) + +Returns a Build / Release Definition or Task Group by its ID with added metadata for easy use + +### [Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList](/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.md) + +Returns an array of Build / Release Definition or Task Group by an array of names with added metadata for easy use + +### [Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped](/docs/functions/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.md) + +Returns a YAML Prepped PSObject with the Steps of a pipeline (Build Definition) based on a(n Array) of Build Definitions + +### [Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped](/docs/functions/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.md) + +Returns a YAML Prepped PSObject with the Triggers of a pipeline (Build Definition) based on a(n Array) of Build Definitions + +### [Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped](/docs/functions/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.md) + +Returns a YAML Prepped PSObject with the Variables of a pipeline (Build Definition) based on a(n Array) of Build Definitions + +### [Get-AzDoAPIToolsDefinitonsTaskGroupNames](/docs/functions/Get-AzDoAPIToolsDefinitonsTaskGroupNames.md) + +Returns an array of names for all available Build / Release Definitions / Task Groups in a project. + +### [Get-AzDoAPIToolsTaskGroupAsYAMLPrepped](/docs/functions/Get-AzDoAPIToolsTaskGroupAsYAMLPrepped.md) + +Returns a YAML Prepped PSObject with all the elements of a Task Group based on a(n Array) of Task Groups + +### [Set-AzdoAPIToolsConfig](/docs/functions/Set-AzdoAPIToolsConfig.md) + +A simple tool to add & overwrite the config.json file used by the module diff --git a/docs/classic-to-yaml-conversion.md b/docs/classic-to-yaml-conversion.md new file mode 100644 index 0000000..669ec0c --- /dev/null +++ b/docs/classic-to-yaml-conversion.md @@ -0,0 +1,677 @@ +# Conversion of Classical Pipelines to YAML pipelines & Task Groups to Yaml templates + +The main driver for combining this toolset was to automate the steps to convert classical pipelines (aka Build / Release Definitions) as well as Task Groups into YAML Pipelines / YAML Templates. This functionality mimics the 'View YAML' button inside steps / jobs. when doing a migration for a customer from classical to YAML pipelines I noticed how Azure DevOps does not provide a full experience. + +Curently when you want to migrate a classical to YAML pipeline in Azure Devops it has to be done manually. You have the 'View YAML' button in classical pipelines for single steps which will give you the code for that particular step. However it does not work when you are selecting a Task Group step inside your classical pipeline. Furthermore useful properties such as triggers, variables, schedules, options and source options are not available from the Azure DevOps UI. + +This means you are left with a lot of manual labor by having to select each and every step inside a single pipeline add it to a YAML file and when there are Task Groups in the mix you would need to open them one by one and copy the single steps inside them and paste them. Then you would have to add the other very important properties of a pipeline yourself to that same YAML file you are trying to make. + +This is not only tedious and prone to errors but if you have invested a lot of time and effort into Task Groups as a mean to 'Create Once, Use Many' philosophy in CI/CD best practices you would like to convert those to YAML Templates files and call them from your YAML Pipeline. + +This module does exactly that and automatically. It currently has two main features: + +- Convert Task Groups to YAML step templates + - steps: Iterates over steps and add them to the template + - inputs used in steps that are currently in the variable format $(variable) and are no [predefined variables](https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml) will be converted into ${{parameter.parametername}} + - Task Groups: When a Task Group is found it can either be added as \-template: or be expanded and added as \-task: + - Inputs: Task Group Inputs are converted to template parameters +- Convert Build Definitions to YAML Pipelines including pipelines properties such as: + - Triggers: (both Path and Branch) + - Schedules: (already converted into CRON and UTC w/o DST) + - Variables: (Secret variables are skipped and settable at queue time will be converted to pipeline parameters) + - Agent Pools: (both at job and pipeline level) + - Jobs: (and interdependendancies between them such as dependancies and conditions) + - steps: (including non-default properties, inputs and conditions) + - Task Groups: When a Task Group is found it can either be added as \-template: or be expanded and added as \-task: + +The result of this conversion can either be used as a PSObject or be converted into a \*.yml file for direct usage. In the future I will create functionality to also import the results of the conversion as a new YAML Pipeline definition inside Azure DevOps. For items such as definition specific properties (triggers, schedules etc.) it will be an option to include them in the YAML file or you wish so (not recommended) have them be part of your YAML Pipeline definition. + +## Usage + +Below is explained how the module will do its work and what functions to call. No functions have pipeline support. I will be working on converting the module to have this as well as make use of Variable Sets, Classes and Types. I have just learned how this works in Powershell and did not want to halt the project on refactoring everthing. + +### Task Group Conversion + +In order to start with Task Group Conversion you will need a Powershell Array with Task Group names which you want to convert. This would look something like this: `$listofnames = @('Task Group 1', 'Task Group 2')`. + +If you do not have this list available you can call `Get-AzDoAPIToolsDefinitonsTaskGroupNames -ApiType 'Taskgroup' -Projectname 'Project on AzDo'` to get a list of names. + +Optionally you can provide the `-Profilename 'profile'` to address a specific profile saved in your `config.json`. By default it will pick the first entry in the `config.json` if not specified. + +--- + +you will need to feed this array into `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` adding the array as `-nameslist $listofnames` and using `-apitype 'Taskgroup' -projectname 'Project on AzDo'`. Optionally you can use the `-profilename` to specify a different profile. this function knows a set of switches which can be called optionally: + +- `-includeTGdrafts` : if your nameslist has Task Groups which are in draft the draft version will be included into the output of this function. This means you could have duplicate names in your resulting array which can cause overwritten files. It is best to seperate Draft Task Groups in a seperate nameslist or use `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` +- `-includeTGpreview` : If your nameslist includes Task Groups of which you have a preview currently it will be taken into account when using this switch. By default the function will try to determine the highestversion of a task group and will exclude previews and only use stable versions. If you want your task groups to be converted if a preview is available use this switch to allow the highestversion of a Task Group to be a preview version. +- `-AllTGVersions` : For converting Task Groups it is not recommended to use this switch. Using this switch will return all versions of the task group in nameslist which will lead to duplicates. It might be useful for other puroposes but not for converting. By default the function will return the highest version of a Task Group. + +Alternatively if you have an ID of a single Task Group or if you need a specific version / Draft of a task group other than the highest version you can use `Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID -ApiType 'Taskgroup' -projectname 'Project on AzDo'` It knows the same three switches as mentioned in `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` but also features `-TGVersion ` to retrieve a specific version of a Task Group (see example below) + +These functions will add some necessary MetaData as well as the actual definition to a PSObject which then can be used by subsequent functions which actually convert your Build Definition or to extract parts of it. + +By running either function and assigning it to a variable you are now ready to convert one or more Task Groups to YAML Templates. + +--- + +use `Get-AzDoAPIToolsTaskGroupAsYAMLPrepped -TaskGroupsToConvert $arrayresult -projectname 'Project on AzDo'` To output the YAML Prepped PSObject for a Task Group. You can use `-profilename` to specify a different profile. + +if you specify the `-ExpandNestedTaskGroups` switch every 'nested Task Group' inside The current Task Group to be converted will be expanded into \- task: steps rather than \- template: calls to other Task Groups. However you want to use it is up to you. + +#### Sidenote on Task Group Versions + +The Template referenced / expanded will be of the version which is being used by the initiating Task Group. That could result in expanding different versions of Task Groups if you have configured it like so. When not using this switch and using different versions of a Task Group it will only refer to the version of the originating task group you converted. See below example. + +Say you have 2 Task Groups referencing Different versions of another Task Group: + +- Task Group 1 : References Task Group 3 Version 1 +- Task Group 2 : References Task Group 3 Version 2 + +When __not__ using the `-ExpandNestedTaskGroups` both converted Task Group 1 & 2 will have a \-template: Task Group 3.yml reference. The reference to the template used will be dependant to which version of Task Group 3 you have converted into a \*.yml file. __Both will refer to the same version__. + +If you had used the `-ExpandNestedTaskGroups` switch Task Group 1 would have the expanded steps of Task Group 3 Version 1 whereas Task Group 2 would have the expanded steps of Task Group 3 Version 2. + +If you need different versions of templates you will need to convert both templates with `Get-AzDoAPIToolsDefinitionsTaskGroupsByID -TGVersion` and apply some manual labor. For the module it is impossible to convert this granularity without some manual labor. Maybe I will include the version number as a part of the filename when exporting in the future. + +#### Sidenote on Template Usage + +When referencing to (nested) Task Groups the module assumes that you have all templates in the same directory. If you wish to store your templates in a different directory that is possible. Any references to existing templates in the form of \- template: taskgroup.yaml need to be changed to be prefixed with a pathname. The converted YAML templates ofcourse need to be present in the mentioned path. + +If you wish to store your templates in a seperate repository you need to add a [repository resource](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#repository-resource) component to the YAML file and suffix the reference to the YAML template with a `@aliastoexternalresource`. + +#### Converting to YAML + +With these settings the function will output a PSObject with the YAML Prepped properties and values. If you wish to output it as a YAML file you need to use the `-Outputasfile` switch. If you do you will also need to specify the `-OutputPath` argument with a filepath to which the task groups are saved. By default the filename will be \.yml. + +### Build Definition conversion + +For converting Classical Build Definitions we have two options to consider: + +- Convert a complete Build Definition into a \*.yml file +- Extract parts of a build definition (such as triggers, schedules etc.) if you only specific components of a build definition to a PSObject which is YAML Prepped. + +Both Functionality is roughly the same and the build up in using is roughly the same. + +--- + +In order to start with Build Definition Conversion you will need a Powershell Array with Build Definition names which you want to convert. This would look something like this: `$listofnames = @('Build Definition 1', 'Build Definition 2')`. + +If you do not have this list available you can call `Get-AzDoAPIToolsDefinitonsTaskGroupNames -ApiType 'Builddefinition' -Projectname 'Project on AzDo'` to get a list of names. + +Optionally you can provide the `-Profilename 'profile'` to address a specific profile saved in your `config.json`. By default it will pick the first entry in the `config.json` if not specified. + +--- + +you will need to feed this array into `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` adding the array as `-nameslist $listofnames` and using `-apitype 'Builddefinition' -projectname 'Project on AzDo'`. Optionally you can use the `-profilename` to specify a different profile. + +Alternatively if you have an ID of a single Build Definition you want to convert you can use `Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID -ApiType 'Builddefinition' -projectname 'Project on AzDo'` + +These functions will add some necessary MetaData as well as the actual definition to a PSObject which then can be used by subsequent functions which actually convert your Build Definition or to extract parts of it. + +#### Converting a complete Build Definition + +use `Get-AzDoAPIToolsDefinitionAsYAMLPrepped -DefinitionsToConvert $arrayresult -projectname 'Project on AzDo'` To output the YAML Prepped PSObject for a Build Definition. You can use `-profilename` to specify a different profile. + +if you specify the `-ExpandNestedTaskGroups` switch every 'nested Task Group' inside The current Build Definition to be converted will be expanded into \- task: steps rather than \- template: calls to other Task Groups. However you want to use it is up to you. + +See the [Sidenote on Task Group Versions](#Sidenote-on-Task-Group-Versions) in the Task groups section to have an explanation of different Task group versions. Also have a look at [Sidenote on Template Usage](#Sidenote-on-Template-Usage) + +With these settings the function will output a PSObject with the YAML Prepped properties and values. If you wish to output it as a YAML file you need to use the `-Outputasfile` switch. If you do you will also need to specify the `-OutputPath` argument with a filepath to which the converted Build Definitions are saved. By default the filename will be \.yml. + +#### Extracting a specific component of a Build Definition + +You can use one of below functions to retrieve a specific component of a Build Definition and have it YAML Prepped for you. Input for each function would be the array of PSObjects retrieved either by `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID`. Though it is recommended to use these for single Build Definition since the output does not contain any metadata to relate it to a Build Definition. + +This section is all about usage of the functions. See [Examples](#Examples) to see how conversion is done. + +##### Schedules + +Use `Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped -InputDefinitions `. + +The result is a YAML prepped PSObject which contains Properties according to the [YAML Schema on Schedules](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#scheduled-trigger) + +##### Steps + +Use `Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped -InputDefinitions `. Since this functions needs to call other parts of Azure DevOps it is neccessary to include the `-Projectname ` to specify which project to refer to. + +You can use the `-profilename ` argument to specify a different profile if applicable. To convert references Task Groups inside the definition to separate steps use the `ExpandNestedTaskGroups` switch. + +The result is a YAML prepped PSObject which contains Steps according to the [YAML Schema on Steps](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#steps). If your definitions contain multiple jobs or if your job uses a different agent pool as defined on pipeline level this function will include a jobs / job construct according to the [YAML Schema on Jobs](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#job) including any job properties which were defined in the Build Definiton. + +##### Triggers + +Use `Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped -InputDefinitions `. + +The result is a YAML prepped PSObject which contains Push / CI Triggers according to the [YAML Schema on Push Triggers](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#push-trigger) + +##### Variables (& Parameters) + +Use `Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped -InputDefinitions `. + +Secret Variables are skipped. if your variable has the property 'Settable at queue time'. If you have any variable group(s) linked they will be referenced in the YAML output. + +The result is a YAML prepped PSObject which contains Parameters according to the [YAML Schema on Parameters](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#parameters) as well as Variables according to the [YAML Schema on Variables](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#variables) + +--- + +The results as mentioned are PSObjects so they are not yet converted to YAML. If you wish to do so you can either pipe them to `ConvertTo-Yaml` to use the [Powershell-YAML](https://github.com/cloudbase/powershell-yaml) module to convert them to YAML in your PS Session. If you want the output as a \*.yml file you can use `Convert-AzDoAPIToolsYamlObjectToYAMLFile -InputObject $PSOBjectYouWantToConvert -outputpath 'Path to export to' -outputfilename 'filename.yml'`. + +### Release definition conversion + +This is not yet supported. See [Limitations](#manual-stages-in-Release-definitions) for the reason why it is not supported. + +## Examples + +This section has some examples on how Task Groups and Build Definitions are converted by the module and shows example results. + +### Example Task Group Conversion + +For demonstrating this purpose i have created two task groups: + +![Task Groups](./images/2020-09-10-23-34-21.png) +*Task Groups Example* + +__Example-Task-Group-1__ contains two tasks and in between a nested task group __Nested-TG-Example__. __Nested-TG-Example__ on its turn has one task: + +![Example Task group 1](./images/2020-09-11-09-32-19.png) +*Example Task Group 1* + +![Nested Task Group](./images/2020-09-11-09-34-35.png) +*Nested Task Group* + +We can also see that the original Task group has 3 inputs. tgvariable1 & tgvariable2 are included because of the tasks itself inside the original task group. tgvariable3 comes with that because the Nested Task Group has it as an input. + +So let's see how this converts to YAML :) There are two ways to approach this. With and without `-ExpandNestedTaskGroups` switch. + +#### Without expansion + +if we would run the functions in this module without the `-ExpandNestedTaskGroups` for both task groups we would get 2 \*.yml files: + +- Example-Task-Group-1.yml +- Nested-TG-Example.yml + +`Example-Task-Group-1.yml` would look like this in YAML syntax: + +```yaml +parameters: +- name: tgvariable1 + type: string +- name: tgvariable2 + type: string +- name: tgvariable3 + type: string +steps: +- task: PowerShell@2 + displayName: PowerShell Script - TG Task 1 + inputs: + filePath: ${{parameters.tgvariable1}} +- template: Nested-TG-Example.yml + parameters: + tgvariable3: ${{parameters.tgvariable3}} +- task: PowerShell@2 + displayName: PowerShell Script - TG Task 2 + inputs: + filePath: ${{parameters.tgvariable2}} +``` + +We can see that the converted Task groups has 3 parameters as input. Since Task Groups inputs are always strings they are typed as such. We can see that the same three inputs are available as inputs as we would have in the Classical Task Group. + +Also notice that there are no default values assigned. The way Default values work for Classical Task Groups is that they form a suggestion when you add them to your Build / Release Definition. The function of this module is to convert existing Task Groups and Build / Release Definitons. That means that wherever you reference a Task group you have provided the inputs you want. The sole purpose of these YAML templates is to 'Create once, Use Many'. By removing the default values you are being made concious about what you would need to fill when calling them. Also when you are converting Build Definitions which call a Task Group the inputs you have assigned to the template are also converted. + +Furthermore we can see that the steps of this YAML template are exactly as in the original Task group. except that calling another Task Group is replaced with the \- template: construct in providing the Nested YAML template. That would require you to also convert the nested TG as a YAML Template. + +`Nested-TG-Example.yml` would look like this in YAML syntax: + +```yaml +parameters: + name: tgvariable3 + type: string +steps: + task: PowerShell@2 + displayName: PowerShell Script - Nested TG Task 1 + inputs: + filePath: ${{parameters.tgvariable3}} +``` + +in the Nested Template we can see a single parameter and a single task. which is exactly what is in the Classical Task group. Since it is being called by the original YAML Template the steps inside this Nested template will be expanded when Azure DevOps expands the complete pipeline at queue time. + +You should not use Task group Expansion when you have a complex set of Task Groups which you want to template out as YAML templates for reusability. Bear in mind that if you version your Task Groups and [are in needed of multiple versions](#Sidenote-on-Task-Group-Versions) at the same time you might want to convert multiple versions of the task groups used and apply different names manually and change the references to the yaml templates. The same applies when you want to [centralize your templates](#Sidenote-on-Template-Usage) in one or more repo's. You will need the add resource constructs to the pipelines calling the templates and update the references to the called templates. + +--- + +#### With expansion + +When we would have the `-ExpandNestedTaskGroups` switch present it means that all nested task groups will be expanded and put in place where the call to that template was. If we were to convert both Task Groups again in our example we would get the following two files: + +- Example-Task-Group-1.yml +- Nested-TG-Example.yml + +`Example-Task-Group-1.yml` would look like this in YAML syntax: + +```yaml +parameters: +- name: tgvariable1 + type: string +- name: tgvariable2 + type: string +- name: tgvariable3 + type: string +steps: +- task: PowerShell@2 + displayName: PowerShell Script - TG Task 1 + inputs: + filePath: ${{parameters.tgvariable1}} +- task: PowerShell@2 + displayName: PowerShell Script - Nested TG Task 1 + inputs: + filePath: ${{parameters.tgvariable3}} +- task: PowerShell@2 + displayName: PowerShell Script - TG Task 2 + inputs: + filePath: ${{parameters.tgvariable2}} +``` + +The difference with not using the `-ExpandNestedTaskGroups` is that the contents of the Nested Task Group are placed instead of calling the Converted YAML file for the Nested Task Group with the \- template: construct. If the Nested Task groups would have had multiple steps or even Nested Task Groups of itself they would also be expanded and put in the right order. The converted Task Group will have all paramaters from all nested task groups added to the list. + +`Nested-TG-Example.yml` would look like this in YAML syntax: + +```yaml +parameters: + name: tgvariable3 + type: string +steps: + task: PowerShell@2 + displayName: PowerShell Script - Nested TG Task 1 + inputs: + filePath: ${{parameters.tgvariable3}} +``` + +We can see that this file is exactly the same as in the example without using the `-ExpandNestedTaskGroups` switch. And since it is already present in expanded form in the original converted Task Group this converted Task group would initially have no extra value unless you would add it to a YAML definition or another YAML Template by hand. There might be special occasions where you would want a scenario like this which is why I included the functionality. + +Use the `-ExpandNestedTaskGroups` switch if you want to simplify and or merge various Task groups together in a single template. Be aware that this functionality is situational and that it should fit your strategy. + +--- + +### Example Build Definition conversion + +Let's have a look at below classical pipeline and deduct all GUI components and how they will look after conversion: + +![Example Pipeline](./images/2020-09-10-15-57-40.png) +*Example Pipeline with a nested Task group* + +So let's start with the name of the pipeline: Example-Pipeline. The filename after conversion of this pipeline will be Example-Pipeline.yml + +We can see that the Pipeline section is highlighted. We can see that the default agent pool in this pipeline is VS2017-Win2016 and is a Microsoft hosted Azure Devops agent. + +after conversion that will look like: + +```yaml +pool: + vmImage: vs2017-win2016 +``` + +--- + +![sources](./images/2020-09-10-16-08-53.png) +*sources properties* + +Looking at the Sources section we can see several checkout options. Currently these will be ignored. This is on the [ToDo list](#Apply-resource-checkout-options). After conversion it should look like this: + +```yaml +steps: +- checkout: self | none | repository name + clean: true | false + fetchDepth: number + lfs: true | false + submodules: true | recursive + path: string + persistCredentials: true | false +``` + +--- + +![Agent Job](./images/2020-09-10-16-17-04.png) +*Job properties part1* +![Agent Job part 2](./images/2020-09-10-16-17-45.png) +*Job properties part 2* + +On to the agent job specific settings we can see that a custom pool is being used. More specifically the Default Pool. Also we can see two demands for the agents. Those are ignored for now and is on the [ToDo list](#Include-agent-pool-demands-for-custom-self-hosted-pools). in the second image we can see that there are no dependancies and other properties are default. Since this job uses a different agent pool as the pipeline we can't omit the jobs / job section in YAML and as such the result will be converted like this: + +```yaml +jobs: +- job: Agent_job_1 + pool: + name: Default +``` + +Notice how the job name is converted from Agent Job 1 to Agent_job_1. This has to do with the notation limit of job aliases in YAML pipelines. They cannot include spaces. + +When the ToDo is finished for demands the result should look like this: + +```yaml +jobs: +- job: Agent_job_1 + pool: + name: Default + demands: + - inlinedemand1 + - inlinedemand2 -equals inlinevalue1 +``` + +--- + +![Build def steps](./images/2020-09-10-16-28-30.png) +*Original steps of build definition* + +![refered TG](./images/2020-09-10-16-28-57.png) +*Referenced Task Group from Build Definition* + +![nested TG](./images/2020-09-10-16-30-30.png) +*Nested Task Group in referenced Task Group* + +Looking at the steps we can see that the Build Definition itself contains a step and a task group. Conversion of these steps would look like this if we did not use `-ExpandNestedTaskGroups`: + +```yaml +steps: + - template: testtaskgroup.yml + - task: PowerShell@2 + displayName: PowerShell Script - Build Definition step + inputs: + script: | + # Write your PowerShell commands here. + + Write-Host "Hello World actual Step in Build Def" + targetType: inline +``` + +If we would have used the `-ExpandNestedTaskGroups` we can see the full chain of steps all in the correct order: + +```yaml +steps: + - task: PowerShell@2 + displayName: PowerShell Script - Task Group Step + inputs: + script: | + # Write your PowerShell commands here. + + Write-Host "Hello World Task Group test" + targetType: inline + - task: PowerShell@2 + displayName: PowerShell Script - Nested Task Group Task + inputs: + script: | + # Write your PowerShell commands here. + + Write-Host "Hello World Nested TG Test" + targetType: inline + - task: PowerShell@2 + displayName: PowerShell Script - Build Definition step + inputs: + script: | + # Write your PowerShell commands here. + + Write-Host "Hello World actual Step in Build Def" + targetType: inline + +``` + +--- + +![variables](./images/2020-09-10-17-00-04.png) +*Variable settings* + +![variable groups](./images/2020-09-10-16-54-08.png) +*Linked Variable Group(s)* + +On to the Variables tab in the build definition. We can see that we have 1 secret variable, 1 static variable, 1 variable settable at queue time and 1 linked variable group. When converted this will be the output: + +```yaml +parameters: +- name: parametervar + type: string + default: testvalue +variables: +- name: system.debug + value: "false" +- name: statictestvar + value: staticvalue +- group: testgroup +``` + +We can see that the secret var is skipped, the settable var is included as a parameter, the static variable is in the variables section and that we have 1 linked variablegroup. + +Oddly enough system.debug is not considered a predefined variable whereas the others are. these variables come with every pipeline generated in the UI. I'm still deciding whether i should ignore the system.debug variable since we have a diagnostics button in YAML pipelines which does the same. + +--- + +![CI triggers](./images/2020-09-10-17-05-51.png) +*CI triggers* + +We have 1 included branch to trigger on, 2 paths to filter on (1 included, 1 excluded) and Batch changes is checked. This would become the following YAML: + +```yaml +trigger: + branches: + include: + - refs/heads/master + paths: + - include: + - pathtoinclude + - exclude: + - pathtoexclude + batch: true +``` + +--- + +There are 4 schedules in this job to demonstrate the timezone corrections we have to do for the expected CRON notation: + +![ScheduleMinusOffset](./images/2020-09-10-17-09-21.png) +*Schedule with minus offset* + +The First Schedule is set at 01:00 (AM) on Saturday on master branch with a UTC - 9 offset. This means that 9 hours will be added to the schedule. Since the new time is on the same day no shift in days is expected. After conversion this will become Saturday 10:00 (AM): + +```yaml +- cron: 0 10 * * 6 + branches: + include: + - refs/heads/master +``` + +![ScheduleDST](./images/2020-09-10-21-51-17.png) +*Schedule with Daylight Savings Time* + +The Second Schedule is set at 19:07 (7:07 PM) on all days except for sunday on master branch with a UTC + 1 offset. At the time of writing this Timezone has Daylight Savings Time applied and the actual offset to UTC is +2. Despite this correction on DST it should not be reflected in the CRON YAML schedule notation. Therefor the applied schedule should be running on all days except sunday at 18:07 (6:07 PM): + +```yaml +- cron: 7 18 * * 1,2,3,4,5,6 + branches: + include: + - refs/heads/master +``` + +![Schedulenooffsetsalldays](./images/2020-09-10-21-58-29.png) +*Schedule on UTC, Exclusion and always triggered* + +The Third Schedule is set at 06:00 (AM) on every day of the week on every branch but master branch using UTC time. There should be no change to this schedule in the CRON YAML notation. However notice the checkbox for 'Only schedule builds if the source or pipeline has changed' being unchecked. This means that this schedule should always run regardless of the source code. The resulting schedule should have no shift in time, exclude the master branch and always run: + +```yaml +- cron: 0 6 * * 0,1,2,3,4,5,6 + branches: + exclude: + - refs/heads/master + always: "true" +``` + +![Scheduleplusoffset](./images/2020-09-10-22-01-42.png) +*Schedule with plus offset* + +The Fourth Schedule is set at 01:00 (AM) on weekdays on master branch with a UTC + 9 offset. This schedule demonstrates the shift in days because converting the schedule to UTC will extract 9 hours from the current time and would result in the UTC schedule running on the previous day at 16:00 (4:00 PM). this means that the weekday schedule will also shift one day left. The resulting schedule should run Sunday through Thursday at 16:00 (4:00 PM): + +```yaml +- cron: 0 16 * * 0,1,2,3,4 + branches: + include: + - refs/heads/master +``` + +The complete schedule section will look like this for the four schedules mentioned in this example: + +```yaml +schedules: +- cron: 0 10 * * 6 + branches: + include: + - refs/heads/master +- cron: 7 18 * * 1,2,3,4,5,6 + branches: + include: + - refs/heads/master +- cron: 0 6 * * 0,1,2,3,4,5,6 + branches: + exclude: + - refs/heads/master + always: "true" +- cron: 0 16 * * 0,1,2,3,4 + branches: + include: + - refs/heads/master +``` + +--- + +![options tab](./images/2020-09-10-22-34-22.png) +*Options tab* + +On the options tab there is not much we need to take into consideration. If you have a custom Build number format it will copied as: + +```yaml +name: $(buildid)-custompart-example +``` + +if the Build Number format field is empty we default to the `$(buildid)`: + +```yaml +name: $(buildid)-custompart-example +``` + +Other settings like timeoutinminuts and jobcanceltimeout which are mentioned here are valid for every job inside the Build Definition. I guess I could apply them to every job inside the pipeline if they are not the default settings. This is not implemented yet. See [this topic](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?tabs=yaml&view=azure-devops#timeouts) by Microsoft to see the correct YAML notation if you wish to apply it yourself. If you have specified a Job timeout in the Job part of the pipeline for that specific job it will be converted as a job property. + +Demands is something which is also a [pending feature](#Include-agent-pool-demands-for-custom-self-hosted-pools). The demands specified here are valid for the pipeline agent pool specified not the ones in jobs. + +## Assumptions + +Some assumptions had to be made while developing this module. Below is the explanation of these assumptions. + +### Schedules are converted to UTC notation disregarding DST + +When converting schedules from the built-in GUI editor to CRON notation inside YAML I had to follow the guidelines which are stated [here](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/scheduled-triggers?view=azure-devops&tabs=yaml#migrating-from-the-classic-editor). These instructions tells us that schedules in YAML pipelines are expected as CRON notation, but more notably in UTC format. In the classical pipeline you can determine in which timezone OffSet you want to run the schedule. In YAML there is no such way and it expects a UTC based notation. + +DayLightSaving corrections are ignored by YAML pipelines. e.g CEST will not be +2 based on UTC but rather +1 (based of the non-DST timezone CET). Converted schedules are formatted as UTC w/o DST correction. This might mean that the schedule in the YAML file has a different day / time as configured in the GUI pipeline. this is all based on the [guidelines for manually converting schedules to YAML notation](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/scheduled-triggers?view=azure-devops&tabs=yaml#migrating-from-the-classic-editor) + +#### Example + +Schedule on Tokyo Time (UTC + 9) on Saturday at 1:00 (AM) means it needs to be corrected with -9 hours to be in UTC. Your converted YAML Schedule in CRON will read Friday 16:00 (PM) (0 16 \* \* 5) + +similarly if you use an Alaska Time (UTC - 9) schedule on Saturday 22:00 (10:00 PM) it means the timezone correction will be + 9 instead. The Cron notation will instead be Sunday 07:00 (AM) (0 7 \* \* 0) + +Daylight Saving Times will be ignored. This means if you specified Amsterdam in your pipeline it reads UTC + 1. During winter this means this will be CET (UTC + 1) timezone. in Summertime this will be CEST timezone (UTC + 2). Schedules which are specified in the Build Definition which are curently in their DST format will still be considered as if they were running winter time. In our example for Amsterdam CEST only 1 hour will be substracted from the original schedule instead of 2. + +### Variables settable at queue time --> Parameters + +Classical Pipelines know variables. One particular property of a variable in classical pipelines is the ability to override values for variables at queue time rendering them into sort of parameters. With YAML Pipelines we now have the option to actually declare parameters in our yaml file. Variables (declared in a yaml file, not in definitions) are always static. + +The assumption is that if you have declared any variables with the property 'Settable at queue time' you want them to be turned into YAML parameters. + +If the module is expanded and the adding of converted yaml pipeline definitions (not the *.yml file itself but its definition) becomes available i might consider exporting variables into the definition rather than the yaml file (see next assumption) + +### Definition specific properties are supposed to be inside YAML files rathen then in the YAML definition + +Now this needs some explanation... With classical pipelines you need to declare variables / triggers inside the Build / Release definition. However with YAML Pipelines you have the option to declare these in two possible ways: + +- inside the YAML Pipeline definition (similar place as they would exist in a Build / Release definition) +- inside the YAML Pipeline itself (the *.yml file). These would become the trigger: / variables: properties of the YAML file. + +The assumption from this tool is that users strive to use the best practices associated and that you want variables / triggers for your definition inside the yaml file rather than in the definition. + +### Calls to other templates assume they are in the same folder as the calling YAML file + +When converting Task Groups to YAML Templates they will be exported to a single output folder. If you have nested task groups or definitions caling task groups (and not using the ExpandNestedTaskgroups switch) the tool assumes the called / nested task group is in the same folder as its caller / parent. If you want to have this in a different way and use an external template repository you should add the resource for it, the path to where you store them and the @alias suffix yourself. When i have time I will attempt to add settings which will do these prefixes / suffixes based on a json file. + +## Limitations + +Some buyers beware and known limitations of this module due to the behavior of Azure DevOps and the differences in classical vs YAML pipelines. These limitations will not be solved unless Micrsosoft will update their product and if i can find a stable way to implement it. + +### Incompatible tasks + +If your task group contains a task which does not work with YAML pipelines expect your pipeline to crash on it. I noticed this first when running the [Add Tag](https://github.com/colindembovsky/cols-agent-tasks/tree/master/Tasks/TagBuild) task which can tag a release definition or build definition. Since YAML Pipelines are living in the 'Build' area of Azure DevOps it can't apply a tag to a release definition (since they no longer exist in YAML). These tasks are converted to YAML but will obviously fail when running the pipeline. Unfortunately it is quite impossible / undoable to determine if a custom task is YAML-proof. + +### Secret variables + +If you have any secret variables inside your Build / Release Definitions they will not be exported. Any calls or uses of these variables (in the $() format will be converted.). Since YAML is clear text it would not be advisable to store these secrets. Besides the default behavior of Azure DevOps is to not copy these over when cloning or importing a Build / Release Definition I decided not to convert them (technically also impossible). + +My suggestion is you use the built-in functionality in the Definition settings or Azure Keyvault to store these secrets instead. If you name them similar as your original variables they will be accessible to converted tasks. + +### Incompatible folders (predefined variables) + +This predominantly concerns Tasks / Task Groups which are working in the Release Definition area. In YAML Pipelines all pipelines operate in the build area. Therefor Artifacts are downloaded to the path $(pipeline.workspace)\\\ directory Which translates to \\\\. In Release definitions this would have been $(Release.ArtifactsDirectory) which tranlates to \\\a\\\. This means that all tasks that expect to work from the \a directory will not automatically work. Predefined variables which are affected are: + +- $(System.defaultworkingdirectory) +- $(Release.ArtifactsDirectory) +- $(Agent.ReleaseDirectory) + +Also this means that predefined variables which start with release.* will not work. + +Since it is impossible for me to determine which task groups are used for which purpose in your use-case i opted for not converting these affected inputs to the $(pipeline.workspace). This might mean that your converted YAML pipeline will fail on folder errors / file not found errors. + +If there is enough interest I can add a switch which does this for you though. Still it would mean manually flagging Task Groups which would need this behavior. + +This will not fix the default behavior of some of your 3rd party extensions / tasks. some of these will prefix $(system.defaultworkingdirectory) if you provide a relative path as an input to a task. These will have to be fixed by the original author. In most tasks if you prefix $(pipeline.workspace)\\\ it will work most of the times. + +### Manual stages in Release Definitions + +Currently Microsoft does not allow for manual triggered stages in YAML Pipelines. In Classical Pipelines it was possible to add multiple stages to your release definition which would not automatically trigger when a release was started. Instead you had to open up the newly created release and 'Deploy' to that stage. In YAML pipeline such a mechanic does not exist (yet). + +This deficit means that if you make use of these manually triggered stages in your release definitions they will be exported as separate stages in the converted YAML file. However the behavior of YAML Pipelines is that all stages will run in parallel unless they have the dependsOn property. And even if that was declared it would still mean the stage would automatically execute. The only measure to prevent this would be to use manual approvals on an Environment. However this is still not a solution to this limitation. + +My suggestion is not to convert those classical pipelines to YAML or find a way to parameterize your pipeline in combination with say conditions to mimic the intended behavior. This is clearly out of the scope of this tool since we do not want to make too much assumptions in how the user wants its target produce. + +According to [this](https://developercommunity.visualstudio.com/idea/697467/manually-triggered-stages-in-yaml-multi-stage-pipe.html) Feature Request it is on the roadmap. According to [this](https://developercommunity.visualstudio.com/idea/629260/specify-manual-stages-in-multi-stage-yaml-pipeline.html) Feature request it is not. If you want to use Multi-Stage pipelines with manually triggered stages please upvote so Microsoft will prioritize this. If this gets implemented I will be adding conversion functionality for release definitions (some plumbing has already been done). + +## ToDo list + +Below is a short To Do list of functionality I wish to implement asap. the order in which they occur here is the priority I gave them. + +### Include agent pool demands for custom self-hosted pools + +Currently demands for Self-hosted pools are ignored. This will be added asap. This will only be implemented for Self-Hosted pools as Microsoft decided that for YAML they only allow demands for custom pools. if you have demands for Microsoft hosted pools (which you can set in the GUI) they will be ingored. + +### Apply resource checkout options + +stuff like checkout: clean, LFS and other git options which are specified in a build definitions sources part needs to be translated to steps - checkout options. + +### Converted parameters from queue time variables are called as variable instead of a parameter + +When converting variables to parameters which have the AllowOveride property and are thus settable at queue time are put into the parameters section of the YAML file. + +However when calling such parameter inside a step input it is being referred to as $(variablename) rather than $(parameters.variablename) / ${{parameters.variablename}}. This means an overhaul of the private `Get-TaskInputs` function and rethink the logic behind it. + +### Adding converted Pipelines as actual definitions to Azure DevOps + +right now the goal is to produce \*.yml files which then can be used to create a new pipeline in Azure DevOps via the 'New Pipeline' button. This will create a 'Build Definition' on the background which points to the created \*.yml file. It would be awesome if the user would not need to perform this manual step and have the already converted classical pipeline be automatically added as a new definition into Azure DevOps. + +### Variables as Definition variables rather than YAML Template variables + +As mentioned in the assumptions part of this readme right now Variables are assumed to be wanted as the variables: property inside a *.yml file. However even with YAML pipelines it is possible to have these variables defined as variables managed by Azure DevOps. If the option becomes available to add converted pipelines as a new definition the assumption on variables becomes a choice. + +### support for TFS / Azure DevOps Server installations + +Right now all the URL creation function allows for Azure DevOps URL creation. it does not support TFS / Azure DevOps Server local installation. In the case of TFS I am even unsure if YAML is supported at all. This needs further investigation. + +### Support for TFVC, GitHub, BitBucket source + +Right now the use-cases at the clients i've worked with was to deal with Azure DevOps hosted source code. I will need to do investigation / confirmation that this also works if you host your code externally and if not what changes are needed to incorporate this. + +### Release Definitions + +I wanted to push this Module to GitHub and make it publicly available knowing this feature was not implemented yet. The reason is that release definitions are quite complex and are not fully compatible between classical vs YAML Pipelines. This has to do predominantly with the [manual stages limitation](#Manual-stages-in-Release-Definitions). whenever Microsoft fixes this I will continue working on this. If interest is high enough I will make efforts into converting Classical Release Definitons to YAML pipelines knowing there is a serious limitation to the product by Microsoft. diff --git a/docs/functions/Convert-AzDoAPIToolsYamlObjectToYAMLFile.md b/docs/functions/Convert-AzDoAPIToolsYamlObjectToYAMLFile.md new file mode 100644 index 0000000..5c27867 --- /dev/null +++ b/docs/functions/Convert-AzDoAPIToolsYamlObjectToYAMLFile.md @@ -0,0 +1,92 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Convert-AzDoAPIToolsYamlObjectToYAMLFile + +## SYNOPSIS +Converts a YAML PSOBject to YAML using Powershell-YAML and outs to a UTF-8 yml file + +## SYNTAX + +``` +Convert-AzDoAPIToolsYamlObjectToYAMLFile [-InputObject] [-outputpath] + [-Outputfilename] [] +``` + +## DESCRIPTION +Converts a YAML PSOBject to YAML using Powershell-YAML and outs to a UTF-8 yml file + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Convert-AzDoAPIToolsYamlObjectToYAMLFile -InputObject $objectToConvert -outputpath 'C:\OutPutPathToUse' + -Outputfilename 'FileNameToUse.yml' +``` + +This Example will convert the $ObjectToConvert to YAML notation and output it to C:\OutPutPathToUse\FileNameToUse.yml. If the path does not exist it will prompt to create it for you. + +## PARAMETERS + +### -InputObject +Object which is YAMLPrepped and needs conversion. + +```yaml +Type: Object +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Outputfilename +String filename to use including extension to write. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -outputpath +String path name where you want the yml file to be written to. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.md b/docs/functions/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.md new file mode 100644 index 0000000..531c9ea --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionAsYAMLPrepped.md @@ -0,0 +1,158 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionAsYAMLPrepped + +## SYNOPSIS +Returns a YAML Prepped PSObject or yml file with all the elements of a YAML pipeline based on a(n Array) of Build Definitions. + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionAsYAMLPrepped [-DefinitionsToConvert] [-Projectname] + [[-profilename] ] [[-OutputPath] ] [-ExpandNestedTaskGroups] [-Outputasfile] + [] +``` + +## DESCRIPTION +This function takes one or more Build Definition where metadata is present from `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` and prepares all elements in it for conversion to a YAML Pipeline. if chosen it will output each build definition to a seperate yml file. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionAsYAMLPrepped -DefinitionsToConvert $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract all elements from a Build Definition and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be converted to a template call. + +### Example 2 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionAsYAMLPrepped -DefinitionsToConvert $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' -Profilename 'Alternative Alias in config.json' +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract all elements from a Build Definition and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be converted to a template call. Will use the Connection details specified in -profilename rather than the first entry in config.json. + +### Example 3 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionAsYAMLPrepped -DefinitionsToConvert $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' -ExpandNestedTaskGroups +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract all elements from a Build Definition and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be iterated over and expanded as seperate steps in the converted Build Definition. + +### Example 4 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionAsYAMLPrepped -DefinitionsToConvert $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' -ExpandNestedTaskGroups -OutputAsFile -OutPutPath 'C:\OutPut' +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract all elements from a Build Definition and converts them to a yml file called BuildDefinitionName.yml inside C:\OutPut. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be iterated over and expanded as seperate steps in the converted Build Definition. + +## PARAMETERS + +### -DefinitionsToConvert +Array of Build Definitions with MetaData to be converted to YAML Pipelines + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExpandNestedTaskGroups +Switch to determine whether or not to expand found Task Groups or to call them as templates. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OutputPath +If Specified will write to this path when -OutPutAsFile is used. If the specified path does not exist you will be prompted to have it created. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Outputasfile +Switch to specify the output has to written to a file. When used also specify -OutPutPath. If not used the function will return a PSObject instead. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Projectname +The project to use to call the AzDo API. Should be the same as the project used to add Metadata / retrieve the Build Definitions. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -profilename +Optional parameter to target a specific alias inside the config.json to use as a connection to AzDo. The -ProjectName provided should be accessible from this profile. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.md b/docs/functions/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.md new file mode 100644 index 0000000..e41dcda --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped.md @@ -0,0 +1,60 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped + +## SYNOPSIS +Returns a YAML Prepped PSObject with the schedules of a YAML pipeline based on a(n Array) of Build Definitions + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped [-InputDefinitions] [] +``` + +## DESCRIPTION +This function takes one or more Build Definition where metadata is present from `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` and extracts the schedules from it and prepare it for conversion use in a YAML Pipeline. If desired use `Convert-AzDoAPIToolsYamlObjectToYAMLFile` to convert the extracted elements to a seperate yml file. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionSchedulesAsYAMLPrepped -InputDefinitions $ArrayOfBuildDefinitions +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract the schedule elements from a Build Definition and converts them to a YAML ready PSObject. + +## PARAMETERS + +### -InputDefinitions +Array of Build Definitions with MetaData to be converted to YAML Pipelines + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.md b/docs/functions/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.md new file mode 100644 index 0000000..55367b7 --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped.md @@ -0,0 +1,120 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped + +## SYNOPSIS +Returns a YAML Prepped PSObject with the steps of a YAML pipeline based on a(n Array) of Build Definitions + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped [-InputDefinitions] [-Projectname] + [[-profilename] ] [-ExpandNestedTaskGroups] [] +``` + +## DESCRIPTION +This function takes one or more Build Definition where metadata is present from `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` and extracts the schedules from it and prepare it for conversion use in a YAML Pipeline. If desired use `Convert-AzDoAPIToolsYamlObjectToYAMLFile` to convert the extracted elements to a seperate yml file. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped -InputDefinitions $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract the steps from a Build Definition and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be converted to a template call. + +### Example 2 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped -InputDefinitions $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' -Profilename 'Alternative Alias in config.json' +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract the steps from a Build Definition and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be converted to a template call. Will use the Connection details specified in -profilename rather than the first entry in config.json. + +### Example 3 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionStepsAsYAMLPrepped -InputDefinitions $ArrayOfBuildDefinitions -Projectname 'YourAzDoProject' -ExpandNestedTaskGroups +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract the steps from a Build Definition and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Build Definition steps will be iterated over and expanded as seperate steps in the converted Build Definition. + +## PARAMETERS + +### -ExpandNestedTaskGroups +Switch to determine whether or not to expand found Task Groups or to call them as templates. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -InputDefinitions +Array of Build Definitions with MetaData to be converted to YAML Pipelines + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Projectname +The project to use to call the AzDo API. Should be the same as the project used to add Metadata / retrieve the Build Definitions. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -profilename +Optional parameter to target a specific alias inside the config.json to use as a connection to AzDo. The -ProjectName provided should be accessible from this profile. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.md b/docs/functions/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.md new file mode 100644 index 0000000..af46b02 --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped.md @@ -0,0 +1,60 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped + +## SYNOPSIS +Returns a YAML Prepped PSObject with the triggers of a YAML pipeline based on a(n Array) of Build Definitions + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped [-InputDefinitions] [] +``` + +## DESCRIPTION +This function takes one or more Build Definition where metadata is present from `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` and extracts the triggers from it and prepare it for conversion use in a YAML Pipeline. If desired use `Convert-AzDoAPIToolsYamlObjectToYAMLFile` to convert the extracted elements to a seperate yml file. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> GGet-AzDoAPIToolsDefinitionTriggersAsYAMLPrepped -InputDefinitions $ArrayOfBuildDefinitions +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract the trigger elements from a Build Definition and converts them to a YAML ready PSObject. + +## PARAMETERS + +### -InputDefinitions +Array of Build Definitions with MetaData to be converted to YAML Pipelines + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.md b/docs/functions/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.md new file mode 100644 index 0000000..7dfa880 --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped.md @@ -0,0 +1,60 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped + +## SYNOPSIS +Returns a YAML Prepped PSObject with the parameters and variables of a YAML pipeline based on a(n Array) of Build Definitions + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped [-InputDefinitions] [] +``` + +## DESCRIPTION +This function takes one or more Build Definition where metadata is present from `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` and extracts the variables from it, determine whether it needs conversion and if so as a parameter or a variable and prepare it for conversion use in a YAML Pipeline. If desired use `Convert-AzDoAPIToolsYamlObjectToYAMLFile` to convert the extracted elements to a seperate yml file. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionVariablesAsYAMLPrepped -InputDefinitions $ArrayOfBuildDefinitions +``` + +Will Take The definitions specified in $ArrayOfBuildDefinitions and for each definition will extract the variable elements from a Build Definition, determine if it needs conversion and if so as a parameter or a variable and converts them to a YAML ready PSObject. + +## PARAMETERS + +### -InputDefinitions +Array of Build Definitions with MetaData to be converted to YAML Pipelines + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByID.md b/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByID.md new file mode 100644 index 0000000..996c612 --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByID.md @@ -0,0 +1,224 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionsTaskGroupsByID + +## SYNOPSIS +Returns a Build / Release Definition or Task Group by its ID with added metadata for easy use + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionsTaskGroupsByID [-ID] [[-TGVersion] ] [-Projectname] + [[-profilename] ] [-ApiType] [-includeTGdrafts] [-includeTGpreview] [-AllTGVersions] + [] +``` + +## DESCRIPTION +Returns a Build / Release Definition or Task Group by its ID with added metadata for easy use. For Task Groups it has control over preview, version and drafts. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'BuildDefinition' -Projectname 'YourAzDoProject' +``` + +Will look for Build Definition with ID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the build definition along with some additional metdata. + +### Example 2 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'BuildDefinition' -Projectname 'YourAzDoProject' -Profilename 'Alternative Alias in config.json' +``` + +Will look for Build Definition with ID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the build definition along with some additional metdata. Will use the Connection details specified in -profilename rather than the first entry in config.json. + +### Example 3 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'ReleaseDefinition' -Projectname 'YourAzDoProject' +``` + +Will look for Release Definition with ID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Release definition along with some additional metdata. EXPERIMENTAL FEATURE!!! + +### Example 4 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'TaskGroup' -Projectname 'YourAzDoProject' +``` + +Will look for Task Group with GUID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the highest version non draft non preview Task Group along with some additional metdata. + +### Example 5 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -TGVersion 2 +``` + +Will look for Task Group with GUID $ID and version 2 in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Task Group version 2 along with some additional metdata. + +### Example 6 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -AllTGVersions +``` + +Will look for Task Group with GUID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Task Group and all its non-draft non-preview versions along with some additional metdata. + +### Example 7 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -AllTGVersions -includeTGdrafts -includeTGpreview +``` + +Will look for Task Group with GUID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Task Group and all its draft and preview versions along with some additional metdata. + +### Example 8 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -includeTGpreview +``` + +Will look for Task Group with GUID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the highest version non-draft and with previews of the Task Group along with some additional metdata. + +### Example 9 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByID -ID $ID -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -includeTGdrafts +``` + +Will look for Task Group with GUID $ID in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the highest version draft non-preview of the Task Group along with some additional metdata. + +## PARAMETERS + +### -AllTGVersions +Switch to have the function return all versions of a Task Group given. Will only work for APIType 'TaskGroup'. If ommitted it will by default return the highest found version of the Task Group + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ApiType +Specifies what API to call. Knows BuildDefinition, ReleaseDefinition and TaskGroup as accepted values. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: BuildDefinition, ReleaseDefinition, TaskGroup + +Required: True +Position: 4 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ID +ID of the Build / Release definition or Task Group to lookup. for Build / Release definitions expects an integer and for Task Groups a GUID. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Projectname +The project to use to call the AzDo API. Should be the same as the project used to add Metadata / retrieve the Build Definitions. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TGVersion +Used in combination with -APIType TaskGroup. Specifies a specific version to return for the given Task Group. + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -includeTGdrafts +If this switch is used it will include any Task Groups which are in Draft State. If you have specified a Draft ID and not use this switch your result will be null. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -includeTGpreview +If this switch is used the results of the function will include previews of a task group. This might affect the Highest version returned from a task group. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -profilename +Optional parameter to target a specific alias inside the config.json to use as a connection to AzDo. The -ProjectName provided should be accessible from this profile. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.md b/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.md new file mode 100644 index 0000000..30e7cbf --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList.md @@ -0,0 +1,202 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList + +## SYNOPSIS +Returns an array of Build / Release Definition or Task Group by an array of names with added metadata for easy use + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList [-NamesList] [-Projectname] + [[-profilename] ] [-ApiType] [-includeTGdrafts] [-includeTGpreview] [-AllTGVersions] + [] +``` + +## DESCRIPTION +Returns a Build / Release Definition or Task Group by an array of names with added metadata for easy use. For Task Groups it has control over preview, version and drafts. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'BuildDefinition' -Projectname 'YourAzDoProject' +``` + +Will look for Build Definitions with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the build definition along with some additional metdata. + +### Example 2 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'BuildDefinition' -Projectname 'YourAzDoProject' -Profilename 'Alternative Alias in config.json' +``` + +Will look for Build Definitions with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the build definition along with some additional metdata. Will use the Connection details specified in -profilename rather than the first entry in config.json. + +### Example 3 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'ReleaseDefinition' -Projectname 'YourAzDoProject' +``` + +Will look for Release Definitions with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Release definition along with some additional metdata. EXPERIMENTAL FEATURE!!! + +### Example 4 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'TaskGroup' -Projectname 'YourAzDoProject' +``` + +Will look for Task Groups with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the highest version non draft non preview Task Group along with some additional metdata. + +### Example 5 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -AllTGVersions +``` + +Will look for Task Groups with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Task Group and all its non-draft non-preview versions along with some additional metdata. + +### Example 6 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -AllTGVersions -includeTGdrafts -includeTGpreview +``` + +Will look for Task Groups with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the Task Group and all its draft and preview versions along with some additional metdata. + +### Example 7 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -includeTGpreview +``` + +Will look for Task Groups with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the highest version non-draft and with previews of the Task Group along with some additional metdata. + +### Example 8 +```powershell +PS C:\> Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList -NamesList $nameslist -APIType 'TaskGroup' -Projectname 'YourAzDoProject' -includeTGdrafts +``` + +Will look for Task Groups with names found in $nameslist in the Azure DevOps Project 'YourAzDoProject' The result will be a PSObject with the highest version draft non-preview of the Task Group along with some additional metdata. + +## PARAMETERS + +### -AllTGVersions +Switch to have the function return all versions of a Task Group given. Will only work for APIType 'TaskGroup'. If ommitted it will by default return the highest found version of the Task Group. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ApiType +Specifies what API to call. Knows BuildDefinition, ReleaseDefinition and TaskGroup as accepted values. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: BuildDefinition, ReleaseDefinition, TaskGroup + +Required: True +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NamesList +Array of names to look for in Build / Release Definitions or Task Groups. + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Projectname +The project to use to call the AzDo API. Should be the same as the project used to add Metadata / retrieve the Build Definitions. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -includeTGdrafts +If this switch is used it will include any Task Groups which are in Draft State. If you have specified a Draft ID and not use this switch your result will be null. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -includeTGpreview +If this switch is used the results of the function will include previews of a task group. This might affect the Highest version returned from a task group. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -profilename +Optional parameter to target a specific alias inside the config.json to use as a connection to AzDo. The -ProjectName provided should be accessible from this profile. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsDefinitonsTaskGroupNames.md b/docs/functions/Get-AzDoAPIToolsDefinitonsTaskGroupNames.md new file mode 100644 index 0000000..90f2d3c --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsDefinitonsTaskGroupNames.md @@ -0,0 +1,113 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsDefinitonsTaskGroupNames + +## SYNOPSIS +Returns an array of names for all available Build / Release Definitions / Task Groups in a project. + +## SYNTAX + +``` +Get-AzDoAPIToolsDefinitonsTaskGroupNames [-ApiType] [-Projectname] [[-profilename] ] + [] +``` + +## DESCRIPTION +Returns an array of names for all available Build / Release Definitions / Task Groups in a project. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> {Get-AzDoAPIToolsDefinitonsTaskGroupNames -ApiType 'BuildDefinition' -Projectname 'YourAzDoProject' +``` + +Will return an array with unique names of Build Definitions for YourAzDoProject. + +### Example 2 +```powershell +PS C:\> {Get-AzDoAPIToolsDefinitonsTaskGroupNames -ApiType 'ReleaseDefinition' -Projectname 'YourAzDoProject' +``` + +Will return an array with unique names of Release Definitions for YourAzDoProject. + +### Example 3 +```powershell +PS C:\> {Get-AzDoAPIToolsDefinitonsTaskGroupNames -ApiType 'TaskGroup' -Projectname 'YourAzDoProject' +``` + +Will return an array with unique names of found Task Groups for YourAzDoProject. + +### Example 4 +```powershell +PS C:\> {Get-AzDoAPIToolsDefinitonsTaskGroupNames -ApiType 'TaskGroup' -Projectname 'YourAzDoProject' -Profilename 'Alternative Alias in config.json' +``` + +Will return an array with unique names of found Task Groups for YourAzDoProject. Will use the Connection details specified in -profilename rather than the first entry in config.json. + +## PARAMETERS + +### -ApiType +Specifies what API to call. Knows BuildDefinition, ReleaseDefinition and TaskGroup as accepted values. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: BuildDefinition, ReleaseDefinition, TaskGroup + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Projectname +The project to use to call the AzDo API. Should be the same as the project used to add Metadata / retrieve the Build Definitions. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -profilename +Optional parameter to target a specific alias inside the config.json to use as a connection to AzDo. The -ProjectName provided should be accessible from this profile. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Get-AzDoAPIToolsTaskGroupAsYAMLPrepped.md b/docs/functions/Get-AzDoAPIToolsTaskGroupAsYAMLPrepped.md new file mode 100644 index 0000000..70239b6 --- /dev/null +++ b/docs/functions/Get-AzDoAPIToolsTaskGroupAsYAMLPrepped.md @@ -0,0 +1,158 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Get-AzDoAPIToolsTaskGroupAsYAMLPrepped + +## SYNOPSIS +Returns a YAML Prepped PSObject or yml file with all the elements of a YAML pipeline based on a(n Array) of Task Groups. + +## SYNTAX + +``` +Get-AzDoAPIToolsTaskGroupAsYAMLPrepped [-TaskGroupsToConvert] [-Projectname] + [[-profilename] ] [-OutputPath] [-ExpandNestedTaskGroups] [-Outputasfile] + [] +``` + +## DESCRIPTION +This function takes one or more Task Groups where metadata is present from `Get-AzDoAPIToolsDefinitionsTaskGroupsByNamesList` or `Get-AzDoAPIToolsDefinitionsTaskGroupsByID` and prepares all elements in it for conversion to a YAML Template. if chosen it will output each Task Group to a seperate yml file. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Get-AzDoAPIToolsTaskGroupAsYAMLPrepped -TaskGroupsToConvert $ArrayOfTaskGroups -Projectname 'YourAzDoProject' +``` + +Will Take The Task Groups specified in $ArrayOfTaskGroups and for each object will extract all elements from a Task Group and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Task Group steps will be converted to a template call. + +### Example 2 +```powershell +PS C:\> Get-AzDoAPIToolsTaskGroupAsYAMLPrepped -TaskGroupsToConvert $ArrayOfTaskGroups -Projectname 'YourAzDoProject' -Profilename 'Alternative Alias in config.json' +``` + +Will Take The Task Groups specified in $ArrayOfTaskGroups and for each object will extract all elements from a Task Group and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Task Group steps will be converted to a template call. Will use the Connection details specified in -profilename rather than the first entry in config.json. + +### Example 3 +```powershell +PS C:\> Get-AzDoAPIToolsTaskGroupAsYAMLPrepped -TaskGroupsToConvert $ArrayOfTaskGroups -Projectname 'YourAzDoProject' -ExpandNestedTaskGroups +``` + +Will Take The Task Groups specified in $ArrayOfTaskGroups and for each object will extract all elements from a Task Group and converts them to a YAML ready PSObject. It will do this for the specified -Projectname on AzDo. Task Groups found in Task Group steps will be iterated over and expanded as seperate steps in the converted Task Group Template. + +### Example 4 +```powershell +PS C:\> Get-AzDoAPIToolsTaskGroupAsYAMLPrepped -TaskGroupsToConvert $ArrayOfTaskGroups -Projectname 'YourAzDoProject' -ExpandNestedTaskGroups -OutputAsFile -OutPutPath 'C:\OutPut' +``` + +Will Take The Task Groups specified in $ArrayOfTaskGroups and for each object will extract all elements from a Task Group and converts them to a yml file called TaskGroupName.yml inside C:\OutPut. It will do this for the specified -Projectname on AzDo. Task Groups found in Task Group steps will be iterated over and expanded as seperate steps in the converted Task Group Template. + +## PARAMETERS + +### -ExpandNestedTaskGroups +Switch to determine whether or not to expand found Task Groups or to call them as templates. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OutputPath +If Specified will write to this path when -OutPutAsFile is used. If the specified path does not exist you will be prompted to have it created. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Outputasfile +{Switch to specify the output has to written to a file. When used also specify -OutPutPath. If not used the function will return a PSObject instead. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Projectname +The project to use to call the AzDo API. Should be the same as the project used to add Metadata / retrieve the Build Definitions. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TaskGroupsToConvert +Array of Task Groups with MetaData to be converted to YAML Pipelines + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -profilename +Optional parameter to target a specific alias inside the config.json to use as a connection to AzDo. The -ProjectName provided should be accessible from this profile. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/functions/Set-AzdoAPIToolsConfig.md b/docs/functions/Set-AzdoAPIToolsConfig.md new file mode 100644 index 0000000..f103b85 --- /dev/null +++ b/docs/functions/Set-AzdoAPIToolsConfig.md @@ -0,0 +1,67 @@ +--- +external help file: AzdoAPITools-help.xml +Module Name: AzDoAPITools +online version: +schema: 2.0.0 +--- + +# Set-AzdoAPIToolsConfig + +## SYNOPSIS +A simple function to add & overwrite the config.json file used by the module. + +## SYNTAX + +``` +Set-AzdoAPIToolsConfig [[-configfilepath] ] [] +``` + +## DESCRIPTION +A simple function to add & overwrite the config.json file used by the module. + +## EXAMPLES + +### Example 1 +```powershell +PS C:\> Set-AzdoAPIToolsConfig +``` + +Will start the process to save a config.json file used by this module in %AppData%\AzDoAPITools. If a config file exists you will be prompted if you want to overwrite the existing file or if you want to append / add to the existing config.json file. If you choose to overwrite it will create a new config.json file with the details requested. If you choose to append / add you will be prompted for the details. If the Alias you entered for the connection to save exists in the configfile you will be prompted if it needs to be overwritten or appended. + +### Example 2 +```powershell +PS C:\> Set-AzdoAPIToolsConfig -configfilepath C:\testpath +``` + +Will start the process to save a config.json file used by this module in C:\testpath. If a config file exists you will be prompted if you want to overwrite the existing file or if you want to append / add to the existing config.json file. If you choose to overwrite it will create a new config.json file with the details requested. If you choose to append / add you will be prompted for the details. If the Alias you entered for the connection to save exists in the configfile you will be prompted if it needs to be overwritten or appended. + +## PARAMETERS + +### -configfilepath +Specifies a path where to save your configfile. + +```yaml +Type: Object +Parameter Sets: (All) +Aliases: + +Required: False +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.Object +## NOTES + +## RELATED LINKS diff --git a/docs/images/2020-09-10-15-57-40.png b/docs/images/2020-09-10-15-57-40.png new file mode 100644 index 0000000..24e52cc Binary files /dev/null and b/docs/images/2020-09-10-15-57-40.png differ diff --git a/docs/images/2020-09-10-16-08-53.png b/docs/images/2020-09-10-16-08-53.png new file mode 100644 index 0000000..5786abf Binary files /dev/null and b/docs/images/2020-09-10-16-08-53.png differ diff --git a/docs/images/2020-09-10-16-17-04.png b/docs/images/2020-09-10-16-17-04.png new file mode 100644 index 0000000..c130253 Binary files /dev/null and b/docs/images/2020-09-10-16-17-04.png differ diff --git a/docs/images/2020-09-10-16-17-45.png b/docs/images/2020-09-10-16-17-45.png new file mode 100644 index 0000000..d769605 Binary files /dev/null and b/docs/images/2020-09-10-16-17-45.png differ diff --git a/docs/images/2020-09-10-16-28-30.png b/docs/images/2020-09-10-16-28-30.png new file mode 100644 index 0000000..dadebdd Binary files /dev/null and b/docs/images/2020-09-10-16-28-30.png differ diff --git a/docs/images/2020-09-10-16-28-57.png b/docs/images/2020-09-10-16-28-57.png new file mode 100644 index 0000000..f7dcf9f Binary files /dev/null and b/docs/images/2020-09-10-16-28-57.png differ diff --git a/docs/images/2020-09-10-16-30-30.png b/docs/images/2020-09-10-16-30-30.png new file mode 100644 index 0000000..4733c0f Binary files /dev/null and b/docs/images/2020-09-10-16-30-30.png differ diff --git a/docs/images/2020-09-10-16-53-28.png b/docs/images/2020-09-10-16-53-28.png new file mode 100644 index 0000000..3747676 Binary files /dev/null and b/docs/images/2020-09-10-16-53-28.png differ diff --git a/docs/images/2020-09-10-16-54-08.png b/docs/images/2020-09-10-16-54-08.png new file mode 100644 index 0000000..e2ce2e3 Binary files /dev/null and b/docs/images/2020-09-10-16-54-08.png differ diff --git a/docs/images/2020-09-10-17-00-04.png b/docs/images/2020-09-10-17-00-04.png new file mode 100644 index 0000000..5238d27 Binary files /dev/null and b/docs/images/2020-09-10-17-00-04.png differ diff --git a/docs/images/2020-09-10-17-05-51.png b/docs/images/2020-09-10-17-05-51.png new file mode 100644 index 0000000..b81dbda Binary files /dev/null and b/docs/images/2020-09-10-17-05-51.png differ diff --git a/docs/images/2020-09-10-17-09-21.png b/docs/images/2020-09-10-17-09-21.png new file mode 100644 index 0000000..8c9119e Binary files /dev/null and b/docs/images/2020-09-10-17-09-21.png differ diff --git a/docs/images/2020-09-10-21-51-17.png b/docs/images/2020-09-10-21-51-17.png new file mode 100644 index 0000000..db966bd Binary files /dev/null and b/docs/images/2020-09-10-21-51-17.png differ diff --git a/docs/images/2020-09-10-21-58-29.png b/docs/images/2020-09-10-21-58-29.png new file mode 100644 index 0000000..53178d2 Binary files /dev/null and b/docs/images/2020-09-10-21-58-29.png differ diff --git a/docs/images/2020-09-10-22-01-42.png b/docs/images/2020-09-10-22-01-42.png new file mode 100644 index 0000000..2739fab Binary files /dev/null and b/docs/images/2020-09-10-22-01-42.png differ diff --git a/docs/images/2020-09-10-22-34-22.png b/docs/images/2020-09-10-22-34-22.png new file mode 100644 index 0000000..99860c3 Binary files /dev/null and b/docs/images/2020-09-10-22-34-22.png differ diff --git a/docs/images/2020-09-10-23-34-21.png b/docs/images/2020-09-10-23-34-21.png new file mode 100644 index 0000000..aefaf7a Binary files /dev/null and b/docs/images/2020-09-10-23-34-21.png differ diff --git a/docs/images/2020-09-11-09-32-19.png b/docs/images/2020-09-11-09-32-19.png new file mode 100644 index 0000000..0ea7fc2 Binary files /dev/null and b/docs/images/2020-09-11-09-32-19.png differ diff --git a/docs/images/2020-09-11-09-34-35.png b/docs/images/2020-09-11-09-34-35.png new file mode 100644 index 0000000..e1cc41e Binary files /dev/null and b/docs/images/2020-09-11-09-34-35.png differ