vsteam.functions.ps1

# Apply types to the returned objects so format and type files can
# identify the object and act on it.
function _applyTypes {
   [CmdletBinding()]
   param(
      $item,
      $type
   )
   $item.PSObject.TypeNames.Insert(0, $type)
}
function _applyTypesWorkItemType {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.WorkItemType')
}
function _applyTypesToWorkItem {
   [CmdletBinding()]
   param($item)
   # If there are ids in the list that don't map to a work item and empty
   # entry is returned in its place if ErrorPolicy is Omit.
   if ($item) {
      $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.WorkItem')
   }
}
function _applyTypesToWiql {
   [CmdletBinding()]
   param($item)
   if ($item) {
      $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.Wiql')
   }
}
function _applyTypesToTfvcBranch {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.TfvcBranch')
}
function _applyTypesToTeamMember {
   [CmdletBinding()]
   param(
      [Parameter(Mandatory = $true)]
      $item,
      [Parameter(Mandatory = $true)]
      $team,
      [Parameter(Mandatory = $true)]
      $ProjectName
   )
   # Add the team name as a NoteProperty so we can use it further down the pipeline (it's not returned from the REST call)
   $item | Add-Member -MemberType NoteProperty -Name Team -Value $team
   $item | Add-Member -MemberType NoteProperty -Name ProjectName -Value $ProjectName
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.TeamMember')
}
function _applyTypesToApproval {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.Approval')
}
function _applyArtifactTypes {
   $item.PSObject.TypeNames.Insert(0, "vsteam_lib.Build.Artifact")
   if ($item.PSObject.Properties.Match('resource').count -gt 0 -and $null -ne $item.resource -and $item.resource.PSObject.Properties.Match('propeties').count -gt 0) {
      $item.resource.PSObject.TypeNames.Insert(0, 'vsteam_lib.Build.Artifact.Resource')
      $item.resource.properties.PSObject.TypeNames.Insert(0, 'vsteam_lib.Build.Artifact.Resource.Properties')
   }
}
function _applyTypesToAzureSubscription {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.AzureSubscription')
}
function _applyTypesToPolicy {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.Policy')
}
function _applyTypesToPolicyType {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.PolicyType')
}
function _applyTypesToPullRequests {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.PullRequest')
}
function _applyTypesToServiceEndpoint {
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.ServiceEndpoint')
   $item.createdBy.PSObject.TypeNames.Insert(0, 'vsteam_lib.User')
   $item.authorization.PSObject.TypeNames.Insert(0, 'vsteam_lib.authorization')
   $item.data.PSObject.TypeNames.Insert(0, 'vsteam_lib.ServiceEndpoint.Details')
   if ($item.PSObject.Properties.Match('operationStatus').count -gt 0 -and $null -ne $item.operationStatus) {
      # This is VSTS
      $item.operationStatus.PSObject.TypeNames.Insert(0, 'vsteam_lib.OperationStatus')
   }
}
function _applyTypesToServiceEndpointType {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.ServiceEndpointType')
   $item.inputDescriptors.PSObject.TypeNames.Insert(0, 'vsteam_lib.InputDescriptor[]')
   foreach ($inputDescriptor in $item.inputDescriptors) {
      $inputDescriptor.PSObject.TypeNames.Insert(0, 'vsteam_lib.InputDescriptor')
   }
   $item.authenticationSchemes.PSObject.TypeNames.Insert(0, 'vsteam_lib.AuthenticationScheme[]')
   foreach ($authenticationScheme in $item.authenticationSchemes) {
      $authenticationScheme.PSObject.TypeNames.Insert(0, 'vsteam_lib.AuthenticationScheme')
   }
   if ($item.PSObject.Properties.Match('dataSources').count -gt 0 -and $null -ne $item.dataSources) {
      $item.dataSources.PSObject.TypeNames.Insert(0, 'vsteam_lib.DataSource[]')
      foreach ($dataSource in $item.dataSources) {
         $dataSource.PSObject.TypeNames.Insert(0, 'vsteam_lib.DataSource')
      }
   }
}
function _applyTypesToVariableGroup {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.VariableGroup')
   $item.createdBy.PSObject.TypeNames.Insert(0, 'vsteam_lib.User')
   $item.modifiedBy.PSObject.TypeNames.Insert(0, 'vsteam_lib.User')
   if ($item.PSObject.Properties.Match('providerData').count -gt 0 -and $null -ne $item.providerData) {
      $item.providerData.PSObject.TypeNames.Insert(0, 'vsteam_lib.ProviderData')
   }
   $item.variables.PSObject.TypeNames.Insert(0, 'vsteam_lib.Variables')
}
function _applyTypesToYamlPipelineResultType {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.YamlPipelineResult')
}
function _applyTypesToBuildTimelineResultType {
   [CmdletBinding()]
   param($item)
   $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.BuildTimeline')
   if ($item.PSObject.Properties.Match('records').count -gt 0 -and $null -ne $item.records) {
      $item.records.PSObject.TypeNames.Insert(0, 'vsteam_lib.BuildTimelineRecord[]')
      foreach ($records in $item.records) {
         $records.PSObject.TypeNames.Insert(0, 'vsteam_lib.BuildTimelineRecord')
      }
   }
}
function _applyTypesToAgentPoolMaintenance {
   [CmdletBinding()]
   param($item)
      $item.PSObject.TypeNames.Insert(0, 'vsteam_lib.AgentPoolMaintenance')
}
function _callMembershipAPI {
   [CmdletBinding()]
   param(
      [Parameter(Mandatory = $true)]
      [string] $Id,
      [ValidateSet('GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS', 'PUT', 'DEFAULT', 'HEAD', 'MERGE', 'TRACE')]
      [string] $Method = 'GET',
      [ValidateSet('', 'Up', 'Down')]
      [string] $Direction
   )
   # This will throw if this account does not support the graph API
   _supportsGraph
   Write-Verbose "Getting members for $Id"
   $query = @{}
   if ($Direction) {
      $query['direction'] = $Direction
   }
   # Call the REST API
   $resp = _callAPI -Method $Method -SubDomain vssps `
      -Area 'graph' `
      -Resource 'memberships' `
      -Id $Id `
      -QueryString $query `
      -Version $(_getApiVersion Graph)
   return $resp.value
}
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "It is used in other files")]
$profilesPath = "$HOME/vsteam_profiles.json"
# This is the main function for calling TFS and VSTS. It handels the auth and format of the route.
# If you need to call TFS or VSTS this is the function to use.
function _callAPI {
   [CmdletBinding()]
   param(
      [string]$resource,
      [string]$area,
      [string]$id,
      [string]$version,
      [string]$subDomain,
      [ValidateSet('Get', 'Post', 'Patch', 'Delete', 'Options', 'Put', 'Default', 'Head', 'Merge', 'Trace')]
      [string]$method,
      [Parameter(ValueFromPipeline = $true)]
      [object]$body,
      [string]$InFile,
      [string]$OutFile,
      [string]$ContentType,
      [string]$ProjectName,
      [string]$Team,
      [string]$Url,
      [object]$QueryString,
      [hashtable]$AdditionalHeaders = @{ },
      # Some API calls require the Project ID and not the project name.
      # However, the dynamic project name parameter only shows you names
      # and not the Project IDs. Using this flag the project name provided
      # will be converted to the Project ID when building the URI for the API
      # call.
      [switch]$UseProjectId,
      # This flag makes sure that even if a default project is set that it is
      # not used to build the URI for the API call. Not all API require or
      # allow the project to be used. Setting a default project would cause
      # that project name to be used in building the URI that would lead to
      # 404 because the URI would not be correct.
      [Alias('IgnoreDefaultProject')]
      [switch]$NoProject,
      # This flag makes sure that no specific account is used
      # some APIs do not have an account in their API uri because
      # they are not account specific in the url path itself. (e.g. user profile, pipeline billing)
      [switch]$NoAccount
   )
   process {
      # If the caller did not provide a Url build it.
      if (-not $Url) {
         $buildUriParams = @{ } + $PSBoundParameters;
         $extra = 'method', 'body', 'InFile', 'OutFile', 'ContentType', 'AdditionalHeaders'
         foreach ($x in $extra) { $buildUriParams.Remove($x) | Out-Null }
         $Url = _buildRequestURI @buildUriParams
      }
      elseif ($QueryString) {
         # If the caller provided the URL and QueryString we need
         # to add the querystring now
         foreach ($key in $QueryString.keys) {
            $Url += _appendQueryString -name $key -value $QueryString[$key]
         }
      }
      if ($body) {
         Write-Verbose "Body $body"
      }
      $params = $PSBoundParameters
      $params.Add('Uri', $Url)
      $params.Add('UserAgent', (_getUserAgent))
      $params.Add('TimeoutSec', (_getDefaultTimeout))
      # always use utf8 and json as default content type instead of xml
      if ($false -eq $PSBoundParameters.ContainsKey("ContentType")) {
         $params.Add('ContentType', 'application/json; charset=utf-8')
      }
      # do not use header when requested. Then bearer must be provided with additional headers
      $params.Add('Headers', @{ })
      # checking if an authorization token is provided already with the additional headers
      # use case: sometimes other tokens for certain APIs have to be used (buying pipelines) in order to work
      # some parts of internal APIs use their own token based on the PAT
      if (!$AdditionalHeaders.ContainsKey("Authorization")) {
         if (_useWindowsAuthenticationOnPremise) {
            $params.Add('UseDefaultCredentials', $true)
         }
         elseif (_useBearerToken) {
            $params['Headers'].Add("Authorization", "Bearer $env:TEAM_TOKEN")
         }
         else {
            $params['Headers'].Add("Authorization", "Basic $env:TEAM_PAT")
         }
      }
      if ($AdditionalHeaders -and $AdditionalHeaders.PSObject.Properties.name -match "Keys") {
         foreach ($key in $AdditionalHeaders.Keys) {
            $params['Headers'].Add($key, $AdditionalHeaders[$key])
         }
      }
      # We have to remove any extra parameters not used by Invoke-RestMethod
      $extra = 'NoAccount', 'NoProject', 'UseProjectId', 'Area', 'Resource', 'SubDomain', 'Id', 'Version', 'JSON', 'ProjectName', 'Team', 'Url', 'QueryString', 'AdditionalHeaders', 'CustomBearer'
      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 {
         _handleException $_
         throw
      }
   }
}
# Not all versions support the name features.
function _supportsGraph {
   _hasAccount
   if ($false -eq $(_testGraphSupport)) {
      throw 'This account does not support the graph API.'
   }
}
function _testGraphSupport {
   (_getApiVersion Graph) -as [boolean]
}
function _supportsHierarchyQuery {
   _hasAccount
   if ($false -eq $(_testHierarchyQuerySupport)) {
      throw 'This account does not support the hierarchy query API.'
   }
}
function _testHierarchyQuerySupport {
   (_getApiVersion HierarchyQuery) -as [boolean]
}
function _supportsBilling {
   _hasAccount
   if ($false -eq $(_testBillingSupport)) {
      throw 'This account does not support the billing API.'
   }
}
function _testBillingSupport {
   (_getApiVersion Billing) -as [boolean]
}
function _supportVariableGroups {
   _hasAccount
   if ($false -eq $(_testVariableGroupsSupport)) {
      throw 'This account does not support the variable groups.'
   }
}
function _testVariableGroupsSupport {
   (_getApiVersion VariableGroups) -as [boolean]
}
function _supportsSecurityNamespace {
   _hasAccount
   if (([vsteam_lib.Versions]::Version -ne "VSTS") -and ([vsteam_lib.Versions]::Version -ne "AzD")) {
      throw 'Security Namespaces are currently only supported in Azure DevOps Service (Online)'
   }
}
function _supportsMemberEntitlementManagement {
   _hasAccount
   if (-not $(_getApiVersion MemberEntitlementManagement)) {
      throw 'This account does not support Member Entitlement.'
   }
}
function _testAdministrator {
   $user = [Security.Principal.WindowsIdentity]::GetCurrent()
   (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
# When you mock this in tests be sure to add a Parameter Filter that matches
# the Service that should be used.
# Mock _getApiVersion { return '1.0-gitUnitTests' } -ParameterFilter { $Service -eq 'Git' }
# Also test in the Assert-MockCalled that the correct version was used in the URL that was
# built for the API call.
function _getApiVersion {
   [CmdletBinding(DefaultParameterSetName = 'Service')]
   [OutputType([string])]
   param (
      [parameter(ParameterSetName = 'Service', Mandatory = $true, Position = 0)]
      [ValidateSet('Build', 'Release', 'Core', 'Git', 'DistributedTask',
         'DistributedTaskReleased', 'VariableGroups', 'Tfvc',
         'Packaging', 'MemberEntitlementManagement',
         'ExtensionsManagement', 'ServiceEndpoints', 'Graph',
         'TaskGroups', 'Policy', 'Processes', 'HierarchyQuery', 'Pipelines', 'Billing', 'Wiki')]
      [string] $Service,
      [parameter(ParameterSetName = 'Target')]
      [switch] $Target
   )
   if ($Target.IsPresent) {
      return [vsteam_lib.Versions]::GetApiVersion("Version")
   }
   else {
      return [vsteam_lib.Versions]::GetApiVersion($Service)
   }
}
function _getInstance {
   return [vsteam_lib.Versions]::Account
}
function _getDefaultTimeout {
   if ($Global:PSDefaultParameterValues["*-vsteam*:vsteamApiTimeout"]) {
      return $Global:PSDefaultParameterValues["*-vsteam*:vsteamApiTimeout"]
   }
   else {
      return 60
   }
}
function _getDefaultProject {
   return $Global:PSDefaultParameterValues["*-vsteam*:projectName"]
}
function _hasAccount {
   if (-not $(_getInstance)) {
      throw 'You must call Set-VSTeamAccount before calling any other functions in this module.'
   }
}
function _buildRequestURI {
   [CmdletBinding()]
   param(
      [string]$team,
      [string]$resource,
      [string]$area,
      [string]$id,
      [string]$version,
      [string]$subDomain,
      [object]$queryString,
      [Parameter(Mandatory = $false)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      $ProjectName,
      [switch]$UseProjectId,
      [switch]$NoProject,
      [switch]$NoAccount
   )
   process {
      _hasAccount
      $sb = New-Object System.Text.StringBuilder
      $instance = "https://dev.azure.com"
      if ($NoAccount.IsPresent -eq $false) {
         $instance = _getInstance
      }
      $sb.Append($(_addSubDomain -subDomain $subDomain -instance $instance)) | Out-Null
      # There are some APIs that must not have the project added to the URI.
      # However, if they caller set the default project it will be passed in
      # here and added to the URI by mistake. Functions that need the URI
      # created without the project even if the default project is set needs
      # to pass the -NoProject switch.
      if ($ProjectName -and $NoProject.IsPresent -eq $false -and $NoAccount.IsPresent -eq $false) {
         if ($UseProjectId.IsPresent) {
            $projectId = (Get-VSTeamProject -Name $ProjectName | Select-Object -ExpandProperty id)
            $sb.Append("/$projectId") | Out-Null
         }
         else {
            $sb.Append("/$projectName") | Out-Null
         }
      }
      if ($team) {
         $sb.Append("/$team") | 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()
      if ($queryString) {
         foreach ($key in $queryString.keys) {
            $Url += _appendQueryString -name $key -value $queryString[$key]
         }
      }
      return $url
   }
}
function _handleException {
   param(
      [Parameter(Position = 1)]
      $ex
   )
   $handled = $false
   if ($ex.Exception.PSObject.Properties.Match('Response').count -gt 0 -and
      $null -ne $ex.Exception.Response -and
      $ex.Exception.Response.StatusCode -ne "BadRequest") {
      $handled = $true
      $msg = "An error occurred: $($ex.Exception.Message)"
      Write-Warning -Message $msg
   }
   try {
      $e = (ConvertFrom-Json $ex.ToString())
      $hasValueProp = $e.PSObject.Properties.Match('value')
      if (0 -eq $hasValueProp.count) {
         $handled = $true
         Write-Warning -Message $e.message
      }
      else {
         $handled = $true
         Write-Warning -Message $e.value.message
      }
   }
   catch {
      $msg = "An error occurred: $($ex.Exception.Message)"
   }
   if (-not $handled) {
      throw $ex
   }
}
function _isVSTS {
   param(
      [parameter(Mandatory = $true)]
      [string] $instance
   )
   return $instance -like "*.visualstudio.com*" -or $instance -like "https://dev.azure.com/*"
}
function _getVSTeamAPIVersion {
   param(
      [parameter(Mandatory = $true)]
      [string] $instance,
      [string] $Version
   )
   if ($Version) {
      return $Version
   }
   else {
      if (_isVSTS $instance) {
         return 'VSTS'
      }
      else {
         return 'TFS2017'
      }
   }
}
function _isOnWindows {
   $os = Get-OperatingSystem
   return $os -eq 'Windows'
}
function _addSubDomain {
   param(
      [string] $subDomain,
      [string] $instance
   )
   # For VSTS Entitlements is under .vsaex
   if ($subDomain -and $instance.ToLower().Contains('dev.azure.com')) {
      $instance = $instance.ToLower().Replace('dev.azure.com', "$subDomain.dev.azure.com")
   }
   return $instance
}
function _appendQueryString {
   param(
      $name,
      $value,
      # When provided =0 will be outputed otherwise zeros will not be
      # added. I had to add this for the userentitlements that is the only
      # VSTS API I have found that requires Top and Skip to be passed in.
      [Switch]$retainZero
   )
   if ($retainZero.IsPresent) {
      if ($null -ne $value) {
         return "&$name=$value"
      }
   }
   else {
      if ($value) {
         return "&$name=$value"
      }
   }
}
function _getUserAgent {
   [CmdletBinding()]
   param()
   $os = Get-OperatingSystem
   $result = "Team Module/$([vsteam_lib.Versions]::ModuleVersion) ($os) PowerShell/$($PSVersionTable.PSVersion.ToString())"
   Write-Verbose $result
   return $result
}
function _useWindowsAuthenticationOnPremise {
   return (_isOnWindows) -and (!$env:TEAM_PAT) -and -not ($(_getInstance) -like "*visualstudio.com") -and -not ($(_getInstance) -like "https://dev.azure.com/*")
}
function _useBearerToken {
   return (!$env:TEAM_PAT) -and ($env:TEAM_TOKEN)
}
function _getWorkItemTypes {
   param(
      [Parameter(Mandatory = $true)]
      [string] $ProjectName
   )
   if (-not $(_getInstance)) {
      Write-Output @()
      return
   }
   # Call the REST API
   try {
      $resp = _callAPI -ProjectName $ProjectName `
         -area wit `
         -resource workitemtypes `
         -version $(_getApiVersion Core)
      # This call returns JSON with "": which causes the ConvertFrom-Json to fail.
      # To replace all the "": with "_end":
      $resp = $resp.Replace('"":', '"_end":') | ConvertFrom-Json
      if ($resp.count -gt 0) {
         Write-Output ($resp.value).name
      }
   }
   catch {
      Write-Verbose $_
      Write-Output @()
   }
}
function _buildLevelDynamicParam {
   param ()
   # # Only add these options on Windows Machines
   if (_isOnWindows) {
      $ParameterName = 'Level'
      # Create the dictionary
      $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
      # Create the collection of attributes
      $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
      # Create and set the parameters' attributes
      $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
      $ParameterAttribute.Mandatory = $false
      $ParameterAttribute.HelpMessage = "On Windows machines allows you to store the default project at the process, user or machine level. Not available on other platforms."
      # Add the attributes to the attributes collection
      $AttributeCollection.Add($ParameterAttribute)
      # Generate and set the ValidateSet
      if (_testAdministrator) {
         $arrSet = "Process", "User", "Machine"
      }
      else {
         $arrSet = "Process", "User"
      }
      $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
      # Add the ValidateSet to the attributes collection
      $AttributeCollection.Add($ValidateSetAttribute)
      # Create and return the dynamic parameter
      $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
      $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
      return $RuntimeParameterDictionary
   }
}
function _buildProjectNameDynamicParam {
   param(
      [string] $ParameterName = 'ProjectName',
      [string] $ParameterSetName,
      [bool] $Mandatory = $true,
      [string] $AliasName,
      [int] $Position = 0
   )
   # Create the dictionary
   $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
   # Create the collection of attributes
   $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
   # Create and set the parameters' attributes
   $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
   $ParameterAttribute.Mandatory = $Mandatory
   $ParameterAttribute.Position = $Position
   if ($ParameterSetName) {
      $ParameterAttribute.ParameterSetName = $ParameterSetName
   }
   $ParameterAttribute.ValueFromPipelineByPropertyName = $true
   $ParameterAttribute.HelpMessage = "The name of the project. You can tab complete from the projects in your Team Services or TFS account when passed on the command line."
   # Add the attributes to the attributes collection
   $AttributeCollection.Add($ParameterAttribute)
   if ($AliasName) {
      $AliasAttribute = New-Object System.Management.Automation.AliasAttribute(@($AliasName))
      $AttributeCollection.Add($AliasAttribute)
   }
   # Generate and set the ValidateSet
   $arrSet = [vsteam_lib.ProjectCache]::GetCurrent($false)
   if ($arrSet) {
      Write-Verbose "arrSet = $arrSet"
      $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
      # Add the ValidateSet to the attributes collection
      $AttributeCollection.Add($ValidateSetAttribute)
   }
   # Create and return the dynamic parameter
   $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
   $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
   return $RuntimeParameterDictionary
   <#
   Builds a dynamic parameter that can be used to tab complete the ProjectName
   parameter of functions from a list of projects from the added TFS Account.
   You must call Set-VSTeamAccount before trying to use any function that relies
   on this dynamic parameter or you will get an error.
   This can only be used in Advanced Fucntion with the [CmdletBinding()] attribute.
   The function must also have a begin block that maps the value to a common variable
   like this.
      DynamicParam {
         # Generate and set the ValidateSet
         $arrSet = Get-VSTeamProjects | Select-Object -ExpandProperty Name
         _buildProjectNameDynamicParam -arrSet $arrSet
      }
      process {
         # Bind the parameter to a friendly variable
         $ProjectName = $PSBoundParameters[$ParameterName]
      }
   #>

}
function _buildProcessNameDynamicParam {
   param(
      [string] $ParameterName = 'ProcessName',
      [string] $ParameterSetName,
      [bool] $Mandatory = $true,
      [string] $AliasName,
      [int] $Position = 0
   )
   # Create the dictionary
   $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
   # Create the collection of attributes
   $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
   # Create and set the parameters' attributes
   $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
   $ParameterAttribute.Mandatory = $Mandatory
   $ParameterAttribute.Position = $Position
   if ($ParameterSetName) {
      $ParameterAttribute.ParameterSetName = $ParameterSetName
   }
   $ParameterAttribute.ValueFromPipelineByPropertyName = $true
   $ParameterAttribute.HelpMessage = "The name of the process. You can tab complete from the processes in your Team Services or TFS account when passed on the command line."
   # Add the attributes to the attributes collection
   $AttributeCollection.Add($ParameterAttribute)
   if ($AliasName) {
      $AliasAttribute = New-Object System.Management.Automation.AliasAttribute(@($AliasName))
      $AttributeCollection.Add($AliasAttribute)
   }
   # Generate and set the ValidateSet
   $arrSet = [vsteam_lib.ProcessTemplateCache]::GetCurrent()
   if ($arrSet) {
      Write-Verbose "arrSet = $arrSet"
      $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
      # Add the ValidateSet to the attributes collection
      $AttributeCollection.Add($ValidateSetAttribute)
   }
   # Create and return the dynamic parameter
   $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
   $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
   return $RuntimeParameterDictionary
   <#
   Builds a dynamic parameter that can be used to tab complete the ProjectName
   parameter of functions from a list of projects from the added TFS Account.
   You must call Set-VSTeamAccount before trying to use any function that relies
   on this dynamic parameter or you will get an error.
   This can only be used in Advanced Fucntion with the [CmdletBinding()] attribute.
   The function must also have a begin block that maps the value to a common variable
   like this.
      DynamicParam {
         # Generate and set the ValidateSet
         $arrSet = Get-VSTeamProjects | Select-Object -ExpandProperty Name
         _buildProjectNameDynamicParam -arrSet $arrSet
      }
      process {
         # Bind the parameter to a friendly variable
         $ProjectName = $PSBoundParameters[$ParameterName]
      }
   #>

}
function _buildDynamicParam {
   param(
      [string] $ParameterName = 'QueueName',
      [array] $arrSet,
      [bool] $Mandatory = $false,
      [string] $ParameterSetName,
      [int] $Position = -1,
      [type] $ParameterType = [string],
      [bool] $ValueFromPipelineByPropertyName = $true,
      [string] $AliasName,
      [string] $HelpMessage
   )
   # Create the collection of attributes
   $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
   <#
.SYNOPSIS
Short description
.DESCRIPTION
Long description
.PARAMETER ParameterName
Parameter description
.PARAMETER ParameterSetName
Parameter description
.PARAMETER Mandatory
Parameter description
.PARAMETER AliasName
Parameter description
.PARAMETER Position
Parameter description
.EXAMPLE
An example
.NOTES
General notes
#>

   # Create and set the parameters' attributes
   $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
   $ParameterAttribute.Mandatory = $Mandatory
   $ParameterAttribute.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName
   if ($Position -ne -1) {
      $ParameterAttribute.Position = $Position
   }
   if ($ParameterSetName) {
      $ParameterAttribute.ParameterSetName = $ParameterSetName
   }
   if ($HelpMessage) {
      $ParameterAttribute.HelpMessage = $HelpMessage
   }
   # Add the attributes to the attributes collection
   $AttributeCollection.Add($ParameterAttribute)
   if ($AliasName) {
      $AliasAttribute = New-Object System.Management.Automation.AliasAttribute(@($AliasName))
      $AttributeCollection.Add($AliasAttribute)
   }
   if ($arrSet) {
      # Generate and set the ValidateSet
      $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
      # Add the ValidateSet to the attributes collection
      $AttributeCollection.Add($ValidateSetAttribute)
   }
   # Create and return the dynamic parameter
   return New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, $ParameterType, $AttributeCollection)
}
function _convertSecureStringTo_PlainText {
   [CmdletBinding()]
   param(
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, HelpMessage = 'Secure String')]
      [securestring] $SecureString
   )
   # Convert the securestring to a normal string
   # this was the one technique that worked on Mac, Linux and Windows
   $credential = New-Object System.Management.Automation.PSCredential 'unknown', $SecureString
   return $credential.GetNetworkCredential().Password
}
function _trackProjectProgress {
   param(
      [Parameter(Mandatory = $true)] $Resp,
      [string] $Title,
      [string] $Msg
   )
   $i = 0
   $x = 1
   $y = 10
   $status = $resp.status
   # Track status
   while ($status -ne 'failed' -and $status -ne 'succeeded') {
      $status = (_callAPI -Url $resp.url).status
      # oscillate back a forth to show progress
      $i += $x
      Write-Progress -Activity $title -Status $msg -PercentComplete ($i / $y * 100)
      if ($i -eq $y -or $i -eq 0) {
         $x *= -1
      }
   }
}
$iTracking = 0
$xTracking = 1
$yTracking = 10
$statusTracking = $null
function _trackServiceEndpointProgress {
   param(
      [Parameter(Mandatory = $true)]
      [string] $projectName,
      [Parameter(Mandatory = $true)]
      $resp,
      [string] $title,
      [string] $msg
   )
   $iTracking = 0
   $xTracking = 1
   $yTracking = 10
   $isReady = $false
   # Track status
   while (-not $isReady) {
      $statusTracking = _callAPI -ProjectName $projectName -Area 'distributedtask' -Resource 'serviceendpoints' -Id $resp.id  `
         -Version $(_getApiVersion ServiceEndpoints)
      $isReady = $statusTracking.isReady;
      if (-not $isReady) {
         $state = $statusTracking.operationStatus.state
         if ($state -eq "Failed") {
            throw $statusTracking.operationStatus.statusMessage
         }
      }
      # oscillate back a forth to show progress
      $iTracking += $xTracking
      Write-Progress -Activity $title -Status $msg -PercentComplete ($iTracking / $yTracking * 100)
      if ($iTracking -eq $yTracking -or $iTracking -eq 0) {
         $xTracking *= -1
      }
   }
}
function _getModuleVersion {
   # Read the version from the psd1 file.
   # $content = (Get-Content -Raw "./VSTeam.psd1" | Out-String)
   $content = (Get-Content -Raw "$here\VSTeam.psd1" | Out-String)
   $r = [regex]"ModuleVersion += +'([^']+)'"
   $d = $r.Match($content)
   return $d.Groups[1].Value
}
function _setEnvironmentVariables {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   param (
      [string] $Level = "Process",
      [string] $Pat,
      [string] $Acct,
      [string] $BearerToken,
      [string] $Version
   )
   # You always have to set at the process level or they will Not
   # be seen in your current session.
   $env:TEAM_PAT = $Pat
   $env:TEAM_ACCT = $Acct
   $env:TEAM_VERSION = $Version
   $env:TEAM_TOKEN = $BearerToken
   [vsteam_lib.Versions]::Account = $Acct
   # This is so it can be loaded by default in the next session
   if ($Level -ne "Process") {
      [System.Environment]::SetEnvironmentVariable("TEAM_PAT", $Pat, $Level)
      [System.Environment]::SetEnvironmentVariable("TEAM_ACCT", $Acct, $Level)
      [System.Environment]::SetEnvironmentVariable("TEAM_VERSION", $Version, $Level)
   }
}
# If you remove an account the current default project needs to be cleared as well.
function _clearEnvironmentVariables {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   param (
      [string] $Level = "Process"
   )
   $env:TEAM_PROJECT = $null
   $env:TEAM_TIMEOUT = $null
   [vsteam_lib.Versions]::DefaultProject = ''
   [vsteam_lib.Versions]::DefaultTimeout = ''
   $Global:PSDefaultParameterValues.Remove("*-vsteam*:projectName")
   $Global:PSDefaultParameterValues.Remove("*-vsteam*:vsteamApiTimeout")
   # This is so it can be loaded by default in the next session
   if ($Level -ne "Process") {
      [System.Environment]::SetEnvironmentVariable("TEAM_PROJECT", $null, $Level)
      [System.Environment]::SetEnvironmentVariable("TEAM_TIMEOUT", $null, $Level)
   }
   _setEnvironmentVariables -Level $Level -Pat '' -Acct '' -UseBearerToken '' -Version ''
}
function _convertToHex() {
   [cmdletbinding()]
   param(
      [parameter(Mandatory = $true)]
      [string]$Value
   )
   $bytes = $Value | Format-Hex -Encoding Unicode
   $hexString = ($bytes.Bytes | ForEach-Object ToString X2) -join ''
   return $hexString.ToLowerInvariant();
}
function _getVSTeamIdFromDescriptor {
   [cmdletbinding()]
   param(
      [parameter(Mandatory = $true)]
      [string]$Descriptor
   )
   $identifier = $Descriptor.Split('.')[1]
   # We need to Pad the string for FromBase64String to work reliably (AzD Descriptors are not padded)
   $ModulusValue = ($identifier.length % 4)
   Switch ($ModulusValue) {
      '0' { $Padded = $identifier }
      '1' { $Padded = $identifier.Substring(0, $identifier.Length - 1) }
      '2' { $Padded = $identifier + ('=' * (4 - $ModulusValue)) }
      '3' { $Padded = $identifier + ('=' * (4 - $ModulusValue)) }
   }
   return [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($Padded))
}
function _getPermissionInheritanceInfo {
   [cmdletbinding()]
   [OutputType([System.Collections.Hashtable])]
   param(
      [parameter(Mandatory = $true)]
      [string] $projectName,
      [parameter(Mandatory = $true)]
      [string] $resourceName,
      [Parameter(Mandatory = $true)]
      [ValidateSet('Repository', 'BuildDefinition', 'ReleaseDefinition')]
      [string] $resourceType
   )
   $projectId = (Get-VSTeamProject -Name $projectName | Select-Object -ExpandProperty id)
   Switch ($resourceType) {
      "Repository" {
         $securityNamespaceID = "2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87"
         $repositoryId = (Get-VSTeamGitRepository -Name "$resourceName" -projectName $projectName | Select-Object -ExpandProperty id )
         if ($null -eq $repositoryId) {
            Write-Error "Unable to retrieve repository information. Ensure that the resourceName provided matches a repository name exactly."
            return
         }
         $token = "repoV2/$($projectId)/$repositoryId"
      }
      "BuildDefinition" {
         $securityNamespaceID = "33344d9c-fc72-4d6f-aba5-fa317101a7e9"
         $buildDefinitionId = (Get-VSTeamBuildDefinition -projectName $projectName | Where-Object name -eq "$resourceName" | Select-Object -ExpandProperty id)
         if ($null -eq $buildDefinitionId) {
            Write-Error "Unable to retrieve build definition information. Ensure that the resourceName provided matches a build definition name exactly."
            return
         }
         $token = "$($projectId)/$buildDefinitionId"
      }
      "ReleaseDefinition" {
         $securityNamespaceID = "c788c23e-1b46-4162-8f5e-d7585343b5de"
         $releaseDefinition = (Get-VSTeamReleaseDefinition -projectName $projectName | Where-Object -Property name -eq "$resourceName")
         if ($null -eq $releaseDefinition) {
            Write-Error "Unable to retrieve release definition information. Ensure that the resourceName provided matches a release definition name exactly."
            return
         }
         if (($releaseDefinition).path -eq "/") {
            $token = "$($projectId)/$($releaseDefinition.id)"
         }
         else {
            $token = "$($projectId)" + "$($releaseDefinition.path -replace "\\","/")" + "/$($releaseDefinition.id)"
         }
      }
   }
   return @{
      Token               = $token
      ProjectID           = $projectId
      SecurityNamespaceID = $securityNamespaceID
   }
}
function _getDescriptorForACL {
   [cmdletbinding()]
   param(
      [parameter(Mandatory = $true, ParameterSetName = "ByUser")]
      [vsteam_lib.User]$User,
      [parameter(MAndatory = $true, ParameterSetName = "ByGroup")]
      [vsteam_lib.Group]$Group
   )
   if ($User) {
      switch ($User.Origin) {
         "vsts" {
            $sid = _getVSTeamIdFromDescriptor -Descriptor $User.Descriptor
            if ($User.Descriptor.StartsWith('svc.')) {
               $descriptor = "Microsoft.TeamFoundation.ServiceIdentity;$sid"
            }
            else {
               $descriptor = "Microsoft.TeamFoundation.Identity;$sid"
            }
         }
         "aad" {
            $descriptor = "Microsoft.IdentityModel.Claims.ClaimsIdentity;$($User.Domain)\\$($User.PrincipalName)"
         }
         default { throw "User type not handled yet for ACL. Please report this as an issue on the VSTeam Repository: https://github.com/MethodsAndPractices/vsteam/issues" }
      }
   }
   if ($Group) {
      switch ($Group.Origin) {
         "vsts" {
            $sid = _getVSTeamIdFromDescriptor -Descriptor $Group.Descriptor
            $descriptor = "Microsoft.TeamFoundation.Identity;$sid"
         }
         default { throw "Group type not handled yet for Add-VSTeamGitRepositoryPermission. Please report this as an issue on the VSTeam Repository: https://github.com/MethodsAndPractices/vsteam/issues" }
      }
   }
   return $descriptor
}
function _getBillingToken {
   # get a billing access token by using the given PAT.
   # this token can be used for buying pipelines or artifacts
   # or other things used for billing (except user access levels)
   [CmdletBinding()]
   param (
      #billing token can have different scopes. They are defined by named token ids.
      #They should be validated to be specific by it's name
      [Parameter(Mandatory=$true)]
      [string]
      [ValidateSet('AzCommDeploymentProfile','CommerceDeploymentProfile')]
      $NamedTokenId
   )
   $sessionToken = @{
      appId        = 00000000 - 0000 - 0000 - 0000 - 000000000000
      force        = $false
      tokenType    = 0
      namedTokenId = $NamedTokenId
   }
   $billingToken = _callAPI `
      -NoProject `
      -method POST `
      -ContentType "application/json" `
      -area "WebPlatformAuth" `
      -resource "SessionToken" `
      -version '3.2-preview.1' `
      -body ($sessionToken | ConvertTo-Json -Depth 50 -Compress)
   return $billingToken
}
# Create a team in a team project.
#
# Get-VSTeamOption 'core' 'teams'
# id : d30a3dd1-f8ba-442a-b86a-bd0c0c383e59
# area : core
# resourceName : teams
# routeTemplate : _apis/projects/{projectId}/{resource}/{*teamId}
# https://bit.ly/Add-VSTeam
function Add-VSTeam {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeam')]
   param(
      [Parameter(Mandatory = $true, Position = 0)]
      [Alias('TeamName')]
      [string] $Name,
      [Parameter(Position = 1)]
      [string] $Description = '',
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $body = '{ "name": "' + $Name + '", "description": "' + $Description + '" }'
      $resp = _callAPI -Method POST -NoProject `
         -Resource "projects/$ProjectName/teams" `
         -Body $body `
         -Version $(_getApiVersion Core)
      $team = [vsteam_lib.Team]::new($resp, $ProjectName)
      Write-Output $team
   }
}
# Add or update ACEs in the ACL for the provided token. The request body
# contains the target token, a list of ACEs and a optional merge parameter.
# In the case of a collision (by identity descriptor) with an existing ACE
# in the ACL, the "merge" parameter determines the behavior. If set, the
# existing ACE has its allow and deny merged with the incoming ACE's allow
# and deny. If unset, the existing ACE is displaced.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Add-VSTeamAccessControlEntry {
   [CmdletBinding(DefaultParameterSetName = 'ByNamespace',
      HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamAccessControlEntry')]
   param(
      [Parameter(ParameterSetName = 'ByNamespace', Mandatory = $true, ValueFromPipeline = $true)]
      [vsteam_lib.SecurityNamespace] $SecurityNamespace,
      [Parameter(ParameterSetName = 'ByNamespaceId', Mandatory = $true)]
      [guid] $SecurityNamespaceId,
      [Parameter(Mandatory = $true)]
      [string] $Token,
      [Parameter(Mandatory = $true)]
      [string] $Descriptor,
      [Parameter(Mandatory = $true)]
      [ValidateRange(0, [int]::MaxValue)]
      [int] $AllowMask,
      [Parameter(Mandatory = $true)]
      [ValidateRange(0, [int]::MaxValue)]
      [int] $DenyMask
   )
   process {
      if ($AllowMask -eq 0 -and $DenyMask -eq 0) {
         Write-Warning "Permission masks for Allow and Deny do not inlude any permission. No Permission will change!"
      }
      if ($SecurityNamespace) {
         $SecurityNamespaceId = $SecurityNamespace.ID
      }
      $body =
      @"
   {
      "token": "$Token",
      "merge": true,
      "accessControlEntries": [
         {
            "descriptor": "$Descriptor",
            "allow": $AllowMask,
            "deny": $DenyMask,
            "extendedinfo": {}
         }
      ]
   }
"@

      $resp = _callAPI -Method POST -NoProject `
         -Resource "accesscontrolentries" `
         -Id $SecurityNamespaceId `
         -Body $body `
         -Version $(_getApiVersion Core)
      if ($resp.count -ne 1) {
         throw "Expected 1 result, but got $($rep.count)"
      }
      # Storing the object before you return it cleaned up the pipeline.
      # When I just write the object from the constructor each property
      # seemed to be written
      $acl = [vsteam_lib.AccessControlEntry]::new($resp.value[0])
      Write-Output $acl
   }
}
# Create new or update an existing classification node.
#
# Get-VSTeamOption 'wit' 'classificationNodes'
# id : 5a172953-1b41-49d3-840a-33f79c3ce89f
# area : wit
# resourceName : classificationNodes
# routeTemplate : {project}/_apis/{area}/{resource}/{structureGroup}/{*path}
# https://bit.ly/Add-VSTeamClassificationNode
function Add-VSTeamArea {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamArea')]
   param(
      [Parameter(Mandatory = $true, Position = 0)]
      [string] $Name,
      [Parameter(Mandatory = $false)]
      [string] $Path,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $resp = Add-VSTeamClassificationNode -ProjectName $ProjectName `
         -Name $Name `
         -StructureGroup areas `
         -Path $Path
      Write-Output $resp
   }
}
# Create a service endpoint.
#
# Get-VSTeamOption 'distributedtask' 'serviceendpoints'
# id : dca61d2f-3444-410a-b5ec-db2fc4efb4c5
# area : distributedtask
# resourceName : serviceendpoints
# routeTemplate : {project}/_apis/{area}/{resource}/{endpointId}
# https://bit.ly/Add-VSTeamServiceEndpoint
function Add-VSTeamAzureRMServiceEndpoint {
   [CmdletBinding(DefaultParameterSetName = 'Automatic',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamAzureRMServiceEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('displayName')]
      [string] $subscriptionName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $subscriptionId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $subscriptionTenantId,
      [Parameter(ParameterSetName = 'Manual', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $servicePrincipalId,
      [Parameter(ParameterSetName = 'Manual', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $servicePrincipalKey,
      [string] $endpointName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if (-not $endpointName) {
         $endpointName = $subscriptionName
      }
      if (-not $servicePrincipalId) {
         $creationMode = 'Automatic'
      }
      else {
         $creationMode = 'Manual'
      }
      $obj = @{
         authorization = @{
            parameters = @{
               serviceprincipalid  = $servicePrincipalId
               serviceprincipalkey = $servicePrincipalKey
               tenantid            = $subscriptionTenantId
            }
            scheme     = 'ServicePrincipal'
         }
         data          = @{
            subscriptionId   = $subscriptionId
            subscriptionName = $subscriptionName
            creationMode     = $creationMode
         }
         url           = 'https://management.azure.com/'
      }
      return Add-VSTeamServiceEndpoint -ProjectName $ProjectName `
         -endpointName $endpointName `
         -endpointType azurerm `
         -object $obj
   }
}
# Queues a build.
#
# Get-VSTeamOption 'build' 'Builds'
# id : 0cd358e1-9217-4d94-8269-1c1ee6f93dcf
# area : Build
# resourceName : Builds
# routeTemplate : {project}/_apis/build/{resource}/{buildId}
# https://bit.ly/Add-VSTeamBuild
function Add-VSTeamBuild {
   [CmdletBinding(DefaultParameterSetName = 'ByName',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamBuild')]
   param(
      [Parameter(ParameterSetName = 'ByID', ValueFromPipelineByPropertyName = $true)]
      [Int32] $BuildDefinitionId,
      [Parameter(Mandatory = $false)]
      [string] $SourceBranch,
      [Parameter(Mandatory = $false)]
      [hashtable] $BuildParameters,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [ArgumentCompleter([vsteam_lib.QueueCompleter])]
      [string] $QueueName,
      [ArgumentCompleter([vsteam_lib.BuildDefinitionCompleter])]
      [string] $BuildDefinitionName
   )
   begin {
      if ($BuildDefinitionId) {
         $body = @{
            definition = @{
               id = $BuildDefinitionId
            }
         }
      }
      elseif ($BuildDefinitionName) {
         # Find the BuildDefinition id from the name
         $id = (Get-VSTeamBuildDefinition -ProjectName "$ProjectName" -Filter $BuildDefinitionName).id
         if (-not $id) {
            throw "'$BuildDefinitionName' is not a valid build definition. Use Get-VSTeamBuildDefinition to get a list of build names"
            return
         }
         $body = @{
            definition = @{
               id = $id
            }
         }
      }
      else {
         throw "'No build definition was given. Use Get-VSTeamBuildDefinition to get a list of builds"
         return
      }
      if ($QueueName) {
         $queueId = (Get-VSTeamQueue -ProjectName "$ProjectName" -queueName "$QueueName").id
         if (-not $queueId) {
            throw "'$QueueName' is not a valid Queue. Use Get-VSTeamQueue to get a list of queues"
            return
         }
         else {
            $body["queue"] = @{id = $queueId }
         }
      }
   }
   process {
      if ($SourceBranch) {
         $body.Add('sourceBranch', $SourceBranch)
      }
      if ($BuildParameters) {
         $body.Add('parameters', ($BuildParameters | ConvertTo-Json -Depth 100 -Compress))
      }
      $resp = _callAPI -Method POST -ProjectName $ProjectName `
         -Area "build" `
         -Resource "builds" `
         -Body ($body | ConvertTo-Json -Compress -Depth 100) `
         -Version $(_getApiVersion Build)
      $build = [vsteam_lib.Build]::new($resp, $ProjectName)
      Write-Output $build
   }
}
# Creates a new definition.
#
# Get-VSTeamOption 'build' 'Definitions'
# id : dbeaf647-6167-421a-bda9-c9327b25e2e6
# area : Build
# resourceName : Definitions
# routeTemplate : {project}/_apis/build/{resource}/{definitionId}
# https://bit.ly/Add-VSTeamBuildDefinition
function Add-VSTeamBuildDefinition {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamBuildDefinition')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $InFile,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      return _callAPI -Method POST -ProjectName $ProjectName `
         -Area "build" `
         -Resource "definitions" `
         -infile $InFile `
         -Version $(_getApiVersion Build)
   }
}
# Add or update ACEs in the ACL for the provided token. The request body
# contains the target token, a list of ACEs and a optional merge parameter.
# In the case of a collision (by identity descriptor) with an existing ACE
# in the ACL, the "merge" parameter determines the behavior. If set, the
# existing ACE has its allow and deny merged with the incoming ACE's allow
# and deny. If unset, the existing ACE is displaced.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Add-VSTeamBuildPermission {
   [CmdletBinding(DefaultParameterSetName = 'ByProjectAndUser',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamBuildPermission')]
   param(
      [parameter(Mandatory = $true)]
      [string]$ProjectID,
      [parameter(Mandatory = $false)]
      [string]$BuildID,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndDescriptor")]
      [string]$Descriptor,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndGroup")]
      [object]$Group,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndUser")]
      [object]$User,
      [parameter(Mandatory = $false)]
      [vsteam_lib.BuildPermissions]$Allow,
      [parameter(Mandatory = $false)]
      [vsteam_lib.BuildPermissions]$Deny
   )
   process {
      # SecurityNamespaceID: 33344d9c-fc72-4d6f-aba5-fa317101a7e9
      # Token: <projectId>/<pipelineId>
      $securityNamespaceId = "33344d9c-fc72-4d6f-aba5-fa317101a7e9"
      # Resolve Group to Descriptor
      if ($Group) {
         $Descriptor = _getDescriptorForACL -Group $Group
      }
      # Resolve User to Descriptor
      if ($User) {
         $Descriptor = _getDescriptorForACL -User $User
      }
      $token = $null
      if ($BuildID) {
         $token = "$ProjectID/$($BuildID)"
      }
      else {
         $token = "$ProjectID"
      }
      Add-VSTeamAccessControlEntry -SecurityNamespaceId $securityNamespaceId `
         -Descriptor $Descriptor `
         -Token $token `
         -AllowMask ([int]$Allow) `
         -DenyMask ([int]$Deny)
   }
}
# Adds a tag to a build.
#
# Get-VSTeamOption 'build' 'tags'
# id : 6e6114b2-8161-44c8-8f6c-c5505782427f
# area : build
# resourceName : tags
# routeTemplate : {project}/_apis/{area}/builds/{buildId}/{resource}/{*tag}
# http://bit.ly/Add-VSTeamBuildTag
function Add-VSTeamBuildTag {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamBuildTag')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
      [string[]] $Tags,
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Add-VSTeamBuildTag")) {
            foreach ($tag in $tags) {
               _callAPI -Method PUT -ProjectName $projectName `
                  -Area "build/builds/$id" `
                  -Resource "tags" `
                  -id $tag `
                  -Version $(_getApiVersion Build) | Out-Null
            }
         }
      }
   }
}
# Create new or update an existing classification node.
#
# Get-VSTeamOption 'wit' 'classificationNodes'
# id : 5a172953-1b41-49d3-840a-33f79c3ce89f
# area : wit
# resourceName : classificationNodes
# routeTemplate : {project}/_apis/{area}/{resource}/{structureGroup}/{*path}
# https://bit.ly/Add-VSTeamClassificationNode
function Add-VSTeamClassificationNode {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamClassificationNode')]
   param(
      [Parameter(Mandatory = $true)]
      [string] $Name,
      [ValidateSet("areas", "iterations")]
      [Parameter(Mandatory = $true)]
      [string] $StructureGroup,
      [Parameter(Mandatory = $false)]
      [string] $Path = $null,
      [Parameter(Mandatory = $false)]
      [datetime] $StartDate,
      [Parameter(Mandatory = $false)]
      [datetime] $FinishDate,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $id = $StructureGroup
      $Path = [uri]::UnescapeDataString($Path)
      if ($Path) {
         $Path = [uri]::EscapeUriString($Path)
         $Path = $Path.TrimStart("/")
         $id += "/$Path"
      }
      $body = @{
         name = $Name
      }
      if ($StructureGroup -eq "iterations") {
         $body.attributes = @{
            startDate  = $StartDate
            finishDate = $FinishDate
         }
      }
      $bodyAsJson = $body | ConvertTo-Json -Compress -Depth 100
      # Call the REST API
      $resp = _callAPI -Method POST -ProjectName $ProjectName `
         -Area "wit" `
         -Resource "classificationnodes" `
         -id $id `
         -body $bodyAsJson `
         -Version $(_getApiVersion Core)
      $resp = [vsteam_lib.ClassificationNode]::new($resp, $ProjectName)
      Write-Output $resp
   }
}
# Install the specified extension into the account / project collection.
#
# Get-VSTeamOption 'extensionmanagement' 'installedextensionsbyname' -subDomain 'extmgmt'
# id : fb0da285-f23e-4b56-8b53-3ef5f9f6de66
# area : ExtensionManagement
# resourceName : InstalledExtensionsByName
# routeTemplate : _apis/{area}/{resource}/{publisherName}/{extensionName}/{version}
# http://bit.ly/Add-VSTeamExtension
function Add-VSTeamExtension {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamExtension')]
   param(
      [parameter(Mandatory = $true)]
      [string] $PublisherId,
      [parameter(Mandatory = $true)]
      [string] $ExtensionId,
      [parameter(Mandatory = $false)]
      [string] $Version
   )
   Process {
      $id = "$PublisherId/$ExtensionId"
      if ($version) {
         $id += '/' + $Version
      }
      $resp = _callAPI -Method POST -SubDomain 'extmgmt' `
         -Area "extensionmanagement" `
         -Resource "installedextensionsbyname" `
         -Id $id `
         -Version $(_getApiVersion ExtensionsManagement)
      $item = [vsteam_lib.Extension]::new($resp)
      Write-Output $item
   }
}
# Adds a new feed to package management.
#
# Get-VSTeamOption 'packaging' 'feeds' -subDomain 'feeds'
# id : c65009a7-474a-4ad1-8b42-7d852107ef8c
# area : Packaging
# resourceName : Feeds
# routeTemplate : {project}/_apis/{area}/{resource}/{feedId}
# http://bit.ly/Add-VSTeamFeed
function Add-VSTeamFeed {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamFeed')]
   param (
      [Parameter(Position = 0, Mandatory = $true)]
      [string] $Name,
      [Parameter(Position = 1)]
      [string] $Description,
      [switch] $EnableUpstreamSources,
      [switch] $showDeletedPackageVersions
   )
   process {
      $body = @{
         name                       = $Name
         description                = $Description
         hideDeletedPackageVersions = $true
      }
      if ($showDeletedPackageVersions.IsPresent) {
         $body.hideDeletedPackageVersions = $false
      }
      if ($EnableUpstreamSources.IsPresent) {
         $body.upstreamEnabled = $true
         $body.upstreamSources = @(
            @{
               id                 = [System.Guid]::NewGuid()
               name               = 'npmjs'
               protocol           = 'npm'
               location           = 'https://registry.npmjs.org/'
               upstreamSourceType = 1
            },
            @{
               id                 = [System.Guid]::NewGuid()
               name               = 'nuget.org'
               protocol           = 'nuget'
               location           = 'https://api.nuget.org/v3/index.json'
               upstreamSourceType = 1
            }
         )
      }
      $bodyAsJson = $body | ConvertTo-Json -Compress -Depth 100
      # Call the REST API
      $resp = _callAPI -Method POST -subDomain "feeds" `
         -Area "packaging" `
         -Resource "feeds" `
         -body $bodyAsJson `
         -Version $(_getApiVersion Packaging)
      Write-Output [vsteam_lib.Feed]::new($resp)
   }
}
# Adds a Git repository to your Azure DevOps or Team Foundation Server account.
#
# Get-VSTeamOption 'git' 'repositories'
# id : 225f7195-f9c7-4d14-ab28-a83f7ff77e1f
# area : git
# resourceName : repositories
# routeTemplate : {project}/_apis/{area}/{resource}/{repositoryId}
# http://bit.ly/Add-VSTeamGitRepository
function Add-VSTeamGitRepository {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamGitRepository')]
   param(
      [parameter(Mandatory = $true)]
      [string] $Name,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $body = '{"name": "' + $Name + '"}'
      try {
         # Call the REST API
         $resp = _callAPI -Method POST -ProjectName $ProjectName `
            -Area "git" `
            -Resource "repositories" `
            -Body $body `
            -Version $(_getApiVersion Git)
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $repo = [vsteam_lib.GitRepository]::new($resp, $ProjectName)
         Write-Output $repo
      }
      catch {
         _handleException $_
      }
   }
}
# Add or update ACEs in the ACL for the provided token. The request body
# contains the target token, a list of ACEs and a optional merge parameter.
# In the case of a collision (by identity descriptor) with an existing ACE
# in the ACL, the "merge" parameter determines the behavior. If set, the
# existing ACE has its allow and deny merged with the incoming ACE's allow
# and deny. If unset, the existing ACE is displaced.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Add-VSTeamGitRepositoryPermission {
   [CmdletBinding(DefaultParameterSetName = 'ByProjectAndUser',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamGitRepositoryPermission')]
   param(
      [parameter(Mandatory = $true)]
      [vsteam_lib.Project]$Project,
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryIdAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryIdAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryIdAndDescriptor")]
      [guid]$RepositoryId,
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryNameAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryNameAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryNameAndDescriptor")]
      [string]$RepositoryName,
      [parameter(Mandatory = $false, ParameterSetName = "ByRepositoryIdAndGroup")]
      [parameter(Mandatory = $false, ParameterSetName = "ByRepositoryIdAndUser")]
      [parameter(Mandatory = $false, ParameterSetName = "ByRepositoryNameAndGroup")]
      [parameter(Mandatory = $false, ParameterSetName = "ByRepositoryNameAndUser")]
      [parameter(Mandatory = $false, ParameterSetName = "ByRepositoryIdAndDescriptor")]
      [parameter(Mandatory = $false, ParameterSetName = "ByRepositoryNameAndDescriptor")]
      [string]$BranchName,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryIdAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryNameAndDescriptor")]
      [string]$Descriptor,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryIdAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryNameAndGroup")]
      [vsteam_lib.Group]$Group,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryIdAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByRepositoryNameAndUser")]
      [vsteam_lib.User]$User,
      [parameter(Mandatory = $false)]
      [vsteam_lib.GitRepositoryPermissions]$Allow,
      [parameter(Mandatory = $false)]
      [vsteam_lib.GitRepositoryPermissions]$Deny
   )
   process {
      # SecurityNamespaceID: 2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87
      # Token: repoV2/<projectId>" <-- Whole project
      # Token: repoV2/<projectId>/<repositoryId>" <-- Whole repository
      # Token: repoV2/<projectId>/<repositoryId>/refs/heads/<branchName>" <-- Single branch
      $securityNamespaceId = "2e9eb7ed-3c0a-47d4-87c1-0ffdd275fd87"
      # Resolve Repository Name to ID
      if ($RepositoryName) {
         $repo = Get-VSTeamGitRepository -ProjectName $Project.Name -Name $RepositoryName
         if (!$repo) {
            throw "Repository not found"
         }
         $RepositoryId = $repo.ID
      }
      # Resolve Group to Descriptor
      if ($Group) {
         $Descriptor = _getDescriptorForACL -Group $Group
      }
      # Resolve User to Descriptor
      if ($User) {
         $Descriptor = _getDescriptorForACL -User $User
      }
      $token = "repoV2/$($Project.ID)"
      if ($RepositoryId) {
         $token += "/$($RepositoryId)"
      }
      if ($BranchName) {
         $branchHex = _convertToHex($BranchName)
         $token += "/refs/heads/$($branchHex)"
      }
      Add-VSTeamAccessControlEntry -SecurityNamespaceId $securityNamespaceId `
         -Descriptor $Descriptor `
         -Token $token `
         -AllowMask ([int]$Allow) `
         -DenyMask ([int]$Deny)
   }
}
# Create new or update an existing classification node.
#
# Get-VSTeamOption 'wit' 'classificationNodes'
# id : 5a172953-1b41-49d3-840a-33f79c3ce89f
# area : wit
# resourceName : classificationNodes
# routeTemplate : {project}/_apis/{area}/{resource}/{structureGroup}/{*path}
# https://bit.ly/Add-VSTeamClassificationNode
function Add-VSTeamIteration {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamIteration')]
   param(
      [Parameter(Mandatory = $true)]
      [string] $Name,
      [Parameter(Mandatory = $false)]
      [string] $Path,
      [Parameter(Mandatory = $false)]
      [datetime] $StartDate,
      [Parameter(Mandatory = $false)]
      [datetime] $FinishDate,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $params = @{}
      if ($StartDate) {
         $params.StartDate = $StartDate
      }
      if ($FinishDate) {
         $params.FinishDate = $FinishDate
      }
      $resp = Add-VSTeamClassificationNode @params -ProjectName $ProjectName `
         -Name $Name `
         -StructureGroup iterations `
         -Path $Path
      Write-Output $resp
   }
}
# Create a service endpoint.
#
# Get-VSTeamOption 'distributedtask' 'serviceendpoints'
# id : dca61d2f-3444-410a-b5ec-db2fc4efb4c5
# area : distributedtask
# resourceName : serviceendpoints
# routeTemplate : {project}/_apis/{area}/{resource}/{endpointId}
# https://bit.ly/Add-VSTeamServiceEndpoint
function Add-VSTeamKubernetesEndpoint {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamKubernetesEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $endpointName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $kubeconfig,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $kubernetesUrl,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $clientCertificateData,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $clientKeyData,
      [switch] $acceptUntrustedCerts,
      [switch] $generatePfx,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # Process switch parameters
      $untrustedCerts = $false
      if ($acceptUntrustedCerts.IsPresent) {
         $untrustedCerts = $true
      }
      $pfx = $false
      if ($generatePfx.IsPresent) {
         $pfx = $true
      }
      $obj = @{
         authorization = @{
            parameters = @{
               clientCertificateData = $clientCertificateData
               clientKeyData         = $clientKeyData
               generatePfx           = $pfx
               kubeconfig            = $Kubeconfig
            };
            scheme     = 'None'
         };
         data          = @{
            acceptUntrustedCerts = $untrustedCerts
         };
         url           = $kubernetesUrl
      }
      return Add-VSTeamServiceEndpoint -ProjectName $ProjectName `
         -endpointName $endpointName `
         -endpointType kubernetes `
         -object $obj
   }
}
# Adds a membership to a container.
#
# Get-VSTeamOption 'graph' 'memberships' -subDomain 'vssps'
# id : 3fd2e6ca-fb30-443a-b579-95b19ed0934c
# area : Graph
# resourceName : Memberships
# routeTemplate : _apis/{area}/{resource}/{subjectDescriptor}/{containerDescriptor}
# http://bit.ly/Add-VSTeamMembership
function Add-VSTeamMembership {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamMembership')]
   param(
      [Parameter(Mandatory = $true)]
      [string] $MemberDescriptor,
      [Parameter(Mandatory = $true)]
      [string] $ContainerDescriptor
   )
   process {
      return _callMembershipAPI -Method PUT `
         -Id "$MemberDescriptor/$ContainerDescriptor"
   }
}
# Create a service endpoint.
#
# Get-VSTeamOption 'distributedtask' 'serviceendpoints'
# id : dca61d2f-3444-410a-b5ec-db2fc4efb4c5
# area : distributedtask
# resourceName : serviceendpoints
# routeTemplate : {project}/_apis/{area}/{resource}/{endpointId}
# https://bit.ly/Add-VSTeamServiceEndpoint
function Add-VSTeamNuGetEndpoint {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
   [CmdletBinding(DefaultParameterSetName = 'SecureApiKey')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $EndpointName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $NuGetUrl,
      [Parameter(ParameterSetName = 'ClearToken', Mandatory = $true, HelpMessage = 'Personal Access Token')]
      [string] $PersonalAccessToken,
      [Parameter(ParameterSetName = 'ClearApiKey', Mandatory = $true, HelpMessage = 'ApiKey')]
      [string] $ApiKey,
      [Parameter(ParameterSetName = 'SecurePassword', Mandatory = $true, HelpMessage = 'Username')]
      [string] $Username,
      [Parameter(ParameterSetName = 'SecureToken', Mandatory = $true, HelpMessage = 'Personal Access Token')]
      [securestring] $SecurePersonalAccessToken,
      [Parameter(ParameterSetName = 'SecureApiKey', Mandatory = $true, HelpMessage = 'ApiKey')]
      [securestring] $SecureApiKey,
      [Parameter(ParameterSetName = 'SecurePassword', Mandatory = $true, HelpMessage = 'Password')]
      [securestring] $SecurePassword,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($PersonalAccessToken) {
         $Authentication = 'Token'
         $token = $PersonalAccessToken
      }
      elseif ($ApiKey) {
         $Authentication = 'ApiKey'
         $token = $ApiKey
      }
      elseif ($SecureApiKey) {
         $Authentication = 'ApiKey'
         $credential = New-Object System.Management.Automation.PSCredential "ApiKey", $SecureApiKey
         $token = $credential.GetNetworkCredential().Password
      }
      elseif ($SecurePassword) {
         $Authentication = 'UsernamePassword'
         $credential = New-Object System.Management.Automation.PSCredential "Password", $SecurePassword
         $token = $credential.GetNetworkCredential().Password
      }
      else {
         $Authentication = 'Token'
         $credential = New-Object System.Management.Automation.PSCredential "token", $securePersonalAccessToken
         $token = $credential.GetNetworkCredential().Password
      }
      $obj = @{
         data = @{ }
         url  = $NuGetUrl
      }
      if ($Authentication -eq 'ApiKey') {
         $obj['authorization'] = @{
            parameters = @{
               nugetkey = $token
            }
            scheme     = 'None'
         }
      }
      elseif ($Authentication -eq 'Token') {
         $obj['authorization'] = @{
            parameters = @{
               apitoken = $token
            }
            scheme     = 'Token'
         }
      }
      else {
         $obj['authorization'] = @{
            parameters = @{
               username = $Username
               password = $token
            }
            scheme     = 'UsernamePassword'
         }
      }
      return Add-VSTeamServiceEndpoint -ProjectName $ProjectName `
         -endpointName $endpointName `
         -endpointType externalnugetfeed `
         -object $obj
   }
}
# Adds a new policy to the specified project.
#
# Get-VSTeamOption 'policy' 'configurations'
# id : dad91cbe-d183-45f8-9c6e-9c1164472121
# area : policy
# resourceName : configurations
# routeTemplate : {project}/_apis/{area}/{resource}/{configurationId}
# http://bit.ly/Add-VSTeamPolicy
function Add-VSTeamPolicy {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamPolicy')]
   param(
      [Parameter(Mandatory = $true)]
      [guid] $type,
      [switch] $enabled,
      [switch] $blocking,
      [Parameter(Mandatory = $true)]
      [hashtable] $settings,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $body = @{
         isEnabled  = $enabled.IsPresent;
         isBlocking = $blocking.IsPresent;
         type       = @{
            id = $type
         };
         settings   = $settings
      } | ConvertTo-Json -Depth 10 -Compress
      try {
         # Call the REST API
         $resp = _callAPI -Method POST -ProjectName $ProjectName `
            -Area "policy" `
            -Resource "configurations" `
            -Body $body `
            -Version $(_getApiVersion Policy)
         Write-Output $resp
      }
      catch {
         _handleException $_
      }
   }
}
function Add-VSTeamPool {
   [CmdletBinding(
      HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamPool')]
   param(
      [Parameter(Mandatory = $true, Position = 1)]
      [string] $Name,
      [Parameter(Mandatory = $false)]
      [string] $Description,
      [Parameter(Mandatory = $false)]
      [switch] $AutoProvision,
      [Parameter(Mandatory = $false)]
      [switch] $AutoAuthorize,
      [Parameter(Mandatory = $false)]
      [switch] $NoAutoUpdates
   )
   process {
      $body = @{
         name = $Name
         autoProvision = $AutoProvision.IsPresent
         autoUpdate = !$NoAutoUpdates.IsPresent
         properties = @{
            "System.AutoAuthorize" = $AutoAuthorize.IsPresent
         }
      }
      $bodyAsJson = $body | ConvertTo-Json -Compress
      $resp = _callAPI -Method Post -NoProject -Area distributedtask -Resource pools -Version $(_getApiVersion DistributedTask) -Body $bodyAsJson
      $pool = [vsteam_lib.AgentPool]::new($resp)
      if ($resp -and $Description) {
         $descriptionAsJson = $Description | ConvertTo-Json -Compress
         $null = _callAPI -Method Put -NoProject -Area distributedtask -Resource pools -Id "$($pool.id)/poolmetadata" -Version $(_getApiVersion DistributedTask) -Body $descriptionAsJson
      }
      Write-Output $pool
   }
}
# Stores your account name and personal access token as a profile for use with
# the Add-TeamAccount function in this module.
function Add-VSTeamProfile {
   [CmdletBinding(DefaultParameterSetName = 'Secure',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamProfile')]
   param(
      [parameter(ParameterSetName = 'Windows', Mandatory = $true, Position = 1)]
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, Position = 1)]
      [Parameter(ParameterSetName = 'Plain')]
      [string] $Account,
      [parameter(ParameterSetName = 'Plain', Mandatory = $true, Position = 2, HelpMessage = 'Personal Access Token')]
      [string] $PersonalAccessToken,
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, HelpMessage = 'Personal Access Token')]
      [securestring] $SecurePersonalAccessToken,
      [string] $Name,
      [ValidateSet('TFS2017', 'TFS2018', 'AzD2019', 'VSTS', 'AzD', 'TFS2017U1', 'TFS2017U2', 'TFS2017U3', 'TFS2018U1', 'TFS2018U2', 'TFS2018U3', 'AzD2019U1')]
      [string] $Version,
      [switch] $UseBearerToken
   )
   DynamicParam {
      # Only add these options on Windows Machines
      if (_isOnWindows) {
         # Create the dictionary
         $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
         $ParameterName2 = 'UseWindowsAuthentication'
         # Create the collection of attributes
         $AttributeCollection2 = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
         # Create and set the parameters' attributes
         $ParameterAttribute2 = New-Object System.Management.Automation.ParameterAttribute
         $ParameterAttribute2.Mandatory = $true
         $ParameterAttribute2.ParameterSetName = "Windows"
         $ParameterAttribute2.HelpMessage = "On Windows machines allows you to use the active user identity for authentication. Not available on other platforms."
         # Add the attributes to the attributes collection
         $AttributeCollection2.Add($ParameterAttribute2)
         # Create and return the dynamic parameter
         $RuntimeParameter2 = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName2, [switch], $AttributeCollection2)
         $RuntimeParameterDictionary.Add($ParameterName2, $RuntimeParameter2)
         return $RuntimeParameterDictionary
      }
   }
   process {
      if ($SecurePersonalAccessToken) {
         # Even when promoted for a Secure Personal Access Token you can just
         # press enter. This leads to an empty PAT so error below.
         # Convert the securestring to a normal string
         # this was the one technique that worked on Mac, Linux and Windows
         $_pat = _convertSecureStringTo_PlainText -SecureString $SecurePersonalAccessToken
      }
      else {
         $_pat = $PersonalAccessToken
      }
      if (_isOnWindows) {
         # Bind the parameter to a friendly variable
         $UsingWindowsAuth = $PSBoundParameters[$ParameterName2]
         if (!($_pat) -and !($UsingWindowsAuth)) {
            Write-Error 'Personal Access Token must be provided if you are not using Windows Authentication; please see the help.'
            return
         }
      }
      # If they only gave an account name add https://dev.azure.com
      if ($Account -notlike "*/*") {
         if (-not $Name) {
            $Name = $Account
         }
         $Account = "https://dev.azure.com/$($Account)"
      }
      # If they gave https://dev.azure.com extract Account and Profile name
      if ($Account -match "(?<protocol>https\://)?(?<domain>dev\.azure\.com/)(?<account>[A-Z0-9][-A-Z0-9]*[A-Z0-9])") {
         if (-not $Name) {
            $Name = $matches.account
         }
         $Account = "https://dev.azure.com/$($matches.account)"
      }
      # If they gave https://xxx.visualstudio.com extract Account and Profile name, convert to new URL
      if ($Account -match "(?<protocol>https?\://)?(?<account>[A-Z0-9][-A-Z0-9]*[A-Z0-9])(?<domain>\.visualstudio\.com)") {
         if (-not $Name) {
            $Name = $matches.account
         }
         $Account = "https://dev.azure.com/$($matches.account)"
      }
      if ($UseBearerToken.IsPresent) {
         $authType = 'Bearer'
         $token = $_pat
         $encodedPat = ''
      }
      else {
         $token = ''
         $authType = 'Pat'
         $encodedPat = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$_pat"))
      }
      # If no SecurePersonalAccessToken is entered, and on windows, are we using default credentials for REST calls
      if ((!$_pat) -and (_isOnWindows) -and ($UsingWindowsAuth)) {
         Write-Verbose 'Using Default Windows Credentials for authentication; no Personal Access Token required'
         $encodedPat = ''
         $token = ''
         $authType = 'OnPremise'
      }
      if (-not $Name) {
         $Name = $Account
      }
      # See if this item is already in there
      # I am testing URL because the user may provide a different
      # name and I don't want two with the same URL.
      # The @() forces even 1 item to an array
      $profiles = @(Get-VSTeamProfile | Where-Object URL -ne $Account)
      $newProfile = [PSCustomObject]@{
         Name    = $Name
         URL     = $Account
         Type    = $authType
         Pat     = $encodedPat
         Token   = $token
         Version = (_getVSTeamAPIVersion -Instance $Account -Version $Version)
      }
      $profiles += $newProfile
      $contents = ConvertTo-Json $profiles -Depth 100
      Set-Content -Path $profilesPath -Value $contents
   }
}
# Adds a Team Project to your account.
#
# id : 603fe2ac-9723-48b9-88ad-09305aa6c6e1
# area : core
# resourceName : projects
# routeTemplate : _apis/{resource}/{*projectId}
# http://bit.ly/Add-VSTeamProject
function Add-VSTeamProject {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamProject')]
   param(
      [parameter(Mandatory = $true)]
      [Alias('Name')]
      [string] $ProjectName,
      [string] $Description,
      [switch] $TFVC,
      [vsteam_lib.ProcessTemplateValidateAttribute()]
      [ArgumentCompleter([vsteam_lib.ProcessTemplateCompleter])]
      [string] $ProcessTemplate
   )
   process {
      if ($TFVC.IsPresent) {
         $srcCtrl = "Tfvc"
      }
      else {
         $srcCtrl = 'Git'
      }
      if ($ProcessTemplate) {
         Write-Verbose "Finding $ProcessTemplate id"
         $templateTypeId = (Get-VSTeamProcess -Name $ProcessTemplate).Id
      }
      else {
         # Default to Scrum Process Template
         $ProcessTemplate = 'Scrum'
         $templateTypeId = '6b724908-ef14-45cf-84f8-768b5384da45'
      }
      $body = @{
         name         = $ProjectName
         description  = $Description
         capabilities = @{
            versioncontrol  = @{
               sourceControlType = $srcCtrl
            }
            processTemplate = @{
               templateTypeId = $templateTypeId
            }
         }
      }
      try {
         # Call the REST API
         $resp = _callAPI -Method POST `
            -Resource "projects" `
            -Body ($body | ConvertTo-Json -Compress -Depth 100) `
            -Version $(_getApiVersion Core)
         _trackProjectProgress -resp $resp -title 'Creating team project' -msg "Name: $($ProjectName), Template: $($processTemplate), Src: $($srcCtrl)"
         # Invalidate any cache of projects.
         [vsteam_lib.ProjectCache]::Invalidate()
         Start-Sleep -Seconds 5
         return Get-VSTeamProject $ProjectName
      }
      catch {
         _handleException $_
      }
   }
}
# Add or update ACEs in the ACL for the provided token. The request body
# contains the target token, a list of ACEs and a optional merge parameter.
# In the case of a collision (by identity descriptor) with an existing ACE
# in the ACL, the "merge" parameter determines the behavior. If set, the
# existing ACE has its allow and deny merged with the incoming ACE's allow
# and deny. If unset, the existing ACE is displaced.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Add-VSTeamProjectPermission {
   [CmdletBinding(DefaultParameterSetName = 'ByProjectAndUser',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamProjectPermission')]
   param(
      [parameter(Mandatory = $true)]
      [vsteam_lib.Project]$Project,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndDescriptor")]
      [string]$Descriptor,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndGroup")]
      [vsteam_lib.Group]$Group,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndUser")]
      [vsteam_lib.User]$User,
      [parameter(Mandatory = $false)]
      [vsteam_lib.ProjectPermissions]$Allow,
      [parameter(Mandatory = $false)]
      [vsteam_lib.ProjectPermissions]$Deny
   )
   process {
      # SecurityNamespaceID: 52d39943-cb85-4d7f-8fa8-c6baac873819
      # Token: $PROJECT:vstfs:///Classification/TeamProject/<projectId>
      $securityNamespaceId = "52d39943-cb85-4d7f-8fa8-c6baac873819"
      # Resolve Group to Descriptor
      if ($Group) {
         $Descriptor = _getDescriptorForACL -Group $Group
      }
      # Resolve User to Descriptor
      if ($User) {
         $Descriptor = _getDescriptorForACL -User $User
      }
      $token = "`$PROJECT:vstfs:///Classification/TeamProject/$($Project.ID)"
      Add-VSTeamAccessControlEntry -SecurityNamespaceId $securityNamespaceId `
         -Descriptor $Descriptor `
         -Token $token `
         -AllowMask ([int]$Allow) `
         -DenyMask ([int]$Deny)
   }
}
# Creates a new Pull Request.
#
# id : 88aea7e8-9501-45dd-ac58-b069aa73b926
# area : git
# resourceName : repositories
# routeTemplate : _apis/{area}/{projectId}/{resource}/{repositoryId}
# http://bit.ly/Add-VSTeamPullRequest
function Add-VSTeamPullRequest {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamPullRequest')]
   param(
      [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Alias('Id')]
      [Guid] $RepositoryId,
      [Parameter(Mandatory = $true, HelpMessage = "Should be a ref like refs/heads/MyBranch")]
      [ValidatePattern('^refs/.*')]
      [string] $SourceRefName,
      [Parameter(Mandatory = $true, HelpMessage = "Should be a ref like refs/heads/MyBranch")]
      [ValidatePattern('^refs/.*')]
      [string] $TargetRefName,
      [Parameter(Mandatory = $true)]
      [string] $Title,
      [Parameter(Mandatory = $true)]
      [string] $Description,
      [Parameter()]
      [switch] $Draft,
      [Parameter()]
      [switch] $Force,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      Write-Verbose "Add-VSTeamPullRequest"
      $body = '{"sourceRefName": "' + $SourceRefName + '", "targetRefName": "' + $TargetRefName + '", "title": "' + $Title + '", "description": "' + $Description + '", "isDraft": ' + $Draft.ToString().ToLower() + '}'
      Write-Verbose $body
      # Call the REST API
      if ($force -or $pscmdlet.ShouldProcess($Title, "Add Pull Request")) {
         try {
            Write-Debug 'Add-VSTeamPullRequest Call the REST API'
            $resp = _callAPI -Method POST -ProjectName $ProjectName `
               -Area "git" `
               -Resource "repositories" `
               -Id "$RepositoryId/pullrequests" `
               -Body $body `
               -Version $(_getApiVersion Git)
            _applyTypesToPullRequests -item $resp
            Write-Output $resp
         }
         catch {
            _handleException $_
         }
      }
   }
}
# Queues a new release.
#
# Get-VSTeamOption 'release' 'releases' -subDomain 'vsrm'
# id : a166fde7-27ad-408e-ba75-703c2cc9d500
# area : Release
# resourceName : releases
# routeTemplate : {project}/_apis/{area}/{resource}/{releaseId}
# http://bit.ly/Add-VSTeamRelease
function Add-VSTeamRelease {
   [CmdletBinding(DefaultParameterSetName = 'ById', SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamRelease')]
   param(
      [Parameter(ParameterSetName = 'ById', Mandatory = $true)]
      [int] $DefinitionId,
      [Parameter(Mandatory = $false)]
      [string] $Description,
      [Parameter(ParameterSetName = 'ById', Mandatory = $true)]
      [string] $ArtifactAlias,
      [Parameter()]
      [string] $Name,
      [Parameter(ParameterSetName = 'ById', Mandatory = $true)]
      [string] $BuildId,
      [ArgumentCompleter([vsteam_lib.BuildCompleter])]
      [Parameter(ParameterSetName = 'ByName', Mandatory = $true)]
      [string] $BuildNumber,
      [Parameter()]
      [string] $SourceBranch,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [ArgumentCompleter([vsteam_lib.ReleaseDefinitionCompleter])]
      [string] $DefinitionName
   )
   begin {
      if ($BuildNumber) {
         $buildID = (Get-VSTeamBuild -ProjectName $ProjectName -BuildNumber $BuildNumber).id
         if (-not $buildID) { throw "'$BuildnNumber' is not a valid build Use Get-VsTeamBuild to get a list of valid build numbers." }
      }
      if ($DefinitionName -and -not $artifactAlias) {
         $def = Get-VSTeamReleaseDefinition -ProjectName $ProjectName | Where-Object { $_.name -eq $DefinitionName }
         $DefinitionId = $def.id
         $artifactAlias = $def.artifacts[0].alias
      }
   }
   process {
      $body = '{"definitionId": ' + $DefinitionId + ', "description": "' + $description + '", "artifacts": [{"alias": "' + $artifactAlias + '", "instanceReference": {"id": "' + $buildId + '", "name": "' + $Name + '", "sourceBranch": "' + $SourceBranch + '"}}]}'
      Write-Verbose $body
      # Call the REST API
      if ($force -or $pscmdlet.ShouldProcess($description, "Add Release")) {
         try {
            Write-Debug 'Add-VSTeamRelease Call the REST API'
            $resp = _callAPI -Method POST -SubDomain "vsrm" -ProjectName $ProjectName `
               -Area "release" `
               -Resource "releases" `
               -Body $body `
               -Version $(_getApiVersion Release)
            Write-Output $([vsteam_lib.Release]::new($resp, $ProjectName))
         }
         catch {
            _handleException $_
         }
      }
   }
}
# Creates a new release definition from a JSON file.
#
# Get-VSTeamOption 'release' 'definitions' -subDomain vsrm
# id : d8f96f24-8ea7-4cb6-baab-2df8fc515665
# area : Release
# resourceName : definitions
# routeTemplate : {project}/_apis/{area}/{resource}/{definitionId}
# http://bit.ly/Add-VSTeamReleaseDefinition
function Add-VSTeamReleaseDefinition {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamReleaseDefinition')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $inFile,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $resp = _callAPI -Method POST -subDomain "vsrm" -ProjectName $ProjectName `
         -Area "release" `
         -Resource "definitions" `
         -inFile $inFile `
         -Version $(_getApiVersion Release)
      Write-Output $resp
   }
}
# Create a service endpoint.
#
# Get-VSTeamOption 'distributedtask' 'serviceendpoints'
# id : dca61d2f-3444-410a-b5ec-db2fc4efb4c5
# area : distributedtask
# resourceName : serviceendpoints
# routeTemplate : {project}/_apis/{area}/{resource}/{endpointId}
# https://bit.ly/Add-VSTeamServiceEndpoint
function Add-VSTeamServiceEndpoint {
   [CmdletBinding(DefaultParameterSetName = 'Secure',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamServiceEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $endpointName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $endpointType,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [hashtable] $object,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $object['name'] = $endpointName
      $object['type'] = $endpointType
      $body = $object | ConvertTo-Json -Depth 100
      # Call the REST API
      $resp = _callAPI -Method POST -ProjectName $projectName `
         -Area "distributedtask" `
         -Resource "serviceendpoints" `
         -body $body `
         -Version $(_getApiVersion ServiceEndpoints)
      _trackServiceEndpointProgress -projectName $projectName -resp $resp -title 'Creating Service Endpoint' -msg "Creating $endpointName"
      return Get-VSTeamServiceEndpoint -ProjectName $ProjectName -id $resp.id
   }
}
# Create a service endpoint.
#
# Get-VSTeamOption 'distributedtask' 'serviceendpoints'
# id : dca61d2f-3444-410a-b5ec-db2fc4efb4c5
# area : distributedtask
# resourceName : serviceendpoints
# routeTemplate : {project}/_apis/{area}/{resource}/{endpointId}
# https://bit.ly/Add-VSTeamServiceEndpoint
function Add-VSTeamServiceFabricEndpoint {
   [CmdletBinding(DefaultParameterSetName = 'Certificate',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamServiceFabricEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('displayName')]
      [string] $endpointName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $url,
      [parameter(ParameterSetName = 'Certificate', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $certificate,
      [Parameter(ParameterSetName = 'Certificate', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [securestring] $certificatePassword,
      [parameter(ParameterSetName = 'Certificate', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [parameter(ParameterSetName = 'AzureAd', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $serverCertThumbprint,
      [Parameter(ParameterSetName = 'AzureAd', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $username,
      [Parameter(ParameterSetName = 'AzureAd', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [securestring] $password,
      [Parameter(ParameterSetName = 'None', Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
      [string] $clusterSpn,
      [Parameter(ParameterSetName = 'None', Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
      [bool] $useWindowsSecurity,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      switch ($PSCmdlet.ParameterSetName) {
         "Certificate" {
            # copied securestring usage from Set-VSTeamAccount
            # while we don't actually have a username here, PSCredential requires that a non empty string is provided
            $credential = New-Object System.Management.Automation.PSCredential $serverCertThumbprint, $certificatePassword
            $certPass = $credential.GetNetworkCredential().Password
            $authorization = @{
               parameters = @{
                  certificate          = $certificate
                  certificatepassword  = $certPass
                  servercertthumbprint = $serverCertThumbprint
               }
               scheme     = 'Certificate'
            }
         }
         "AzureAd" {
            # copied securestring usage from Set-VSTeamAccount
            $credential = New-Object System.Management.Automation.PSCredential $username, $password
            $pass = $credential.GetNetworkCredential().Password
            $authorization = @{
               parameters = @{
                  password             = $pass
                  servercertthumbprint = $serverCertThumbprint
                  username             = $username
               }
               scheme     = 'UsernamePassword'
            }
         }
         Default {
            $authorization = @{
               parameters = @{
                  ClusterSpn         = $clusterSpn
                  UseWindowsSecurity = $useWindowsSecurity
               }
               scheme     = 'None'
            }
         }
      }
      $obj = @{
         authorization = $authorization
         data          = @{}
         url           = $url
      }
      return Add-VSTeamServiceEndpoint -ProjectName $ProjectName `
         -endpointName $endpointName `
         -endpointType servicefabric `
         -object $obj
   }
}
# Create a service endpoint.
#
# Get-VSTeamOption 'distributedtask' 'serviceendpoints'
# id : dca61d2f-3444-410a-b5ec-db2fc4efb4c5
# area : distributedtask
# resourceName : serviceendpoints
# routeTemplate : {project}/_apis/{area}/{resource}/{endpointId}
# https://bit.ly/Add-VSTeamServiceEndpoint
function Add-VSTeamSonarQubeEndpoint {
   [CmdletBinding(DefaultParameterSetName = 'Secure',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamSonarQubeEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $endpointName,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $sonarqubeUrl,
      [parameter(ParameterSetName = 'Plain', Mandatory = $true, Position = 2, HelpMessage = 'Personal Access Token')]
      [string] $personalAccessToken,
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, HelpMessage = 'Personal Access Token')]
      [securestring] $securePersonalAccessToken,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($personalAccessToken) {
         $token = $personalAccessToken
      }
      else {
         $credential = New-Object System.Management.Automation.PSCredential "nologin", $securePersonalAccessToken
         $token = $credential.GetNetworkCredential().Password
      }
      # Bind the parameter to a friendly variable
      $ProjectName = $PSBoundParameters['ProjectName']
      $obj = @{
         authorization = @{
            parameters = @{
               username = $token;
               password = ''
            };
            scheme     = 'UsernamePassword'
         };
         data          = @{ };
         url           = $sonarqubeUrl
      }
      try {
         return Add-VSTeamServiceEndpoint -ProjectName $ProjectName `
            -endpointName $endpointName `
            -endpointType sonarqube `
            -object $obj
      }
      catch [System.Net.WebException] {
         if ($_.Exception.status -eq 'ProtocolError') {
            $errorDetails = ConvertFrom-Json $_.ErrorDetails
            $message = $errorDetails.message
            # The error message is different on TFS and VSTS
            if ($message.StartsWith("Endpoint type couldn't be recognized 'sonarqube'") -or
               $message.StartsWith("Unable to find service endpoint type 'sonarqube'")) {
               Write-Error -Message 'The Sonarqube extension not installed. Please install from https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube'
               return
            }
         }
         throw
      }
   }
}
# Adds a task group.
#
# Get-VSTeamOption 'distributedtask' 'taskgroups'
# id : 6c08ffbf-dbf1-4f9a-94e5-a1cbd47005e7
# area : distributedtask
# resourceName : taskgroups
# routeTemplate : {project}/_apis/{area}/{resource}/{taskGroupId}
# http://bit.ly/Add-VSTeamTaskGroup
function Add-VSTeamTaskGroup {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamTaskGroup')]
   param(
      [Parameter(ParameterSetName = 'ByFile', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $InFile,
      [Parameter(ParameterSetName = 'ByBody', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Body,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         Method      = 'Post'
         Area        = 'distributedtask'
         Resource    = 'taskgroups'
         ProjectName = $ProjectName
         Version     = $(_getApiVersion TaskGroups)
      }
      if ($InFile) {
         $resp = _callAPI @commonArgs -InFile $InFile
      }
      else {
         $resp = _callAPI @commonArgs -Body $Body
      }
      return $resp
   }
}
# Add a user, assign license and extensions and make them a member of a
# project group in an account.
#
# Get-VSTeamOption 'MemberEntitlementManagement' 'UserEntitlements' -subDomain 'vsaex'
# id : 387f832c-dbf2-4643-88e9-c1aa94dbb737
# area : MemberEntitlementManagement
# resourceName : UserEntitlements
# routeTemplate : _apis/{resource}/{userDescriptor}
# http://bit.ly/Add-VSTeamUserEntitlement
function Add-VSTeamUserEntitlement {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamUserEntitlement')]
   param(
      [Parameter(Mandatory = $true)]
      [Alias('UserEmail')]
      [string]$Email,
      [ValidateSet('Advanced', 'EarlyAdopter', 'Express', 'None', 'Professional', 'StakeHolder')]
      [string]$License = 'EarlyAdopter',
      [ValidateSet('Custom', 'ProjectAdministrator', 'ProjectContributor', 'ProjectReader', 'ProjectStakeholder')]
      [string]$Group = 'ProjectContributor',
      [ValidateSet('account', 'auto', 'msdn', 'none', 'profile', 'trial')]
      [string]$LicensingSource = "account",
      [ValidateSet('eligible', 'enterprise', 'none', 'platforms', 'premium', 'professional', 'testProfessional', 'ultimate')]
      [string]$MSDNLicenseType = "none",
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # Thi swill throw if this account does not support MemberEntitlementManagement
      _supportsMemberEntitlementManagement
      $obj = @{
         accessLevel         = @{
            accountLicenseType = $License
            licensingSource    = $LicensingSource
            msdnLicenseType    = $MSDNLicenseType
         }
         user                = @{
            principalName = $email
            subjectKind   = 'user'
         }
         projectEntitlements = @{
            group      = @{
               groupType = $Group
            }
            projectRef = @{
               id = $ProjectName
            }
         }
      }
      $body = $obj | ConvertTo-Json -Depth 100
      # Call the REST API
      _callAPI -Method POST -SubDomain "vsaex" `
         -Resource "userentitlements" `
         -Body $body `
         -Version $(_getApiVersion MemberEntitlementManagement)
   }
}
# Adds a variable group.
#
# Get-VSTeamOption 'distributedtask' 'variablegroups'
# id : f5b09dd5-9d54-45a1-8b5a-1c8287d634cc
# area : distributedtask
# resourceName : variablegroups
# routeTemplate : {project}/_apis/{area}/{resource}/{groupId}
# http://bit.ly/Add-VSTeamVariableGroup
function Add-VSTeamVariableGroup {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamVariableGroup')]
   param(
      [Parameter(ParameterSetName = 'ByHashtable', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Name,
      [Parameter(ParameterSetName = 'ByHashtable', Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
      [string] $Description,
      [Parameter(ParameterSetName = 'ByHashtable', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [hashtable] $Variables,
      [Parameter(ParameterSetName = 'ByBody', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Body,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   DynamicParam {
      $dp = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
      if ([vsteam_lib.Versions]::Version -ne "TFS2017" -and $PSCmdlet.ParameterSetName -eq "ByHashtable") {
         $ParameterName = 'Type'
         $rp = _buildDynamicParam -ParameterName $ParameterName -arrSet ('Vsts', 'AzureKeyVault') -Mandatory $true
         $dp.Add($ParameterName, $rp)
         $ParameterName = 'ProviderData'
         $rp = _buildDynamicParam -ParameterName $ParameterName -Mandatory $false -ParameterType ([hashtable])
         $dp.Add($ParameterName, $rp)
      }
      return $dp
   }
   Process {
      # This will throw if this account does not support the variable groups
      _supportVariableGroups
      if ([string]::IsNullOrWhiteSpace($Body)) {
         $bodyAsHashtable = @{
            name        = $Name
            description = $Description
            variables   = $Variables
         }
         if ([vsteam_lib.Versions]::Version -ne "TFS2017") {
            $Type = $PSBoundParameters['Type']
            $bodyAsHashtable.Add("type", $Type)
            $ProviderData = $PSBoundParameters['ProviderData']
            if ($null -ne $ProviderData) {
               $bodyAsHashtable.Add("providerData", $ProviderData)
            }
         }
         $body = $bodyAsHashtable | ConvertTo-Json -Depth 100
      }
      # Call the REST API
      $resp = _callAPI -Method POST -ProjectName $projectName `
         -Area "distributedtask" `
         -Resource "variablegroups" `
         -body $body `
         -Version $(_getApiVersion VariableGroups)
      return Get-VSTeamVariableGroup -ProjectName $ProjectName -id $resp.id
   }
}
function Add-VSTeamWiki {
   [CmdletBinding(DefaultParameterSetName = 'projectWiki',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamWiki')]
   param(
      [Parameter(Mandatory = $true)]
      [Alias('WikiName')]
      [string] $Name,
      [Parameter(Mandatory = $true, ParameterSetName = 'codeWiki')]
      [string] $RepositoryId,
      [Parameter(Mandatory = $true, ParameterSetName = 'codeWiki')]
      [string] $Branch,
      [Parameter(Mandatory = $true, ParameterSetName = 'codeWiki')]
      [string] $MappedPath,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true )]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # Create the body for a projectWiki type
      $body = [ordered]@{
         type = $PSCmdlet.ParameterSetName
         name = $Name
         projectId = (Get-VSTeamProject -Name $ProjectName).id
      }
      # if its a code wiki include additional properties
      if ($PSCmdlet.ParameterSetName -eq 'codeWiki'){
         $body+= [ordered]@{
            Version = @{
               Version = $Branch
            }
            repositoryId = $RepositoryId
            mappedPath = $MappedPath
         }
      }
      $commonArgs = [ordered]@{
         Method = 'POST'
         Area = 'wiki'
         Resource = 'wikis'
         Body = $($body | ConvertTo-Json -Depth 100 -compress)
         ContentType = 'application/json'
         Version = $(_getApiVersion Wiki)
      }
      $resp = _callAPI @commonArgs
      Write-Output $resp
   }
}
# Adds a work item to your project.
#
# Get-VSTeamOption 'wit' 'workItems'
# id : 62d3d110-0047-428c-ad3c-4fe872c91c74
# area : wit
# resourceName : workItems
# routeTemplate : {project}/_apis/{area}/{resource}/${type}
# http://bit.ly/Add-VSTeamWorkItem
function Add-VSTeamWorkItem {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamWorkItem')]
   param(
      [Parameter(Mandatory = $true)]
      [string] $Title,
      [Parameter(Mandatory = $false)]
      [string] $Description,
      [Parameter(Mandatory = $false)]
      [string] $IterationPath,
      [Parameter(Mandatory = $false)]
      [string] $AssignedTo,
      [Parameter(Mandatory = $false)]
      [int] $ParentId,
      [Parameter(Mandatory = $false)]
      [hashtable] $AdditionalFields,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [Parameter(Mandatory = $true)]
      [vsteam_lib.WorkItemTypeValidateAttribute()]
      [ArgumentCompleter([vsteam_lib.WorkItemTypeCompleter])]
      [string] $WorkItemType
   )
   Process {
      # The type has to start with a $
      # We can't reasign to $WorkItemType because the validate attribute
      # above will fire again and throw exception.
      $fullWorkItemType = '$' + $WorkItemType
      # Constructing the contents to be send.
      # Empty parameters will be skipped when converting to json.
      [Array]$body = @(
         @{
            op    = "add"
            path  = "/fields/System.Title"
            value = $Title
         }
         @{
            op    = "add"
            path  = "/fields/System.Description"
            value = $Description
         }
         @{
            op    = "add"
            path  = "/fields/System.IterationPath"
            value = $IterationPath
         }
         @{
            op    = "add"
            path  = "/fields/System.AssignedTo"
            value = $AssignedTo
         }) | Where-Object { $_.value }
      if ($ParentId) {
         $parentUri = _buildRequestURI -ProjectName $ProjectName -Area 'wit' -Resource 'workitems' -id $ParentId
         $body += @{
            op    = "add"
            path  = "/relations/-"
            value = @{
               "rel" = "System.LinkTypes.Hierarchy-Reverse"
               "url" = $parentURI
            }
         }
      }
      #this loop must always come after the main work item fields defined in the function parameters
      if ($AdditionalFields) {
         foreach ($fieldName in $AdditionalFields.Keys) {
            #check that main properties are not added into the additional fields hashtable
            $foundFields = $body | Where-Object { $null -ne $_ -and $_.path -like "*$fieldName" }
            if ($null -ne $foundFields) {
               throw "Found duplicate field '$fieldName' in parameter AdditionalFields, which is already a parameter. Please remove it."
            }
            else {
               $body += @{
                  op    = "add"
                  path  = "/fields/$fieldName"
                  value = $AdditionalFields[$fieldName]
               }
            }
         }
      }
      # It is very important that even if the user only provides
      # a single value above that the item is an array and not
      # a single object or the call will fail.
      # You must call ConvertTo-Json passing in the value and not
      # not using pipeline.
      # https://stackoverflow.com/questions/18662967/convertto-json-an-array-with-a-single-item
      $json = ConvertTo-Json @($body) -Compress -Depth 100
      # Call the REST API
      $resp = _callAPI -Method POST -ProjectName $ProjectName `
         -Area "wit" `
         -Resource "workitems" `
         -id $fullWorkItemType `
         -Body $json `
         -ContentType 'application/json-patch+json; charset=utf-8' `
         -Version $(_getApiVersion Core)
      _applyTypesToWorkItem -item $resp
      return $resp
   }
}
# Add or update ACEs in the ACL for the provided token. The request body
# contains the target token, a list of ACEs and a optional merge parameter.
# In the case of a collision (by identity descriptor) with an existing ACE
# in the ACL, the "merge" parameter determines the behavior. If set, the
# existing ACE has its allow and deny merged with the incoming ACE's allow
# and deny. If unset, the existing ACE is displaced.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Add-VSTeamWorkItemAreaPermission {
   [CmdletBinding(DefaultParameterSetName = 'ByProjectAndAreaIdAndUser',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamWorkItemAreaPermission')]
   param(
      [parameter(Mandatory = $true)]
      [vsteam_lib.Project]$Project,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaIdAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaIdAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaIdAndUser")]
      [int]$AreaID,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaPathAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaPathAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaPathAndGroup")]
      [string]$AreaPath,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaIdAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaPathAndDescriptor")]
      [string]$Descriptor,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaIdAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaPathAndGroup")]
      [vsteam_lib.Group]$Group,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaPathAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndAreaIdAndUser")]
      [vsteam_lib.User]$User,
      [parameter(Mandatory = $false)]
      [vsteam_lib.WorkItemAreaPermissions]$Allow,
      [parameter(Mandatory = $false)]
      [vsteam_lib.WorkItemAreaPermissions]$Deny
   )
   process {
      # SecurityID: 83e28ad4-2d72-4ceb-97b0-c7726d5502c3
      # Token: vstfs:///Classification/Node/862eb45f-3873-41d7-89c8-4b2f8802eaa9 (https://dev.azure.com/<organization>/<project>/_apis/wit/classificationNodes/Areas)
      # "token": "vstfs:///Classification/Node/ae76de05-8b53-4e02-9205-e73e2012585e:vstfs:///Classification/Node/f8c5b667-91dd-4fe7-bf23-3138c439d07e",
      $securityNamespaceId = "83e28ad4-2d72-4ceb-97b0-c7726d5502c3"
      if ($AreaID) {
         $area = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -Id $AreaID
      }
      if ($AreaPath) {
         $area = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -Path $AreaPath -StructureGroup "areas"
      }
      if (-not $area) {
         throw "Area not found"
      }
      if ($area.StructureType -ne "area") {
         throw "This is not an Area"
      }
      $nodes = @()
      $nodes += $area
      while ($area.ParentUrl) {
         $path = $area.ParentUrl -ireplace ".*(classificationNodes/Areas)\/?"
         if ($path.length -gt 0) {
            # We have a Path to resolve
            $area = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -Path $path -StructureGroup "Areas"
         }
         else {
            # We need to get the "root" node now
            $area = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -StructureGroup "Areas"
         }
         $nodes += $area
      }
      # Build Token from Path
      [array]::Reverse($nodes)
      $token = ($nodes | ForEach-Object { "vstfs:///Classification/Node/$($_.Identifier)" }) -join ":"
      # Resolve Group to Descriptor
      if ($Group) {
         $Descriptor = _getDescriptorForACL -Group $Group
      }
      # Resolve User to Descriptor
      if ($User) {
         $Descriptor = _getDescriptorForACL -User $User
      }
      Add-VSTeamAccessControlEntry -SecurityNamespaceId $securityNamespaceId `
         -Descriptor $Descriptor `
         -Token $token `
         -AllowMask ([int]$Allow) `
         -DenyMask ([int]$Deny)
   }
}
# Add or update ACEs in the ACL for the provided token. The request body
# contains the target token, a list of ACEs and a optional merge parameter.
# In the case of a collision (by identity descriptor) with an existing ACE
# in the ACL, the "merge" parameter determines the behavior. If set, the
# existing ACE has its allow and deny merged with the incoming ACE's allow
# and deny. If unset, the existing ACE is displaced.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Add-VSTeamWorkItemIterationPermission {
   [CmdletBinding(DefaultParameterSetName = 'ByProjectAndIterationIdAndUser',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Add-VSTeamWorkItemIterationPermission')]
   param(
      [parameter(Mandatory = $true)]
      [vsteam_lib.Project]$Project,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationIdAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationIdAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationIdAndUser")]
      [int]$IterationID,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationPathAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationPathAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationPathAndGroup")]
      [string]$IterationPath,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationIdAndDescriptor")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationPathAndDescriptor")]
      [string]$Descriptor,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationIdAndGroup")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationPathAndGroup")]
      [vsteam_lib.Group]$Group,
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationPathAndUser")]
      [parameter(Mandatory = $true, ParameterSetName = "ByProjectAndIterationIdAndUser")]
      [vsteam_lib.User]$User,
      [parameter(Mandatory = $false)]
      [vsteam_lib.WorkItemIterationPermissions]$Allow,
      [parameter(Mandatory = $false)]
      [vsteam_lib.WorkItemIterationPermissions]$Deny
   )
   process {
      # SecurityID: bf7bfa03-b2b7-47db-8113-fa2e002cc5b1
      # Token: vstfs:///Classification/Node/862eb45f-3873-41d7-89c8-4b2f8802eaa9 (https://dev.azure.com/<organization>/<project>/_apis/wit/classificationNodes/Iterations)
      # "token": "vstfs:///Classification/Node/ae76de05-8b53-4e02-9205-e73e2012585e:vstfs:///Classification/Node/f8c5b667-91dd-4fe7-bf23-3138c439d07e",
      $securityNamespaceId = "bf7bfa03-b2b7-47db-8113-fa2e002cc5b1"
      if ($IterationID) {
         $iteration = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -Id $IterationID
      }
      if ($IterationPath) {
         $iteration = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -Path $IterationPath -StructureGroup "iterations"
      }
      if (-not $iteration) {
         throw "Iteration not found"
      }
      if ($iteration.StructureType -ne "iteration") {
         throw "This is not an Iteration"
      }
      $nodes = @()
      $nodes += $iteration
      while ($iteration.ParentUrl) {
         $path = $iteration.ParentUrl -ireplace ".*(classificationNodes/Iterations)\/?"
         if ($path.length -gt 0) {
            # We have a Path to resolve
            $iteration = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -Path $path -StructureGroup "Iterations"
         }
         else {
            # We need to get the "root" node now
            $iteration = Get-VSTeamClassificationNode -ProjectName $Project.Name -Depth 0 -StructureGroup "Iterations"
         }
         $nodes += $iteration
      }
      # Build Token from Path
      [array]::Reverse($nodes)
      $token = ($nodes | ForEach-Object { "vstfs:///Classification/Node/$($_.Identifier)" }) -join ":"
      # Resolve Group to Descriptor
      if ($Group) {
         $Descriptor = _getDescriptorForACL -Group $Group
      }
      # Resolve User to Descriptor
      if ($User) {
         $Descriptor = _getDescriptorForACL -User $User
      }
      Add-VSTeamAccessControlEntry -SecurityNamespaceId $securityNamespaceId `
         -Descriptor $Descriptor `
         -Token $token `
         -AllowMask ([int]$Allow) `
         -DenyMask ([int]$Deny)
   }
}
function Clear-VSTeamDefaultAPITimeout {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   [CmdletBinding()]
   param()
   DynamicParam {
      # # Only add these options on Windows Machines
      if (_isOnWindows) {
         $ParameterName = 'Level'
         # Create the dictionary
         $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
         # Create the collection of attributes
         $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
         # Create and set the parameters' attributes
         $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
         $ParameterAttribute.Mandatory = $false
         $ParameterAttribute.HelpMessage = "On Windows machines allows you to store the default timeout at the process, user or machine level. Not available on other platforms."
         # Add the attributes to the attributes collection
         $AttributeCollection.Add($ParameterAttribute)
         # Generate and set the ValidateSet
         if (_testAdministrator) {
            $arrSet = "Process", "User", "Machine"
         }
         else {
            $arrSet = "Process", "User"
         }
         $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
         # Add the ValidateSet to the attributes collection
         $AttributeCollection.Add($ValidateSetAttribute)
         # Create and return the dynamic parameter
         $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
         $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
         return $RuntimeParameterDictionary
      }
   }
   begin {
      if (_isOnWindows) {
         # Bind the parameter to a friendly variable
         $Level = $PSBoundParameters[$ParameterName]
      }
   }
   process {
      if (_isOnWindows) {
         if (-not $Level) {
            $Level = "Process"
         }
      }
      else {
         $Level = "Process"
      }
      # You always have to set at the process level or they will Not
      # be seen in your current session.
      $env:TEAM_TIMEOUT = $null
      if (_isOnWindows) {
         [System.Environment]::SetEnvironmentVariable("TEAM_TIMEOUT", $null, $Level)
      }
      [vsteam_lib.Versions]::DefaultTimeout = ''
      $Global:PSDefaultParameterValues.Remove("*-vsteam*:vsteamApiTimeout")
      Write-Output "Removed default timeout"
   }
}
function Clear-VSTeamDefaultProject {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   [CmdletBinding()]
   param()
   DynamicParam {
      _buildLevelDynamicParam
   }
   begin {
      if (_isOnWindows) {
         # Bind the parameter to a friendly variable
         $Level = $PSBoundParameters['Level']
      }
   }
   process {
      if (_isOnWindows) {
         if (-not $Level) {
            $Level = "Process"
         }
      }
      else {
         $Level = "Process"
      }
      # You always have to set at the process level or they will Not
      # be seen in your current session.
      $env:TEAM_PROJECT = $null
      if (_isOnWindows) {
         [System.Environment]::SetEnvironmentVariable("TEAM_PROJECT", $null, $Level)
      }
      [vsteam_lib.Versions]::DefaultProject = ''
      $Global:PSDefaultParameterValues.Remove("*-vsteam*:projectName")
      Write-Output "Removed default project"
   }
}
function Clear-VSTeamDefaultProjectCount {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   [CmdletBinding()]
   param()
   DynamicParam {
      _buildLevelDynamicParam
   }
   begin {
      if (_isOnWindows) {
         # Bind the parameter to a friendly variable
         $Level = $PSBoundParameters['Level']
      }
   }
   process {
      if (_isOnWindows) {
         if (-not $Level) {
            $Level = "Process"
         }
      }
      else {
         $Level = "Process"
      }
      # You always have to set at the process level or they will Not
      # be seen in your current session.
      $env:TEAM_PROJECTCOUNT = $null
      if (_isOnWindows) {
         [System.Environment]::SetEnvironmentVariable("TEAM_PROJECTCOUNT", $null, $Level)
      }
      Write-Output "Default project count cleared"
   }
}
# Disables an agent in a pool.
#
# Get-VSTeamOption 'distributedtask' 'agents'
# id : e298ef32-5878-4cab-993c-043836571f42
# area : distributedtask
# resourceName : agents
# routeTemplate : _apis/{area}/pools/{poolId}/{resource}/{agentId}
# http://bit.ly/Disable-VSTeamAgent
function Disable-VSTeamAgent {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Disable-VSTeamAgent')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int] $PoolId,
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('AgentID')]
      [int[]] $Id,
      [switch] $Force
   )
   process {
      if ($Force -or $pscmdlet.ShouldProcess("Agent $Id", "Disable Agent")) {
         foreach ($item in $Id) {
            try {
               _callAPI -Method PATCH -NoProject `
                  -Area "distributedtask/pools/$PoolId" `
                  -Resource "agents" `
                  -Id $item `
                  -Body "{'enabled':false,'id':$item,'maxParallelism':1}" `
                  -Version $(_getApiVersion DistributedTask) | Out-Null
               Write-Output "Disabled agent $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Enable-VSTeamAgent {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Enable-VSTeamAgent')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int] $PoolId,
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('AgentID')]
      [int[]] $Id
   )
   process {
      foreach ($item in $Id) {
         try {
            _callAPI -Method PATCH -NoProject `
               -Area "distributedtask/pools/$PoolId" `
               -Resource agents `
               -Id $item `
               -Body "{'enabled':true,'id':$item,'maxParallelism':1}" `
               -Version $(_getApiVersion DistributedTask) | Out-Null
            Write-Output "Enabled agent $item"
         }
         catch {
            _handleException $_
         }
      }
   }
}
function Get-VSTeam {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeam')]
   param (
      [Parameter(ParameterSetName = 'List')]
      [int] $Top,
      [Parameter(ParameterSetName = 'List')]
      [int] $Skip,
      [Parameter(ParameterSetName = 'ByName', Position = 0)]
      [Alias('TeamName')]
      [string[]] $Name,
      [Parameter(ParameterSetName = 'ByID')]
      [Alias('TeamId')]
      [string[]] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         Area     = 'projects'
         Resource = "$ProjectName/teams"
         Version  = $(_getApiVersion Core)
      }
      if ($Id) {
         foreach ($item in $Id) {
            # Call the REST API
            $resp = _callAPI @commonArgs -id $item
            $team = [vsteam_lib.Team]::new($resp, $ProjectName)
            Write-Output $team
         }
      }
      elseif ($Name) {
         foreach ($item in $Name) {
            # Call the REST API
            $resp = _callAPI @commonArgs -id $item
            $team = [vsteam_lib.Team]::new($resp, $ProjectName)
            Write-Output $team
         }
      }
      else {
         # Call the REST API
         $resp = _callAPI @commonArgs `
            -QueryString @{
            '$top'  = $top
            '$skip' = $skip
         }
         $obj = @()
         # Create an instance for each one
         foreach ($item in $resp.value) {
            $obj += [vsteam_lib.Team]::new($item, $ProjectName)
         }
         Write-Output $obj
      }
   }
}
function Get-VSTeamAccessControlList {
   [CmdletBinding(DefaultParameterSetName = 'ByNamespace',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamAccessControlList')]
   param(
      [Parameter(ParameterSetName = 'ByNamespace', Mandatory = $true, ValueFromPipeline = $true)]
      [vsteam_lib.SecurityNamespace] $SecurityNamespace,
      [Parameter(ParameterSetName = 'ByNamespaceId', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('ID')]
      [guid] $SecurityNamespaceId,
      [string] $Token,
      [string[]] $Descriptors,
      [switch] $IncludeExtendedInfo,
      [switch] $Recurse
   )
   process {
      if ($SecurityNamespace) {
         $SecurityNamespaceId = $SecurityNamespace.ID
      }
      $queryString = @{ }
      if ($Token) {
         $queryString.token = $Token
      }
      if ($Descriptors -and $Descriptors.Length -gt 0) {
         $queryString.descriptors = $Descriptors -join ","
      }
      if ($IncludeExtendedInfo.IsPresent) {
         $queryString.includeExtendedInfo = $true
      }
      if ($Recurse.IsPresent) {
         $queryString.recurse = $true
      }
      # Call the REST API
      $resp = _callAPI -NoProject `
         -Resource accesscontrollists `
         -id $SecurityNamespaceId `
         -QueryString $queryString `
         -Version $(_getApiVersion Core)
      try {
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += [vsteam_lib.AccessControlList]::new($item)
         }
         Write-Output $objs
      }
      catch {
         # I catch because using -ErrorAction Stop on the Invoke-RestMethod
         # was still running the foreach after and reporting useless errors.
         # This casuses the first error to terminate this execution.
         _handleException $_
      }
   }
}
function Get-VSTeamAccountBilling {
   [CmdletBinding(
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamAccountBilling')]
   param()
   process {
      # This will throw if this account does not support the Billing API
      _supportsBilling
      $userProfileId = (Get-VSTeamUserProfile -MyProfile).id
      $currentAccount = (Get-VSTeamAccounts -MemberId $userProfileId) | Where-Object { (_getInstance).EndsWith($_.accountName) }
      try {
         # need to use the url, since this specific api needs to have
         # the account GUID instead of the clear name in the url
         # _callAPI does not support this, hence full URL is used
         _callAPI `
            -Url "https://azdevopscommerce.dev.azure.com/$($currentAccount.accountId)/_apis/AzComm/BillingSetup?api-version=$(_getApiVersion Billing)"
      }
      catch {
         _handleException $_
      }
   }
}
function Get-VSTeamAccounts {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
   [CmdletBinding(HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamAccounts')]
   param(
      [Parameter(Mandatory = $true, ParameterSetName = "MemberId")]
      [string] $MemberId,
      [Parameter(Mandatory = $true, ParameterSetName = "OwnerId")]
      [string] $OwnerId
   )
   process {
      $queryString = @{ }
      if ($PsCmdlet.ParameterSetName -eq "MemberId") {
         $queryString = @{
            memberId = $MemberId
         }
      }
      if ($PsCmdlet.ParameterSetName -eq "OwnerId") {
         $queryString = @{
            ownerId = $OwnerId
         }
      }
      try {
         # Call the REST API
         $resp = _callAPI -NoAccount `
            -NoProject `
            -area 'accounts' `
            -subDomain 'vssps' `
            -QueryString $queryString `
            -version $(_getApiVersion Core)
         Write-Output $resp.value
      }
      catch {
         _handleException $_
      }
   }
}
function Get-VSTeamAgent {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamAgent')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
      [int] $PoolId,
      [Parameter(ParameterSetName = 'ByID', Mandatory = $true, Position = 1)]
      [Alias('AgentID')]
      [int] $Id
   )
   process {
      $commonArgs = @{
         NoProject = $true
         Area      = "distributedtask/pools/$PoolId"
         Resource  = 'agents'
         Body      = @{ includeCapabilities = 'true' }
         Version   = $(_getApiVersion DistributedTaskReleased)
      }
      if ($id) {
         $resp = _callAPI @commonArgs -Id $id
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $item = [vsteam_lib.Agent]::new($resp, $PoolId)
         Write-Output $item
      }
      else {
         $resp = _callAPI @commonArgs
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += [vsteam_lib.Agent]::new($item, $PoolId)
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamAgentPoolMaintenance {
   [CmdletBinding(
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamAgentPoolMaintenance')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('PoolID')]
      [int] $Id
   )
   process {
      $resp = _callAPI -Method Get -NoProject -Area distributedtask -Resource pools -Id "$Id/maintenancedefinitions" -Version $(_getApiVersion DistributedTask)
      if ($resp -and $resp.count -gt 0) {
         foreach ($schedule in $resp.value) {
            _applyTypesToAgentPoolMaintenance -item $schedule
         }
      }
      Write-Output $resp.value
   }
}
function Get-VSTeamAPIVersion {
   [CmdletBinding(HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamAPIVersion')]
   [OutputType([System.Collections.Hashtable])]
   param(
      [Parameter(Mandatory = $false, Position = 0)]
      [ValidateSet('Build', 'Release', 'Core', 'Git', 'DistributedTask',
                   'DistributedTaskReleased', 'VariableGroups', 'Tfvc',
                   'Packaging', 'MemberEntitlementManagement',
                   'ExtensionsManagement', 'ServiceEndpoints', 'Graph',
                   'TaskGroups', 'Policy', 'Processes', 'HierarchyQuery',
                   'Pipelines', 'Billing')]
      [string] $Service
   )
   if ($Service) {
      return $(_getApiVersion $Service)
   }
   else {
      # Casting to a [PSCustomObject] returns the properties in this
      # exact order. If not this is a hashtable and the order is
      # nondeterministic
      return [PSCustomObject]@{
         Billing                     = $(_getApiVersion Billing)
         Build                       = $(_getApiVersion Build)
         Core                        = $(_getApiVersion Core)
         DistributedTask             = $(_getApiVersion DistributedTask)
         DistributedTaskReleased     = $(_getApiVersion DistributedTaskReleased)
         ExtensionsManagement        = $(_getApiVersion ExtensionsManagement)
         Git                         = $(_getApiVersion Git)
         Graph                       = $(_getApiVersion Graph)
         HierarchyQuery              = $(_getApiVersion HierarchyQuery)
         MemberEntitlementManagement = $(_getApiVersion MemberEntitlementManagement)
         Packaging                   = $(_getApiVersion Packaging)
         Pipelines                   = $(_getApiVersion Pipelines)
         Policy                      = $(_getApiVersion Policy)
         Processes                   = $(_getApiVersion Processes)
         Release                     = $(_getApiVersion Release)
         ServiceEndpoints            = $(_getApiVersion ServiceEndpoints)
         TaskGroups                  = $(_getApiVersion TaskGroups)
         Tfvc                        = $(_getApiVersion Tfvc)
         VariableGroups              = $(_getApiVersion VariableGroups)
         Version                     = $(_getApiVersion -Target)
      }
   }
}
function Get-VSTeamApproval {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamApproval')]
   param(
      [ValidateSet('Approved', 'ReAssigned', 'Rejected', 'Canceled', 'Pending', 'Rejected', 'Skipped', 'Undefined')]
      [string] $StatusFilter,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [int[]] $ReleaseId,
      [string] $AssignedToFilter,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      try {
         # Build query string and determine if the includeMyGroupApprovals should be added.
         $queryString = @{statusFilter = $StatusFilter; assignedtoFilter = $AssignedToFilter; releaseIdsFilter = ($ReleaseId -join ',') }
         # The support in TFS and VSTS are not the same.
         $instance = $(_getInstance)
         if (_isVSTS $instance) {
            if ([string]::IsNullOrEmpty($AssignedToFilter) -eq $false) {
               $queryString.includeMyGroupApprovals = 'true';
            }
         }
         else {
            # For TFS all three parameters must be set before you can add
            # includeMyGroupApprovals.
            if ([string]::IsNullOrEmpty($AssignedToFilter) -eq $false -and
               [string]::IsNullOrEmpty($ReleaseId) -eq $false -and
               $StatusFilter -eq 'Pending') {
               $queryString.includeMyGroupApprovals = 'true';
            }
         }
         # Call the REST API
         $resp = _callAPI -SubDomain vsrm -ProjectName $ProjectName `
            -Area release `
            -Resource approvals `
            -QueryString $queryString `
            -Version $(_getApiVersion Release)
         # Apply a Type Name so we can use custom format view and custom type extensions
         foreach ($item in $resp.value) {
            _applyTypesToApproval -item $item
         }
         Write-Output $resp.value
      }
      catch {
         _handleException $_
      }
   }
}
function Get-VSTeamArea {
   [CmdletBinding(DefaultParameterSetName = 'ByPath',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamArea')]
   param(
      [Parameter(Mandatory = $false, ParameterSetName = "ByPath")]
      [string] $Path,
      [Parameter(Mandatory = $false, ParameterSetName = "ByIds")]
      [int[]] $Id,
      [int] $Depth,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($PSCmdlet.ParameterSetName -eq "ByPath") {
         $resp = Get-VSTeamClassificationNode -ProjectName $ProjectName `
            -StructureGroup "areas" `
            -Path $Path `
            -Depth $Depth
      }
      else {
         $resp = Get-VSTeamClassificationNode -ProjectName $ProjectName `
            -Depth $Depth `
            -Id $Id
      }
      Write-Output $resp
   }
}
function Get-VSTeamBuild {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamBuild')]
   param (
      [Parameter(ParameterSetName = 'List')]
      [int] $Top,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('succeeded', 'partiallySucceeded', 'failed', 'canceled')]
      [string] $ResultFilter,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('manual', 'individualCI', 'batchedCI', 'schedule', 'userCreated', 'validateShelveset', 'checkInShelveset', 'triggered', 'all')]
      [string] $ReasonFilter,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('inProgress', 'completed', 'cancelling', 'postponed', 'notStarted', 'all')]
      [string] $StatusFilter,
      [Parameter(ParameterSetName = 'List')]
      [int[]] $Queues,
      [Parameter(ParameterSetName = 'List')]
      [int[]] $Definitions,
      [ArgumentCompleter([vsteam_lib.BuildCompleter])]
      [Parameter(ParameterSetName = 'List')]
      [string] $BuildNumber,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('build', 'xaml')]
      [string] $Type,
      [Parameter(ParameterSetName = 'List')]
      [int] $MaxBuildsPerDefinition,
      [Parameter(ParameterSetName = 'List')]
      [string[]] $Properties,
      [Parameter(ParameterSetName = 'ByID', ValueFromPipeline = $true)]
      [Alias('BuildID')]
      [int[]] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      try {
         if ($id) {
            foreach ($item in $id) {
               # Build the url to return the single build
               $resp = _callAPI -ProjectName $projectName `
                  -Area build `
                  -Resource builds `
                  -id $item `
                  -Version $(_getApiVersion Build)
               $build = [vsteam_lib.Build]::new($resp, $ProjectName)
               Write-Output $build
            }
         }
         else {
            # Build the url to list the builds
            $querystring = @{
               '$top'                   = $top
               'type'                   = $type
               'buildNumber'            = $buildNumber
               'resultFilter'           = $resultFilter
               'statusFilter'           = $statusFilter
               'reasonFilter'           = $reasonFilter
               'maxBuildsPerDefinition' = $maxBuildsPerDefinition
               'queues'                 = ($queues -join ',')
               'properties'             = ($properties -join ',')
               'definitions'            = ($definitions -join ',')
            }
            $resp = _callAPI -ProjectName $projectName `
               -Area build `
               -Resource builds `
               -Querystring $queryString `
               -Version $(_getApiVersion Build)
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.Build]::new($item, $ProjectName)
            }
            Write-Output $objs
         }
      }
      catch {
         _handleException $_
      }
   }
}
function Get-VSTeamBuildArtifact {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamBuildArtifact')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $resp = _callAPI -ProjectName $projectName `
         -Area build `
         -Resource builds `
         -Id "$Id/artifacts" `
         -Version $(_getApiVersion Build)
      foreach ($item in $resp.value) {
         _applyArtifactTypes -item $item
      }
      Write-Output $resp.value
   }
}
function Get-VSTeamBuildDefinition {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamBuildDefinition')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [string] $Filter,
      [Alias('BuildDefinitionID')]
      [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ByIdRaw')]
      [Parameter(Position = 0, ParameterSetName = 'ByID', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [Parameter(ParameterSetName = 'ByID')]
      [Parameter(ParameterSetName = 'ByIdRaw')]
      [int] $Revision,
      [switch] $JSON,
      [Parameter(Mandatory = $true, ParameterSetName = 'ByIdRaw')]
      [switch] $raw,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($id) {
         foreach ($item in $id) {
            $resp = _callAPI -ProjectName $ProjectName `
               -Area build `
               -Resource definitions `
               -Id $item `
               -QueryString @{revision = $revision } `
               -Version $(_getApiVersion Build)
            if ($JSON.IsPresent) {
               $resp | ConvertTo-Json -Depth 99
            }
            else {
               if (-not $raw.IsPresent) {
                  $item = [vsteam_lib.BuildDefinition]::new($resp, $ProjectName)
                  Write-Output $item
               }
               else {
                  Write-Output $resp
               }
            }
         }
      }
      else {
         $resp = _callAPI -ProjectName $ProjectName `
            -Area build `
            -Resource definitions `
            -Version $(_getApiVersion Build) `
            -QueryString @{name = $filter; includeAllProperties = $true }
         if ($JSON.IsPresent) {
            $resp | ConvertTo-Json -Depth 99
         }
         else {
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.BuildDefinition]::new($item, $ProjectName)
            }
            Write-Output $objs
         }
      }
   }
}
function Get-VSTeamBuildLog {
   [CmdletBinding(DefaultParameterSetName = 'ByID',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamBuildLog')]
   param (
      [Parameter(Mandatory = $true, ParameterSetName = 'ByID', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int[]] $Id,
      [int] $Index,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if (-not $Index) {
            # Build the url to return the logs of the build
            # Call the REST API to get the number of logs for the build
            $resp = _callAPI -ProjectName $projectName `
               -Area build `
               -Resource builds `
               -Id "$item/logs" `
               -Version $(_getApiVersion Build)
            $fullLogIndex = $($resp.count)
         }
         else {
            $fullLogIndex = $Index
         }
         # Now call REST API with the index for the fullLog
         # Build the url to return the single build
         # Call the REST API to get the number of logs for the build
         $resp = _callAPI -ProjectName $projectName `
            -Area build `
            -Resource builds `
            -Id "$item/logs/$fullLogIndex" `
            -Version $(_getApiVersion Build)
         Write-Output $resp
      }
   }
}
function Get-VSTeamBuildTag {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamBuildTag')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # Call the REST API
      $resp = _callAPI -ProjectName $projectName `
         -Area build `
         -Resource builds `
         -ID "$Id/tags" `
         -Version $(_getApiVersion Build)
      return $resp.value
   }
}
function Get-VSTeamBuildTimeline {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamBuildTimeline')]
   param (
      [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 1)]
      [int[]] $Id,
      [Guid] $TimelineId,
      [int] $ChangeId,
      [Guid] $PlanId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   Process {
      foreach ($item in $Id) {
         # Build the url to return the single build
         $resource = "builds/$item/timeline"
         if ($TimelineId) {
            $resource = "builds/$item/timeline/$TimelineId"
         }
         $resp = _callAPI -ProjectName $projectName `
            -Area 'build' `
            -Resource $resource `
            -Version $([vsteam_lib.Versions]::Build) `
            -Querystring @{
            'changeId' = $ChangeId
            'planId'   = $PlanId
         }
         _applyTypesToBuildTimelineResultType -item $resp
         Write-Output $resp
      }
   }
}
function Get-VSTeamClassificationNode {
   [CmdletBinding(DefaultParameterSetName = 'ById',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamClassificationNode')]
   param(
      [ValidateSet("areas", "iterations")]
      [Parameter(Mandatory = $true, ParameterSetName = "ByPath")]
      [string] $StructureGroup,
      [Parameter(Mandatory = $false, ParameterSetName = "ByPath")]
      [string] $Path,
      [Parameter(Mandatory = $true, ParameterSetName = "ById")]
      [int[]] $Id,
      [int] $Depth,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $idArg = $StructureGroup
      $Path = [uri]::UnescapeDataString($Path)
      if ($Path) {
         $Path = [uri]::EscapeUriString($Path)
         $Path = $Path.TrimStart("/")
         $idArg += "/$Path"
      }
      $queryString = @{ }
      if ($Depth) {
         $queryString.Add("`$Depth", $Depth)
      }
      if ($Id) {
         $queryString.Add("ids", $Id -join ",")
      }
      $commonArgs = @{
         ProjectName = $ProjectName
         Area        = 'wit'
         Resource    = "classificationnodes"
         id          = $idArg
         Version     = $(_getApiVersion Core)
      }
      if ($queryString.Count -gt 0) {
         # Call the REST API
         $resp = _callAPI @commonArgs -QueryString $queryString
      }
      else {
         # Call the REST API
         $resp = _callAPI @commonArgs
      }
      if ([bool]($resp.PSobject.Properties.name -match "value")) {
         try {
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.ClassificationNode]::new($item, $ProjectName)
            }
            Write-Output $objs
         }
         catch {
            # I catch because using -ErrorAction Stop on the Invoke-RestMethod
            # was still running the foreach after and reporting useless errors.
            # This casuses the first error to terminate this execution.
            _handleException $_
         }
      }
      else {
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $classificationNode = [vsteam_lib.ClassificationNode]::new($resp, $ProjectName)
         Write-Output $classificationNode
      }
   }
}
function Get-VSTeamCloudSubscription {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamCloudSubscription')]
   param()
   # Call the REST API
   $resp = _callAPI -NoProject `
      -Area distributedtask `
      -Resource 'serviceendpointproxy/azurermsubscriptions' `
      -Version $(_getApiVersion DistributedTask)
   # Apply a Type Name so we can use custom format view and custom type extensions
   foreach ($item in $resp.value) {
      _applyTypesToAzureSubscription -item $item
   }
   Write-Output $resp.value
}
function Get-VSTeamDescriptor {
   [CmdletBinding(DefaultParameterSetName = 'ByStorageKey',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamDescriptor')]
   param(
      [Parameter(ParameterSetName = 'ByStorageKey', Mandatory = $true, Position = 0)]
      [string] $StorageKey
   )
   process {
      # This will throw if this account does not support the graph API
      _supportsGraph
      # Call the REST API
      $resp = _callAPI -SubDomain vssps -NoProject `
         -Area graph `
         -Resource descriptors `
         -id $StorageKey `
         -Version $(_getApiVersion Graph)
      # Storing the object before you return it cleaned up the pipeline.
      # When I just write the object from the constructor each property
      # seemed to be written
      $descriptor = [vsteam_lib.Descriptor]::new($resp)
      Write-Output $descriptor
   }
}
function Get-VSTeamExtension {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamExtension')]
   param (
      [Parameter(ParameterSetName = 'List', Mandatory = $false)]
      [switch] $IncludeInstallationIssues,
      [Parameter(ParameterSetName = 'List', Mandatory = $false)]
      [switch] $IncludeDisabledExtensions,
      [Parameter(ParameterSetName = 'List', Mandatory = $false)]
      [switch] $IncludeErrors,
      [Parameter(ParameterSetName = 'GetById', Mandatory = $true)]
      [string] $PublisherId,
      [Parameter(ParameterSetName = 'GetById', Mandatory = $true)]
      [string] $ExtensionId
   )
   Process {
      if ($PublisherId -and $ExtensionId) {
         $id = "$PublisherId/$ExtensionId"
         $resp = _callAPI -SubDomain 'extmgmt' `
            -Area 'extensionmanagement' `
            -Resource 'installedextensionsbyname' `
            -Id $id `
            -Version $(_getApiVersion ExtensionsManagement)
         $item = [vsteam_lib.Extension]::new($resp)
         Write-Output $item
      }
      else {
         $queryString = @{ }
         if ($IncludeInstallationIssues.IsPresent) {
            $queryString.includeCapabilities = $true
         }
         if ($IncludeDisabledExtensions.IsPresent) {
            $queryString.includeDisabledExtensions = $true
         }
         if ($IncludeErrors.IsPresent) {
            $queryString.includeErrors = $true
         }
         $resp = _callAPI -SubDomain 'extmgmt' `
            -Area extensionmanagement `
            -Resource installedextensions `
            -QueryString $queryString `
            -Version $(_getApiVersion ExtensionsManagement)
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += [vsteam_lib.Extension]::new($item)
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamFeed {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamFeed')]
   param (
      [Parameter(ParameterSetName = 'ByID', Position = 0)]
      [Alias('FeedId')]
      [string[]] $Id
   )
   process {
      $commonArgs = @{
         subDomain = 'feeds'
         area      = 'packaging'
         resource  = 'feeds'
         NoProject = $true
         version   = $(_getApiVersion Packaging)
      }
      if ($id) {
         foreach ($item in $id) {
            $resp = _callAPI @commonArgs -Id $item
            Write-Verbose $resp
            $item = [vsteam_lib.Feed]::new($resp)
            Write-Output $item
         }
      }
      else {
         $resp = _callAPI @commonArgs
         $objs = @()
         foreach ($item in $resp.value) {
            Write-Verbose $item
            $objs += [vsteam_lib.Feed]::new($item)
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamGitCommit {
   [CmdletBinding(DefaultParameterSetName = 'All',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamGitCommit')]
   param (
      [Parameter(ParameterSetName = 'All', ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Parameter(ParameterSetName = 'ItemVersion', ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Parameter(ParameterSetName = 'CompareVersion', ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Parameter(ParameterSetName = 'ByIds', ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Parameter(ParameterSetName = 'ItemPath', ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Guid] $RepositoryID,
      [Parameter(ParameterSetName = 'All', HelpMessage = "FromDate, in UTC")]
      [Parameter(ParameterSetName = 'ItemVersion', HelpMessage = "FromDate, in UTC")]
      [Parameter(ParameterSetName = 'CompareVersion', HelpMessage = "FromDate, in UTC")]
      [Parameter(ParameterSetName = 'ItemPath', HelpMessage = "FromDate, in UTC")]
      [DateTime] $FromDate,
      [Parameter(ParameterSetName = 'All', HelpMessage = "ToDate, in UTC")]
      [Parameter(ParameterSetName = 'ItemVersion', HelpMessage = "ToDate, in UTC")]
      [Parameter(ParameterSetName = 'CompareVersion', HelpMessage = "ToDate, in UTC")]
      [Parameter(ParameterSetName = 'ItemPath', HelpMessage = "ToDate, in UTC")]
      [DateTime] $ToDate,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion', Mandatory = $true)]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [ValidateSet('branch', 'commit', 'tag')]
      [string] $ItemVersionVersionType,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion', Mandatory = $true)]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [string] $ItemVersionVersion,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion', Mandatory = $false)]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [ValidateSet('firstParent', 'none', 'previousChange')]
      [string] $ItemVersionVersionOptions,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'CompareVersion', Mandatory = $true)]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [ValidateSet('branch', 'commit', 'tag')]
      [string] $CompareVersionVersionType,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'CompareVersion', Mandatory = $true)]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [string] $CompareVersionVersion,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'CompareVersion', Mandatory = $false)]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [ValidateSet('firstParent', 'none', 'previousChange')]
      [string] $CompareVersionVersionOptions,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [string] $FromCommitId,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [string] $ToCommitId,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [string] $Author,
      [Parameter(ParameterSetName = "ByIds")]
      [string[]] $Id,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemPath', Mandatory = $true)]
      [string] $ItemPath,
      [Parameter(ParameterSetName = 'ItemPath')]
      [switch] $ExcludeDeletes,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [int] $Top,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [int] $Skip,
      [Parameter(ParameterSetName = 'ItemPath')]
      [ValidateSet('firstParent', 'fullHistory', 'fullHistorySimplifyMerges', 'simplifiedHistory')]
      [string] $HistoryMode,
      [Parameter(ParameterSetName = 'All')]
      [Parameter(ParameterSetName = 'ItemVersion')]
      [Parameter(ParameterSetName = 'CompareVersion')]
      [Parameter(ParameterSetName = 'ItemPath')]
      [string] $User,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if (($ItemVersionVersionType -eq "commit") -and ($null -eq $ItemVersionVersion -or $ItemVersionVersion -eq '')) {
         throw "If you have a -ItemVersionVersionType of 'commit' you need to set a commit id as -ItemVersionVersion";
      }
      if (($CompareVersionVersionType -eq "commit") -and ($null -eq $CompareVersionVersion -or $CompareVersionVersion -eq '')) {
         throw "If you have a -CompareVersionVersionType of 'commit' you need to set a commit id as -CompareVersionVersion";
      }
      try {
         $queryString = @{
            'searchCriteria.fromDate'                      = if ($FromDate) { $FromDate.ToString('yyyy-MM-ddTHH:mm:ssZ') } else { $null }
            'searchCriteria.toDate'                        = if ($ToDate) { $ToDate.ToString('yyyy-MM-ddTHH:mm:ssZ') } else { $null }
            'searchCriteria.itemVersion.versionType'       = $ItemVersionVersionType
            'searchCriteria.itemVersion.version'           = $ItemVersionVersion
            'searchCriteria.itemVersion.versionOptions'    = $ItemVersionVersionOptions
            'searchCriteria.compareVersion.versionType'    = $CompareVersionVersionType
            'searchCriteria.compareVersion.version'        = $CompareVersionVersion
            'searchCriteria.compareVersion.versionOptions' = $CompareVersionVersionOptions
            'searchCriteria.fromCommitId'                  = $FromCommitId
            'searchCriteria.toCommitId'                    = $ToCommitId
            'searchCriteria.author'                        = $Author
            'searchCriteria.ids'                           = $Id
            'searchCriteria.itemPath'                      = $ItemPath
            'searchCriteria.excludeDeletes'                = $ExcludeDeletes
            'searchCriteria.historyMode'                   = $HistoryMode
            'searchCriteria.$top'                          = $Top
            'searchCriteria.$skip'                         = $Skip
            'searchCriteria.user'                          = $User
         }
         $resp = _callAPI -ProjectName $ProjectName -Id "$RepositoryID/commits" -Area git -Resource repositories -Version $(_getApiVersion Git) -QueryString $queryString
         $obj = @()
         foreach ($item in $resp.value) {
            $obj += [vsteam_lib.GitCommitRef]::new($item, $ProjectName)
         }
         Write-Output $obj
      }
      catch {
         throw $_
      }
   }
}
function Get-VSTeamGitRef {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamGitRef')]
   param (
      [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Alias('Id')]
      [guid] $RepositoryID,
      [string] $Filter,
      [string] $FilterContains,
      [int] $Top,
      [string] $ContinuationToken,
      [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      try {
         $queryString = @{
            '$top'              = $Top
            'filter'            = $Filter
            'filterContains'    = $FilterContains
            'continuationToken' = $continuationToken
         }
         $url = _buildRequestURI -Area git -Resource repositories -Version $(_getApiVersion Git) -ProjectName $ProjectName -Id "$RepositoryID/refs"
         $resp = _callAPI -url $url -QueryString $queryString
         $obj = @()
         foreach ($item in $resp.value) {
            $obj += [vsteam_lib.GitRef]::new($item, $ProjectName)
         }
         Write-Output $obj
      }
      catch {
         throw $_
      }
   }
}
function Get-VSTeamGitRepository {
   [CmdletBinding(DefaultParameterSetName = 'ByID',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamGitRepository')]
   param (
      [Parameter(ParameterSetName = 'ByID', ValueFromPipeline = $true)]
      [Alias('RepositoryID')]
      [guid[]] $Id,
      [Parameter(ParameterSetName = 'ByName', ValueFromPipeline = $true)]
      [string[]] $Name,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($id) {
         foreach ($item in $id) {
            try {
               $resp = _callAPI -ProjectName $ProjectName -Id $item -Area git -Resource repositories -Version $(_getApiVersion Git)
               # Storing the object before you return it cleaned up the pipeline.
               # When I just write the object from the constructor each property
               # seemed to be written
               $item = [vsteam_lib.GitRepository]::new($resp, $ProjectName)
               Write-Output $item
            }
            catch {
               throw $_
            }
         }
      }
      elseif ($Name) {
         foreach ($item in $Name) {
            try {
               $resp = _callAPI -ProjectName $ProjectName -Id $item -Area git -Resource repositories -Version $(_getApiVersion Git)
               # Storing the object before you return it cleaned up the pipeline.
               # When I just write the object from the constructor each property
               # seemed to be written
               $item = [vsteam_lib.GitRepository]::new($resp, $ProjectName)
               Write-Output $item
            }
            catch {
               throw $_
            }
         }
      }
      else {
         if($ProjectName) {
            $resp = _callAPI -ProjectName $ProjectName -Area git -Resource repositories -Version $(_getApiVersion Git)
         } else {
            $resp = _callAPI -Area git -Resource repositories -Version $(_getApiVersion Git)
         }
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += [vsteam_lib.GitRepository]::new($item, $ProjectName)
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamGitStat {
   [CmdletBinding(DefaultParameterSetName = "ByOptionalName",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamGitStat')]
   param (
      [Parameter(ParameterSetName = "ByVersion", ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
      [Parameter(ParameterSetName = "ByOptionalName", ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
      [Alias('Id')]
      [guid] $RepositoryId,
      [Parameter(ParameterSetName = 'ByVersion', Mandatory = $false)]
      [Parameter(ParameterSetName = 'ByOptionalName', Mandatory = $false)]
      [string] $BranchName,
      [ValidateSet("firstParent", "none", "previousChange")]
      [Parameter(ParameterSetName = 'ByVersion', Mandatory = $false)]
      [string] $VersionOptions,
      [Parameter(ParameterSetName = 'ByVersion', Mandatory = $true)]
      [string] $Version,
      [Parameter(ParameterSetName = 'ByVersion', Mandatory = $true)]
      [ValidateSet("branch", "commit", "tag")]
      [string] $VersionType,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if (($VersionType -eq "commit") -and ($null -eq $Version -or $Version -eq '')) {
         throw "If you have a -VersionType of 'commit' you need to set a commit id as -Version";
      }
      try {
         $queryString = @{
            'name'                                 = $BranchName
            'baseVersionDescriptor.versionType'    = $VersionType
            'baseVersionDescriptor.version'        = $Version
            'baseVersionDescriptor.versionOptions' = $VersionOptions
         }
         $resp = _callAPI -ProjectName $ProjectName -Id "$RepositoryID/stats/branches" -Area git -Resource repositories -Version $(_getApiVersion Git) -QueryString $queryString
         $hasValueProp = $resp.PSObject.Properties.Match('value')
         if (0 -eq $hasValueProp.count) {
            _applyTypes $resp "vsteam_lib.GitStat"
            Write-Output $resp
         }
         else {
            $obj = @()
            foreach ($item in $resp.value) {
               _applyTypes $item "vsteam_lib.GitStat"
               $obj += $item
            }
            Write-Output $obj
         }
      }
      catch {
         throw $_
      }
   }
}
function Get-VSTeamGroup {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamGroup')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [Parameter(ParameterSetName = 'ListByProjectName')]
      [ValidateSet('vssgp', 'aadgp')]
      [string[]] $SubjectTypes,
      [Parameter(ParameterSetName = 'List')]
      [string] $ScopeDescriptor,
      [Parameter(ParameterSetName = 'ByGroupDescriptor', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('GroupDescriptor')]
      [Alias('containerDescriptor')]
      [string] $Descriptor,
      [Parameter(ParameterSetName = 'ListByProjectName', Mandatory = $true)]
      [vsteam_lib.ProjectValidateAttribute($true)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # This will throw if this account does not support the graph API
      _supportsGraph
      $commonArgs = @{
         subDomain = 'vssps'
         area      = 'graph'
         resource  = 'groups'
         noProject = $true
         version   = $(_getApiVersion Graph)
      }
      if ($Descriptor) {
         # Call the REST API
         $resp = _callAPI @commonArgs -id $Descriptor
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $group = [vsteam_lib.Group]::new($resp)
         Write-Output $group
      }
      else {
         if ($ProjectName) {
            $project = Get-VSTeamProject -Name $ProjectName
            $ScopeDescriptor = Get-VSTeamDescriptor -StorageKey $project.id | Select-Object -ExpandProperty Name
         }
         $queryString = @{ }
         if ($ScopeDescriptor) {
            $queryString.scopeDescriptor = $ScopeDescriptor
         }
         if ($SubjectTypes -and $SubjectTypes.Length -gt 0) {
            $queryString.subjectTypes = $SubjectTypes -join ','
         }
         try {
            # Call the REST API
            $resp = _callAPI @commonArgs -QueryString $queryString
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.Group]::new($item)
            }
            Write-Output $objs
         }
         catch {
            # I catch because using -ErrorAction Stop on the Invoke-RestMethod
            # was still running the foreach after and reporting useless errors.
            # This casuses the first error to terminate this execution.
            _handleException $_
         }
      }
   }
}
function Get-VSTeamInfo {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamInfo')]
   param ()
   return @{
      Account             = _getInstance
      Version             = $(_getApiVersion -Target)
      ModuleVersion       = [vsteam_lib.Versions]::ModuleVersion
      DefaultProject      = $Global:PSDefaultParameterValues['*-vsteam*:projectName']
      DefaultTimeout      = $Global:PSDefaultParameterValues['*-vsteam*:vsteamApiTimeout']
      DefaultProjectCount = $env:TEAM_PROJECTCOUNT
   }
}
function Get-VSTeamIteration {
   [CmdletBinding(DefaultParameterSetName = 'ByPath',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamIteration')]
   param(
      [Parameter(Mandatory = $false, ParameterSetName = "ByPath")]
      [string] $Path,
      [Parameter(Mandatory = $false, ParameterSetName = "ById")]
      [int[]] $Id,
      [int] $Depth,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($PSCmdlet.ParameterSetName -eq "ByPath") {
         $resp = Get-VSTeamClassificationNode -ProjectName $ProjectName `
            -StructureGroup "iterations" `
            -Path $Path `
            -Depth $Depth
      }
      else {
         $resp = Get-VSTeamClassificationNode -ProjectName $ProjectName `
            -Depth $Depth `
            -Id $Id
      }
      Write-Output $resp
   }
}
function Get-VSTeamJobRequest {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamJobRequest')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
      [int] $PoolId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Position = 1)]
      [Alias('ID')]
      [int] $AgentID,
      [int] $completedRequestCount
   )
   process {
      if ($null -ne $completedRequestCount) {
         $body = @{
            agentid               = $AgentID
            completedRequestCount = $completedRequestCount
         }
      }
      else {
         $body = @{agentid = $AgentID }
      }
      $resp = _callAPI -Area "distributedtask/pools/$PoolId" `
         -Resource jobrequests `
         -QueryString $body `
         -Version $(_getApiVersion DistributedTaskReleased)
      $objs = @()
      foreach ($item in $resp.value) {
         $objs += [vsteam_lib.JobRequest]::new($item)
      }
      Write-Output $objs
   }
}
function Get-VSTeamMember {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamMember')]
   param (
      [Parameter()]
      [int] $Top,
      [Parameter()]
      [int] $Skip,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('Name')]
      [Alias('Id')]
      [string] $TeamId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true )]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $resp = _callAPI -Resource "projects/$ProjectName/teams" `
         -Id "$TeamId/members" `
         -QueryString @{ '$top' = $top; '$skip' = $skip } `
         -Version $(_getApiVersion Core)
      # Apply a Type Name so we can use custom format view and custom type extensions
      foreach ($item in $resp.value) {
         _applyTypesToTeamMember -item $item -team $TeamId -ProjectName $ProjectName
      }
      Write-Output $resp.value
   }
}
function Get-VSTeamMembership {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamMembership')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "ByContainerId")]
      [string] $ContainerDescriptor,
      [Parameter(Mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "ByMemberId")]
      [string] $MemberDescriptor
   )
   process {
      if ($MemberDescriptor) {
         Write-Verbose 'Up with MemberDescriptor'
         Write-Output $(_callMembershipAPI -Id $MemberDescriptor -Direction Up)
      }
      else {
         Write-Verbose 'Down with ContainerDescriptor'
         Write-Output $(_callMembershipAPI -Id $ContainerDescriptor -Direction Down)
      }
   }
}
function Get-VSTeamOption {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamOption')]
   param(
      [string] $subDomain,
      [ArgumentCompleter([vsteam_lib.InvokeCompleter])]
      [Parameter(Position = 0)]
      [Alias("Service")]
      [string] $area,
      [ArgumentCompleter([vsteam_lib.InvokeCompleter])]
      [Parameter(Position = 1)]
      [string] $resource
   )
   # Build the url to list the projects
   $params = @{ "Method" = "Options" }
   if ($subDomain) {
      $params.Add("SubDomain", $subDomain)
   }
   if ($area) {
      $params.Add("Area", $area)
   }
   if ($resource) {
      $params.Add("Resource", $resource)
   }
   # Call the REST API
   $resp = _callAPI @params
   # Apply a Type Name so we can use custom format view and custom type extensions
   foreach ($item in $resp.value) {
      _applyTypes -item $item -type 'vsteam_lib.Option'
   }
   Write-Output $resp.value
}
function Get-VSTeamPackage {
   [CmdletBinding(DefaultParameterSetName = 'List',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamPackage')]
   param(
      [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
      [Alias("feedName")]
      [string] $feedId,
      [Parameter(Position = 1, Mandatory, ParameterSetName = 'ById')]
      [Alias("id")]
      [guid] $packageId,
      [switch] $includeAllVersions,
      [switch] $includeDeleted,
      [switch] $includeDescription,
      [switch] $hideUrls,
      [Parameter(ParameterSetName = 'List')]
      [string] $protocolType,
      [Parameter(ParameterSetName = 'List')]
      [string] $packageNameQuery,
      [Parameter(ParameterSetName = 'List')]
      [int] $Top,
      [Parameter(ParameterSetName = 'List')]
      [int] $Skip
   )
   process {
      # Build query string
      $qs = @{}
      $qs['$top'] = $top
      $qs['$skip'] = $skip
      $qs.protocolType = $protocolType
      $qs.packageNameQuery = $packageNameQuery
      $qs.includeDeleted = $includeDeleted.IsPresent
      $qs.includeDescription = $includeDescription.IsPresent
      $qs.includeAllVersions = $includeAllVersions.IsPresent
      # Values of empty string, false or 0 are not added to the
      # query string. So in a case like this where we need
      # includeUrls=false on the query string we have to set false
      # as a string and not $false
      if ($hideUrls.IsPresent) {
         $qs.includeUrls = 'false'
      }
      # Call the REST API
      $resp = _callAPI -Subdomain 'feeds' -NoProject `
         -Area 'Packaging' `
         -Resource 'Feeds' `
         -Id "$feedId/Packages/$packageId" `
         -QueryString $qs `
         -Version $(_getApiVersion packaging)
      if ($null -ne $packageId) {
         return [vsteam_lib.Package]::new($resp, $feedId)
      }
      $objs = @()
      foreach ($item in $resp.value) {
         $objs += [vsteam_lib.Package]::new($item, $feedId)
      }
      Write-Output $objs
   }
}
function Get-VSTeamPackageVersion {
   [CmdletBinding(HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamPackageVersion')]
   param(
      [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
      [Alias("feedName")]
      [string] $feedId,
      [Parameter(Position = 1, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
      [Alias("id")]
      [guid] $packageId,
      [switch] $hideUrls
   )
   process {
      # Build query string
      $qs = @{}
      # Values of empty string, false or 0 are not added to the
      # query string. So in a case like this where we need
      # includeUrls=false on the query string we have to set false
      # as a string and not $false
      if ($hideUrls.IsPresent) {
         $qs.includeUrls = 'false'
      }
      # Call the REST API
      $resp = _callAPI -Subdomain 'feeds' -NoProject `
         -Area 'Packaging' `
         -Resource 'Feeds' `
         -Id "$feedId/Packages/$packageId/versions" `
         -QueryString $qs `
         -Version $(_getApiVersion packaging)
      $objs = @()
      foreach ($item in $resp.value) {
         $objs += [vsteam_lib.PackageVersion]::new($item)
      }
      Write-Output $objs
   }
}
# Returns true or false.
# Get-VSTeamOption -area Contribution -resource HierarchyQuery
# id : 3353e165-a11e-43aa-9d88-14f2bb09b6d9
# area : Contribution
# resourceName : HierarchyQuery
# routeTemplate : _apis/{area}/{resource}/{scopeName}/{scopeValue}
# This is an undocumented API
function Get-VSTeamPermissionInheritance {
   [OutputType([System.String])]
   [CmdletBinding()]
   param(
      [Parameter(Mandatory, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
      [string] $Name,
      [Parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
      [ValidateSet('Repository', 'BuildDefinition', 'ReleaseDefinition')]
      [string] $resourceType,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # This will throw if this account does not support the HierarchyQuery API
      _supportsHierarchyQuery
      Write-Verbose "Creating VSTeamPermissionInheritance"
      $item = _getPermissionInheritanceInfo -projectName $ProjectName -resourceName $Name -resourceType $resourceType
      $token = $item.Token
      $projectID = $item.ProjectID
      $securityNamespaceID = $item.SecurityNamespaceID
      Write-Verbose "Token = $token"
      Write-Verbose "Version = $Version"
      Write-Verbose "ProjectID = $ProjectID"
      Write-Verbose "SecurityNamespaceID = $SecurityNamespaceID"
      if ($resourceType -eq "Repository") {
         Write-Output (Get-VSTeamAccessControlList -SecurityNamespaceId $securityNamespaceID -token $token | Select-Object -ExpandProperty InheritPermissions)
      }
      else {
         $body = @"
{
    "contributionIds":["ms.vss-admin-web.security-view-data-provider"],
    "dataProviderContext":
    {
        "properties":
        {
            "permissionSetId":"$securityNamespaceID",
            "permissionSetToken":"$token",
        }
    }
}
"@

         $resp = _callAPI -method POST `
            -area Contribution `
            -resource HierarchyQuery `
            -id "project/$projectID" `
            -Body $body `
            -Version $(_getApiVersion HierarchyQuery)
         Write-Verbose $($resp | ConvertTo-Json -Depth 99)
         Write-Output ($resp |
            Select-Object -ExpandProperty dataProviders |
            Select-Object -ExpandProperty 'ms.vss-admin-web.security-view-data-provider' |
            Select-Object -ExpandProperty permissionsContextJson |
            ConvertFrom-Json |
            Select-Object -ExpandProperty inheritPermissions)
      }
   }
}
function Get-VSTeamPolicy {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamPolicy')]
   param (
      [Parameter(ValueFromPipeline = $true)]
      [int[]] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($id) {
         foreach ($item in $id) {
            try {
               $resp = _callAPI -ProjectName $ProjectName `
                  -Area policy `
                  -Resource configurations `
                  -Id $item `
                  -Version $(_getApiVersion Policy)
               _applyTypesToPolicy -item $resp
               Write-Output $resp
            }
            catch {
               throw $_
            }
         }
      }
      else {
         try {
            $resp = _callAPI -ProjectName $ProjectName `
               -Area policy `
               -Resource configurations `
               -Version $(_getApiVersion Policy)
            # Apply a Type Name so we can use custom format view and custom type extensions
            foreach ($item in $resp.value) {
               _applyTypesToPolicy -item $item
            }
            Write-Output $resp.value
         }
         catch {
            throw $_
         }
      }
   }
}
function Get-VSTeamPolicyType {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamPolicyType')]
   param (
      [Parameter(ValueFromPipeline = $true)]
      [guid[]] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($id) {
         foreach ($item in $id) {
            try {
               $resp = _callAPI -ProjectName $ProjectName -Id $item -Area policy -Resource types -Version $(_getApiVersion Policy)
               _applyTypesToPolicyType -item $resp
               Write-Output $resp
            }
            catch {
               throw $_
            }
         }
      }
      else {
         try {
            $resp = _callAPI -ProjectName $ProjectName -Area policy -Resource types -Version $(_getApiVersion Policy)
            # Apply a Type Name so we can use custom format view and custom type extensions
            foreach ($item in $resp.value) {
               _applyTypesToPolicyType -item $item
            }
            Write-Output $resp.value
         }
         catch {
            throw $_
         }
      }
   }
}
function Get-VSTeamPool {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamPool')]
   param(
      [Parameter(ParameterSetName = 'ByID', Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('PoolID')]
      [int] $Id
   )
   process {
      $commonArgs = @{
         NoProject = $true
         Area      = 'distributedtask'
         Resource  = 'pools'
         Version   = $(_getApiVersion DistributedTaskReleased)
      }
      if ($id) {
         $resp = _callAPI @commonArgs -Id $id
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $item = [vsteam_lib.AgentPool]::new($resp)
         Write-Output $item
      }
      else {
         $resp = _callAPI @commonArgs
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += [vsteam_lib.AgentPool]::new($item)
         }
         Write-Output $objs
      }
   }
}
# id: 02cc6a73-5cfb-427d-8c8e-b49fb086e8af
# area: processes
# resource name: processes
# route template: _apis/work/{resource}/{processTypeId}
#
# First appears in TFS 2017 U2 with same values in TFS 2017 U3:
# resourceVersion : 1
# minVersion : 2.1
# maxVersion : 3.2
# releasedVersion : 0.0
# However, I was unable to get any combination of versions to work.
#
# TFS 2018 U1 returns values
# resourceVersion : 1
# minVersion : 2.1
# maxVersion : 4.0
# releasedVersion : 0.0
function Get-VSTeamProcess {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamProcess')]
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
   param(
      [Parameter(ParameterSetName = 'ByName', Position = 0)]
      [ArgumentCompleter([vsteam_lib.ProcessTemplateCompleter])]
      [Alias('ProcessName', 'ProcessTemplate')]
      $Name = '*',
      [Parameter(ParameterSetName = 'ByID')]
      [Alias('ProcessTemplateID')]
      [string] $Id
   )
   process {
      $commonArgs = @{
         # In later APIs you can get the process templates from the 'work'
         # area. For older APIs the process templates are in the 'processes'
         # area. Default to the newer way of accessing process templates.
         # Get-VSTeamOption -area 'work' -resource 'processes' returns nothing
         # this is odd but the call works.
         area      = 'work'
         resource  = 'processes'
         NoProject = $true
         version   = $(_getApiVersion Processes)
      }
      # If this returns an empty string use the old area of 'process'
      if (-not $commonArgs.version) {
         $commonArgs.area = 'process'
      }
      # Return either a single process by ID or a list of processes
      if ($id) {
         # Call the REST API with an ID
         $resp = _callAPI @commonArgs -id $id
         $process = [vsteam_lib.Process]::new($resp)
         Write-Output $process
      }
      else {
         try {
            # Call the REST API
            $resp = _callAPI @commonArgs
            # We just fetched all the processes so let's update the cache. Also, cache the URLS for processes
            [vsteam_lib.ProcessTemplateCache]::Update([string[]]$($resp.value | Select-Object -ExpandProperty Name | Sort-Object))
            $resp.value | ForEach-Object {
               [vsteam_lib.Process]::new($_)
            } | Where-Object { $_.name -like $Name } | Sort-Object -Property Name
         }
         catch {
            # I catch because using -ErrorAction Stop on the Invoke-RestMethod
            # was still running the foreach after and reporting useless errors.
            # This casuses the first error to terminate this execution.
            _handleException $_
         }
      }
   }
}
function Get-VSTeamProfile {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamProfile')]
   param(
      # Name is an array so I can pass an array after -Name
      # I can also use pipe
      [parameter(Mandatory = $false, Position = 1, ValueFromPipelineByPropertyName = $true)]
      [string] $Name
   )
   process {
      if (Test-Path $profilesPath) {
         try {
            # We needed to add ForEach-Object to unroll and show the inner type
            $result = Get-Content $profilesPath | ConvertFrom-Json
            # convert old URL in profile to new URL
            if ($result) {
               $result | ForEach-Object {
                  if ($_.URL -match "(?<protocol>https?\://)?(?<account>[A-Z0-9][-A-Z0-9]*[A-Z0-9])(?<domain>\.visualstudio\.com)") {
                     $_.URL = "https://dev.azure.com/$($matches.account)"
                  }
               }
            }
            if ($Name) {
               $result = $result | Where-Object Name -eq $Name
            }
            if ($result) {
               $result | ForEach-Object {
                  # Setting the type lets me format it
                  $_.PSObject.TypeNames.Insert(0, 'vsteam_lib.Profile')
                  if ($_.PSObject.Properties.Match('Token').count -eq 0) {
                     # This is a profile that was created before the module supported
                     # bearer tokens. The rest of the module expects there to be a Token
                     # property. Add one with a value of ''
                     $_ | Add-Member NoteProperty 'Token' ''
                  }
                  $_
               }
            }
         }
         catch {
            # Catch any error and fail to the return empty array below
            Write-Error "Error ready Profiles file. Use Add-VSTeamProfile to generate new file."
         }
      }
   }
}
function Get-VSTeamProject {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamProject')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('WellFormed', 'CreatePending', 'Deleting', 'New', 'All')]
      [string] $StateFilter = 'WellFormed',
      [Parameter(ParameterSetName = 'List')]
      [int] $Top = $env:TEAM_PROJECTCOUNT,
      [Parameter(ParameterSetName = 'List')]
      [int] $Skip,
      [Parameter(ParameterSetName = 'ByID')]
      [Alias('ProjectID')]
      [string] $Id,
      [switch] $IncludeCapabilities,
      [Parameter(ParameterSetName = 'ByName', Position = 0, Mandatory = $true)]
      [vsteam_lib.ProjectValidateAttribute($true)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $Name
   )
   process {
      # Bind the parameter to a friendly variable
      $ProjectName = $PSBoundParameters["Name"]
      if ($id) {
         $ProjectName = $id
      }
      $commonArgs = @{
         Resource             = 'projects'
         IgnoreDefaultProject = $true
         Version              = $(_getApiVersion Core)
      }
      if ($ProjectName) {
         $queryString = @{ }
         if ($includeCapabilities.IsPresent) {
            $queryString.includeCapabilities = $true
         }
         # Call the REST API
         $resp = _callAPI @commonArgs -id $ProjectName `
            -QueryString $queryString
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $project = [vsteam_lib.Project]::new($resp)
         Write-Output $project
      }
      else {
         try {
            # Call the REST API
            $resp = _callAPI @commonArgs `
               -QueryString @{
               stateFilter = $stateFilter
               '$top'      = $top
               '$skip'     = $skip
            }
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.Project]::new($item)
            }
            Write-Output $objs
         }
         catch {
            # I catch because using -ErrorAction Stop on the Invoke-RestMethod
            # was still running the foreach after and reporting useless errors.
            # This casuses the first error to terminate this execution.
            _handleException $_
         }
      }
   }
}
function Get-VSTeamPullRequest {
   [CmdletBinding(DefaultParameterSetName = "SearchCriteriaWithStatus",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamPullRequest')]
   param (
      [Alias('PullRequestId')]
      [Parameter(ParameterSetName = "ById")]
      [Parameter(ParameterSetName = "IncludeCommits")]
      [string] $Id,
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [Parameter(ParameterSetName = "ById")]
      [Parameter(ParameterSetName = "IncludeCommits", Mandatory)]
      [Guid] $RepositoryId,
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [Guid] $SourceRepositoryId,
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [ValidatePattern('^refs/.*')]
      [string] $SourceBranchRef,
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [ValidatePattern('^refs/.*')]
      [string] $TargetBranchRef,
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [ValidateSet("abandoned", "active", "all", "completed", "notSet")]
      [string] $Status,
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [switch] $All,
      [Parameter(ParameterSetName = "IncludeCommits")]
      [switch] $IncludeCommits,
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [int] $Top,
      [Parameter(ParameterSetName = "SearchCriteriaWithAll")]
      [Parameter(ParameterSetName = "SearchCriteriaWithStatus")]
      [int] $Skip,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      try {
         if ($Id) {
            if ($RepositoryId) {
               $queryString = @{
                  'includeCommits' = $IncludeCommits
               }
               $resp = _callAPI -Id "$RepositoryId/pullRequests/$Id" -Area git -Resource repositories -Version $(_getApiVersion Git) -QueryString $queryString
            }
            elseif ($ProjectName) {
               $resp = _callAPI -ProjectName $ProjectName -Area git -Resource pullRequests -Version $(_getApiVersion Git) -Id $Id
            }
            else {
               $resp = _callAPI -Area git -Resource pullRequests -Version $(_getApiVersion Git) -Id $Id
            }
         }
         else {
            $queryString = @{
               'searchCriteria.sourceRefName'      = $SourceBranchRef
               'searchCriteria.sourceRepositoryId' = $SourceRepositoryId
               'searchCriteria.targetRefName'      = $TargetBranchRef
               'searchCriteria.status'             = if ($All.IsPresent) { 'all' } else { $Status }
               '$top'                              = $Top
               '$skip'                             = $Skip
            }
            if ($RepositoryId) {
               if ($ProjectName) {
                  $resp = _callAPI -ProjectName $ProjectName -Id "$RepositoryId/pullRequests" -Area git -Resource repositories -Version $(_getApiVersion Git) -QueryString $queryString
               }
               else {
                  $resp = _callAPI -Id "$RepositoryId/pullRequests" -Area git -Resource repositories -Version $(_getApiVersion Git) -QueryString $queryString
               }
            }
            else {
               if ($ProjectName) {
                  $resp = _callAPI -ProjectName $ProjectName -Area git -Resource pullRequests -Version $(_getApiVersion Git) -QueryString $queryString
               }
               else {
                  $resp = _callAPI -Area git -Resource pullRequests -Version $(_getApiVersion Git) -QueryString $queryString
               }
            }
         }
         if ($resp.PSobject.Properties.Name -contains "value") {
            $pullRequests = $resp.value
         }
         else {
            $pullRequests = $resp
         }
         foreach ($respItem in $pullRequests) {
            _applyTypesToPullRequests -item $respItem
         }
         Write-Output $pullRequests
      }
      catch {
         _handleException $_
      }
   }
}
function Get-VSTeamQuery {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamQuery')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [int] $Depth = 1,
      [switch] $IncludeDeleted,
      [ValidateSet('all', 'clauses', 'minimal', 'none', 'wiql')]
      [string] $Expand = 'none'
   )
   process {
      $resp = _callAPI -ProjectName $projectName `
         -Area wit `
         -Resource queries `
         -QueryString @{
         '$depth'          = $Depth
         '$expand'         = $Expand
         '$includeDeleted' = $IncludeDeleted.IsPresent
      } `
         -Version $(_getApiVersion Core)
      $obj = @()
      foreach ($item in $resp.value) {
         _applyTypes $item "vsteam_lib.Query"
         $obj += $item
      }
      Write-Output $obj
   }
}
function Get-VSTeamQueue {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamQueue')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [string] $queueName,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('None', 'Manage', 'Use')]
      [string] $actionFilter,
      [Parameter(ParameterSetName = 'ByID')]
      [Alias('QueueID')]
      [string] $id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         ProjectName = $projectName
         Area        = 'distributedtask'
         Resource    = 'queues'
         Version     = $(_getApiVersion DistributedTask)
      }
      if ($id) {
         $resp = _callAPI @commonArgs -Id $id
         $item = [vsteam_lib.Queue]::new($resp, $ProjectName)
         Write-Output $item
      }
      else {
         $resp = _callAPI @commonArgs -QueryString @{ queueName = $queueName; actionFilter = $actionFilter }
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += [vsteam_lib.Queue]::new($item, $ProjectName)
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamRelease {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamRelease')]
   param(
      [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ByIdRaw')]
      [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'ByID', ValueFromPipelineByPropertyName = $true)]
      [Alias('ReleaseID')]
      [int[]] $id,
      [Parameter(ParameterSetName = 'List')]
      [string] $searchText,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('Draft', 'Active', 'Abandoned')]
      [string] $statusFilter,
      [ValidateSet('environments', 'artifacts', 'approvals', 'none')]
      [string] $expand,
      [Parameter(ParameterSetName = 'List')]
      [int] $definitionId,
      [Parameter(ParameterSetName = 'List')]
      [string] $artifactVersionId,
      [Parameter(ParameterSetName = 'List')]
      [int] $top,
      [Parameter(ParameterSetName = 'List')]
      [string] $createdBy,
      [Parameter(ParameterSetName = 'List')]
      [DateTime] $minCreatedTime,
      [Parameter(ParameterSetName = 'List')]
      [DateTime] $maxCreatedTime,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('ascending', 'descending')]
      [string] $queryOrder,
      [Parameter(ParameterSetName = 'List')]
      [string] $continuationToken,
      [switch] $JSON,
      [Parameter(Mandatory = $true, ParameterSetName = 'ByIdRaw')]
      [switch] $raw,
      [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         subDomain = 'vsrm'
         area      = 'release'
         resource  = 'releases'
         version   = $(_getApiVersion Release)
      }
      if ($id) {
         foreach ($item in $id) {
            $resp = _callAPI @commonArgs -ProjectName $ProjectName -id $item
            if ($JSON.IsPresent) {
               $resp | ConvertTo-Json -Depth 99
            }
            else {
               if (-not $raw.IsPresent) {
                  Write-Output $([vsteam_lib.Release]::new($resp, $ProjectName))
               }
               else {
                  Write-Output $resp
               }
            }
         }
      }
      else {
         if ($ProjectName) {
            $listurl = _buildRequestURI @commonArgs -ProjectName $ProjectName
         }
         else {
            $listurl = _buildRequestURI @commonArgs
         }
         $queryString = @{
            '$top'              = $top
            '$expand'           = $expand
            'createdBy'         = $createdBy
            'queryOrder'        = $queryOrder
            'searchText'        = $searchText
            'statusFilter'      = $statusFilter
            'definitionId'      = $definitionId
            'minCreatedTime'    = $minCreatedTime
            'maxCreatedTime'    = $maxCreatedTime
            'continuationToken' = $continuationToken
            'artifactVersionId' = $artifactVersionId
         }
         # Call the REST API
         $resp = _callAPI -url $listurl -QueryString $queryString
         if ($JSON.IsPresent) {
            $resp | ConvertTo-Json -Depth 99
         }
         else {
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.Release]::new($item, $ProjectName)
            }
            Write-Output $objs
         }
      }
   }
}
function Get-VSTeamReleaseDefinition {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamReleaseDefinition')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('environments', 'artifacts', 'none')]
      [string] $Expand = 'none',
      [Parameter(Mandatory = $true, ParameterSetName = 'ByID', ValueFromPipelineByPropertyName = $true)]
      [Parameter(Mandatory = $true, ParameterSetName = 'ByIdRaw', ValueFromPipelineByPropertyName = $true)]
      [Alias('ReleaseDefinitionID')]
      [int[]] $Id,
      [switch]$JSON,
      [Parameter(Mandatory = $true, ParameterSetName = 'ByIdRaw')]
      [switch]$raw,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         subDomain   = 'vsrm'
         area        = 'release'
         resource    = 'definitions'
         projectName = $ProjectName
         version     = $(_getApiVersion Release)
      }
      if ($id) {
         foreach ($item in $id) {
            $resp = _callAPI @commonArgs -id $item
            if ($JSON.IsPresent) {
               $resp | ConvertTo-Json -Depth 99
            }
            else {
               if (-not $raw.IsPresent) {
                  $item = [vsteam_lib.ReleaseDefinition]::new($resp, $ProjectName)
                  Write-Output $item
               }
               else {
                  Write-Output $resp
               }
            }
         }
      }
      else {
         $listurl = _buildRequestURI @commonArgs
         if ($expand -ne 'none') {
            $listurl += "&`$expand=$($expand)"
         }
         # Call the REST API
         $resp = _callAPI -url $listurl
         if ($JSON.IsPresent) {
            $resp | ConvertTo-Json -Depth 99
         }
         else {
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.ReleaseDefinition]::new($item, $ProjectName)
            }
            Write-Output $objs
         }
      }
   }
}
function Get-VSTeamResourceArea {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamResourceArea')]
   param()
   # Call the REST API
   $resp = _callAPI -Resource 'resourceareas'
   # Apply a Type Name so we can use custom format view and custom type extensions
   foreach ($item in $resp.value) {
      _applyTypes -item $item -type 'vsteam_lib.ResourceArea'
   }
   Write-Output $resp.value
}
function Get-VSTeamSecurityNamespace {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamSecurityNamespace')]
   param(
      [Parameter(ParameterSetName = 'ByNamespaceName', Mandatory = $true)]
      [string] $Name,
      [Parameter(ParameterSetName = 'ByNamespaceId', Mandatory = $true)]
      [guid] $Id,
      [Parameter(ParameterSetName = 'List', Mandatory = $false)]
      [switch] $LocalOnly
   )
   process {
      _supportsSecurityNamespace
      $commonArgs = @{
         Resource  = 'securitynamespaces'
         NoProject = $true
         Version   = $(_getApiVersion Core)
      }
      if ($Id) {
         # Call the REST API
         $resp = _callAPI @commonArgs -id $Id
      }
      else {
         $queryString = @{ }
         if ($LocalOnly.IsPresent) {
            $queryString.localOnly = $true
         }
         $resp = _callAPI @commonArgs -QueryString $queryString
      }
      Write-Verbose $resp | Select-Object -ExpandProperty value
      if ($resp.count -le 0) {
         Write-Output $null
      }
      if ($resp.count -gt 1) {
         # If we only need to find one specific by name
         if ($Name) {
            $selected = $resp.value | Where-Object { $_.name -eq $Name }
            if ($selected) {
               Write-Output [vsteam_lib.SecurityNamespace]::new($selected)
            }
            else {
               Write-Output $null
            }
         }
         try {
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.SecurityNamespace]::new($item)
            }
            Write-Output $objs
         }
         catch {
            # I catch because using -ErrorAction Stop on the Invoke-RestMethod
            # was still running the foreach after and reporting useless errors.
            # This casuses the first error to terminate this execution.
            _handleException $_
         }
      }
      else {
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $acl = [vsteam_lib.SecurityNamespace]::new($resp.value[0])
         Write-Output $acl
      }
   }
}
function Get-VSTeamServiceEndpoint {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamServiceEndpoint')]
   param(
      [Parameter(ParameterSetName = 'ByID', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($id) {
         # Call the REST API
         $resp = _callAPI -ProjectName $ProjectName `
            -Area 'distributedtask' `
            -Resource 'serviceendpoints' `
            -Id $id `
            -Version $(_getApiVersion ServiceEndpoints)
         _applyTypesToServiceEndpoint -item $resp
         Write-Output $resp
      }
      else {
         # Call the REST API
         $resp = _callAPI -ProjectName $ProjectName `
            -Area 'distributedtask' `
            -Resource 'serviceendpoints'  `
            -Version $(_getApiVersion ServiceEndpoints)
         # Apply a Type Name so we can use custom format view and custom type extensions
         foreach ($item in $resp.value) {
            _applyTypesToServiceEndpoint -item $item
         }
         return $resp.value
      }
   }
}
function Get-VSTeamServiceEndpointType {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamServiceEndpointType')]
   param(
      [Parameter(ParameterSetName = 'ByType')]
      [string] $Type,
      [Parameter(ParameterSetName = 'ByType')]
      [string] $Scheme
   )
   Process {
      if ($Type -ne '' -or $Scheme -ne '') {
         if ($Type -ne '' -and $Scheme -ne '') {
            $body = @{
               type   = $Type
               scheme = $Scheme
            }
         }
         elseif ($Type -ne '') {
            $body = @{
               type = $Type
            }
         }
         else {
            $body = @{
               scheme = $Scheme
            }
         }
         # Call the REST API
         $resp = _callAPI -Area 'distributedtask' -Resource 'serviceendpointtypes'  `
            -Version $(_getApiVersion ServiceEndpoints) -body $body
      }
      else {
         # Call the REST API
         $resp = _callAPI -Area 'distributedtask' -Resource 'serviceendpointtypes'  `
            -Version $(_getApiVersion ServiceEndpoints)
      }
      # Apply a Type Name so we can use custom format view and custom type extensions
      foreach ($item in $resp.value) {
         _applyTypesToServiceEndpointType -item $item
      }
      return $resp.value
   }
}
function Get-VSTeamTaskGroup {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamTaskGroup')]
   param(
      [Parameter(ParameterSetName = 'ByID', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Id,
      [Parameter(ParameterSetName = 'ByName', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Name,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         Area        = 'distributedtask'
         Resource    = 'taskgroups'
         ProjectName = $ProjectName
         Version     = $(_getApiVersion TaskGroups)
      }
      if ($Id) {
         $resp = _callAPI @commonArgs -Id $Id
         _applyTypes -item $($resp.value) -type 'vsteam_lib.TaskGroup'
         Write-Output $resp.value
      }
      else {
         $resp = _callAPI @commonArgs
         if ($Name) {
            if ($resp.value) {
               foreach ($item in $resp.value) {
                  if ($item.PSObject.Properties.name -contains "name") {
                     if ($Name -eq $item.name) {
                        _applyTypes -item $item -type 'vsteam_lib.TaskGroup'
                        return $item
                     }
                  }
               }
            }
         }
         else {
            foreach ($item in $resp.value) {
               _applyTypes -item $item -type 'vsteam_lib.TaskGroup'
               Write-Output $item
            }
         }
      }
   }
}
# Gets a branch for a given path from TFVC source control.
#
# id : bc1f417e-239d-42e7-85e1-76e80cb2d6eb
# area : tfvc
# resourceName : branches
# routeTemplate : {project}/_apis/{area}/{resource}/{*path}
# http://bit.ly/Get-VSTeamTfvcBranch
function Get-VSTeamTfvcBranch {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamTfvcBranch')]
   param(
      [parameter(ParameterSetName = 'ByPath', Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [string[]] $Path,
      [parameter(Mandatory = $false)]
      [switch] $IncludeChildren = $false,
      [parameter(Mandatory = $false)]
      [switch] $IncludeParent = $false,
      [parameter(Mandatory = $false)]
      [switch] $IncludeDeleted = $false,
      [Parameter(ParameterSetName = 'List', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $queryString = [ordered]@{
         includeChildren = $IncludeChildren;
         includeParent   = $IncludeParent;
         includeDeleted  = $IncludeDeleted;
      }
      if ($null -eq $Path) {
         $resp = _callAPI -ProjectName $ProjectName `
            -Area "tfvc" `
            -Resource "branches" `
            -QueryString $queryString `
            -Version $(_getApiVersion Tfvc)
         foreach ($item in $resp.value) {
            _applyTypesToTfvcBranch -item $item
         }
         Write-Output $resp.value
      }
      else {
         foreach ($item in $Path) {
            $resp = _callAPI -Area "tfvc" `
               -Resource "branches" `
               -Id $item `
               -QueryString $queryString `
               -Version $(_getApiVersion Tfvc)
            _applyTypesToTfvcBranch -item $resp
            Write-Output $resp
         }
      }
   }
}
function Get-VSTeamTfvcRootBranch {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamTfvcRootBranch')]
   param(
      [parameter(Mandatory = $false)]
      [switch] $IncludeChildren = $false,
      [parameter(Mandatory = $false)]
      [switch] $IncludeDeleted = $false
   )
   process {
      $queryString = [ordered]@{
         includeChildren = $IncludeChildren;
         includeDeleted  = $IncludeDeleted;
      }
      $resp = _callAPI -Area tfvc `
         -Resource branches `
         -QueryString $queryString `
         -Version $(_getApiVersion Tfvc)
      if ($resp | Get-Member -Name value -MemberType Properties) {
         foreach ($item in $resp.value) {
            _applyTypesToTfvcBranch -item $item
         }
         Write-Output $resp.value
      }
      else {
         _applyTypesToTfvcBranch -item $resp
         Write-Output $resp
      }
   }
}
function Get-VSTeamUser {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamUser')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('msa', 'aad', 'svc', 'imp', 'vss')]
      [string[]] $SubjectTypes,
      [Parameter(ParameterSetName = 'ByUserDescriptor', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserDescriptor')]
      [Alias('memberDescriptor')]
      [string] $Descriptor
   )
   process {
      # This will throw if this account does not support the graph API
      _supportsGraph
      $commonArgs = @{
         subDomain = 'vssps'
         area      = 'graph'
         resource  = 'users'
         noProject = $true
         version   = $(_getApiVersion Graph)
      }
      if ($Descriptor) {
         # Call the REST API
         $resp = _callAPI @commonArgs -id $Descriptor
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $user = [vsteam_lib.User]::new($resp)
         Write-Output $user
      }
      else {
         $queryString = @{ }
         if ($SubjectTypes -and $SubjectTypes.Length -gt 0) {
            $queryString.subjectTypes = $SubjectTypes -join ','
         }
         try {
            # Call the REST API
            $resp = _callAPI @commonArgs -QueryString $queryString
            $objs = @()
            foreach ($item in $resp.value) {
               $objs += [vsteam_lib.User]::new($item)
            }
            Write-Output $objs
         }
         catch {
            # I catch because using -ErrorAction Stop on the Invoke-RestMethod
            # was still running the foreach after and reporting useless errors.
            # This casuses the first error to terminate this execution.
            _handleException $_
         }
      }
   }
}
function Get-VSTeamUserEntitlement {
   [CmdletBinding(DefaultParameterSetName = 'List',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamUserEntitlement')]
   param (
      [Parameter(ParameterSetName = 'List')]
      [int] $Top = 100,
      [Parameter(ParameterSetName = 'List')]
      [int] $Skip = 0,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('Projects', 'Extensions', 'Grouprules')]
      [string[]] $Select,
      [Parameter(ParameterSetName = 'ByID')]
      [Alias('UserId')]
      [string[]] $Id
   )
   process {
      # This will throw if this account does not support MemberEntitlementManagement
      _supportsMemberEntitlementManagement
      $commonArgs = @{
         subDomain = 'vsaex'
         resource  = 'userentitlements'
         version   = $(_getApiVersion MemberEntitlementManagement)
      }
      if ($Id) {
         foreach ($item in $Id) {
            # Build the url to return the single build
            # Call the REST API
            $resp = _callAPI @commonArgs -id $item
            Write-Output $([vsteam_lib.UserEntitlement]::new($resp))
         }
      }
      else {
         # Build the url to list the teams
         $listurl = _buildRequestURI @commonArgs
         $listurl += _appendQueryString -name "top" -value $top -retainZero
         $listurl += _appendQueryString -name "skip" -value $skip -retainZero
         $listurl += _appendQueryString -name "select" -value ($select -join ",")
         # Call the REST API
         $resp = _callAPI -url $listurl
         $objs = @()
         foreach ($item in $resp.members) {
            $objs += [vsteam_lib.UserEntitlement]::new($item)
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamUserProfile {
   [CmdletBinding(HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamUserProfile')]
   param(
      [Parameter(Mandatory = $true, ParameterSetName = "Id")]
      [string] $Id,
      [Parameter(Mandatory = $true, ParameterSetName = "Me")]
      [switch] $MyProfile
   )
   process {
      try {
         if ($MyProfile) {
            $Id = 'me'
         }
         # Call the REST API
         $resp = _callAPI `
            -NoProject `
            -Method GET `
            -subDomain 'vssps' `
            -area 'profile' `
            -resource 'profiles' `
            -id $Id `
            -version $(_getApiVersion Core)
         Write-Output $resp
      }
      catch {
         _handleException $_
      }
   }
}
function Get-VSTeamVariableGroup {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamVariableGroup')]
   param(
      [Parameter(Position = 0, ParameterSetName = 'ByID', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Id,
      [Parameter(Position = 0, ParameterSetName = 'ByName', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Name,
      [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter]) ]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         ProjectName = $ProjectName
         Area        = 'distributedtask'
         Resource    = 'variablegroups'
         Version     = $(_getApiVersion VariableGroups)
      }
      if ($Id) {
         # Call the REST API
         $resp = _callAPI @commonArgs -Id $Id
         _applyTypesToVariableGroup -item $resp
         Write-Output $resp
      }
      else {
         if ($Name) {
            $resp = _callAPI @commonArgs -QueryString @{groupName = $Name }
            _applyTypesToVariableGroup -item $resp.value
            Write-Output $resp.value
         }
         else {
            # Call the REST API
            $resp = _callAPI @commonArgs
            # Apply a Type Name so we can use custom format view and custom type extensions
            foreach ($item in $resp.value) {
               _applyTypesToVariableGroup -item $item
            }
            return $resp.value
         }
      }
   }
}
function Get-VSTeamWiki {
   [CmdletBinding(DefaultParameterSetName = 'ListAllProjects',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamWiki')]
   param(
      [Parameter(Mandatory = $true, ParameterSetName = 'ById')]
      [Alias('WikiName')]
      [Alias('WikiId')]
      [string] $Name,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true , ParameterSetName = 'ListSingleProject')]
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true , ParameterSetName = 'ById')]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $commonArgs = @{
         area        = 'wiki'
         resource    = 'wikis'
         projectName = $ProjectName
         version     = $(_getApiVersion Wiki)
      }
      if ($Name) { # Name was specified, do a GET request
         $resp = _callAPI @commonArgs -id $Name
         Write-Output $resp
      }
      else { # Name not specified, do LIST
         $listurl = _buildRequestURI @commonArgs
         # Call the REST API
         $resp = _callAPI -url $listurl
         $objs = @()
         foreach ($item in $resp.value) {
            $objs += $item
         }
         Write-Output $objs
      }
   }
}
function Get-VSTeamWiql {
   [CmdletBinding(DefaultParameterSetName = 'ByID',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamWiql')]
   param(
      [vsteam_lib.QueryTransformToIDAttribute()]
      [ArgumentCompleter([vsteam_lib.QueryCompleter])]
      [Parameter(ParameterSetName = 'ByID', Mandatory = $true, Position = 0)]
      [string] $Id,
      [Parameter(ParameterSetName = 'ByQuery', Mandatory = $true)]
      [string] $Query,
      [string] $Team,
      [Parameter(Mandatory = $false, Position = 2)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      $ProjectName,
      [int] $Top = 100,
      [Switch] $TimePrecision,
      [Switch] $Expand
   )
   process {
      # build paramters for _callAPI
      $params = @{
         ProjectName = $ProjectName
         Area        = 'wit'
         Resource    = 'wiql'
         Version     = [vsteam_lib.Versions]::Core
         QueryString = @{
            '$top'        = $Top
            timePrecision = $TimePrecision
         }
      }
      if ($Query) {
         $params['body'] = @{query = $Query } | ConvertTo-Json -Depth 100
         $params['method'] = 'POST'
         $params['ContentType'] = 'application/json'
      }
      else {
         $params['id'] = $Id
      }
      if ($Team) {
         $params['Team'] = $Team
      }
      $resp = _callAPI  @params
      #expand only when at least one workitem has been found
      if ($Expand -and $resp.workItems.Count -gt 0) {
         # Handle queries for work item links also allow for the tests not Setting the query result type.
         if ($resp.psobject.Properties['queryResultType'] -and $resp.queryResultType -eq 'workItemLink') {
            Add-Member -InputObject $resp -MemberType NoteProperty -Name Workitems -Value @()
            $Ids = $resp.workItemRelations.Target.id
         }
         else {
            $Ids = $resp.workItems.id
         }
         # splitting id array by 200, since a maximum of 200 ids are allowed per call
         $countIds = $Ids.Count
         $resp.workItems = for ($beginRange = 0; $beginRange -lt $countIds; $beginRange += 200) {
            $endRange = 1
            $workItemIds = @()
            #for more than one work item being found, line below would return $endrange = 0 for one woritem
            #in the accessed array it would result to $Ids[0..0] which is not valid
            if ($countIds -gt 1) {
               # in case strict mode is on,pick lesser of 0..199 and 0..count-1
               $endRange = [math]::Min(($beginRange + 199), ($countIds - 1))
               $workItemIds = $Ids[$beginRange..$endRange]
            }else {
               $workItemIds = $Ids
            }
            # if query contains "*" don't specify fields, otherwise use fields returned.
            if ($Query -match "\*") {
               Get-VSTeamWorkItem -Id $workItemIds
            }
            else {
               Get-VSTeamWorkItem -Id $workItemIds -Fields $resp.columns.referenceName
            }
         }
      }
      _applyTypesToWiql -item $resp
      return $resp
   }
}
function Get-VSTeamWorkItem {
   [CmdletBinding(DefaultParameterSetName = 'ByID',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamWorkItem')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int[]] $Id,
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('fail', 'omit')]
      [string] $ErrorPolicy = 'omit',
      [ValidateSet('None', 'Relations', 'Fields', 'Links', 'All')]
      [string] $Expand = 'None',
      [string[]] $Fields
   )
   Process {
      # Call the REST API
      $commonArgs = @{
         NoProject = $true
         Area      = 'wit'
         Resource  = 'workitems'
         Version   = $(_getApiVersion Core)
      }
      if ($Id.Length -gt 1) {
         $resp = _callAPI @commonArgs `
            -Querystring @{
            '$Expand'   = $Expand
            fields      = ($Fields -join ',')
            errorPolicy = $ErrorPolicy
            ids         = ($Id -join ',')
         }
         foreach ($item in $resp.value) {
            _applyTypesToWorkItem -item $item
         }
         return $resp.value
      }
      else {
         $a = $Id[0]
         $resp = _callAPI @commonArgs -id "$a" `
            -Querystring @{
            '$Expand' = $Expand
            fields    = ($Fields -join ',')
         }
         _applyTypesToWorkItem -item $resp
         return $resp
      }
   }
}
function Get-VSTeamWorkItemType {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Get-VSTeamWorkItemType')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [Parameter()]
      [vsteam_lib.WorkItemTypeValidateAttribute()]
      [ArgumentCompleter([vsteam_lib.WorkItemTypeCompleter])]
      [string] $WorkItemType
   )
   Process {
      # Call the REST API
      $commonArgs = @{
         ProjectName = $ProjectName
         Area        = 'wit'
         Resource    = 'workitemtypes'
         Version     = $(_getApiVersion Core)
      }
      if ($WorkItemType) {
         $resp = _callAPI @commonArgs -id $WorkItemType
         # This call returns JSON with "": which causes the ConvertFrom-Json to fail.
         # To replace all the "": with "_end":
         $resp = $resp.Replace('"":', '"_end":') | ConvertFrom-Json
         _applyTypesWorkItemType -item $resp
         return $resp
      }
      else {
         $resp = _callAPI @commonArgs
         # This call returns JSON with "": which causes the ConvertFrom-Json to fail.
         # To replace all the "": with "_end":
         $resp = $resp.Replace('"":', '"_end":') | ConvertFrom-Json
         # Apply a Type Name so we can use custom format view and custom type extensions
         foreach ($item in $resp.value) {
            _applyTypesWorkItemType -item $item
         }
         return $resp.value
      }
   }
}
function Invoke-VSTeamRequest {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Invoke-VSTeamRequest')]
   param(
      [ArgumentCompleter([vsteam_lib.InvokeCompleter])]
      [string] $resource,
      [ArgumentCompleter([vsteam_lib.InvokeCompleter])]
      [string] $area,
      [string] $id,
      [string] $version,
      [string] $subDomain,
      [ValidateSet('Get', 'Post', 'Patch', 'Delete', 'Options', 'Put', 'Default', 'Head', 'Merge', 'Trace')]
      [string] $method,
      [Parameter(ValueFromPipeline = $true)]
      [object] $body,
      [string] $InFile,
      [string] $OutFile,
      [switch] $JSON,
      [string] $ContentType,
      [string] $Url,
      [hashtable] $AdditionalHeaders,
      [object] $QueryString,
      [string] $Team,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [switch] $UseProjectId,
      [switch] $NoProject)
   process {
      $params = $PSBoundParameters
      # We have to remove any extra parameters not used by Invoke-RestMethod
      $params.Remove('JSON') | Out-Null
      $output = _callAPI @params
      if ($JSON.IsPresent) {
         $output | ConvertTo-Json -Depth 99
      }
      else {
         $output
      }
   }
}
function Remove-VSTeam {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeam')]
   param(
      [Parameter(Mandatory = $True, Position = 0, ValueFromPipelineByPropertyName = $true)]
      [Alias('Name', 'TeamId', 'TeamName')]
      [string]$Id,
      [switch]$Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($Force -or $PSCmdlet.ShouldProcess($Id, "Delete team")) {
         # Call the REST API
         _callAPI -Method DELETE `
            -Resource "projects/$ProjectName/teams" `
            -Id $Id `
            -Version $(_getApiVersion Core) | Out-Null
         Write-Output "Deleted team $Id"
      }
   }
}
# Removes specified ACEs in the ACL for the provided token. The request URI
# contains the namespace ID, the target token, and a single or list of
# descriptors that should be removed. Only supports removing AzD based
# users/groups.
#
# Get-VSTeamOption 'Security' 'AccessControlEntries'
# id : ac08c8ff-4323-4b08-af90-bcd018d380ce
# area : Security
# resourceName : AccessControlEntries
# routeTemplate : _apis/{resource}/{securityNamespaceId}
# https://bit.ly/Add-VSTeamAccessControlEntry
function Remove-VSTeamAccessControlEntry {
   [CmdletBinding(DefaultParameterSetName = 'byNamespace', SupportsShouldProcess = $true, ConfirmImpact = 'High',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamAccessControlEntry')]
   [OutputType([System.String])]
   param(
      [Parameter(ParameterSetName = 'byNamespace', Mandatory = $true, ValueFromPipeline = $true)]
      [vsteam_lib.SecurityNamespace] $securityNamespace,
      [Parameter(ParameterSetName = 'byNamespaceId', Mandatory = $true)]
      [guid] $securityNamespaceId,
      [string] $token,
      [System.Array] $descriptor,
      [switch] $force
   )
   process {
      if ($securityNamespace) {
         $securityNamespaceId = ($securityNamespace | Select-Object -ExpandProperty id)
      }
      if (($descriptor).count -gt 1) {
         $descriptor = @()
         foreach ($uniqueDescriptor in $descriptor) {
            $uniqueDescriptor = ($uniqueDescriptor).split(".")[1]
            try {
               $uniqueDescriptor = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("$uniqueDescriptor"))
            }
            catch [FormatException] {
               Write-Error "Could not convert base64 string to string."
               continue
            }
            $uniqueDescriptor = "Microsoft.TeamFoundation.Identity;" + "$uniqueDescriptor"
            $descriptor += $uniqueDescriptor
         }
         if (($descriptor).count -eq 0) {
            Write-Error "No valid descriptors provided."
            return
         }
         else {
            $descriptor = $descriptor -join ","
         }
      }
      else {
         $descriptor = ($descriptor).split(".")[1]
         try {
            $descriptor = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($descriptor))
         }
         catch {
            trap [FormatException] { }
            Write-Error "Could not convert base64 string to string."
            return
         }
         $descriptor = "Microsoft.TeamFoundation.Identity;" + "$descriptor"
      }
      if ($Force -or $pscmdlet.ShouldProcess($token, "Remove ACE from ACL")) {
         # Call the REST API
         $resp = _callAPI -Method DELETE `
            -Resource "accesscontrolentries" `
            -id $securityNamespaceId `
            -QueryString @{ token = $token; descriptors = $descriptor } `
            -Version $(_getApiVersion Core)
      }
      switch ($resp) {
         { ($resp -eq $true) } {
            return "Removal of ACE from ACL succeeded."
         }
         { ($resp -eq $false) } {
            Write-Error "Removal of ACE from ACL failed. Ensure descriptor and token are correct."
            return
         }
         { ($resp -ne $true) -and ($resp -ne $false) } {
            Write-Error "Unexpected response from REST API."
            return
         }
      }
   }
}
function Remove-VSTeamAccessControlList {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'ByNamespace',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamAccessControlList')]
   param(
      [Parameter(ParameterSetName = 'ByNamespace', Mandatory = $true, ValueFromPipeline = $true)]
      [vsteam_lib.SecurityNamespace] $SecurityNamespace,
      [Parameter(ParameterSetName = 'ByNamespaceId', Mandatory = $true)]
      [guid] $SecurityNamespaceId,
      [Parameter(ParameterSetName = 'ByNamespace', Mandatory = $true)]
      [Parameter(ParameterSetName = 'ByNamespaceId', Mandatory = $true)]
      [string[]] $Tokens,
      [Parameter(ParameterSetName = 'ByNamespace', Mandatory = $false)]
      [Parameter(ParameterSetName = 'ByNamespaceId', Mandatory = $false)]
      [switch] $Recurse,
      [switch] $Force
   )
   process {
      if ($SecurityNamespace) {
         $SecurityNamespaceId = $SecurityNamespace.ID
      }
      $queryString = @{ }
      if ($Tokens) {
         $queryString.tokens = $Tokens -join ","
      }
      if ($Recurse.IsPresent) {
         $queryString.recurse = $true
      }
      if ($Force -or $pscmdlet.ShouldProcess($queryString.tokens, "Delete ACL")) {
         # Call the REST API
         $resp = _callAPI -Method DELETE `
            -Resource accesscontrollists `
            -id $SecurityNamespaceId  `
            -QueryString $queryString `
            -Version $(_getApiVersion Core)
         Write-Output $resp
      }
   }
}
function Remove-VSTeamAccount {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamAccount')]
   param(
      [switch] $Force
   )
   DynamicParam {
      # Only add this option on Windows Machines
      if (_isOnWindows) {
         Write-Verbose 'On a Windows machine'
         $ParameterName = 'Level'
         # Create the dictionary
         $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
         # Create the collection of attributes
         $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
         # Create and set the parameters' attributes
         $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
         $ParameterAttribute.Mandatory = $false
         $ParameterAttribute.HelpMessage = "On Windows machines allows you to store the account information at the process, user or machine level. Not available on other platforms."
         # Add the attributes to the attributes collection
         $AttributeCollection.Add($ParameterAttribute)
         # Generate and set the ValidateSet
         if (_testAdministrator) {
            $arrSet = "All", "Process", "User", "Machine"
         }
         else {
            $arrSet = "All", "Process", "User"
         }
         $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
         # Add the ValidateSet to the attributes collection
         $AttributeCollection.Add($ValidateSetAttribute)
         # Create and return the dynamic parameter
         $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
         $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
         return $RuntimeParameterDictionary
      }
      else {
         Write-Verbose 'Not on a Windows machine'
      }
   }
   process {
      if (_isOnWindows) {
         # Bind the parameter to a friendly variable
         $Level = $PSBoundParameters[$ParameterName]
         if (-not $Level) {
            $Level = "Process"
         }
      }
      else {
         $Level = "Process"
      }
      switch ($Level) {
         "User" {
            $whatIf = "user level"
         }
         "All" {
            $whatIf = "all levels"
         }
         Default {
            $whatIf = "$Level level"
         }
      }
      if ($Force -or $pscmdlet.ShouldProcess($whatIf, "Remove Team Account")) {
         switch ($Level) {
            "Process" {
               Write-Verbose "Removing from user level."
               _clearEnvironmentVariables "Process"
            }
            "All" {
               Write-Verbose "Removing from all levels."
               Write-Verbose "Removing from proces level."
               _clearEnvironmentVariables "Process"
               Write-Verbose "Removing from user level."
               _clearEnvironmentVariables "User"
               if (_testAdministrator) {
                  Write-Verbose "Removing from machine level."
                  _clearEnvironmentVariables "Machine"
               }
               else {
                  Write-Warning "Must run as administrator to clear machine level."
               }
            }
            Default {
               Write-Verbose "Removing from $Level level."
               _clearEnvironmentVariables $Level
            }
         }
         Write-Output "Removed default project and team account information"
      }
   }
}
function Remove-VSTeamAgent {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamAgent')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int] $PoolId,
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('AgentID')]
      [int[]] $Id,
      [switch] $Force
   )
   process {
      foreach ($item in $Id) {
         if ($force -or $pscmdlet.ShouldProcess($item, "Delete agent")) {
            try {
               _callAPI -Method DELETE `
                  -Area "distributedtask/pools/$PoolId" `
                  -Resource agents `
                  -Id $item `
                  -Version $(_getApiVersion DistributedTaskReleased) | Out-Null
               Write-Output "Deleted agent $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Remove-VSTeamArea {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamArea')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('NodeId')]
      [int] $ReClassifyId,
      [Parameter(Mandatory = $true, Position = 0)]
      [string] $Path,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [switch] $Force
   )
   process {
      if ($force -or $pscmdlet.ShouldProcess($Path, "Delete area")) {
         $null = Remove-VSTeamClassificationNode -ProjectName $ProjectName `
            -StructureGroup 'areas' `
            -Path $Path `
            -ReClassifyId $ReClassifyId `
            -Force
      }
   }
}
function Remove-VSTeamBuild {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamBuild')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Build")) {
            try {
               _callAPI -Method DELETE -ProjectName $ProjectName `
                  -Area build `
                  -Resource builds `
                  -id $item `
                  -Version $(_getApiVersion Build) | Out-Null
               Write-Output "Deleted build $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Remove-VSTeamBuildDefinition {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamBuildDefinition')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Build Definition")) {
            # Call the REST API
            _callAPI -Method DELETE -ProjectName $ProjectName `
               -Area build `
               -Resource definitions `
               -Id $item `
               -Version $(_getApiVersion Build) | Out-Null
            Write-Output "Deleted build definition $item"
         }
      }
   }
}
function Remove-VSTeamBuildTag {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamBuildTag')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
      [string[]] $Tags,
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Remove-VSTeamBuildTag")) {
            foreach ($tag in $tags) {
               # Call the REST API
               _callAPI -Method DELETE -ProjectName $projectName `
                  -Area build `
                  -Resource builds `
                  -Id "$Id/tags" `
                  -Querystring @{tag = $tag } `
                  -Version $(_getApiVersion Build) | Out-Null
            }
         }
      }
   }
}
function Remove-VSTeamClassificationNode {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamClassificationNode')]
   param(
      [Parameter(Mandatory = $false)]
      [int] $ReClassifyId,
      [ValidateSet("areas", "iterations")]
      [Parameter(Mandatory = $true)]
      [string] $StructureGroup,
      [Parameter(Mandatory = $false)]
      [string] $Path,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [switch] $Force
   )
   process {
      $id = $StructureGroup
      $Path = [uri]::UnescapeDataString($Path)
      if ($Path) {
         $Path = [uri]::EscapeUriString($Path)
         $Path = $Path.TrimStart("/")
         $id += "/$Path"
      }
      $queryString = @{ }
      if ($ReClassifyId) {
         $queryString.Add("`$ReClassifyId", $ReClassifyId)
      }
      # Call the REST API
      if ($force -or $pscmdlet.ShouldProcess($Path, "Delete classification node")) {
         $null = _callAPI -Method DELETE -ProjectName $ProjectName `
            -Area wit `
            -Resource classificationnodes `
            -id $id `
            -QueryString $queryString `
            -Version $(_getApiVersion Core)
      }
   }
}
function Remove-VSTeamExtension {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamExtension')]
   param (
      [parameter(Mandatory = $true)]
      [string] $PublisherId,
      [parameter(Mandatory = $true)]
      [string] $ExtensionId,
      [switch] $Force
   )
   if ($Force -or $pscmdlet.ShouldProcess($ExtensionId, "Remove extension")) {
      $id = "$PublisherId/$ExtensionId"
      $resp = _callAPI -Method DELETE -NoProject -SubDomain extmgmt `
         -Area extensionmanagement `
         -Resource installedextensionsbyname `
         -Id $id `
         -Version $(_getApiVersion ExtensionsManagement)
      Write-Output $resp
   }
}
function Remove-VSTeamFeed {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamFeed')]
   param (
      [Parameter(ParameterSetName = 'ByID', Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('FeedId')]
      [string[]] $Id,
      [switch] $Force
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Package Feed")) {
            # Call the REST API
            _callAPI -Method DELETE -subDomain feeds `
               -Area packaging `
               -Resource feeds `
               -Id $item `
               -Version  $(_getApiVersion Packaging) | Out-Null
            Write-Output "Deleted Feed $item"
         }
      }
   }
}
function Remove-VSTeamGitRepository {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamGitRepository')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [guid[]] $Id,
      [switch] $Force
   )
   Process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Repository")) {
            try {
               _callAPI -Method DELETE -Id $item -Area git -Resource repositories -Version $(_getApiVersion Git) | Out-Null
               Write-Output "Deleted repository $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Remove-VSTeamIteration {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamIteration')]
   param(
      [Parameter(Mandatory = $true)]
      [int] $ReClassifyId,
      [Parameter(Mandatory = $true)]
      [string] $Path,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [switch] $Force
   )
   process {
      if ($force -or $pscmdlet.ShouldProcess($Path, "Delete iteration")) {
         $null = Remove-VSTeamClassificationNode -ProjectName $ProjectName `
            -StructureGroup 'iterations' `
            -Path $Path `
            -ReClassifyId $ReClassifyId `
            -Force
      }
   }
}
function Remove-VSTeamMembership {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamMembership')]
   param(
      [Parameter(Mandatory = $true)]
      [string] $MemberDescriptor,
      [Parameter(Mandatory = $true)]
      [string] $ContainerDescriptor,
      [switch] $Force
   )
   process {
      if ($Force -or $PSCmdlet.ShouldProcess("$MemberDescriptor/$ContainerDescriptor", "Delete Membership")) {
         return _callMembershipAPI -Id "$MemberDescriptor/$ContainerDescriptor" -Method DELETE
      }
   }
}
function Remove-VSTeamPolicy {
   [CmdletBinding(SupportsShouldProcess = $true,
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamPolicy')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Policy")) {
            try {
               _callAPI -Method DELETE -ProjectName $ProjectName `
                  -Area policy `
                  -Resource configurations `
                  -Id $item `
                  -Version $(_getApiVersion Policy) | Out-Null
               Write-Output "Deleted policy $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Remove-VSTeamPool {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamPool')]
   [CmdletBinding()]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('PoolID')]
      [int] $Id
   )
   process {
      if ($force -or $pscmdlet.ShouldProcess($Id, "Remove Pool")) {
         $null = _callAPI -Method Delete -NoProject -Area distributedtask -Resource pools -Id $id -Version $(_getApiVersion DistributedTask)
      }
   }
}
function Remove-VSTeamProfile {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamProfile')]
   param(
      # Name is an array so I can pass an array after -Name
      # I can also use pipe
      [parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
      [string[]] $Name,
      [switch] $Force
   )
   begin {
      $profiles = Get-VSTeamProfile
   }
   process {
      foreach ($item in $Name) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Remove-VSTeamProfile")) {
            # See if this item is already in there
            $profiles = $profiles | Where-Object Name -ne $item
         }
      }
   }
   end {
      $contents = ConvertTo-Json $profiles -Depth 100
      # As of version 7.0 of PowerShell core To-Json contains the string null
      if ([string]::isnullorempty($contents) -or 'null' -eq $contents) {
         $contents = ''
      }
      Set-Content -Path $profilesPath -Value $contents
   }
}
function Remove-VSTeamProject {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamProject')]
   param(
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('ProjectName')]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $Name
   )
   Process {
      # Bind the parameter to a friendly variable
      $ProjectName = $PSBoundParameters["Name"]
      if ($Force -or $pscmdlet.ShouldProcess($ProjectName, "Delete Project")) {
         # Call the REST API
         $resp = _callAPI -Method DELETE `
            -Resource projects `
            -Id (Get-VSTeamProject $ProjectName).id `
            -Version $(_getApiVersion Core)
         _trackProjectProgress -resp $resp -title 'Deleting team project' -msg "Deleting $ProjectName"
         # Invalidate any cache of projects.
         [vsteam_lib.ProjectCache]::Invalidate()
         Write-Output "Deleted $ProjectName"
      }
   }
}
function Remove-VSTeamRelease {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamRelease')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($force -or $pscmdlet.ShouldProcess($item, "Delete Release")) {
            try {
               # Call the REST API
               _callAPI -Method DELETE -SubDomain vsrm -ProjectName $ProjectName `
                  -Area release `
                  -Resource releases `
                  -id $item `
                  -Version $(_getApiVersion Release) | Out-Null
               Write-Output "Deleted release $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Remove-VSTeamReleaseDefinition {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamReleaseDefinition')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      Write-Debug 'Remove-VSTeamReleaseDefinition Process'
      foreach ($item in $id) {
         if ($force -or $pscmdlet.ShouldProcess($item, "Delete Release Definition")) {
            _callAPI -Method DELETE -subDomain vsrm -projectName $ProjectName `
               -Area release `
               -Resource definitions `
               -id $item `
               -Version $(_getApiVersion Release) | Out-Null
            Write-Output "Deleted release definition $item"
         }
      }
   }
}
function Remove-VSTeamServiceEndpoint {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamServiceEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string[]] $id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Service Endpoint")) {
            # Call the REST API
            _callAPI -projectName $projectName -Area 'distributedtask' -Resource 'serviceendpoints' -Id $item  `
               -Method DELETE -Version $(_getApiVersion ServiceEndpoints) | Out-Null
            Write-Output "Deleted service endpoint $item"
         }
      }
   }
}
function Remove-VSTeamTaskGroup {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamTaskGroup')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string[]] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $Id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Task Group")) {
            # Call the REST API
            _callAPI -Method DELETE -ProjectName $ProjectName `
               -Area distributedtask `
               -Resource taskgroups `
               -Id $item `
               -Version $(_getApiVersion TaskGroups) | Out-Null
            Write-Output "Deleted task group $item"
         }
      }
   }
}
function Remove-VSTeamUserEntitlement {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'ById',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamUserEntitlement')]
   param(
      [Parameter(ParameterSetName = 'ById', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserId')]
      [string]$Id,
      [Parameter(ParameterSetName = 'ByEmail', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserEmail')]
      [string]$Email,
      [switch]$Force
   )
   process {
      # This will throw if this account does not support MemberEntitlementManagement
      _supportsMemberEntitlementManagement
      if ($email) {
         # We have to go find the id
         $user = Get-VSTeamUserEntitlement | Where-Object email -eq $email
         if (-not $user) {
            throw "Could not find user with an email equal to $email"
         }
         $id = $user.id
      }
      else {
         $user = Get-VSTeamUserEntitlement -Id $id
      }
      if ($Force -or $PSCmdlet.ShouldProcess("$($user.userName) ($($user.email))", "Delete user")) {
         # Call the REST API
         _callAPI -Method DELETE -SubDomain vsaex `
            -Resource userentitlements `
            -Id $Id `
            -Version $(_getApiVersion MemberEntitlementManagement) | Out-Null
         Write-Output "Deleted user $($user.userName) ($($user.email))"
      }
   }
}
function Remove-VSTeamVariableGroup {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamVariableGroup')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string[]] $id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      foreach ($item in $id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Variable Group")) {
            # Call the REST API
            _callAPI -Method DELETE -ProjectName $projectName `
               -Area distributedtask `
               -Resource variablegroups `
               -Id $item `
               -Version $(_getApiVersion VariableGroups) | Out-Null
            Write-Output "Deleted variable group $item"
         }
      }
   }
}
function Remove-VSTeamWiki {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
      HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamWiki')]
   param(
      [Parameter(Mandatory = $true, ParameterSetName = 'ByName')]
      [Alias('Name')]
      [string] $WikiName,
      [Parameter(Mandatory = $true, ParameterSetName = 'ById')]
      [Alias('Id')]
      [guid] $WikiId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true , ParameterSetName = 'ByName')]
      [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true , ParameterSetName = 'ById')]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($PsCmdlet.ParameterSetName -eq 'ById'){
         $WikiToDelete = Get-VSTeamWiki | Where-Object id -eq $WikiId
      } else{
         $WikiToDelete = Get-VSTeamWiki -ProjectName $ProjectName -Name $WikiName
      }
      if ($null -ne $WikiToDelete){
         if($WikiToDelete.type -eq 'codeWiki'){ # unpublish the Wiki
            try {
               _callAPI -Method DELETE -Id $WikiToDelete.id -Area wiki -Resource wikis -Version $(_getApiVersion Wiki)
               Write-Output "Unpublished Wiki $($WikiToDelete.id)"
            }
            catch {
               _handleException $_
            }
         }else{ # this is a project wiki, repo needs to be deleted
            Remove-VSTeamGitRepository -Id $WikiToDelete.id -Force
         }
      } else {
         throw 'Wiki not found in project provided'
      }
   }
}
function Remove-VSTeamWorkItem {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'ByID',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Remove-VSTeamWorkItem')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int[]] $Id,
      [switch] $Destroy,
      [switch] $Force
   )
   Process {
      # Call the REST API
      foreach ($item in $Id) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "Delete Work Item")) {
            try {
               _callAPI -Method DELETE `
                  -Area "wit" `
                  -Resource "workitems" `
                  -id $item `
                  -Querystring @{ destroy = $Destroy } `
                  -Version $(_getApiVersion Core) | Out-Null
               Write-Output "Deleted Work item with ID $item"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Set-VSTeamAccount {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low", DefaultParameterSetName = 'Secure',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamAccount')]
   param(
      [parameter(ParameterSetName = 'Windows', Mandatory = $true, Position = 1)]
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, Position = 1)]
      [Parameter(ParameterSetName = 'Plain', Mandatory = $true, Position = 1)]
      [string] $Account,
      [parameter(ParameterSetName = 'Plain', Mandatory = $true, Position = 2, HelpMessage = 'Personal Access or Bearer Token')]
      [Alias('Token')]
      [string] $PersonalAccessToken,
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, HelpMessage = 'Personal Access or Bearer Token')]
      [Alias('PAT')]
      [securestring] $SecurePersonalAccessToken,
      [parameter(ParameterSetName = 'Windows')]
      [parameter(ParameterSetName = 'Secure')]
      [Parameter(ParameterSetName = 'Plain')]
      [ValidateSet('TFS2017', 'TFS2018', 'AzD2019', 'VSTS', 'AzD', 'TFS2017U1', 'TFS2017U2', 'TFS2017U3', 'TFS2018U1', 'TFS2018U2', 'TFS2018U3', 'AzD2019U1')]
      [string] $Version,
      [string] $Drive,
      [parameter(ParameterSetName = 'Secure')]
      [Parameter(ParameterSetName = 'Plain')]
      [switch] $UseBearerToken,
      [switch] $Force
   )
   DynamicParam {
      # Create the dictionary
      $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
      $vsteamProfileArrSet = Get-VSTeamProfile | Select-Object -ExpandProperty Name
      if ($vsteamProfileArrSet) {
         $vsteamProfileParam = _buildDynamicParam -ParameterName 'Profile' -ParameterSetName 'Profile' -arrSet $vsteamProfileArrSet
      }
      else {
         $vsteamProfileParam = _buildDynamicParam -ParameterName 'Profile' -ParameterSetName 'Profile'
      }
      $RuntimeParameterDictionary.Add('Profile', $vsteamProfileParam)
      # Only add these options on Windows Machines
      if (_isOnWindows) {
         # Generate and set the ValidateSet
         $arrSet = "Process", "User"
         if (_testAdministrator) {
            $arrSet += "Machine"
         }
         $levelParam = _buildDynamicParam -ParameterName 'Level' -arrSet $arrSet
         $RuntimeParameterDictionary.Add('Level', $levelParam)
         $winAuthParam = _buildDynamicParam -ParameterName 'UseWindowsAuthentication' -Mandatory $true -ParameterSetName 'Windows' -ParameterType ([switch])
         $RuntimeParameterDictionary.Add('UseWindowsAuthentication', $winAuthParam)
      }
      return $RuntimeParameterDictionary
   }
   process {
      # invalidate cache when changing account/collection
      # otherwise dynamic parameters being picked for a wrong collection
      [vsteam_lib.ProjectCache]::Invalidate()
      # Bind the parameter to a friendly variable
      $vsteamProfile = $PSBoundParameters['Profile']
      if (_isOnWindows) {
         # Bind the parameter to a friendly variable
         $Level = $PSBoundParameters['Level']
         if (-not $Level) {
            $Level = "Process"
         }
         $UsingWindowsAuth = $PSBoundParameters['UseWindowsAuthentication']
      }
      else {
         $Level = "Process"
      }
      if ($vsteamProfile) {
         $info = Get-VSTeamProfile | Where-Object Name -eq $vsteamProfile
         if ($info) {
            $encodedPat = $info.Pat
            $account = $info.URL
            $version = $info.Version
            $token = $info.Token
         }
         else {
            Write-Error "The profile provided was not found."
            return
         }
      }
      else {
         if ($SecurePersonalAccessToken) {
            # Convert the securestring to a normal string
            # this was the one technique that worked on Mac, Linux and Windows
            $credential = New-Object System.Management.Automation.PSCredential $account, $SecurePersonalAccessToken
            $_pat = $credential.GetNetworkCredential().Password
         }
         else {
            $_pat = $PersonalAccessToken
         }
         # If they only gave an account name add https://dev.azure.com
         if ($Account -notlike "*/*") {
            $Account = "https://dev.azure.com/$($Account)"
         }
         # If they gave https://xxx.visualstudio.com convert to new URL
         if ($Account -match "(?<protocol>https?\://)?(?<account>[A-Z0-9][-A-Z0-9]*[A-Z0-9])(?<domain>\.visualstudio\.com)") {
            $Account = "https://dev.azure.com/$($matches.account)"
         }
         if ($UseBearerToken.IsPresent) {
            $token = $_pat
            $encodedPat = ''
         }
         else {
            $token = ''
            $encodedPat = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$_pat"))
         }
         # If no SecurePersonalAccessToken is entered, and on windows, are we using default credentials for REST calls
         if ((!$_pat) -and (_isOnWindows) -and ($UsingWindowsAuth)) {
            Write-Verbose "Using Default Windows Credentials for authentication; no Personal Access Token required"
            $encodedPat = ''
            $token = ''
         }
      }
      if ((_isOnWindows) -and ($UsingWindowsAuth) -and $(_isVSTS $Account)) {
         Write-Error "Windows Auth can only be used with Team Fondation Server or Azure DevOps Server.$([Environment]::NewLine)Provide a Personal Access Token or Bearer Token to connect to Azure DevOps Services."
         return
      }
      if ($Force -or $pscmdlet.ShouldProcess($Account, "Set Account")) {
         # Piped to null so callers can pipe to Invoke-Expression to mount the drive on one line.
         Clear-VSTeamDefaultProject *> $null
         _setEnvironmentVariables -Level $Level -Pat $encodedPat -Acct $account -BearerToken $token -Version $Version
         Set-VSTeamAPIVersion -Target (_getVSTeamAPIVersion -Instance $account -Version $Version)
         if ($Drive) {
            # Assign to null so nothing is writen to output.
            Write-Output "# To map a drive run the following command or pipe to iex:`nNew-PSDrive -Name $Drive -PSProvider SHiPS -Root 'VSTeam#vsteam_lib.Provider.Account' -Description $account"
         }
      }
   }
}
function Set-VSTeamAgentPoolMaintenance {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium", DefaultParameterSetName = "Disabled",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamAgentPoolMaintenance')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('PoolID')]
      [int] $Id,
      [Parameter(Mandatory = $false, ParameterSetName = "Disabled")]
      [switch] $Disable,
      [ValidateRange(0, [int]::MaxValue)]
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [int] $JobTimeoutInMinutes,
      [ValidateRange(0, 100)]
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [int] $MaxConcurrentAgentsPercentage,
      [ValidateRange(0, [int]::MaxValue)]
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [int] $NumberOfHistoryRecordsToKeep,
      [ValidateRange(0, [int]::MaxValue)]
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [int] $WorkingDirectoryExpirationInDays,
      [ValidateRange(0, 23)]
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [int] $StartHours,
      [ValidateRange(0, 59)]
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [int] $StartMinutes,
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [vsteam_lib.TimeZoneValidateAttribute()]
      [ArgumentCompleter([vsteam_lib.TimeZoneCompleter])]
      [string] $TimeZoneId,
      [Parameter(Mandatory = $true, ParameterSetName = "Enabled")]
      [vsteam_lib.AgentPoolMaintenanceDays] $WeekDaysToBuild
   )
   process {
      if ($force -or $pscmdlet.ShouldProcess($Id, "Set Pool Maintenance")) {
         $resp = _callAPI -Method Get -NoProject -Area distributedtask -Resource pools -Id "$Id/maintenancedefinitions" -Version $(_getApiVersion DistributedTask)
         $hasSchedule = $resp.count -gt 0
         if ($Disable.IsPresent -and $false -eq $hasSchedule) {
            Throw "Cannot deactivate. No Maintenance Schedule existing!"
         }
         $body = $null
         if ($Disable.IsPresent) {
            $body = $resp.value[0]
            $body.PSObject.Properties.Remove('pool')
            $body.enabled = $false
         }else {
            $body = @{
               id = 0
               enabled = $true
               jobTimeoutInMinutes = $JobTimeoutInMinutes
               maxConcurrentAgentsPercentage = $MaxConcurrentAgentsPercentage
               retentionPolicy = @{
                  numberOfHistoryRecordsToKeep = $NumberOfHistoryRecordsToKeep
               }
               options = @{
                  workingDirectoryExpirationInDays = $WorkingDirectoryExpirationInDays
               }
               scheduleSetting = @{
                  scheduleJobId = (New-Guid).ToString()
                  startHours = $StartHours
                  startMinutes = $StartMinutes
                  daysToBuild = ([int]$WeekDaysToBuild)
                  timeZoneId = $TimeZoneId
               }
            }
         }
         $param = @{ Id = ""; Method = ""}
         if ($hasSchedule) {
            #reuse existing schedule id
            $body.scheduleSetting.scheduleJobId = $resp.value[0].scheduleSetting.scheduleJobId
            $param.Id = "$Id/maintenancedefinitions/$($resp.value[0].id)"
            $param.Method = "Put"
         }else {
            $param.Id = "$Id/maintenancedefinitions"
            $param.Method = "Post"
         }
         $bodyAsJson = $body | ConvertTo-Json -Compress -Depth 50
         $updateResp = _callAPI -NoProject -Area distributedtask -Resource pools -Body $bodyAsJson -Version $(_getApiVersion DistributedTask) @param
         _applyTypesToAgentPoolMaintenance -item $updateResp
         Write-Output updateResp
      }
   }
}
function Set-VSTeamAlias {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamAlias')]
   param(
      [switch] $Force
   )
   if ($Force -or $pscmdlet.ShouldProcess("Set Alias")) {
      New-Alias ata Set-VSTeamAccount -Scope Global -Force
      New-Alias sta Set-VSTeamAccount -Scope Global -Force
      New-Alias gti Get-VSTeamInfo -Scope Global -Force
      New-Alias ivr Invoke-VSTeamRequest -Scope Global -Force
      New-Alias Get-ServiceEndpoint Get-VSTeamServiceEndpoint -Scope Global -Force
      New-Alias Add-AzureRMServiceEndpoint Add-VSTeamAzureRMServiceEndpoint -Scope Global -Force
      New-Alias Remove-ServiceEndpoint Remove-VSTeamServiceEndpoint -Scope Global -Force
      New-Alias Add-SonarQubeEndpoint Add-VSTeamSonarQubeEndpoint -Scope Global -Force
      New-Alias Add-KubernetesEndpoint Add-VSTeamKubernetesEndpoint -Scope Global -Force
      New-Alias Add-ServiceEndpoint Add-VSTeamServiceEndpoint -Scope Global -Force
      New-Alias Update-ServiceEndpoint Update-VSTeamServiceEndpoint -Scope Global -Force
      New-Alias Add-ServiceFabricEndpoint Add-VSTeamServiceFabricEndpoint -Scope Global -Force
      New-Alias Remove-ServiceFabricEndpoint Remove-VSTeamServiceFabricEndpoint -Scope Global -Force
      New-Alias Remove-AzureRMServiceEndpoint Remove-VSTeamAzureRMServiceEndpoint -Scope Global -Force
      New-Alias Remove-SonarQubeEndpoint Remove-VSTeamSonarQubeEndpoint -Scope Global -Force
      New-Alias Get-Build Get-VSTeamBuild -Scope Global -Force
      New-Alias Show-Build Show-VSTeamBuild -Scope Global -Force
      New-Alias Get-BuildLog Get-VSTeamBuildLog -Scope Global -Force
      New-Alias Get-BuildTag Get-VSTeamBuildTag -Scope Global -Force
      New-Alias Get-BuildArtifact Get-VSTeamBuildArtifact -Scope Global -Force
      New-Alias Add-Build Add-VSTeamBuild -Scope Global -Force
      New-Alias Add-BuildTag Add-VSTeamBuildTag -Scope Global -Force
      New-Alias Remove-Build Remove-VSTeamBuild -Scope Global -Force
      New-Alias Remove-BuildTag Remove-VSTeamBuildTag -Scope Global -Force
      New-Alias Update-Build Update-VSTeamBuild -Scope Global -Force
      New-Alias Get-BuildDefinition Get-VSTeamBuildDefinition -Scope Global -Force
      New-Alias Add-BuildDefinition Add-VSTeamBuildDefinition -Scope Global -Force
      New-Alias Show-BuildDefinition Show-VSTeamBuildDefinition -Scope Global -Force
      New-Alias Remove-BuildDefinition Remove-VSTeamBuildDefinition -Scope Global -Force
      New-Alias Show-Approval Show-VSTeamApproval -Scope Global -Force
      New-Alias Get-Approval Get-VSTeamApproval -Scope Global -Force
      New-Alias Set-Approval Set-VSTeamApproval -Scope Global -Force
      New-Alias Get-CloudSubscription Get-VSTeamCloudSubscription -Scope Global -Force
      New-Alias Get-GitRepository Get-VSTeamGitRepository -Scope Global -Force
      New-Alias Show-GitRepository Show-VSTeamGitRepository -Scope Global -Force
      New-Alias Add-GitRepository Add-VSTeamGitRepository -Scope Global -Force
      New-Alias Remove-GitRepository Remove-VSTeamGitRepository -Scope Global -Force
      New-Alias Get-Pool Get-VSTeamPool -Scope Global -Force
      New-Alias Get-Project Get-VSTeamProject -Scope Global -Force
      New-Alias Show-Project Show-VSTeamProject -Scope Global -Force
      New-Alias Update-Project Update-VSTeamProject -Scope Global -Force
      New-Alias Add-Project Add-VSTeamProject -Scope Global -Force
      New-Alias Remove-Project Remove-VSTeamProject -Scope Global -Force
      New-Alias Get-Queue Get-VSTeamQueue -Scope Global -Force
      New-Alias Get-ReleaseDefinition Get-VSTeamReleaseDefinition -Scope Global -Force
      New-Alias Show-ReleaseDefinition Show-VSTeamReleaseDefinition -Scope Global -Force
      New-Alias Add-ReleaseDefinition Add-VSTeamReleaseDefinition -Scope Global -Force
      New-Alias Remove-ReleaseDefinition Remove-VSTeamReleaseDefinition -Scope Global -Force
      New-Alias Get-Release Get-VSTeamRelease -Scope Global -Force
      New-Alias Show-Release Show-VSTeamRelease -Scope Global -Force
      New-Alias Add-Release Add-VSTeamRelease -Scope Global -Force
      New-Alias Remove-Release Remove-VSTeamRelease -Scope Global -Force
      New-Alias Set-ReleaseStatus Set-VSTeamReleaseStatus -Scope Global -Force
      New-Alias Add-ReleaseEnvironment Add-VSTeamReleaseEnvironment -Scope Global -Force
      New-Alias Get-TeamInfo Get-VSTeamInfo -Scope Global -Force
      New-Alias Add-TeamAccount Set-VSTeamAccount -Scope Global -Force
      New-Alias Remove-TeamAccount Remove-VSTeamAccount -Scope Global -Force
      New-Alias Get-TeamOption Get-VSTeamOption -Scope Global -Force
      New-Alias Get-TeamResourceArea Get-VSTeamResourceArea -Scope Global -Force
      New-Alias Clear-DefaultProject Clear-VSTeamDefaultProject -Scope Global -Force
      New-Alias Set-DefaultProject Set-VSTeamDefaultProject -Scope Global -Force
      New-Alias Get-TeamMember Get-VSTeamMember -Scope Global -Force
      New-Alias Get-Team Get-VSTeam -Scope Global -Force
      New-Alias Add-Team Add-VSTeam -Scope Global -Force
      New-Alias Update-Team Update-VSTeam -Scope Global -Force
      New-Alias Remove-Team Remove-VSTeam -Scope Global -Force
      New-Alias Add-Profile Add-VSTeamProfile -Scope Global -Force
      New-Alias Remove-Profile Remove-VSTeamProfile -Scope Global -Force
      New-Alias Get-Profile Get-VSTeamProfile -Scope Global -Force
      New-Alias Set-APIVersion Set-VSTeamAPIVersion -Scope Global -Force
      New-Alias Add-UserEntitlement Add-VSTeamUserEntitlement -Scope Global -Force
      New-Alias Remove-UserEntitlement Remove-VSTeamUserEntitlement -Scope Global -Force
      New-Alias Get-UserEntitlement Get-VSTeamUserEntitlement -Scope Global -Force
      New-Alias Update-UserEntitlement Update-VSTeamUserEntitlement -Scope Global -Force
      New-Alias Set-EnvironmentStatus Set-VSTeamEnvironmentStatus -Scope Global -Force
      New-Alias Get-ServiceEndpointType Get-VSTeamServiceEndpointType -Scope Global -Force
      New-Alias Update-BuildDefinition Update-VSTeamBuildDefinition -Scope Global -Force
      New-Alias Get-TfvcRootBranch Get-VSTeamTfvcRootBranch -Scope Global -Force
      New-Alias Get-TfvcBranch Get-VSTeamTfvcBranch -Scope Global -Force
      New-Alias Get-WorkItemType Get-VSTeamWorkItemType -Scope Global -Force
      New-Alias Add-WorkItem Add-VSTeamWorkItem -Scope Global -Force
      New-Alias Get-WorkItem Get-VSTeamWorkItem -Scope Global -Force
      New-Alias Remove-WorkItem Remove-VSTeamWorkItem -Scope Global -Force
      New-Alias Show-WorkItem Show-VSTeamWorkItem -Scope Global -Force
      New-Alias Get-Policy Get-VSTeamPolicy -Scope Global -Force
      New-Alias Get-PolicyType Get-VSTeamPolicyType -Scope Global -Force
      New-Alias Add-Policy Add-VSTeamPolicy -Scope Global -Force
      New-Alias Update-Policy Update-VSTeamPolicy -Scope Global -Force
      New-Alias Remove-Policy Remove-VSTeamPolicy -Scope Global -Force
      New-Alias Get-GitRef Get-VSTeamGitRef -Scope Global -Force
      New-Alias Get-Agent Get-VSTeamAgent -Scope Global -Force
      New-Alias Remove-Agent Remove-VSTeamAgent -Scope Global -Force
      New-Alias Enable-Agent Enable-VSTeamAgent -Scope Global -Force
      New-Alias Disable-Agent Disable-VSTeamAgent -Scope Global -Force
      New-Alias Update-Profile Update-VSTeamProfile -Scope Global -Force
      New-Alias Get-APIVersion Get-VSTeamAPIVersion -Scope Global -Force
      New-Alias Add-NuGetEndpoint Add-VSTeamNuGetEndpoint -Scope Global -Force
      New-Alias Get-Feed Get-VSTeamFeed -Scope Global -Force
      New-Alias Add-Feed Add-VSTeamFeed -Scope Global -Force
      New-Alias Show-Feed Show-VSTeamFeed -Scope Global -Force
      New-Alias Remove-Feed Remove-VSTeamFeed -Scope Global -Force
      New-Alias Get-PullRequest Get-VSTeamPullRequest -Scope Global -Force
      New-Alias Show-PullRequest Show-VSTeamPullRequest -Scope Global -Force
      New-Alias Add-Extension Add-VSTeamExtension -Scope Global -Force
      New-Alias Get-Extension Get-VSTeamExtension -Scope Global -Force
      New-Alias Update-Extension Update-VSTeamExtension -Scope Global -Force
      New-Alias Remove-Extension Remove-VSTeamExtension -Scope Global -Force
      New-Alias Update-WorkItem Update-VSTeamWorkItem -Scope Global -Force
      New-Alias Get-JobRequest Get-VSTeamJobRequest -Scope Global -Force
      New-Alias Update-ReleaseDefinition Update-VSTeamReleaseDefinition -Scope Global -Force
   }
}
function Set-VSTeamAPIVersion {
   [CmdletBinding(DefaultParameterSetName = 'Target', SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamAPIVersion')]
   param(
      [parameter(ParameterSetName = 'Target', Mandatory = $false, Position = 0)]
      [ValidateSet('TFS2017', 'TFS2018', 'AzD2019', 'VSTS', 'AzD', 'TFS2017U1',
                   'TFS2017U2', 'TFS2017U3', 'TFS2018U1', 'TFS2018U2',
                   'TFS2018U3', 'AzD2019U1')]
      [string] $Target = 'TFS2017',
      [parameter(ParameterSetName = 'Service', Mandatory = $true, Position = 0)]
      [ValidateSet('Build', 'Release', 'Core', 'Git', 'DistributedTask',
                   'DistributedTaskReleased', 'VariableGroups', 'Tfvc',
                   'Packaging', 'MemberEntitlementManagement',
                   'ExtensionsManagement', 'ServiceEndpoints', 'Graph',
                   'TaskGroups', 'Policy', 'Processes', 'HierarchyQuery',
                   'Pipelines', 'Billing')]
      [string] $Service,
      [parameter(ParameterSetName = 'Service', Mandatory = $true, Position = 1)]
      [string] $Version,
      [switch] $Force
   )
   if ($Force -or $pscmdlet.ShouldProcess($Target, "Set-VSTeamAPIVersion")) {
      if ($PSCmdlet.ParameterSetName -eq 'Service') {
         [vsteam_lib.Versions]::SetApiVersion($Service, $Version);
      }
      else {
         # https://docs.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.1#api-and-tfs-version-mapping
         switch ($Target) {
            'AzD2019' {
               [vsteam_lib.Versions]::Version = 'AzD2019'
               [vsteam_lib.Versions]::Git = '5.0'
               [vsteam_lib.Versions]::Core = '5.0'
               [vsteam_lib.Versions]::Build = '5.0'
               [vsteam_lib.Versions]::Release = '5.0'
               [vsteam_lib.Versions]::DistributedTask = '5.0-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = ''
               [vsteam_lib.Versions]::HierarchyQuery = '5.0-preview'
               [vsteam_lib.Versions]::VariableGroups = '5.0-preview'
               [vsteam_lib.Versions]::Tfvc = '5.0'
               [vsteam_lib.Versions]::Packaging = '5.0-preview'
               [vsteam_lib.Versions]::TaskGroups = '5.0-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = ''
               [vsteam_lib.Versions]::ServiceEndpoints = '5.0-preview'
               [vsteam_lib.Versions]::ExtensionsManagement = '5.0-preview'
               [vsteam_lib.Versions]::Graph = ''
               [vsteam_lib.Versions]::Policy = '5.0'
               [vsteam_lib.Versions]::Processes = '5.0-preview'
               [vsteam_lib.Versions]::Billing = ''
            }
            'AzD2019U1' {
               [vsteam_lib.Versions]::Version = 'AzD2019'
               [vsteam_lib.Versions]::Git = '5.1'
               [vsteam_lib.Versions]::Core = '5.1'
               [vsteam_lib.Versions]::Build = '5.1'
               [vsteam_lib.Versions]::Release = '5.1'
               [vsteam_lib.Versions]::DistributedTask = '5.1-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = '5.1-preview'
               [vsteam_lib.Versions]::HierarchyQuery = '5.1-preview'
               [vsteam_lib.Versions]::VariableGroups = '5.1-preview'
               [vsteam_lib.Versions]::Tfvc = '5.1'
               [vsteam_lib.Versions]::Packaging = '5.1-preview'
               [vsteam_lib.Versions]::TaskGroups = '5.1-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = ''
               [vsteam_lib.Versions]::ServiceEndpoints = '5.0-preview'
               [vsteam_lib.Versions]::ExtensionsManagement = '5.1-preview'
               [vsteam_lib.Versions]::Graph = ''
               [vsteam_lib.Versions]::Policy = '5.1'
               [vsteam_lib.Versions]::Processes = '5.1-preview'
               [vsteam_lib.Versions]::Billing = ''
            }
            { $_ -eq 'TFS2018' -or $_ -eq 'TFS2018U1' } {
               [vsteam_lib.Versions]::Version = 'TFS2018'
               [vsteam_lib.Versions]::Git = '4.0'
               [vsteam_lib.Versions]::Core = '4.0'
               [vsteam_lib.Versions]::Build = '4.0'
               [vsteam_lib.Versions]::Release = '4.0-preview'
               [vsteam_lib.Versions]::DistributedTask = '4.0-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = ''
               [vsteam_lib.Versions]::HierarchyQuery = ''
               [vsteam_lib.Versions]::VariableGroups = '4.0-preview'
               [vsteam_lib.Versions]::Tfvc = '4.0'
               [vsteam_lib.Versions]::Packaging = '4.0-preview'
               [vsteam_lib.Versions]::TaskGroups = '4.0-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = ''
               [vsteam_lib.Versions]::ServiceEndpoints = '4.0-preview'
               [vsteam_lib.Versions]::ExtensionsManagement = '4.0-preview'
               [vsteam_lib.Versions]::Graph = ''
               [vsteam_lib.Versions]::Policy = '4.0'
               [vsteam_lib.Versions]::Processes = '4.0-preview'
               [vsteam_lib.Versions]::Billing = ''
            }
            { $_ -eq 'TFS2018U2' -or $_ -eq 'TFS2018U3' } {
               [vsteam_lib.Versions]::Version = 'TFS2018'
               [vsteam_lib.Versions]::Git = '4.1'
               [vsteam_lib.Versions]::Core = '4.1'
               [vsteam_lib.Versions]::Build = '4.1'
               [vsteam_lib.Versions]::Release = '4.1-preview'
               [vsteam_lib.Versions]::DistributedTask = '4.1-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = ''
               [vsteam_lib.Versions]::HierarchyQuery = ''
               [vsteam_lib.Versions]::VariableGroups = '4.1-preview'
               [vsteam_lib.Versions]::Tfvc = '4.1'
               [vsteam_lib.Versions]::Packaging = '4.1-preview'
               [vsteam_lib.Versions]::TaskGroups = '4.1-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = ''
               [vsteam_lib.Versions]::ServiceEndpoints = '4.1-preview'
               [vsteam_lib.Versions]::ExtensionsManagement = '4.1-preview'
               [vsteam_lib.Versions]::Graph = ''
               [vsteam_lib.Versions]::Policy = '4.1'
               [vsteam_lib.Versions]::Processes = '4.1-preview'
               [vsteam_lib.Versions]::Billing = ''
            }
            'TFS2017' {
               [vsteam_lib.Versions]::Version = 'TFS2017'
               [vsteam_lib.Versions]::Git = '3.0'
               [vsteam_lib.Versions]::Core = '3.0'
               [vsteam_lib.Versions]::Build = '3.0'
               [vsteam_lib.Versions]::Release = '3.0-preview'
               [vsteam_lib.Versions]::DistributedTask = '3.0-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = ''
               [vsteam_lib.Versions]::HierarchyQuery = ''
               [vsteam_lib.Versions]::VariableGroups = '' # Was introduced in Update 1
               [vsteam_lib.Versions]::TaskGroups = '3.0-preview'
               [vsteam_lib.Versions]::ServiceEndpoints = '3.0-preview'
               [vsteam_lib.Versions]::Tfvc = '3.0'
               [vsteam_lib.Versions]::Packaging = '3.0-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = ''
               [vsteam_lib.Versions]::ExtensionsManagement = '3.0-preview'
               [vsteam_lib.Versions]::Graph = ''
               [vsteam_lib.Versions]::Policy = '3.0'
               [vsteam_lib.Versions]::Processes = ''
               [vsteam_lib.Versions]::Billing = ''
            }
            'TFS2017U1' {
               [vsteam_lib.Versions]::Version = 'TFS2017'
               [vsteam_lib.Versions]::Git = '3.1'
               [vsteam_lib.Versions]::Core = '3.1'
               [vsteam_lib.Versions]::Build = '3.1'
               [vsteam_lib.Versions]::Release = '3.1-preview'
               [vsteam_lib.Versions]::DistributedTask = '3.1-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = ''
               [vsteam_lib.Versions]::HierarchyQuery = ''
               [vsteam_lib.Versions]::VariableGroups = '3.1-preview' # Resource of DistributedTask area
               [vsteam_lib.Versions]::TaskGroups = '3.1-preview' # Resource of DistributedTask area
               [vsteam_lib.Versions]::ServiceEndpoints = '3.1-preview' # The serviceendpoints resource of DistributedTask area
               [vsteam_lib.Versions]::Tfvc = '3.1'
               [vsteam_lib.Versions]::Packaging = '3.1-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = '' # SubDomain vsaex
               [vsteam_lib.Versions]::ExtensionsManagement = '3.1-preview' # Actual area is extensionmanagement
               [vsteam_lib.Versions]::Graph = '' # SubDomain vssps
               [vsteam_lib.Versions]::Policy = '3.1'
               [vsteam_lib.Versions]::Processes = ''
               [vsteam_lib.Versions]::Billing = ''
            }
            # Update 3 of TFS 2017 did not introduce a new API Version
            { $_ -eq 'TFS2017U2' -or $_ -eq 'TFS2017U3' } {
               [vsteam_lib.Versions]::Version = 'TFS2017'
               [vsteam_lib.Versions]::Git = '3.2'
               [vsteam_lib.Versions]::Core = '3.2'
               [vsteam_lib.Versions]::Build = '3.2'
               [vsteam_lib.Versions]::Release = '3.2-preview'
               [vsteam_lib.Versions]::DistributedTask = '3.2-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = ''
               [vsteam_lib.Versions]::Pipelines = ''
               [vsteam_lib.Versions]::HierarchyQuery = ''
               [vsteam_lib.Versions]::VariableGroups = '3.2-preview' # Resource of DistributedTask area
               [vsteam_lib.Versions]::TaskGroups = '3.2-preview' # Resource of DistributedTask area
               [vsteam_lib.Versions]::ServiceEndpoints = '3.2-preview' # The serviceendpoints resource of DistributedTask area
               [vsteam_lib.Versions]::Tfvc = '3.2'
               [vsteam_lib.Versions]::Packaging = '3.2-preview'
               [vsteam_lib.Versions]::MemberEntitlementManagement = '' # SubDomain vsaex
               [vsteam_lib.Versions]::ExtensionsManagement = '3.2-preview' # Actual area is extensionmanagement
               [vsteam_lib.Versions]::Graph = '' # SubDomain vssps
               [vsteam_lib.Versions]::Policy = '3.2'
               [vsteam_lib.Versions]::Processes = ''
               [vsteam_lib.Versions]::Billing = ''
            }
            # AZD, VSTS
            Default {
               [vsteam_lib.Versions]::Version = $Target
               [vsteam_lib.Versions]::Git = '5.1'
               [vsteam_lib.Versions]::Core = '5.1'
               [vsteam_lib.Versions]::Build = '5.1'
               [vsteam_lib.Versions]::Release = '5.1'
               [vsteam_lib.Versions]::DistributedTask = '6.0-preview'
               [vsteam_lib.Versions]::DistributedTaskReleased = '5.1'
               [vsteam_lib.Versions]::Pipelines = '5.1-preview'
               [vsteam_lib.Versions]::HierarchyQuery = '5.1-preview'
               [vsteam_lib.Versions]::VariableGroups = '5.1-preview.1'
               [vsteam_lib.Versions]::TaskGroups = '6.0-preview'
               [vsteam_lib.Versions]::Tfvc = '5.1'
               [vsteam_lib.Versions]::Packaging = '6.0-preview' # SubDoman feeds
               [vsteam_lib.Versions]::MemberEntitlementManagement = '6.0-preview'
               # This version is never passed to the API but is used to evaluate
               # if Service Fabric is enabled for the account. Just set it to
               # match Distributed Task for AzD
               [vsteam_lib.Versions]::ServiceEndpoints = '5.0-preview'
               [vsteam_lib.Versions]::ExtensionsManagement = '6.0-preview' # SubDomain extmgmt
               [vsteam_lib.Versions]::Graph = '6.0-preview' # SubDomain vssps
               [vsteam_lib.Versions]::Policy = '5.1'
               [vsteam_lib.Versions]::Processes = '6.0-preview'
               [vsteam_lib.Versions]::Billing = '5.1-preview.1'
            }
         }
      }
   }
   Write-Verbose [vsteam_lib.Versions]::Version
   # The Get-VSTeamOption comments above each version are the
   # calls you can use to see if the versions match. Once the
   # resources under an area deviates we have to introduce a
   # new version. For example the calls for Service Endpoints
   # used to use DistributedTask until they were no longer the
   # same and the ServiceEndpoints version was added.
   # Get-VSTeamOption -area 'git' -resource 'repositories'
   # Get-VSTeamOption -area 'git' -resource 'pullRequests'
   Write-Verbose "Git: $([vsteam_lib.Versions]::Git)"
   # Get-VSTeamOption -area 'core' -resource 'teams'
   # Get-VSTeamOption -area 'core' -resource 'members'
   # Get-VSTeamOption -area 'core' -resource 'projects'
   # Get-VSTeamOption -area 'wit' -resource 'queries'
   # Get-VSTeamOption -area 'wit' -resource 'workItems'
   # Get-VSTeamOption -area 'wit' -resource 'workItemTypes'
   # Get-VSTeamOption -area 'wit' -resource 'classificationNodes'
   # Get-VSTeamOption -area 'Security' -resource 'SecurityNamespaces'
   # Get-VSTeamOption -area 'Security' -resource 'AccessControlLists'
   # Get-VSTeamOption -area 'Security' -resource 'AccessControlEntries'
   Write-Verbose "Core: $([vsteam_lib.Versions]::Core)"
   # Get-VSTeamOption -area 'build' -resource 'Builds'
   # Get-VSTeamOption -area 'build' -resource 'Definitions'
   Write-Verbose "Build: $([vsteam_lib.Versions]::Build)"
   # Get-VSTeamOption -subDomain vsrm -area 'Release' -resource 'releases'
   # Get-VSTeamOption -subDomain vsrm -area 'Release' -resource 'approvals'
   # Get-VSTeamOption -subDomain vsrm -area 'Release' -resource 'definitions'
   Write-Verbose "Release: $([vsteam_lib.Versions]::Release)"
   # These are distributed task, resources that have released versions
   # Get-VSTeamOption -area 'distributedtask' -resource 'pools'
   # Get-VSTeamOption -area 'distributedtask' -resource 'agents'
   # Get-VSTeamOption -area 'distributedtask' -resource 'messages'
   # Get-VSTeamOption -area 'distributedtask' -resource 'jobrequests'
   Write-Verbose "DistributedTaskReleased: $([vsteam_lib.Versions]::DistributedTaskReleased)"
   # Testing pipelines
   # Get-VSTeamOption -area 'pipelines' -resource 'runs'
   Write-Verbose "Pipelines: $([vsteam_lib.Versions]::Pipelines)"
   # Get-VSTeamOption -area 'Contribution' -resource 'HierarchyQuery'
   Write-Verbose "HierarchyQuery: $([vsteam_lib.Versions]::HierarchyQuery)"
   # Undocumented billing API
   Write-Verbose "Billing: $([vsteam_lib.Versions]::Billing)"
   # These are distributed task, resources that are in preview
   # Get-VSTeamOption -area 'distributedtask' -resource 'queues'
   # Get-VSTeamOption -area 'distributedtask' -resource 'serviceendpoints'
   # Get-VSTeamOption -area 'distributedtask' -resource 'azurermsubscriptions'
   Write-Verbose "DistributedTask: $([vsteam_lib.Versions]::DistributedTask)"
   # Get-VSTeamOption -area 'distributedtask' -resource 'variablegroups'
   Write-Verbose "VariableGroups: $([vsteam_lib.Versions]::VariableGroups)"
   # Get-VSTeamOption -area 'tfvc' -resource 'branches'
   Write-Verbose "Tfvc: $([vsteam_lib.Versions]::Tfvc)"
   # Get-VSTeamOption -subDomain feeds -area 'Packaging' -resource 'Feeds'
   Write-Verbose "Packaging: $([vsteam_lib.Versions]::Packaging)"
   # Get-VSTeamOption -area 'distributedtask' -resource 'taskgroups'
   Write-Verbose "TaskGroups: $([vsteam_lib.Versions]::TaskGroups)"
   # Get-VSTeamOption -subDomain vsaex -area 'MemberEntitlementManagement' -resource 'UserEntitlements'
   Write-Verbose "MemberEntitlementManagement: $([vsteam_lib.Versions]::MemberEntitlementManagement)"
   # Get-VSTeamOption -area 'distributedtask' -resource 'serviceendpoints'
   Write-Verbose "ServiceEndpoints: $([vsteam_lib.Versions]::ServiceEndpoints)"
   # Get-VSTeamOption -subDomain 'extmgmt' -area 'ExtensionManagement' -resource 'InstalledExtensions'
   # Get-VSTeamOption -subDomain 'extmgmt' -area 'ExtensionManagement' -resource 'InstalledExtensionsByName'
   Write-Verbose "ExtensionsManagement: $([vsteam_lib.Versions]::ExtensionsManagement)"
   # Get-VSTeamOption -subDomain vssps -area 'Graph' -resource 'Users'
   # Get-VSTeamOption -subDomain vssps -area 'Graph' -resource 'Groups'
   # Get-VSTeamOption -subDomain vssps -area 'Graph' -resource 'Memberships'
   # Get-VSTeamOption -subDomain vssps -area 'Graph' -resource 'Descriptors'
   Write-Verbose "Graph: $([vsteam_lib.Versions]::Graph)"
   # Get-VSTeamOption -area 'policy' -resource 'configurations'
   Write-Verbose "Policy: $([vsteam_lib.Versions]::Policy)"
   # Get-VSTeamOption -area 'processes' -resource 'processes'
   Write-Verbose "Processes: $([vsteam_lib.Versions]::Processes)"
}
function Set-VSTeamApproval {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamApproval')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [Parameter(Mandatory = $true)]
      [ValidateSet('Approved', 'Rejected', 'Pending', 'ReAssigned')]
      [string] $Status,
      [string] $Approver,
      [string] $Comment,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      $body = '{ "status": "' + $status + '", "approver": "' + $approver + '", "comments": "' + $comment + '" }'
      Write-Verbose $body
      foreach ($item in $id) {
         if ($force -or $pscmdlet.ShouldProcess($item, "Set Approval Status")) {
            try {
               # Call the REST API
               _callAPI -Method PATCH -SubDomain vsrm -ProjectName $ProjectName `
                  -Area release `
                  -Resource approvals `
                  -Id $item `
                  -body $body `
                  -Version $(_getApiVersion Release) | Out-Null
               Write-Output "Approval $item status changed to $status"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Set-VSTeamDefaultAPITimeout {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
   param(
      [switch] $Force,
      [Parameter(Mandatory = $true, Position = 0)]
      [int] $TimeoutSec
   )
   DynamicParam {
      $dp = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
      # Only add these options on Windows Machines
      if (_isOnWindows) {
         $ParameterName = 'Level'
         # Create the collection of attributes
         $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
         # Create and set the parameters' attributes
         $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
         $ParameterAttribute.Mandatory = $false
         $ParameterAttribute.HelpMessage = "On Windows machines allows you to store the default timeout at the process, user or machine level. Not available on other platforms."
         # Add the attributes to the attributes collection
         $AttributeCollection.Add($ParameterAttribute)
         # Generate and set the ValidateSet
         if (_testAdministrator) {
            $arrSet = "Process", "User", "Machine"
         }
         else {
            $arrSet = "Process", "User"
         }
         $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
         # Add the ValidateSet to the attributes collection
         $AttributeCollection.Add($ValidateSetAttribute)
         # Create and return the dynamic parameter
         $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
         $dp.Add($ParameterName, $RuntimeParameter)
      }
      return $dp
   }
   begin {
      if (_isOnWindows) {
         $Level = $PSBoundParameters[$ParameterName]
      }
   }
   process {
      if ($Force -or $pscmdlet.ShouldProcess($TimeoutSec, "Set-VSTeamDefaultAPITimeout")) {
         if (_isOnWindows) {
            if (-not $Level) {
               $Level = "Process"
            }
            # You always have to set at the process level or they will Not
            # be seen in your current session.
            $env:TEAM_TIMEOUT = $TimeoutSec
            [vsteam_lib.Versions]::DefaultTimeout = $TimeoutSec
            [System.Environment]::SetEnvironmentVariable("TEAM_TIMEOUT", $TimeoutSec, $Level)
         }
         $Global:PSDefaultParameterValues["*-vsteam*:vsteamApiTimeout"] = $TimeoutSec
      }
   }
}
function Set-VSTeamDefaultProject {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
   param(
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $Project
   )
   DynamicParam {
      _buildLevelDynamicParam
   }
   begin {
      if (_isOnWindows) {
         $Level = $PSBoundParameters['Level']
      }
   }
   process {
      if ($Force -or $pscmdlet.ShouldProcess($Project, "Set-VSTeamDefaultProject")) {
         if (_isOnWindows) {
            if (-not $Level) {
               $Level = "Process"
            }
            # You always have to set at the process level or they will Not
            # be seen in your current session.
            $env:TEAM_PROJECT = $Project
            [vsteam_lib.Versions]::DefaultProject = $Project
            [System.Environment]::SetEnvironmentVariable("TEAM_PROJECT", $Project, $Level)
         }
         $Global:PSDefaultParameterValues["*-vsteam*:projectName"] = $Project
      }
   }
}
function Set-VSTeamDefaultProjectCount {
   [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")]
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
   param(
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int] $Count
   )
   DynamicParam {
      _buildLevelDynamicParam
   }
   begin {
      if (_isOnWindows) {
         $Level = $PSBoundParameters['Level']
      }
   }
   process {
      if ($Force -or $pscmdlet.ShouldProcess($Count, "Set-VSTeamDefaultProjectCount")) {
         if (_isOnWindows) {
            if (-not $Level) {
               $Level = "Process"
            }
            [System.Environment]::SetEnvironmentVariable("TEAM_PROJECTCOUNT", $Count, $Level)
         }
         # You always have to set at the process level or they will Not
         # be seen in your current session.
         $env:TEAM_PROJECTCOUNT = $Count
      }
   }
}
function Set-VSTeamEnvironmentStatus {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamEnvironmentStatus')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('Id')]
      [int[]] $EnvironmentId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int] $ReleaseId,
      [Parameter(Mandatory = $true, Position = 0)]
      [ValidateSet('canceled', 'inProgress', 'notStarted', 'partiallySucceeded', 'queued', 'rejected', 'scheduled', 'succeeded', 'undefined')]
      [Alias('EnvironmentStatus')]
      [string] $Status,
      [string] $Comment,
      [datetime] $ScheduledDeploymentTime,
      [switch] $Force,
      [Parameter(Position = 1, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter]) ]
      [string] $ProjectName
   )
   process {
      $body = ConvertTo-Json ([PSCustomObject]@{status = $Status; comment = $Comment; scheduledDeploymentTime = $ScheduledDeploymentTime })
      foreach ($item in $EnvironmentId) {
         if ($force -or $pscmdlet.ShouldProcess($item, "Set Status on Environment")) {
            try {
               # Call the REST API
               _callAPI -Method PATCH -SubDomain vsrm -projectName $ProjectName `
                  -Area release `
                  -Resource releases `
                  -id "$ReleaseId/environments/$item" `
                  -body $body `
                  -Version $(_getApiVersion Release) | Out-Null
               Write-Output "Environment $item status changed to $status"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
# Sets the permission inheritance to true or false.
# Get-VSTeamOption -area Contribution -resource HierarchyQuery
# id : 3353e165-a11e-43aa-9d88-14f2bb09b6d9
# area : Contribution
# resourceName : HierarchyQuery
# routeTemplate : _apis/{area}/{resource}/{scopeName}/{scopeValue}
# This is an undocumented API
function Set-VSTeamPermissionInheritance {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamPermissionInheritance')]
   [OutputType([System.String])]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
      [string] $Name,
      [Parameter(Mandatory = $true)]
      [ValidateSet('Repository', 'BuildDefinition', 'ReleaseDefinition')]
      [string] $ResourceType,
      [Parameter(Mandatory = $true)]
      [bool] $NewState,
      [switch] $Force,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      Write-Verbose "Creating VSTeamPermissionInheritance"
      $item = _getPermissionInheritanceInfo -projectName $ProjectName -resourceName $Name -resourceType $resourceType
      $token = $item.Token
      $projectID = $item.ProjectID
      $securityNamespaceID = $item.SecurityNamespaceID
      Write-Verbose "Token = $token"
      Write-Verbose "ProjectID = $ProjectID"
      Write-Verbose "SecurityNamespaceID = $SecurityNamespaceID"
      if ($force -or $PSCmdlet.ShouldProcess($Name, "Set Permission Inheritance")) {
         # The case of the state is important. It must be lowercase.
         $body = @"
{
    "contributionIds":["ms.vss-admin-web.security-view-update-data-provider"],
    "dataProviderContext":
    {
        "properties":
        {
            "changeInheritance":true,
            "permissionSetId":"$securityNamespaceID",
            "permissionSetToken":"$token",
            "inheritPermissions":$($NewState.ToString().ToLower())
        }
    }
}
"@

         # Call the REST API to change the inheritance state
         $resp = _callAPI -method POST -NoProject `
            -area Contribution `
            -resource HierarchyQuery `
            -id $projectID `
            -Body $body `
            -Version $(_getApiVersion HierarchyQuery)
      }
      Write-Verbose "Result: $(ConvertTo-Json -InputObject $resp -Depth 100)"
      if (($resp | Select-Object -ExpandProperty dataProviders | Select-Object -ExpandProperty 'ms.vss-admin-web.security-view-update-data-provider' | Select-Object -ExpandProperty statusCode) -eq "204") {
         return "Inheritance successfully changed for $ResourceType $Name."
      }
      else {
         Write-Error "Inheritance change failed for $ResourceType $Name."
      }
   }
}
function Set-VSTeamPipelineAuthorization {
   [CmdletBinding(DefaultParameterSetName = 'AuthorizeResource', SupportsShouldProcess = $true, ConfirmImpact = "Medium",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamPipelineAuthorization')]
   param (
      [Parameter(ParameterSetName = 'AuthorizeResource', Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int[]] $PipelineIds,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [Parameter(ParameterSetName = 'AuthorizeResource', Mandatory = $true)]
      [Parameter(ParameterSetName = 'AuthorizeAll', Mandatory = $true)]
      [string]
      $ResourceId,
      [Parameter(ParameterSetName = 'AuthorizeResource', Mandatory = $true)]
      [Parameter(ParameterSetName = 'AuthorizeAll', Mandatory = $true)]
      [string]
      [ValidateSet("Queue", "Endpoint", "Environment", "VariableGroup", "SecureFile", "Repository")]
      $ResourceType,
      [Parameter(ParameterSetName = 'AuthorizeResource', Mandatory = $true)]
      [bool]
      $Authorize,
      [Parameter(ParameterSetName = 'AuthorizeAll', Mandatory = $true)]
      [bool]
      $AuthorizeAll
   )
   process {
      $permPipeBody = @{
         # this part is actually disabling auto approval for pipelines (also future) to use this resource
         allPipelines = @{
            authorized = $AuthorizeAll
         }
         # this part is tragetting specific pipelines that are supposed to be authorized for the resource
         pipelines    = @( )
      }
      if ($PipelineIds) {
         foreach ($id in $PipelineIds) {
            $permPipeBody.pipelines += @{
               id         = $id
               authorized = $Authorize
            }
         }
      }
      $permPipeJsonBody = $permPipeBody | ConvertTo-Json -Compress -Depth 100
      $completeResourceId = $null
      switch ($ResourceType) {
         "Repository" {
            $projectId = (Get-VSTeamProject -Name $ProjectName).id
            $completeResourceId = "$ResourceType/$projectId." + $ResourceId
         }
         Default {
            $completeResourceId = "$ResourceType/" + $ResourceId
         }
      }
      if ($PSCmdlet.ShouldProcess("$ResourceType $ResourceId with Pipeline $PipelineId", $Authorized)) {
         $resp = _callAPI -Method PATCH `
            -ProjectName $ProjectName `
            -Area 'Pipelines' `
            -Resource 'pipelinePermissions' `
            -Id $completeResourceId `
            -Body $permPipeJsonBody `
            -Version $(_getApiVersion Pipelines)
         Write-Output $resp
      }
   }
}
function Set-VSTeamPipelineBilling {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamPipelineBilling')]
   param(
      [Parameter(Mandatory = $true)]
      [ValidateSet('HostedPipeline', 'PrivatePipeline')]
      [string] $Type,
      [Parameter(Mandatory = $false)]
      [string] $OrganizationId,
      [Parameter(Mandatory = $true)]
      [ValidateRange(0,200)]
      [int] $Quantity,
      [switch] $Force
   )
   process {
      # This will throw if this account does not support the Billing API
      _supportsBilling
      if ($force -or $pscmdlet.ShouldProcess($Quantity, "Quantity")) {
         $billingToken = _getBillingToken -NamedTokenId 'AzCommDeploymentProfile'
         if ([string]::IsNullOrEmpty($OrganizationId)) {
            $userProfile = Get-VSTeamUserProfile -MyProfile
            $currentOrg = Get-VSTeamAccounts -MemberId  $userProfile.id | Where-Object {
               $instanceUrl = _getInstance
               return $instanceUrl.EndsWith($_.accountName)
            }
            $OrganizationId = $currentOrg.accountId
         }
         $body = @{ }
         if ($Type -eq "HostedPipeline") {
            $body = @{
               meterId = "4bad9897-8d87-43bb-80be-5e6e8fefa3de"
               purchaseQuantity   = $Quantity
            }
         }
         if ($Type -eq "PrivatePipeline") {
            $body = @{
               meterId = "f44a67f2-53ae-4044-bd58-1c8aca386b98"
               #internally the minimum is one. One concurrent private job is always there (for free).
               #strangely this is not handled by the backend internally. Buying one means zero paid private jobs.
               purchaseQuantity   = $Quantity + 1
            }
         }
         Write-Verbose $body
         try {
            # Call the REST API
            #Full URL needs to be used, since the organization ID is accepted only
            _callAPI `
               -NoProject `
               -Method PATCH `
               -Url "https://azdevopscommerce.dev.azure.com/$OrganizationId/_apis/AzComm/MeterResource?api-version=$(_getApiVersion Billing)" `
               -body ($body | ConvertTo-Json -Depth 50 -Compress) `
               -AdditionalHeaders @{ Authorization = "Bearer $($billingToken.token)" } | Out-Null
         }
         catch {
            _handleException $_
         }
      }
   }
}
function Set-VSTeamReleaseStatus {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Set-VSTeamReleaseStatus')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int[]] $Id,
      [ValidateSet('Active', 'Abandoned')]
      [string] $Status,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   Process {
      $body = '{ "id": ' + $id + ', "status": "' + $status + '" }'
      foreach ($item in $id) {
         if ($force -or $pscmdlet.ShouldProcess($item, "Set status on Release")) {
            try {
               # Call the REST API
               _callAPI -Method PATCH -SubDomain vsrm -ProjectName $ProjectName `
                  -Area release `
                  -Resource releases `
                  -id $item `
                  -body $body `
                  -Version $(_getApiVersion Release) | Out-Null
               Write-Output "Release $item status changed to $status"
            }
            catch {
               _handleException $_
            }
         }
      }
   }
}
function Show-VSTeam {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeam')]
   param ()
   process {
      _hasAccount
      Show-Browser "$(_getInstance)"
   }
}
function Show-VSTeamApproval {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamApproval')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('Id')]
      [int] $ReleaseDefinitionId,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      Show-Browser "$(_getInstance)/$ProjectName/_release?releaseId=$ReleaseDefinitionId"
   }
}
function Show-VSTeamBuild {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamBuild')]
   param (
      [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [int[]] $Id,
      [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      Show-Browser "$(_getInstance)/$ProjectName/_build/index?buildId=$Id"
   }
}
function Show-VSTeamBuildDefinition {
   [CmdletBinding(DefaultParameterSetName = 'List',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamBuildDefinition')]
   param(
      [Parameter(ParameterSetName = 'List')]
      [ValidateSet('Mine', 'All', 'Queued', 'XAML')]
      [string] $Type = 'All',
      [Parameter(ParameterSetName = 'ByID', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildDefinitionID')]
      [int[]] $Id,
      [Parameter(ParameterSetName = 'List')]
      [string] $Path = '\',
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # Build the url
      $url = "$(_getInstance)/$ProjectName/_build"
      if ($id) {
         $url += "/index?definitionId=$id"
      }
      else {
         switch ($type) {
            'Mine' {
               $url += '/index?_a=mine&path='
            }
            'XAML' {
               $url += '/xaml&path='
            }
            'Queued' {
               $url += '/index?_a=queued&path='
            }
            Default {
               # All
               $url += '/index?_a=allDefinitions&path='
            }
         }
         # Make sure path starts with \
         if ($Path[0] -ne '\') {
            $Path = '\' + $Path
         }
         $url += [System.Web.HttpUtility]::UrlEncode($Path)
      }
      Show-Browser $url
   }
}
function Show-VSTeamFeed {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamFeed')]
   param(
      [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
      [Alias('ID')]
      [string] $Name
   )
   process {
      _hasAccount
      Show-Browser "$(_getInstance)/_packaging?feed=$Name&_a=feed"
   }
}
function Show-VSTeamGitRepository {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamGitRepository')]
   param (
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [string] $RemoteUrl,
      [Parameter(ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($RemoteUrl) {
         Show-Browser $RemoteUrl
      }
      else {
         Show-Browser "$(_getInstance)/_git/$ProjectName"
      }
   }
}
function Show-VSTeamPackage {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamPackage')]
   param(
      [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
      [vsteam_lib.Package] $package
   )
   process {
      _hasAccount
      Show-Browser "$(_getInstance)/_packaging?_a=package&feedName=$($package.FeedId)&package=$($package.Name)&protocolType=$($package.ProtocolType)&version=$($p.Versions[0].version)"
   }
}
function Show-VSTeamProject {
   [CmdletBinding(DefaultParameterSetName = 'ByName',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamProject')]
   param(
      [Parameter(ParameterSetName = 'ByID')]
      [Alias('ProjectID')]
      [string] $Id,
      [Parameter(ParameterSetName = 'ByName', Position = 0, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter]) ]
      [Alias('ProjectName')]
      [string] $Name
   )
   process {
      _hasAccount
      # Bind the parameter to a friendly variable
      $ProjectName = $PSBoundParameters["Name"]
      if ($id) {
         $ProjectName = $id
      }
      Show-Browser "$(_getInstance)/$ProjectName"
   }
}
function Show-VSTeamPullRequest {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamPullRequest')]
   param(
       [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
       [Alias('PullRequestId')]
       [int] $Id
   )
   process {
       try {
           $pullRequest = Get-VSTeamPullRequest -PullRequestId $Id
           $projectName = [uri]::EscapeDataString($pullRequest.repository.project.name)
           $repositoryId = $pullRequest.repositoryName
           Show-Browser "$(_getInstance)/$projectName/_git/$repositoryId/pullrequest/$Id"
       }
       catch {
           _handleException $_
       }
   }
}
function Show-VSTeamRelease {
   [CmdletBinding(DefaultParameterSetName = 'ById',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamRelease')]
   param(
      [Parameter(ParameterSetName = 'ByID', ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 1)]
      [Alias('ReleaseID')]
      [int] $id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($id -lt 1) {
         Throw "$id is not a valid id. Value must be greater than 0."
      }
      # Build the url
      Show-Browser "$(_getInstance)/$ProjectName/_release?releaseId=$id"
   }
}
function Show-VSTeamReleaseDefinition {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamReleaseDefinition')]
   param(
      [Parameter(ParameterSetName = 'ByID', ValueFromPipelineByPropertyName = $true)]
      [Alias('ReleaseDefinitionID')]
      [int] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      # Build the url
      $url = "$(_getInstance)/$ProjectName/_release"
      if ($id) {
         $url += "?definitionId=$id"
      }
      Show-Browser $url
   }
}
function Show-VSTeamWorkItem {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Show-VSTeamWorkItem')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
      [Alias('WorkItemID')]
      [int] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      Show-Browser "$(_getInstance)/$ProjectName/_workitems/edit/$Id"
   }
}
function Stop-VSTeamBuild {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Stop-VSTeamBuild')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [Int] $Id,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($Force -or $pscmdlet.ShouldProcess($Id, "Stop-VSTeamBuild")) {
         try {
            $body = @{
               "status" = "Cancelling"
            }
            $bodyAsJson = $body | ConvertTo-Json -Compress -Depth 50
            # Call the REST API
            _callAPI -Method PATCH -ProjectName $ProjectName `
               -Area build `
               -Resource builds `
               -Id $Id `
               -body $bodyAsJson `
               -Version $(_getApiVersion Build) | Out-Null
         }
         catch {
            _handleException $_
         }
      }
   }
}
function Test-VSTeamMembership {
   [CmdletBinding(HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Test-VSTeamMembership')]
   [OutputType([System.Boolean])]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = "MemberDescriptor")]
      [string] $MemberDescriptor,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = "ContainerDescriptor")]
      [string] $ContainerDescriptor
   )
   process {
      $PrevWarningPreference = $WarningPreference
      try {
         $WarningPreference = "SilentlyContinue" # avoid 404 warning, since that indicates it doesn't exist
         $null = _callMembershipAPI -Id "$MemberDescriptor/$ContainerDescriptor" -Method HEAD
         return $true
      }
      catch {
         $WarningPreference = $PrevWarningPreference
         $e = $_
         try {
            if ($e.Exception -and $e.Exception.Response -and $e.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
               return $false
            }
         }
         catch {
            Write-Warning "Nested exception $_"
         }
         throw $e
      }
      finally {
         $WarningPreference = $PrevWarningPreference
      }
   }
}
function Test-VSTeamYamlPipeline {
   [CmdletBinding(DefaultParameterSetName = 'WithFilePath',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Test-VSTeamYamlPipeline')]
   param(
      [Parameter(ParameterSetName = 'WithFilePath', Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Parameter(ParameterSetName = 'WithYamlOverride', Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Int32] $PipelineId,
      [Parameter(ParameterSetName = 'WithFilePath', Mandatory = $false)]
      [string] $FilePath,
      [Parameter(ParameterSetName = 'WithYamlOverride', Mandatory = $false)]
      [string] $YamlOverride
   )
   DynamicParam {
      _buildProjectNameDynamicParam
   }
   process {
      # Bind the parameter to a friendly variable
      $ProjectName = $PSBoundParameters["ProjectName"]
      $body = @{
         PreviewRun = $true
      }
      if ($FilePath) {
         $body.YamlOverride = [string](Get-Content -raw $FilePath)
      }
      elseif ($YamlOverride) {
         $body.YamlOverride = $YamlOverride
      }
      try {
         # Call the REST API
         $resp = _callAPI -Method POST -ProjectName $ProjectName `
            -Area pipelines `
            -id "$PipelineId/runs" `
            -Body ($body | ConvertTo-Json -Compress -Depth 100) `
            -Version $(_getApiVersion Pipelines)
      }
      catch {
         if ($PSItem -match 'PipelineValidationException') {
            Write-Error (($PSItem | ConvertFrom-Json).message -replace '/azure-pipelines.yml( ?: ?)? ?', '')
            return
         }
         else {
            throw
         }
      }
      _applyTypesToYamlPipelineResultType -item $resp
      return $resp
   }
}
function Update-VSTeam {
   [CmdletBinding(DefaultParameterSetName = 'UpdateDescription',
      SupportsShouldProcess = $true, ConfirmImpact = "Medium",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeam')]
   param(
      [Parameter(Mandatory = $True, Position = 0, ValueFromPipelineByPropertyName = $true)]
      [Alias('TeamName', 'TeamId', 'TeamToUpdate', 'Id')]
      [string] $Name,
      [Parameter(ParameterSetName = 'UpdateName', Mandatory = $true)]
      [string] $NewTeamName,
      [Parameter(ParameterSetName = 'UpdateName', Mandatory = $false)]
      [Parameter(ParameterSetName = 'UpdateDescription', Mandatory = $true)]
      [string] $Description,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($Force -or $pscmdlet.ShouldProcess($Name, "Update-VSTeam")) {
         if (-not $NewTeamName) {
            $body = '{"description": "' + $Description + '" }'
         }
         if (-not $Description) {
            $body = '{ "name": "' + $NewTeamName + '" }'
         }
         if ($NewTeamName -and $Description) {
            $body = '{ "name": "' + $NewTeamName + '", "description": "' + $Description + '" }'
         }
         # Call the REST API
         $resp = _callAPI -Method PATCH `
            -Resource "projects/$ProjectName/teams" `
            -Id $Name `
            -Body $body `
            -Version $(_getApiVersion Core)
         # Storing the object before you return it cleaned up the pipeline.
         # When I just write the object from the constructor each property
         # seemed to be written
         $team = [vsteam_lib.Team]::new($resp, $ProjectName)
         Write-Output $team
      }
   }
}
function Update-VSTeamAgent {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamAgent')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int] $PoolId,
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('AgentID')]
      [int[]] $Id,
      [switch] $Force
   )
   process {
      foreach ($item in $Id) {
         try {
            if ($Force -or $pscmdlet.ShouldProcess($item, "Update-VSTeamAgent")) {
               _callAPI -Method POST `
                  -Area "distributedtask/pools/$PoolId" `
                  -Resource messages `
                  -QueryString @{agentId = $item } `
                  -Version $(_getApiVersion DistributedTaskReleased) | Out-Null
               Write-Output "Update agent $item"
            }
         }
         catch {
            _handleException $_
         }
      }
   }
}
function Update-VSTeamBuild {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamBuild')]
   param(
      [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [Alias('BuildID')]
      [Int] $Id,
      [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [bool] $KeepForever,
      [parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $BuildNumber,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($Force -or $pscmdlet.ShouldProcess($Id, "Update-VSTeamBuild")) {
         $body = '{'
         $items = New-Object System.Collections.ArrayList
         if ($null -ne $KeepForever) {
            $items.Add("`"keepForever`": $($KeepForever.ToString().ToLower())") > $null
         }
         if ($null -ne $buildNumber -and $buildNumber.Length -gt 0) {
            $items.Add("`"buildNumber`": `"$BuildNumber`"") > $null
         }
         if ($null -ne $items -and $items.count -gt 0) {
            $body += ($items -join ", ")
         }
         $body += '}'
         # Call the REST API
         _callAPI -Method PATCH -ProjectName $ProjectName `
            -Area build `
            -Resource builds `
            -Id $Id `
            -body $body `
            -Version $(_getApiVersion Build) | Out-Null
      }
   }
}
function Update-VSTeamBuildDefinition {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium", DefaultParameterSetName = 'JSON',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamBuildDefinition')]
   Param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int] $Id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'File')]
      [string] $InFile,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'JSON')]
      [string] $BuildDefinition,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if ($Force -or $pscmdlet.ShouldProcess($Id, "Update Build Definition")) {
         # Call the REST API
         if ($InFile) {
            _callAPI -Method PUT -ProjectName $ProjectName `
               -Area build `
               -Resource definitions `
               -Id $Id `
               -InFile $InFile `
               -Version $(_getApiVersion Build) | Out-Null
         }
         else {
            _callAPI -Method PUT -ProjectName $ProjectName `
               -Area build `
               -Resource definitions `
               -Id $Id `
               -Body $BuildDefinition `
               -Version $(_getApiVersion Build) | Out-Null
         }
      }
   }
}
function Update-VSTeamExtension {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamExtension')]
   param (
      [parameter(Mandatory = $true)]
      [string] $PublisherId,
      [parameter(Mandatory = $true)]
      [string] $ExtensionId,
      [parameter(Mandatory = $true)]
      [ValidateSet('none', 'disabled')]
      [string] $ExtensionState,
      [switch] $Force
   )
   if ($Force -or $pscmdlet.ShouldProcess($ExtensionId, "Update extension")) {
      $obj = @{
         extensionId  = $ExtensionId
         publisherId  = $PublisherId
         installState = @{
            flags = $ExtensionState
         }
      }
      $body = $obj | ConvertTo-Json -Depth 100
      $resp = _callAPI -Method PATCH -SubDomain extmgmt `
         -Area extensionmanagement `
         -Resource installedextensions `
         -Body $body `
         -Version $(_getApiVersion ExtensionsManagement)
      $item = [vsteam_lib.Extension]::new($resp)
      Write-Output $item
   }
}
function Update-VSTeamNuGetPackageVersion {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamNuGetPackageVersion')]
   param (
      [parameter(Mandatory)]
      [Alias("feedName")]
      [string] $FeedId,
      [parameter(Mandatory)]
      [string] $PackageName,
      [parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
      [Alias("Version")]
      [string[]] $PackageVersion,
      [parameter(Mandatory = $true)]
      [bool] $isListed,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName,
      [switch] $Force
   )
   process {
      foreach ($item in $PackageVersion) {
         if ($Force -or $pscmdlet.ShouldProcess($item, "update version")) {
            $obj = @{
               listed = $isListed
            }
            $body = $obj | ConvertTo-Json -Compress -Depth 100
            _callAPI -Method PATCH -SubDomain pkgs `
               -ProjectName $ProjectName `
               -Area "packaging/feeds/$FeedId/nuget" `
               -Resource "packages/$PackageName/versions" `
               -Id $item `
               -Body $body `
               -Version $(_getApiVersion Packaging) | Out-Null
         }
      }
   }
}
function Update-VSTeamPolicy {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamPolicy')]
   param(
      [Parameter(Mandatory = $true)]
      [int] $id,
      [Parameter(Mandatory = $false)]
      [guid] $type,
      [switch] $enabled,
      [switch] $blocking,
      [Parameter(Mandatory = $true)]
      [hashtable] $settings,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   process {
      if (-not $type) {
         $policy = Get-VSTeamPolicy -ProjectName $ProjectName -Id $id | Select-Object -First 1
         $type = $policy.type.id
      }
      $body = @{
         isEnabled  = $enabled.IsPresent;
         isBlocking = $blocking.IsPresent;
         type       = @{
            id = $type
         }
         settings   = $settings
      } | ConvertTo-Json -Depth 10 -Compress
      try {
         if ($Force -or $pscmdlet.ShouldProcess($id, "Update Policy")) {
            # Call the REST API
            $resp = _callAPI -Method PUT -ProjectName $ProjectName `
               -Area policy `
               -Resource configurations `
               -id $id `
               -Body $body `
               -Version $(_getApiVersion Policy)
            Write-Output $resp
         }
      }
      catch {
         _handleException $_
      }
   }
}
function Update-VSTeamPool {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamPool')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 1)]
      [Alias('PoolID')]
      [int] $Id,
      [Parameter(Mandatory = $false)]
      [string] $Name,
      [Parameter(Mandatory = $false)]
      [string] $Description,
      [Parameter(Mandatory = $false)]
      [switch] $AutoProvision,
      [Parameter(Mandatory = $false)]
      [switch] $NoAutoUpdates
   )
   process {
      if ($force -or $pscmdlet.ShouldProcess($Id, "Update Pool")) {
         $body = @{
            autoProvision = $AutoProvision.IsPresent
            autoUpdate    = !$NoAutoUpdates.IsPresent
         }
         if ($Name) {
            $body.name = $Name
         }
         $bodyAsJson = $body | ConvertTo-Json -Compress
         $resp = _callAPI -Method Patch -NoProject -Area distributedtask -Resource pools -Version $(_getApiVersion DistributedTask) -Id $Id -Body $bodyAsJson
         $pool = [vsteam_lib.AgentPool]::new($resp)
         if ($resp -and $Description) {
            $descriptionAsJson = $Description | ConvertTo-Json
            $null = _callAPI -Method Put -NoProject -Area distributedtask -Resource pools -Id "$($pool.id)/poolmetadata" -Version $(_getApiVersion DistributedTask) -Body $descriptionAsJson
         }
         Write-Output $pool
      }
   }
}
function Update-VSTeamProfile {
   [CmdletBinding(DefaultParameterSetName = 'Secure', SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamProfile')]
   param(
      [parameter(ParameterSetName = 'Plain', Mandatory = $true, HelpMessage = 'Personal Access Token')]
      [string] $PersonalAccessToken,
      [parameter(ParameterSetName = 'Secure', Mandatory = $true, HelpMessage = 'Personal Access Token')]
      [securestring] $SecurePersonalAccessToken,
      [switch] $Force
   )
   DynamicParam {
      # Create the dictionary
      $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
      $profileArrSet = Get-VSTeamProfile | Select-Object -ExpandProperty Name
      if ($profileArrSet) {
         $profileParam = _buildDynamicParam -ParameterName 'Name' -Mandatory $true -Position 0 -arrSet $profileArrSet
      }
      else {
         $profileParam = _buildDynamicParam -ParameterName 'Name' -Mandatory $true -Position 0
      }
      $RuntimeParameterDictionary.Add('Name', $profileParam)
      return $RuntimeParameterDictionary
   }
   process {
      $Name = $PSBoundParameters['Name']
      if ($Force -or $pscmdlet.ShouldProcess($Name, "Update-VSTeamProfile")) {
         if ($SecurePersonalAccessToken) {
            # Even when promoted for a Secure Personal Access Token you can just
            # press enter. This leads to an empty PAT so error below.
            # Convert the securestring to a normal string
            # this was the one technique that worked on Mac, Linux and Windows
            $_pat = _convertSecureStringTo_PlainText -SecureString $SecurePersonalAccessToken
         }
         else {
            $_pat = $PersonalAccessToken
         }
         $token = ''
         $authType = 'Pat'
         $encodedPat = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$_pat"))
         $vsteamProfile = Get-VSTeamProfile | Where-Object Name -eq $Name
         if ($vsteamProfile) {
            # See if this item is already in there
            # I am testing URL because the user may provide a different
            # name and I don't want two with the same URL.
            # The @() forces even 1 item to an array
            # Now get an array of all profiles but this one and update this one.
            $vsteamProfiles = @(Get-VSTeamProfile | Where-Object Name -ne $Name)
            $newProfile = [PSCustomObject]@{
               Name    = $Name
               URL     = $vsteamProfile.URL
               Type    = $authType
               Pat     = $encodedPat
               Token   = $token
               Version = $vsteamProfile.Version
            }
            $vsteamProfiles += $newProfile
            $contents = ConvertTo-Json $vsteamProfiles -Depth 100
            Set-Content -Path $profilesPath -Value $contents
         }
         else {
            # This will happen when they don't have any profiles.
            Write-Warning 'Profile not found'
         }
      }
   }
}
function Update-VSTeamProject {
   [CmdletBinding(DefaultParameterSetName = 'ByName', SupportsShouldProcess = $true, ConfirmImpact = "High",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamProject')]
   param(
      [string] $NewName = '',
      [string] $NewDescription = '',
      [switch] $Force,
      [Parameter(ParameterSetName = 'ByID', ValueFromPipelineByPropertyName = $true)]
      [string] $Id,
      [Parameter(ParameterSetName = 'ByName', Position = 0, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter]) ]
      [Alias('Name')]
      [string] $ProjectName
   )
   process {
      if ($id) {
         $ProjectName = $id
      }
      else {
         $id = (Get-VSTeamProject $ProjectName).id
      }
      if ($newName -eq '' -and $newDescription -eq '') {
         # There is nothing to do
         Write-Verbose 'Nothing to update'
         return
      }
      if ($Force -or $pscmdlet.ShouldProcess($ProjectName, "Update Project")) {
         # At the end we return the project and need it's name
         # this is used to track the final name.
         $finalName = $ProjectName
         if ($newName -ne '' -and $newDescription -ne '') {
            $finalName = $newName
            $msg = "Changing name and description"
            $body = '{"name": "' + $newName + '", "description": "' + $newDescription + '"}'
         }
         elseif ($newName -ne '') {
            $finalName = $newName
            $msg = "Changing name"
            $body = '{"name": "' + $newName + '"}'
         }
         else {
            $msg = "Changing description"
            $body = '{"description": "' + $newDescription + '"}'
         }
         # Call the REST API
         $resp = _callAPI -Method PATCH -NoProject `
            -Resource projects `
            -id $id `
            -body $body `
            -Version $(_getApiVersion Core)
         _trackProjectProgress -resp $resp -title 'Updating team project' -msg $msg
         # Invalidate any cache of projects.
         [vsteam_lib.ProjectCache]::Invalidate()
         # Return the project now that it has been updated
         return Get-VSTeamProject -Id $finalName
      }
   }
}
function Update-VSTeamPullRequest {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'Draft',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamPullRequest')]
   param(
      [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, Position = 0)]
      [Alias('Id')]
      [Guid] $RepositoryId,
      [Parameter(Mandatory = $true)]
      [int] $PullRequestId,
      [Parameter(ParameterSetName = "Status", Mandatory = $true)]
      [ValidateSet("abandoned", "active", "completed", "notSet")]
      [string] $Status,
      [Parameter(ParameterSetName = "EnableAutoComplete", Mandatory = $true)]
      [Switch] $EnableAutoComplete,
      [Parameter(ParameterSetName = "EnableAutoComplete", Mandatory = $true)]
      [vsteam_lib.User] $AutoCompleteIdentity,
      [Parameter(ParameterSetName = "DisableAutoComplete", Mandatory = $true)]
      [Switch] $DisableAutoComplete,
      [Parameter(ParameterSetName = "Draft", Mandatory = $false)]
      [switch] $Draft,
      [switch] $Force
   )
   process {
      if ($Force -or $pscmdlet.ShouldProcess($PullRequestId, "Update Pull Request ID")) {
         if ($Draft.IsPresent) {
            $body = '{"isDraft": true }'
         }
         else {
            $body = '{"isDraft": false }'
         }
         if ($EnableAutoComplete.IsPresent) {
            $body = '{"autoCompleteSetBy": "' + $AutoCompleteIdentity.Descriptor + '"}'
         }
         if ($DisableAutoComplete.IsPresent) {
            $body = '{"autoCompleteSetBy": null}'
         }
         if ($Status) {
            if ($Status -eq "completed") {
               $lastMergeSourceCommit = Get-VSTeamPullRequest -RepositoryId $RepositoryId | Where-Object { $_.pullRequestId -eq $PullRequestId } | Select-Object -ExpandProperty lastMergeSourceCommit | ConvertTo-Json -Depth 100
               $body = '{"status": "' + $Status + '", "lastMergeSourceCommit": ' + $lastMergeSourceCommit + '}'
            }
            else {
               $body = '{"status": "' + $Status + '"}'
            }
         }
         # Call the REST API
         $resp = _callAPI -Area git -Resource repositories -iD "$RepositoryId/pullrequests/$PullRequestId" `
            -Method PATCH -ContentType 'application/json' -body $body -Version $(_getApiVersion Git)
         _applyTypesToPullRequests -item $resp
         Write-Output $resp
      }
   }
}
function Update-VSTeamRelease {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamRelease')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [int] $Id,
      [Parameter(Mandatory = $true)]
      [PSCustomObject] $Release,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   Process {
      $body = $Release | ConvertTo-Json -Depth 99
      if ($Force -or $pscmdlet.ShouldProcess($Id, "Update Release")) {
         # Call the REST API
         $resp = _callAPI -Method PUT -SubDomain vsrm -ProjectName $projectName `
            -Area release `
            -Resource releases `
            -Id $id `
            -body $body `
            -Version $(_getApiVersion Release)
         Write-Output $resp
      }
   }
}
function Update-VSTeamReleaseDefinition {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium", DefaultParameterSetName = 'JSON',
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamReleaseDefinition')]
   Param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'File')]
      [string] $InFile,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'JSON')]
      [string] $ReleaseDefinition,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   Process {
      $commonArgs = @{
         Method      = 'Put'
         subDomain   = 'vsrm'
         area        = 'release'
         resource    = 'definitions'
         ProjectName = $ProjectName
         version     = $(_getApiVersion Release)
      }
      if ($Force -or $pscmdlet.ShouldProcess('', "Update Release Definition")) {
         # Call the REST API
         if ($InFile) {
            _callAPI @commonArgs -InFile $InFile | Out-Null
         }
         else {
            _callAPI @commonArgs -Body $ReleaseDefinition | Out-Null
         }
      }
   }
}
function Update-VSTeamServiceEndpoint {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamServiceEndpoint')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $id,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [hashtable] $object,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   Process {
      $body = $object | ConvertTo-Json -Depth 100
      if ($Force -or $pscmdlet.ShouldProcess($id, "Update Service Endpoint")) {
         # Call the REST API
         $resp = _callAPI -ProjectName $projectName -Area 'distributedtask' -Resource 'serviceendpoints' -Id $id  `
            -Method PUT -ContentType 'application/json' -body $body -Version $(_getApiVersion ServiceEndpoints)
         _trackServiceEndpointProgress -projectName $projectName -resp $resp -title 'Updating Service Endpoint' -msg "Updating $id"
         return Get-VSTeamServiceEndpoint -ProjectName $ProjectName -id $id
      }
   }
}
function Update-VSTeamTaskGroup {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamTaskGroup')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Id,
      [Parameter(ParameterSetName = 'ByFile', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $InFile,
      [Parameter(ParameterSetName = 'ByBody', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Body,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   Process {
      $commonArgs = @{
         Id          = $Id
         Method      = 'Put'
         Area        = 'distributedtask'
         Resource    = 'taskgroups'
         ProjectName = $ProjectName
         Version     = $(_getApiVersion TaskGroups)
      }
      if ($Force -or $pscmdlet.ShouldProcess("Update Task Group")) {
         if ($InFile) {
            $resp = _callAPI @commonArgs -InFile $InFile
         }
         else {
            $resp = _callAPI @commonArgs -Body $Body
         }
      }
      return $resp
   }
}
function Update-VSTeamUserEntitlement {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High", DefaultParameterSetName = 'ByEmail',
      HelpUri = 'https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamUserEntitlement')]
   param (
      [Parameter(ParameterSetName = 'ById', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserId')]
      [string]$Id,
      [Parameter(ParameterSetName = 'ByEmail', Mandatory = $True, ValueFromPipelineByPropertyName = $true)]
      [Alias('UserEmail')]
      [string]$Email,
      [Parameter(ParameterSetName = 'ById', Mandatory = $false)]
      [Parameter(ParameterSetName = 'ByEmail', Mandatory = $false)]
      [ValidateSet('Advanced', 'EarlyAdopter', 'Express', 'None', 'Professional', 'StakeHolder')]
      [string]$License,
      [ValidateSet('account', 'auto', 'msdn', 'none', 'profile', 'trial')]
      [Parameter(ParameterSetName = 'ById', Mandatory = $false)]
      [Parameter(ParameterSetName = 'ByEmail')]
      [string]$LicensingSource,
      [ValidateSet('eligible', 'enterprise', 'none', 'platforms', 'premium', 'professional', 'testProfessional', 'ultimate')]
      [Parameter(ParameterSetName = 'ById', Mandatory = $false)]
      [Parameter(ParameterSetName = 'ByEmail')]
      [string]$MSDNLicenseType,
      [switch]$Force
   )
   Process {
      # This will throw if this account does not support MemberEntitlementManagement
      _supportsMemberEntitlementManagement
      if ($email) {
         # We have to go find the id
         $user = Get-VSTeamUserEntitlement -Top 10000 | Where-Object email -eq $email
         if (-not $user) {
            throw "Could not find user with an email equal to $email"
         }
         $id = $user.id
      }
      else {
         $user = Get-VSTeamUserEntitlement -Id $id
      }
      $licenseTypeOriginal = $user.accessLevel.accountLicenseType
      $licenseSourceOriginal = $user.accessLevel.licensingSource
      $msdnLicenseTypeOriginal = $user.accessLevel.msdnLicenseType
      $newLicenseType = if ($false -eq [string]::IsNullOrEmpty($License)) { $License } else { $licenseTypeOriginal }
      $newLicenseSource = if ($false -eq [string]::IsNullOrEmpty($LicensingSource)) { $LicensingSource } else { $licenseSourceOriginal }
      $newMSDNLicenseType = if ($false -eq [string]::IsNullOrEmpty($MSDNLicenseType)) { $MSDNLicenseType } else { $msdnLicenseTypeOriginal }
      $obj = @{
         from  = ""
         op    = "replace"
         path  = "/accessLevel"
         value = @{
            accountLicenseType = $newLicenseType
            licensingSource    = $newLicenseSource
            msdnLicenseType    = $newMSDNLicenseType
         }
      }
      $body = ConvertTo-Json -InputObject @($obj) -Depth 100 -Compress
      $msg = "$( $user.userName ) ($( $user.email ))"
      if ($Force -or $PSCmdlet.ShouldProcess($msg, "Update user")) {
         # Call the REST API
         _callAPI -Method PATCH -SubDomain vsaex -NoProject `
            -Resource userentitlements `
            -Id $id `
            -ContentType 'application/json-patch+; charset=utf-8' `
            -Body $body `
            -Version $(_getApiVersion MemberEntitlementManagement) | Out-Null
         Write-Information "Updated user license for $( $user.userName ) ($( $user.email )) from LicenseType: ($licenseTypeOriginal) to ($newLicenseType)"
         Write-Information "Updated user license for $( $user.userName ) ($( $user.email )) from LicenseSource: ($licenseSourceOriginal) to ($newLicenseSource)"
         Write-Information "Updated user license for $( $user.userName ) ($( $user.email )) from MSDNLicenseType: ($msdnLicenseTypeOriginal) to ($newMSDNLicenseType)"
      }
   }
}
function Update-VSTeamVariableGroup {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamVariableGroup')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Id,
      [Parameter(ParameterSetName = 'ByHashtable', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Name,
      [Parameter(ParameterSetName = 'ByHashtable', Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
      [string] $Description,
      [Parameter(ParameterSetName = 'ByHashtable', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [hashtable] $Variables,
      [Parameter(ParameterSetName = 'ByBody', Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [string] $Body,
      [switch] $Force,
      [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
      [vsteam_lib.ProjectValidateAttribute($false)]
      [ArgumentCompleter([vsteam_lib.ProjectCompleter])]
      [string] $ProjectName
   )
   DynamicParam {
      $dp = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
      if ($(_getApiVersion -Target) -ne "TFS2017" -and $PSCmdlet.ParameterSetName -eq "ByHashtable") {
         $ParameterName = 'Type'
         $rp = _buildDynamicParam -ParameterName $ParameterName -arrSet ('Vsts', 'AzureKeyVault') -Mandatory $true
         $dp.Add($ParameterName, $rp)
         $ParameterName = 'ProviderData'
         $rp = _buildDynamicParam -ParameterName $ParameterName -Mandatory $false -ParameterType ([hashtable])
         $dp.Add($ParameterName, $rp)
      }
      return $dp
   }
   Process {
      if ([string]::IsNullOrWhiteSpace($Body)) {
         $bodyAsHashtable = @{
            name        = $Name
            description = $Description
            variables   = $Variables
         }
         if ([vsteam_lib.Versions]::Version -ne "TFS2017") {
            $Type = $PSBoundParameters['Type']
            $bodyAsHashtable.Add("type", $Type)
            $ProviderData = $PSBoundParameters['ProviderData']
            if ($null -ne $ProviderData) {
               $bodyAsHashtable.Add("providerData", $ProviderData)
            }
         }
         $body = $bodyAsHashtable | ConvertTo-Json -Depth 100
      }
      if ($Force -or $pscmdlet.ShouldProcess($Id, "Update Variable Group")) {
         # Call the REST API
         $resp = _callAPI -Method PUT -ProjectName $projectName `
            -Area distributedtask `
            -Resource variablegroups `
            -Id $Id `
            -body $body `
            -Version $(_getApiVersion VariableGroups)
         Write-Verbose $resp
         return Get-VSTeamVariableGroup -ProjectName $ProjectName -Id $Id
      }
   }
}
function Update-VSTeamWorkItem {
   [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium",
    HelpUri='https://methodsandpractices.github.io/vsteam-docs/docs/modules/vsteam/commands/Update-VSTeamWorkItem')]
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
      [int] $Id,
      [Parameter(Mandatory = $false)]
      [string]$Title,
      [Parameter(Mandatory = $false)]
      [string]$Description,
      [Parameter(Mandatory = $false)]
      [string]$IterationPath,
      [Parameter(Mandatory = $false)]
      [string]$AssignedTo,
      [Parameter(Mandatory = $false)]
      [hashtable]$AdditionalFields,
      [switch] $Force
   )
   Process {
      # Constructing the contents to be send.
      # Empty parameters will be skipped when converting to json.
      [Array]$body = @(
         @{
            op    = "add"
            path  = "/fields/System.Title"
            value = $Title
         }
         @{
            op    = "add"
            path  = "/fields/System.Description"
            value = $Description
         }
         @{
            op    = "add"
            path  = "/fields/System.IterationPath"
            value = $IterationPath
         }
         @{
            op    = "add"
            path  = "/fields/System.AssignedTo"
            value = $AssignedTo
         }) | Where-Object { $_.value }
      #this loop must always come after the main work item fields defined in the function parameters
      if ($AdditionalFields) {
         foreach ($fieldName in $AdditionalFields.Keys) {
            #check that main properties are not added into the additional fields hashtable
            $foundFields = $body | Where-Object { $null -ne $_ -and $_.path -like "*$fieldName" }
            if ($null -ne $foundFields) {
               throw "Found duplicate field '$fieldName' in parameter AdditionalFields, which is already a parameter. Please remove it."
            }
            else {
               $body += @{
                  op    = "add"
                  path  = "/fields/$fieldName"
                  value = $AdditionalFields[$fieldName]
               }
            }
         }
      }
      # It is very important that even if the user only provides
      # a single value above that the item is an array and not
      # a single object or the call will fail.
      # You must call ConvertTo-Json passing in the value and not
      # not using pipeline.
      # https://stackoverflow.com/questions/18662967/convertto-json-an-array-with-a-single-item
      $json = ConvertTo-Json @($body) -Compress -Depth 100
      # Call the REST API
      if ($Force -or $pscmdlet.ShouldProcess($Id, "Update-WorkItem")) {
         $resp = _callAPI -Method PATCH -NoProject `
            -Area wit `
            -Resource workitems `
            -id $Id `
            -ContentType 'application/json-patch+json; charset=utf-8' `
            -Body $json `
            -Version $(_getApiVersion Core)
         _applyTypesToWorkItem -item $resp
         return $resp
      }
   }
}