From 963c04ecf4c303296a75d1d76dbd0a119c0f9f5e Mon Sep 17 00:00:00 2001 From: Josh Miller Date: Tue, 25 Oct 2022 12:19:24 -0600 Subject: [PATCH 1/6] Adding new parameter to xVSTSAgent to allow for selection of which version of the agent to install. The script is currently broken because the links for v3.212.0 are currently dead links --- VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 | 13 +++++++++++-- .../DSCResources/xVSTSAgent/xVSTSAgent.schema.mof | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 index ddd3135..04b6cf7 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 @@ -156,7 +156,11 @@ function Set-TargetResource { $Ensure = 'Present', [System.Boolean] - $PrefixComputerName = $false + $PrefixComputerName = $false, + + [parameter(Mandatory = $false)] + [System.String] + $RequiredVersion ) if ( Test-TargetResource @PSBoundParameters ) { return } @@ -181,6 +185,7 @@ function Set-TargetResource { if ( $Work ) { $installArgs['Work'] = $Work } if ( $LogonCredential ) { $installArgs['LogonCredential'] = $LogonCredential } if ( $ProxyUrl ) { $installArgs['ProxyUrl'] = $ProxyUrl } + if ( $RequiredVersion ) { $installArgs['RequiredVersion'] = $RequiredVersion } Install-VSTSAgent @installArgs } @@ -262,7 +267,11 @@ function Test-TargetResource { $Ensure = 'Present', [System.Boolean] - $PrefixComputerName = $false + $PrefixComputerName = $false, + + [parameter(Mandatory = $false)] + [System.String] + $RequiredVersion ) if ( $PrefixComputerName ) { $Name = Get-PrefixComputerName $Name } diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof index 2bae686..864f95d 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof @@ -17,5 +17,6 @@ class xVSTSAgent : OMI_BaseResource [Write] String ProxyUrl; [Write] Boolean PrefixComputerName; [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Write] String RequiredVersion; }; From 9d3de8a8cad0292350f0a05f1a60d8ab7425e783 Mon Sep 17 00:00:00 2001 From: Josh Miller Date: Fri, 17 Mar 2023 09:54:13 -0600 Subject: [PATCH 2/6] Change Find-VSTSAgent method to use Github API instead of scraping the project releases page html Change in Find-VSTSAgent method to not return prerelease versions of the agent Add new xVSTSAgent parameter for GithubApiToken to allow the script to use a personal access token when calling gitHub API for rate limiting (see https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limiting) Add parameter to xVSTSAgent for the agent version number to install, an error was encountered with one of the releases being a prerelease and was getting picked up because it was the most recent version --- .../DSCResources/xVSTSAgent/xVSTSAgent.psm1 | 13 +- .../xVSTSAgent/xVSTSAgent.schema.mof | 1 + VSTSAgent/VSTSAgent.psm1 | 113 ++++++++---------- 3 files changed, 61 insertions(+), 66 deletions(-) diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 index 04b6cf7..1e24368 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 @@ -160,7 +160,11 @@ function Set-TargetResource { [parameter(Mandatory = $false)] [System.String] - $RequiredVersion + $RequiredVersion, + + [parameter(Mandatory = $false)] + [System.String] + $GitHubApiToken ) if ( Test-TargetResource @PSBoundParameters ) { return } @@ -185,7 +189,12 @@ function Set-TargetResource { if ( $Work ) { $installArgs['Work'] = $Work } if ( $LogonCredential ) { $installArgs['LogonCredential'] = $LogonCredential } if ( $ProxyUrl ) { $installArgs['ProxyUrl'] = $ProxyUrl } - if ( $RequiredVersion ) { $installArgs['RequiredVersion'] = $RequiredVersion } + if ( $RequiredVersion ) + { + $installArgs['MinimumVersion'] = $RequiredVersion + $installArgs['MinMaxVersion'] = $RequiredVersion + } + if ($GitHubApiToken) { $installArgs['GitHubApiToken'] = $GitHubApiToken } Install-VSTSAgent @installArgs } diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof index 864f95d..b5a0bcd 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.schema.mof @@ -18,5 +18,6 @@ class xVSTSAgent : OMI_BaseResource [Write] Boolean PrefixComputerName; [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write] String RequiredVersion; + [Write] String GitHubApiToken; }; diff --git a/VSTSAgent/VSTSAgent.psm1 b/VSTSAgent/VSTSAgent.psm1 index ea387bd..be54b5e 100644 --- a/VSTSAgent/VSTSAgent.psm1 +++ b/VSTSAgent/VSTSAgent.psm1 @@ -104,62 +104,63 @@ function Find-VSTSAgent { [parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')] [VSTSAgentVersion]$MaximumVersion, - [parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')] - [VSTSAgentVersion]$RequiredVersion, - [parameter(Mandatory = $true, ParameterSetName = 'Latest')] [switch]$Latest, [parameter(Mandatory = $false)] - [string]$Platform + [string]$Platform, + + [parameter(Mandatory = $false)] + [string]$GitHubApiToken ) - if ( $Latest ) { + $foundAgents = @() - $findArgs = @{ } - if ( $Platform ) { $findArgs['Platform'] = $Platform } - $sortedAgents = Find-VSTSAgent @findArgs | Sort-Object -Descending -Property Version - $sortedAgents | Where-Object { $_.Version -eq $sortedAgents[0].Version } - return - } + if ($Platform) { $Platform += "-x64" } - Set-SecurityProtocol + $headers = @{ } + if ($GitHubApiToken) { $headers["Authorization"] = ("Bearer "+$GitHubApiToken) } - $rootUri = [uri]"https://github.com" - $releasesRelativeUri = [uri]"/Microsoft/vsts-agent/releases" - - $page = [uri]::new( $rootUri, $releasesRelativeUri ) - $queriedPages = @() + $webData = Invoke-WebRequest -Uri "https://api.github.com/repos/microsoft/vsts-agent/releases" -Method Get -Headers $headers + $releases = ConvertFrom-Json $webData.content - do { - - $result = Invoke-WebRequest $page -UseBasicParsing - $result.Links.href | Where-Object { $_ -match "vsts-agent-(\w+)-x64-(\d+\.\d+\.\d+)\..+$" } | ForEach-Object { - - $instance = [PSCustomObject] @{ - 'Platform' = $Matches[1] - 'Version' = [VSTSAgentVersion]$Matches[2] - 'Uri' = [uri]::new($_, [System.UriKind]::RelativeOrAbsolute) - } + $releases = ($releases | Where-Object { -not $_.prerelease }) - # Make it absolute - if ( -not $instance.Uri.IsAbsoluteUri ) { $instance.Uri = [uri]::new($rootUri, $instance.Uri) } - - if ( $RequiredVersion -and $instance.Version -ne $RequiredVersion) { return } - if ( $MinimumVersion -and $instance.Version -lt $MinimumVersion) { return } - if ( $MaximumVersion -and $instance.Version -gt $MaximumVersion) { return } - if ( $Platform -and $instance.Platform -ne $Platform) { return } - - Write-Verbose "Found agent at $($instance.Uri)" - Write-Output $instance + $releases | ForEach-Object { + $release = $_ + $webData = Invoke-WebRequest -Uri $release.assets.browser_download_url + try { + $assetData = [System.Text.Encoding]::ASCII.GetString($webData.Content) | ConvertFrom-Json + if ($assetData) { + $assetData | ForEach-Object { + + $asset = $_ + if ($asset.name -notlike 'vsts*') { return } + + $agent = [PSCustomObject] @{ + 'Platform' = $asset.platform + 'Version' = [VSTSAgentVersion]$asset.version + 'Uri' = [uri]::new($asset.downloadUrl, [System.UriKind]::RelativeOrAbsolute) + } + + if ($Platform -and $agent.Platform -ne $Platform) { return } + if ( $MinimumVersion -and $agent.Version -lt $MinimumVersion) { return } + if ( $MaximumVersion -and $agent.Version -gt $MaximumVersion) { return } + + $foundAgents += $agent + } + } } + catch { + } + } - $queriedPages += $page - $page = $result.Links.href | Where-Object { - $_ -match "$releasesRelativeUri\?after=v(\d+\.\d+\.\d+)$" -and $queriedPages -notcontains $_ - } | Select-Object -First 1 - - } while ($page) + if ($Latest -and $foundAgents -and $foundAgents.Count -gt 1) { + $foundAgents[0] + } + else { + $foundAgents + } } @@ -207,9 +208,6 @@ function Install-VSTSAgent { [parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')] [VSTSAgentVersion]$MaximumVersion, - [parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')] - [VSTSAgentVersion]$RequiredVersion, - [parameter(Mandatory = $false)] [string]$AgentDirectory = [IO.Path]::Combine($env:USERPROFILE, "VSTSAgents"), @@ -253,7 +251,10 @@ function Install-VSTSAgent { [pscredential]$LogonCredential, [parameter(Mandatory = $false)] - [string]$Cache = [io.Path]::Combine($env:USERPROFILE, ".vstsagents") + [string]$Cache = [io.Path]::Combine($env:USERPROFILE, ".vstsagents"), + + [parameter(Mandatory = $false)] + [string]$GitHubApiToken ) if ($PSVersionTable.Platform -and $PSVersionTable.Platform -ne 'Win32NT') { @@ -273,7 +274,7 @@ function Install-VSTSAgent { $findArgs = @{ 'Platform' = 'win' } if ( $MinimumVersion ) { $findArgs['MinimumVersion'] = $MinimumVersion } if ( $MaximumVersion ) { $findArgs['MaximumVersion'] = $MaximumVersion } - if ( $RequiredVersion ) { $findArgs['RequiredVersion'] = $RequiredVersion } + if ( $GitHubApiToken ) { $findArgs['GitHubApiToken'] = $GitHubApiToken } $agent = Find-VSTSAgent @findArgs | Sort-Object -Descending -Property Version | Select-Object -First 1 if ( -not $agent ) { throw "Could not find agent matching requirements." } @@ -376,9 +377,6 @@ function Uninstall-VSTSAgent { [parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')] [VSTSAgentVersion]$MaximumVersion, - [parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')] - [VSTSAgentVersion]$RequiredVersion, - [parameter(Mandatory = $false)] [string]$AgentDirectory, @@ -445,9 +443,6 @@ function Get-VSTSAgent { [parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')] [VSTSAgentVersion]$MaximumVersion, - [parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')] - [VSTSAgentVersion]$RequiredVersion, - [parameter(Mandatory = $false)] [string]$AgentDirectory = [io.Path]::Combine($env:USERPROFILE, "VSTSAgents"), @@ -479,10 +474,6 @@ function Get-VSTSAgent { $version = & $configPath --version - if ( $RequiredVersion -and $version -ne $RequiredVersion) { - Write-Verbose "Skipping agent because $version not match $RequiredVersion" - return - } if ( $MinimumVersion -and $version -lt $MinimumVersion) { Write-Verbose "Skipping agent because $version is less than $MinimumVersion" return @@ -543,9 +534,6 @@ function Start-VSTSAgent { [parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')] [VSTSAgentVersion]$MaximumVersion, - [parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')] - [VSTSAgentVersion]$RequiredVersion, - [parameter(Mandatory = $false)] [string]$AgentDirectory, @@ -589,9 +577,6 @@ function Stop-VSTSAgent { [parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')] [VSTSAgentVersion]$MaximumVersion, - [parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')] - [VSTSAgentVersion]$RequiredVersion, - [parameter(Mandatory = $false)] [string]$AgentDirectory, From dd7a163398d8ec763f43b5997f6d95d04a54ba2e Mon Sep 17 00:00:00 2001 From: Josh Miller Date: Fri, 17 Mar 2023 11:07:37 -0600 Subject: [PATCH 3/6] Add GithubApiToken parameter to DST test resource --- VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 index 1e24368..d49aa45 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 @@ -280,7 +280,11 @@ function Test-TargetResource { [parameter(Mandatory = $false)] [System.String] - $RequiredVersion + $RequiredVersion, + + [parameter(Mandatory = $false)] + [System.String] + $GitHubApiToken ) if ( $PrefixComputerName ) { $Name = Get-PrefixComputerName $Name } From 3a49ea1cd232123aab8776ee4050b9579ed64c62 Mon Sep 17 00:00:00 2001 From: Josh Miller Date: Fri, 17 Mar 2023 11:17:25 -0600 Subject: [PATCH 4/6] fix typo in parameter statement --- VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 index d49aa45..5d49a93 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 @@ -192,7 +192,7 @@ function Set-TargetResource { if ( $RequiredVersion ) { $installArgs['MinimumVersion'] = $RequiredVersion - $installArgs['MinMaxVersion'] = $RequiredVersion + $installArgs['MaxVersion'] = $RequiredVersion } if ($GitHubApiToken) { $installArgs['GitHubApiToken'] = $GitHubApiToken } From 6c4980b5019784aefc50e1d0874a364aa6a65d5f Mon Sep 17 00:00:00 2001 From: Josh Miller Date: Fri, 17 Mar 2023 12:35:32 -0600 Subject: [PATCH 5/6] updated to use correct variable name for maximum version --- VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 index 5d49a93..196d4db 100644 --- a/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 +++ b/VSTSAgent/DSCResources/xVSTSAgent/xVSTSAgent.psm1 @@ -192,7 +192,7 @@ function Set-TargetResource { if ( $RequiredVersion ) { $installArgs['MinimumVersion'] = $RequiredVersion - $installArgs['MaxVersion'] = $RequiredVersion + $installArgs['MaximumVersion'] = $RequiredVersion } if ($GitHubApiToken) { $installArgs['GitHubApiToken'] = $GitHubApiToken } From fd79bcce0ec1fe03c708fa4df2ef81b13f56bcb5 Mon Sep 17 00:00:00 2001 From: Josh Miller Date: Fri, 17 Mar 2023 15:32:29 -0600 Subject: [PATCH 6/6] replaced Invoke-WebRequest and Start-BitsTransfer with System.Net.WebClient object. This is a fix to issue #17 --- VSTSAgent/VSTSAgent.psm1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/VSTSAgent/VSTSAgent.psm1 b/VSTSAgent/VSTSAgent.psm1 index be54b5e..7986e18 100644 --- a/VSTSAgent/VSTSAgent.psm1 +++ b/VSTSAgent/VSTSAgent.psm1 @@ -118,19 +118,18 @@ function Find-VSTSAgent { if ($Platform) { $Platform += "-x64" } - $headers = @{ } - if ($GitHubApiToken) { $headers["Authorization"] = ("Bearer "+$GitHubApiToken) } + $client = (New-Object System.Net.WebClient) + $client.Headers["User-Agent"] = "request" + if ($GitHubApiToken) { $client.Headers["Authorization"] = ("Bearer "+$GitHubApiToken) } - $webData = Invoke-WebRequest -Uri "https://api.github.com/repos/microsoft/vsts-agent/releases" -Method Get -Headers $headers - $releases = ConvertFrom-Json $webData.content + $releases = $client.DownloadString("https://api.github.com/repos/microsoft/vsts-agent/releases") | ConvertFrom-JSON $releases = ($releases | Where-Object { -not $_.prerelease }) $releases | ForEach-Object { $release = $_ - $webData = Invoke-WebRequest -Uri $release.assets.browser_download_url try { - $assetData = [System.Text.Encoding]::ASCII.GetString($webData.Content) | ConvertFrom-Json + $assetData = $client.DownloadString($release.assets.browser_download_url) | ConvertFrom-JSON if ($assetData) { $assetData | ForEach-Object { @@ -292,7 +291,9 @@ function Install-VSTSAgent { } Write-Verbose "Downloading agent from $($agent.Uri)" - try { Start-BitsTransfer -Source $agent.Uri -Destination $destPath } + try { + (New-Object System.Net.WebClient).DownloadFile($agent.Uri, $destPath) + } catch { throw "Downloading $($agent.Uri) failed: $_" } } else { Write-Verbose "Skipping download as $destPath already exists." }