VSTSAgent Cmdlets and DSC Resource

This commit is contained in:
Microsoft GitHub User 2018-05-03 10:48:45 -07:00 коммит произвёл Josh Wittner
Коммит 927b269e37
9 изменённых файлов: 1128 добавлений и 0 удалений

1
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
.vscode

21
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

95
README.md Normal file
Просмотреть файл

@ -0,0 +1,95 @@
# VSTS Agent Powershell Module
Tools for managing and automating your Visual Studio Team Services Agents.
## Builds
The `master` branch is manually built and deployed to the [PowerShell Gallery](https://www.powershellgallery.com/packages/VSTSAgent).
## Installation
```powershell
Install-Module VSTSAgent -Scope CurrentUser
```
## Using
### Cmdlets
Get all of your installed VSTS Agents:
```powershell
Get-VSTSAgent | Format-Table -AutoSize
# Sample Output
# Name Version Account Status Uri
# ---- ------- ------- ------ ---
# Agent01 2.133.3 MyAccount Running file:///C:/Users/me/VSTSAgents/Agent01/
# Agent02 2.133.3 MyAccount Running file:///C:/Users/me/VSTSAgents/Agent01/
```
Install the latest VSTS Agent:
```powershell
$pat = Read-Host -AsSecureString
Install-VSTSAgent -Account 'MyAccount' -PAT $pat -Name 'Agent01'
```
Uninstall any VSTS Agents:
```powershell
Uninstall-VSTSAgent
```
Find available VSTS Agents for installation:
```powershell
Find-VSTSAgent
```
Start and Stop installed Agents:
```powershell
Stop-VSTSAgent
Start-VSTSAgent
```
### DSC
VSTSAgent includes the xVSTSAgent DSC Resource. An example configuration might look like:
```powershell
Configuration Sample_xVSTSAgent_Install {
param
(
[parameter(Mandatory = $true)]
[PSCredential]$AccountCredential
)
Import-DscResource -ModuleName VSTSAgent
Node 'localhost' {
xVSTSAgent VSTSAgent {
Name = 'Agent01'
Account = 'MyAccount'
AccountCredential = $AccountCredential
AgentDirectory = 'C:\VSTSAgents'
Ensure = 'Present'
}
}
}
```
# Feedback
To file issues or suggestions, please use the [Issues](https://github.com/Microsoft/unitysetup.powershell/issues) page for this project on GitHub.
# Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Reporting Security Issues
Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the [MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).

Просмотреть файл

@ -0,0 +1,216 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
function Get-PrefixComputerName {
param(
[parameter(Mandatory = $true)]
[string]$Name
)
"$($env:COMPUTERNAME)-$Name"
}
<#
.SYNOPSIS
Returns the status of vsts agent installs.
.PARAMETER Name
The name of the agent to get.
.PARAMETER AgentDirectory
The directory to search for installed agents.
.PARAMETER Account
Unused - the account is discovered, not dictated.
.PARAMETER AccountCredential
Unused - not necessary for discovery.
.PARAMETER LogonCredential
Unused - not necessary for discovery.
.PARAMETER Ensure
Unused - not necessary for discovery.
#>
function Get-TargetResource {
[CmdletBinding()]
[OutputType([System.Collections.Hashtable])]
param
(
[parameter(Mandatory = $true)]
[System.String]
$Name,
[parameter(Mandatory = $true)]
[System.String]
$AgentDirectory,
[parameter(Mandatory = $true)]
[System.String]
$Account,
[parameter(Mandatory = $true)]
[System.Management.Automation.PSCredential]
$AccountCredential,
[System.Management.Automation.PSCredential]
$LogonCredential,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = 'Present',
[System.Boolean]
$PrefixComputerName = $false
)
if( $PrefixComputerName ) { $Name = Get-PrefixComputerName $Name }
$returnValue = @{ Name = $Name }
$agent = Get-VSTSAgent -NameFilter $Name -AgentDirectory $AgentDirectory
if ( $agent ) {
$returnValue['Account'] = $agent.Account
$returnValue['Ensure'] = 'Present'
}
else {
$returnValue['Ensure'] = 'Absent'
}
$returnValue
}
<#
.SYNOPSIS
Installs or uninstalls the specified agent.
.PARAMETER Name
What's the name of the agent we're concerned with?
.PARAMETER Pool
What's the pool of the agent we're concerned with? Note, only used on install.
.PARAMETER AgentDirectory
What directory is used for agents?
.PARAMETER Account
What VSTS account should we be concerned with?
.PARAMETER AccountCredential
The credential used to auth with VSTS.
.PARAMETER LogonCredential
What credential should the agent service use?
.PARAMETER Ensure
Should we ensure the agent exists or that it doesn't?
#>
function Set-TargetResource {
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[System.String]
$Name,
[System.String]
$Pool = 'Default',
[parameter(Mandatory = $true)]
[System.String]
$AgentDirectory,
[parameter(Mandatory = $true)]
[System.String]
$Account,
[parameter(Mandatory = $true)]
[System.Management.Automation.PSCredential]
$AccountCredential,
[System.Management.Automation.PSCredential]
$LogonCredential,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = 'Present',
[System.Boolean]
$PrefixComputerName = $false
)
if ( Test-TargetResource @PSBoundParameters ) { return }
if( $PrefixComputerName ) { $Name = Get-PrefixComputerName $Name }
if ( $Ensure -eq 'Present') {
$installArgs = @{
Name = $Name
Pool = $Pool
Account = $Account
PAT = $AccountCredential.Password
AgentDirectory = $AgentDirectory
Replace = $true
}
if ( $LogonCredential ) { $installArgs['LogonCredential'] = $LogonCredential }
Install-VSTSAgent @installArgs
}
}
<#
.SYNOPSIS
Test the status of the specified agent.
.PARAMETER Name
What's the name of the agent we're concerned with?
.PARAMETER Pool
Unused - Agent pool is not currently detectable.
.PARAMETER AgentDirectory
What directory should we search for agents?
.PARAMETER Account
What account should the agent use?
.PARAMETER AccountCredential
Unused - testing does not require credentials.
.PARAMETER LogonCredential
Unused - agent service logon user is not currently detectable.
.PARAMETER Ensure
Should the agent be present or absent?
#>
function Test-TargetResource {
[CmdletBinding()]
[OutputType([System.Boolean])]
param
(
[parameter(Mandatory = $true)]
[System.String]
$Name,
[System.String]
$Pool = 'Default',
[parameter(Mandatory = $true)]
[System.String]
$AgentDirectory,
[parameter(Mandatory = $true)]
[System.String]
$Account,
[parameter(Mandatory = $true)]
[System.Management.Automation.PSCredential]
$AccountCredential,
[System.Management.Automation.PSCredential]
$LogonCredential,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = 'Present',
[System.Boolean]
$PrefixComputerName = $false
)
if( $PrefixComputerName ) { $Name = Get-PrefixComputerName $Name }
$agent = Get-VSTSAgent -NameFilter $Name -AgentDirectory $AgentDirectory
switch ($Ensure) {
'Present' {
if ( -not $agent ) { return $false }
if ( $agent.Account -ne $Account ) { return $false }
return $true
}
'Absent' { return (-not $agent) }
}
}
Export-ModuleMember -Function *-TargetResource

Просмотреть файл

@ -0,0 +1,14 @@

[ClassVersion("1.0.0.0"), FriendlyName("xVSTSAgent")]
class xVSTSAgent : OMI_BaseResource
{
[Key] String Name;
[Write] String Pool;
[Required, EmbeddedInstance("MSFT_Credential")] String AccountCredential;
[Required] String Account;
[Write, EmbeddedInstance("MSFT_Credential")] String LogonCredential;
[Required] String AgentDirectory;
[Write] Boolean PrefixComputerName;
[Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
};

Просмотреть файл

@ -0,0 +1,52 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
<#
Create a custom configuration by passing in necessary values
#>
Configuration Sample_xVSTSAgent {
param
(
[parameter(Mandatory = $true)]
[System.String]
$Account,
[System.String]
$Name = "$env:COMPUTERNAME",
[System.String]
$Pool = 'Default',
[parameter(Mandatory = $true)]
[pscredential]
$AccountCredential,
[pscredential]
$LogonCredential,
[System.String]
$AgentDirectory = 'C:\VSTSAgents',
[ValidateSet('Present', 'Absent')]
[System.String]
$Ensure = 'Present',
[System.Boolean]
$PrefixComputerName = $false
)
Import-DscResource -ModuleName VSTSAgent
Node 'localhost' {
xVSTSAgent VSTSAgent {
Name = $Name
Pool = $Pool
Account = $Account
AccountCredential = $AccountCredential
LogonCredential = $LogonCredential
AgentDirectory = $AgentDirectory
Ensure = $Ensure
PrefixComputerName = $PrefixComputerName
}
}
}

132
VSTSAgent/VSTSAgent.psd1 Normal file
Просмотреть файл

@ -0,0 +1,132 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
#
# Module manifest for module 'VSTSAgent'
#
# Generated by: jowitt
#
# Generated on: 2018-04-05
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'VSTSAgent.psm1'
# Version number of this module.
ModuleVersion = '1.1'
# Supported PSEditions
# CompatiblePSEditions = @()
# ID used to uniquely identify this module
GUID = '679b6302-ff19-4e53-a20f-eac2f531b5b6'
# Author of this module
Author = 'Microsoft'
# Company or vendor of this module
CompanyName = 'Microsoft'
# Copyright statement for this module
Copyright = 'Copyright (c) Microsoft Corporation. All rights reserved.'
# Description of the functionality provided by this module
Description = 'Tools for managing and automating your VSTS Agents.'
# Minimum version of the Windows PowerShell engine required by this module
# PowerShellVersion = ''
# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''
# Minimum version of the Windows PowerShell host required by this module
# PowerShellHostVersion = ''
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# DotNetFrameworkVersion = ''
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# CLRVersion = ''
# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''
# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @()
# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()
# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()
# Format files (.ps1xml) to be loaded when importing this module
# FormatsToProcess = @()
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
# NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @(
'Find-VSTSAgent',
'Install-VSTSAgent',
'Get-VSTSAgent',
'Uninstall-VSTSAgent',
'Start-VSTSAgent',
'Stop-VSTSAgent'
)
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
# Variables to export from this module
VariablesToExport = @()
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()
# DSC resources to export from this module
# DscResourcesToExport = @()
# List of all modules packaged with this module
# ModuleList = @()
# List of all files packaged with this module
# FileList = @()
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('VSTS')
# A URL to the license for this module.
LicenseUri = 'https://github.com/Microsoft/VSTSAgent.PowerShell/blob/master/LICENSE'
# A URL to the main website for this project.
ProjectUri = 'https://github.com/Microsoft/VSTSAgent.PowerShell'
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
# ReleaseNotes = ''
} # End of PSData hashtable
} # End of PrivateData hashtable
# HelpInfo URI of this module
HelpInfoURI = 'https://github.com/Microsoft/VSTSAgent.PowerShell/blob/master/README.md'
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''
}

573
VSTSAgent/VSTSAgent.psm1 Normal file
Просмотреть файл

@ -0,0 +1,573 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
class VSTSAgentVersion : System.IComparable {
[int] $Major;
[int] $Minor;
[int] $Revision;
VSTSAgentVersion([string] $version) {
$version -match "(\d+)\.(\d+)\.(\d+)" | Out-Null
if ( $Matches.Count -ne 4 ) { throw "Invalid VSTS Agent version: $version" }
$this.Major = [int]($Matches[1]);
$this.Minor = [int]($Matches[2]);
$this.Revision = [int]($Matches[3]);
}
[string] ToString() {
$result = "$($this.Major).$($this.Minor).$($this.Revision)"
return $result
}
[int] CompareTo([object]$obj) {
if ($null -eq $obj) { return 1 }
if ($obj -isnot [VSTSAgentVersion]) { throw "Object is not a VSTSAgentVersion"}
return [VSTSAgentVersion]::Compare($this, $obj)
}
static [int] Compare([VSTSAgentVersion]$a, [VSTSAgentVersion]$b) {
if ($a.Major -lt $b.Major) { return -1 }
if ($a.Major -gt $b.Major) { return 1 }
if ($a.Minor -lt $b.Minor) { return -1 }
if ($a.Minor -gt $b.Minor) { return 1 }
if ($a.Revision -lt $b.Revision) { return -1 }
if ($a.Revision -gt $b.Revision) { return 1 }
return 0
}
[boolean] Equals ( [object]$obj ) {
return [VSTSAgentVersion]::Compare($this, $obj) -eq 0;
}
}
<#
.SYNOPSIS
Enable TLS12 security protocol required by the GitHub https certs.
#>
function Set-SecurityProtocol {
[CmdletBinding(SupportsShouldProcess)]
param()
$secProtocol = [System.Net.ServicePointManager]::SecurityProtocol
if ( ($secProtocol -band [System.Net.SecurityProtocolType]::Tls12) -ne 0 ) { return }
if ( $PSCmdlet.ShouldProcess('[System.Net.ServicePointManager]::SecurityProtocol', 'Add [System.Net.SecurityProtocolType]::Tls12') ) {
$secProtocol += [System.Net.SecurityProtocolType]::Tls12;
[System.Net.ServicePointManager]::SecurityProtocol = $secProtocol
}
}
<#
.SYNOPSIS
Convert current OS platform to required Agent platform
#>
function Get-Platform {
param ([string]$OS = $PSVersionTable.OS)
switch -regex ($OS) {
'linux' { 'linux' }
'darwin' { 'osx' }
default { 'win' }
}
}
<#
.SYNOPSIS
Finds available VSTS agents
.DESCRIPTION
Searches the agent's Github release pages for available versions of the Agent.
.PARAMETER MinimumVersion
The minimum agent version required.
.PARAMETER MaximumVersion
The maximum agent version allowed.
.PARAMETER RequiredVersion
The required agent version.
.PARAMETER Latest
Find the latest available agent version.
.PARAMETER Platform
The platform required for the agent.
#>
function Find-VSTSAgent {
[CmdletBinding( DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $true, ParameterSetName = 'MinVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MinimumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'MaxVersion')]
[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
)
if ( $Latest ) {
$findArgs = @{ }
if ( $Platform ) { $findArgs['Platform'] = $Platform }
$sortedAgents = Find-VSTSAgent @findArgs | Sort-Object -Descending -Property Version
$sortedAgents | Where-Object { $_.Version -eq $sortedAgents[0].Version }
return
}
Set-SecurityProtocol
$rootUri = [uri]"https://github.com"
$releasesRelativeUri = [uri]"/Microsoft/vsts-agent/releases"
$page = [uri]::new( $rootUri, $releasesRelativeUri )
$queriedPages = @()
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)
}
# 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
}
$queriedPages += $page
$page = $result.Links.href | Where-Object {
$_ -match "$releasesRelativeUri\?after=v(\d+\.\d+\.\d+)$" -and $queriedPages -notcontains $_
} | Select-Object -First 1
} while ($page)
}
<#
.SYNOPSIS
Install a VSTS Agent.
.DESCRIPTION
Download and install a VSTS Agent matching the specified requirements.
.PARAMETER MinimumVersion
The minimum agent version required.
.PARAMETER MaximumVersion
The maximum agent version allowed.
.PARAMETER RequiredVersion
The required agent version.
.PARAMETER AgentDirectory
What directory should agents be installed into?
.PARAMETER Name
What name should the agent use?
.PARAMETER Pool
What pool should the agent be registered into?
.PARAMETER PAT
What personal access token (PAT) should be used to auth with VSTS?
.PARAMETER Account
What account should the agent be registered to? As in "https://$Account.visualstudio.com".
.PARAMETER Replace
Should the new agent replace any existing one on the account?
.PARAMETER LogonCredential
What user credentials should be used by the agent service?
.PARAMETER Cache
Where should agent downloads be cached?
#>
function Install-VSTSAgent {
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $true, ParameterSetName = 'MinVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MinimumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'MaxVersion')]
[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"),
[parameter(Mandatory = $false)]
[string]$Name = [System.Environment]::MachineName + "-$(Get-Random)",
[parameter(Mandatory = $false)]
[string]$Pool = 'Default',
[parameter(Mandatory = $true)]
[securestring]$PAT,
[parameter(Mandatory = $true)]
[string]$Account,
[parameter(Mandatory = $false)]
[switch]$Replace,
[parameter(Mandatory = $false)]
[pscredential]$LogonCredential,
[parameter(Mandatory = $false)]
[string]$Cache = [io.Path]::Combine($env:USERPROFILE, ".vstsagents")
)
if ($PSVersionTable.Platform -and $PSVersionTable.Platform -ne 'Win32NT') {
throw "Not Implemented: Support for $($PSVersionTable.Platform), contributions welcome."
}
if ( $Verbose ) { $VerbosePreference = 'Continue' }
$existing = Get-VSTSAgent -AgentDirectory $AgentDirectory -NameFilter $Name
if ( $existing ) {
if($Replace) {
Uninstall-VSTSAgent -NameFilter $Name -AgentDirectory $AgentDirectory -PAT $PAT -ErrorAction Stop
}
else { throw "Agent $Name already exists in $AgentDirectory" }
}
$findArgs = @{ 'Platform' = 'win' }
if ( $MinimumVersion ) { $findArgs['MinimumVersion'] = $MinimumVersion }
if ( $MaximumVersion ) { $findArgs['MaximumVersion'] = $MaximumVersion }
if ( $RequiredVersion ) { $findArgs['RequiredVersion'] = $RequiredVersion }
$agent = Find-VSTSAgent @findArgs | Sort-Object -Descending -Property Version | Select-Object -First 1
if ( -not $agent ) { throw "Could not find agent matching requirements." }
Write-Verbose "Installing agent at $($agent.Uri)"
$fileName = $agent.Uri.Segments[$agent.Uri.Segments.Length - 1]
$destPath = [IO.Path]::Combine($Cache, "$($agent.Version)\$fileName")
if ( -not (Test-Path $destPath) ) {
$destDirectory = [io.path]::GetDirectoryName($destPath)
if (!(Test-Path $destDirectory -PathType Container)) {
New-Item "$destDirectory" -ItemType Directory | Out-Null
}
Write-Verbose "Downloading agent from $($agent.Uri)"
try { Start-BitsTransfer -Source $agent.Uri -Destination $destPath }
catch { throw "Downloading $($agent.Uri) failed: $_" }
}
else { Write-Verbose "Skipping download as $destPath already exists." }
$agentFolder = [io.path]::Combine($AgentDirectory, $Name)
Write-Verbose "Unzipping $destPath to $agentFolder"
if ( $PSVersionTable.PSVersion.Major -le 5 ) {
Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($destPath, $agentFolder)
$configPath = [io.path]::combine($agentFolder, 'config.cmd')
$configPath = Get-ChildItem $configPath -ErrorAction SilentlyContinue
if ( -not $configPath ) { throw "Agent $agentFolder is missing config.cmd" }
[string[]]$configArgs = @('--unattended', '--url', "https://$Account.visualstudio.com", '--auth', `
'pat', '--pool', "$Pool", '--agent', "$Name", '--runAsService')
if ( $Replace ) { $configArgs += '--replace' }
if ( $LogonCredential ) { $configArgs += '--windowsLogonAccount', $LogonCredential.UserName }
if ( -not $PSCmdlet.ShouldProcess("$configPath $configArgs", "Start-Process") ) { return }
$token = [System.Net.NetworkCredential]::new($null, $PAT).Password
$configArgs += '--token', $token
if ( $LogonCredential ) {
$configArgs += '--windowsLogonPassword', `
[System.Net.NetworkCredential]::new($null, $LogonCredential.Password).Password
}
$outFile = [io.path]::Combine($agentFolder, "out.log")
$errorFile = [io.path]::Combine($agentFolder, "error.log")
Write-Verbose "Registering $Name to $Pool in $Account"
Start-Process $configPath -ArgumentList $configArgs -NoNewWindow -Wait `
-RedirectStandardOutput $outFile -RedirectStandardError $errorFile -ErrorAction Stop
if (Test-Path $errorFile) {
Get-Content $errorFile | Write-Error
}
}
<#
.SYNOPSIS
Uninstall agents.
.DESCRIPTION
Uninstall any agents matching the specified criteria.
.PARAMETER MinimumVersion
Minimum version of agents to uninstall.
.PARAMETER MaximumVersion
Maximum version of agents to uninstall.
.PARAMETER RequiredVersion
Required version of agents to uninstall.
.PARAMETER AgentDirectory
What directory should be searched for existing agents?
.PARAMETER NameFilter
Only agents whose names match this filter will be uninstalled.
.PARAMETER PAT
The personal access token used to auth with VSTS.
#>
function Uninstall-VSTSAgent {
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $true, ParameterSetName = 'MinVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MinimumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'MaxVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MaximumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')]
[VSTSAgentVersion]$RequiredVersion,
[parameter(Mandatory = $false)]
[string]$AgentDirectory,
[parameter(Mandatory = $false)]
[string]$NameFilter,
[parameter(Mandatory = $true)]
[securestring]$PAT
)
$getArgs = @{}
$PSBoundParameters.Keys | Where-Object { $_ -ne 'PAT' } | ForEach-Object {
$getArgs[$_] = $PSBoundParameters[$_]
}
$token = [System.Net.NetworkCredential]::new($null, $PAT).Password
Get-VSTSAgent @getArgs | ForEach-Object {
if ( -not $PSCmdlet.ShouldProcess("$($_.Name) - $($_.Uri)", "Uninstall")) { return }
$configPath = [io.path]::Combine($_.Uri.LocalPath, 'config.cmd')
$configArgs = @('remove', '--unattended', '--auth', 'pat', '--token', "$token")
$outFile = [io.path]::Combine($_.Uri.LocalPath, "out.log")
$errorFile = [io.path]::Combine($_.Uri.LocalPath, "error.log")
Start-Process $configPath -ArgumentList $configArgs -NoNewWindow -Wait `
-RedirectStandardOutput $outFile -RedirectStandardError $errorFile
if ((Test-Path $errorFile) -and (Get-ChildItem $errorFile).Length -gt 0) {
Get-Content $errorFile | Write-Error
return; # Don't remove the agent folder if something went wrong.
}
Remove-Item $_.Uri.LocalPath -Recurse -Force
}
}
<#
.SYNOPSIS
Get all the agents installed.
.PARAMETER MinimumVersion
The minimum agent version to get.
.PARAMETER MaximumVersion
The maximum agent version to get.
.PARAMETER RequiredVersion
The required agent version to get.
.PARAMETER AgentDirectory
What directory should be searched for installed agents?
.PARAMETER NameFilter
Only agents whose names pass the filter are included.
#>
function Get-VSTSAgent {
[CmdletBinding(DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $true, ParameterSetName = 'MinVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MinimumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'MaxVersion')]
[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"),
[parameter(Mandatory = $false)]
[string]$NameFilter = '*'
)
Get-ChildItem $AgentDirectory -Directory -Filter $NameFilter -ErrorAction SilentlyContinue | ForEach-Object {
Write-Verbose "Found agent at $($_.FullName)"
$configPath = [io.path]::combine($_.FullName, 'config.cmd')
$configPath = Get-ChildItem $configPath -ErrorAction SilentlyContinue
if ( -not $configPath ) {
Write-Warning "Agent $_ is missing config.cmd"
return
}
$version = & $configPath --version
if ( $RequiredVersion -and $version -ne $RequiredVersion) { return }
if ( $MinimumVersion -and $version -lt $MinimumVersion) { return }
if ( $MaximumVersion -and $version -gt $MaximumVersion) { return }
$name = $_.Name
$service = Get-Service | ForEach-Object {
if ( $_.Name -match "^vstsagent\.(.+)\.$name$" ) {
[pscustomobject]@{ 'Account' = $Matches[1]; 'Status' = $_.Status }
}
}
[pscustomobject]@{
'Name' = $name
'Version' = $version
'Account' = $service.Account
'Status' = $service.Status
'Uri' = [uri]::new( $configPath.Directory.FullName + [io.path]::DirectorySeparatorChar )
}
}
}
<#
.SYNOPSIS
Get service for installed agent.
.DESCRIPTION
Get service for installed agent using VSTS agent naming scheme.
.PARAMETER Name
Name of the agent to find the service for.
.PARAMETER Account
Account of the agent to find the service for.
.PARAMETER Status
Filter services to only those whose status matches this.
#>
function Get-VSTSAgentService {
param(
[parameter(ValueFromPipelineByPropertyName, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Name,
[parameter(ValueFromPipelineByPropertyName, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Account,
[System.ServiceProcess.ServiceControllerStatus]$Status
)
$serviceName = "vstsagent.$Account.$Name"
$services = Get-Service -Name $serviceName -ErrorAction 'SilentlyContinue'
if ( $services.Count -eq 0 ) {
Write-Error "Agent $($_.Name) has no matching service at $serviceName"
}
if ( $Status ) {
$services = $services | Where-Object { $_.Status -eq $Status }
}
$services
}
<#
.SYNOPSIS
Starts any stopped services for matching VSTS Agents
.PARAMETER MinimumVersion
Mimumum version for agents.
.PARAMETER MaximumVersion
Maximum version for agents.
.PARAMETER RequiredVersion
Required version for agents.
.PARAMETER AgentDirectory
Directory to search installed agents.
.PARAMETER NameFilter
Only start services for agents whose names pass this filter.
#>
function Start-VSTSAgent {
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $true, ParameterSetName = 'MinVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MinimumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'MaxVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MaximumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')]
[VSTSAgentVersion]$RequiredVersion,
[parameter(Mandatory = $false)]
[string]$AgentDirectory,
[parameter(Mandatory = $false)]
[string]$NameFilter
)
Get-VSTSAgent @PSBoundParameters | Get-VSTSAgentService -Status Stopped | ForEach-Object {
if ( $PSCmdlet.ShouldProcess($_.Name, "Start-Service") ) {
Start-Service $_
}
}
}
<#
.SYNOPSIS
Stop any running services for agents.
.PARAMETER MinimumVersion
Mimumum version for agents.
.PARAMETER MaximumVersion
Maximum version for agents.
.PARAMETER RequiredVersion
Required version for agents.
.PARAMETER AgentDirectory
Directory to search installed agents.
.PARAMETER NameFilter
Only start services for agents whose names pass this filter.
#>
function Stop-VSTSAgent {
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "NoVersion")]
param(
[parameter(Mandatory = $true, ParameterSetName = 'MinVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MinimumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'MaxVersion')]
[parameter(Mandatory = $true, ParameterSetName = 'MinMaxVersion')]
[VSTSAgentVersion]$MaximumVersion,
[parameter(Mandatory = $true, ParameterSetName = 'RequiredVersion')]
[VSTSAgentVersion]$RequiredVersion,
[parameter(Mandatory = $false)]
[string]$AgentDirectory,
[parameter(Mandatory = $false)]
[string]$NameFilter
)
Get-VSTSAgent @PSBoundParameters | Get-VSTSAgentService -Status Running | ForEach-Object {
if ( $PSCmdlet.ShouldProcess($_.Name, "Stop-Service") ) {
Stop-Service $_
}
}
}

24
build.ps1 Normal file
Просмотреть файл

@ -0,0 +1,24 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
param([int]$Revision = 0, [string]$Suffix = '')
Import-Module 'PowerShellGet' -MinimumVersion '1.6.0'
$ErrorActionPreference = 'Stop'
$manifest = Test-ModuleManifest .\VSTSAgent\VSTSAgent.psd1
$versionString = $manifest.Version.ToString()
if ($manifest.PrivateData['PSData']['Prerelease']) {
$versionString += "-$($manifest.PrivateData['PSData']['Prerelease'])"
}
Write-Host "Current Module Version: $versionString"
$newVersion = New-Object System.Version($manifest.Version.Major, $manifest.Version.Minor, $Revision)
Update-ModuleManifest -ModuleVersion $newVersion -Prerelease $Suffix -Path .\VSTSAgent\VSTSAgent.psd1
$manifest = Test-ModuleManifest .\VSTSAgent\VSTSAgent.psd1
$versionString = $manifest.Version.ToString()
if ($manifest.PrivateData['PSData']['Prerelease']) {
$versionString += "-$($manifest.PrivateData['PSData']['Prerelease'])"
}
Write-Host "New Module Version: $versionString"