diff --git a/README.md b/README.md index 2a9bdc1..3f62889 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,38 @@ -# Introduction -xTfsDscAgent is a Powershell Desire-State Module to install an configure a TFS / VSTS Build Agents. +# xTfsDscAgent + +## Introduction + +xTfsDscAgent is a Powershell Desire-State Module to install an configure a TFS / VSTS Build Agents. You can use this to automate the provisinig of new Agents as you need it. -# Getting Started +## Getting Started To start it is helpful to know about DSC and know how it works. [Here](https://docs.microsoft.com/en-us/powershell/dsc/overview) is a good documentation for this. To write a config for your agent you must import this Module in your configuratoin. -After this you can use the xTfsDscAgent to configure the agents. In the following table you can see what parameters we currently support: - -| Parameter | Requiered | Desciprion | -| --------- | --------- | ---------- | -| Agentfolder| true | The folder in this the agent will install. This is the key for DSC tho identify is the agent currently there| -| Ensure | true | This is a default DSC Parameter, that describe if the configuration must add or remove the agent. | -| serverUrl | true | This is the url for the VSTS or TFS server. Importent: only input the server url! Dont add the collection name or somethink like this! | -| AgentVersion | false | The Agentversion you want to install and configure. The default is latest. | -| AgentPool | false | The AgentPool the agent joins after installation. The default is default | -| AgentName | false | Here you can define a custom name for the agent. The Default is 'default-' and a Guid | -| AgentAuth | false | This is a option to use all supported auth-options for TFS or VSTS. The Default is 'Integrated'. (This you must change for VSTS!) | -| AgentRunAsService | false | This is a option to run the agent in a windows service. This option only works on windows! The defualt is false. -| WorkFolder |false | Here you can set a custom path. In this Path the Agent will do the work from the build and release jobs from VSTS / TFS. The default option is '_work' in the agentfolder. | -| AgentUser | true | You you must enter the credetials of the service account that have acess to register new agents and run builds. The password is only needed for some auth mechanics! See the TFS Agent doucmentation for this. | -| UserToken | false | Here you can paste a PAT. This is only use for PAT auth! Default is empty | -| ReplaceAgent | false | This define is the registrion can override exsisting registions with the same agent name. The default is false | - -# Example +After this you can use the xTfsDscAgent to configure the agents. In the following table you can see what parameters we currently support: + +| Parameter | Requiered | Desciprion | +| ----------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Agentfolder | true | The folder in this the agent will install. This is the key for DSC tho identify is the agent currently there | +| Ensure | true | This is a default DSC Parameter, that describe if the configuration must add or remove the agent. | +| serverUrl | true | This is the url for the VSTS or TFS server. Importent: only input the server url! Dont add the collection name or somethink like this! | +| AgentVersion | false | The Agentversion you want to install and configure. The default is latest. | +| AgentPool | false | The AgentPool the agent joins after installation. The default is default | +| AgentName | false | Here you can define a custom name for the agent. The Default is 'default-' and a Guid | +| AgentAuth | false | This is a option to use all supported auth-options for TFS or VSTS. The Default is 'Integrated'. (This you must change for VSTS!) | +| AgentRunAsService | false | This is a option to run the agent in a windows service. This option only works on windows! The defualt is false. | +| WorkFolder | false | Here you can set a custom path. In this Path the Agent will do the work from the build and release jobs from VSTS / TFS. The default option is '\_work' in the agentfolder. | +| AgentUser | true | You you must enter the credetials of the service account that have acess to register new agents and run builds. The password is only needed for some auth mechanics! See the TFS Agent doucmentation for this. | +| UserToken | false | Here you can paste a PAT. This is only use for PAT auth! Default is empty. **Important** This must be a token for 'All accessible organizations' if it not the deprovisioning will fail! | +| ReplaceAgent | false | This define is the registrion can override exsisting registions with the same agent name. The default is false | +| filePath | false | Define a path to a downloaded agent ZIP file is. It's recommended to use this option in offline environments. (or in proxy environments) | +| CollectionName | true | Define the name of your project collection / organisation. | + +## Example + Important: First install this powershell module in a psmodule path! -Here you can see a example for a config for a agent: +Here you can see a example for a config for a agent: ```PS Configuration sampleConfig { @@ -35,17 +41,26 @@ Configuration sampleConfig { [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [PsCredential] $agentCredential - ) - Import-DscResource -ModuleName xTfsDscAgent -ModuleVersion 1.0.1 + ) + Import-DscResource -ModuleName xTfsDscAgent -ModuleVersion 1.0.1 Node $AllNodes.Where{$_.Role -eq 'TfsAgent'}.NodeName { - + xTfsDscAgent agent { AgentFolder = "C:\Agent\" Ensure = "Present" - serverUrl = "https://tfs201801.home01.local/" - AgentPool = "default" - AgentUser = $agentCredential + serverUrl = "https://tfs201801.home01.local/" + AgentPool = "default" + AgentUser = $agentCredential + } + + xTfsDscAgent offlineagent { + AgentFolder = "C:\OfflineAgent\" + Ensure = "Present" + serverUrl = "https://tfs201801.home01.local/" + AgentPool = "default" + AgentUser = $agentCredential + filePath = "\\shareserver\setups\agent.zip" } LocalConfigurationManager { @@ -53,12 +68,13 @@ Configuration sampleConfig { } } - + } ``` To run this config you must save this in a file with the name sampleConfig.ps1. After this you can run the config like any other dsc config: + ```PS $ConfigData = @{ AllNodes = @( @@ -83,13 +99,29 @@ Start-DscConfiguration .\mofs -Verbose -Wait ``` -# Build and Test -TODO: Describe and show how to build your code and run the tests. +## Build and Test + +To build the project run the `build.ps1` in the root folder. It will automatically increase the module version number. So you can easy update in your test and dev environment if your changes work. +After this you have a `out` folder with the module in it. This must be copied to the modules folder: + +```powershell +Copy-Item -Recurse -Force 'out/xTfsDscAgent' -Container "C:\Program Files\WindowsPowerShell\Modules" +``` + +After this you can compile your configuration with your own version of xTfsDscAgent. -# Contribute -TODO: Explain how other users and developers can contribute to make your code better. +### Tests + +Don't use them currently is better for you. If you really want use them you need a TFS / Azure DevOps installation and must changes the lines that referee to it in the test file. Then you must set two user environment variables with username and password to login at the TFS / Azure DevOps Server. + +So just don't use them. I will rewrite them sometime in the feature. + +## Contribute + +TODO: Explain how other users and developers can contribute to make your code better. If you want to learn more about creating good readme files then refer the following [guidelines](https://www.visualstudio.com/en-us/docs/git/create-a-readme). You can also seek inspiration from the below readme files: + - [ASP.NET Core](https://github.com/aspnet/Home) - [Visual Studio Code](https://github.com/Microsoft/vscode) - [Chakra Core](https://github.com/Microsoft/ChakraCore) diff --git a/build.ps1 b/build.ps1 index 4ef6fea..8ced87d 100644 --- a/build.ps1 +++ b/build.ps1 @@ -8,13 +8,17 @@ $psdData.ModuleVersion = $versionParts -join "."; Update-ModuleManifest -ModuleVersion $psdData.ModuleVersion -Path ($psdDictionary + "\" + $psdFileName); -$outModuleFolder = ".\out\" + $psdData.ModuleVersion; +$outModuleFolder = ".\out\xTfsDscAgent\" + $psdData.ModuleVersion; $dscFolderName = "xTfsDscAgent" + "_" + $psdData.ModuleVersion; $outDSCFolder = ".\out\" + $dscFolderName; mkdir ($outModuleFolder) -Force -Verbose; mkdir ($outDSCFolder) -Force -Verbose; -Copy-Item -Path ($psdDictionary + "\*") -Destination $outModuleFolder -Container; +Copy-Item -Path ($psdDictionary + "/" + $psdFileName) -Destination $outModuleFolder; +$dscResourceFolder = $outModuleFolder + "/xTfsDscAgent/DscResources/" + $psdFileName.Split('.')[0]; +mkdir ($dscResourceFolder) -Force; +Copy-Item -Path ($psdDictionary + "\*") -Exclude "*.psd1" -Container -Destination $dscResourceFolder; +#Copy-Item -Path ($psdDictionary + "\*") -Destination $outModuleFolder -Container; Copy-Item -Path ($psdDictionary + "\*") -Destination $outDSCFolder -Container; diff --git a/src/xtfsDscAgent/xTfsDscAgent.psd1 b/src/xtfsDscAgent/xTfsDscAgent.psd1 index 5894ff7..62bd8f4 100644 --- a/src/xtfsDscAgent/xTfsDscAgent.psd1 +++ b/src/xtfsDscAgent/xTfsDscAgent.psd1 @@ -1,125 +1,132 @@ # -# Module manifest for module 'PSGet_xTfsDscAgent' +# Modulmanifest für das Modul "PSGet_xTfsDscAgent" # -# Generated by: Paul +# Generiert von: Paul # -# Generated on: 3/17/2018 +# Generiert am: 21.07.2020 # @{ - # Script module or binary module file associated with this manifest. - RootModule = 'xTfsDscAgent.psm1' - - # Version number of this module. - ModuleVersion = '1.0.73' - - # Supported PSEditions - # CompatiblePSEditions = @() - - # ID used to uniquely identify this module - GUID = '129bb685-dcb9-4e78-b172-d831d621b74e' - - # Author of this module - Author = 'Paul' - - # Company or vendor of this module - CompanyName = 'Unknown' - - # Copyright statement for this module - Copyright = '(c) 2017 Paul. Alle Rechte vorbehalten.' - - # Description of the functionality provided by this module - Description = 'DSC Resource for TFS Agent' - - # 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 = @() - - # 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 = 'xTfsDscAgent' - - # 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 = @() - - # A URL to the license for this module. - # LicenseUri = '' - - # A URL to the main website for this project. - # ProjectUri = '' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - # ReleaseNotes = '' - - # External dependent modules of this module - # ExternalModuleDependencies = '' - - } # End of PSData hashtable - - } # End of PrivateData hashtable - - # HelpInfo URI of this module - # HelpInfoURI = '' - - # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. - # DefaultCommandPrefix = '' - - } \ No newline at end of file +# Die diesem Manifest zugeordnete Skript- oder Binärmoduldatei. +RootModule = 'xtfsDscAgent.psm1' + +# Die Versionsnummer dieses Moduls +ModuleVersion = '1.0.87' + +# Unterstützte PSEditions +# CompatiblePSEditions = @() + +# ID zur eindeutigen Kennzeichnung dieses Moduls +GUID = '129bb685-dcb9-4e78-b172-d831d621b74e' + +# Autor dieses Moduls +Author = 'Paul' + +# Unternehmen oder Hersteller dieses Moduls +CompanyName = 'Paule96' + +# Urheberrechtserklärung für dieses Modul +Copyright = '(c) 2017 Paul. Alle Rechte vorbehalten.' + +# Beschreibung der von diesem Modul bereitgestellten Funktionen +Description = 'DSC Resource for TFS Agent' + +# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Moduls +PowerShellVersion = '5.0' + +# Der Name des für dieses Modul erforderlichen Windows PowerShell-Hosts +# PowerShellHostName = '' + +# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Hosts +# PowerShellHostVersion = '' + +# Die für dieses Modul mindestens erforderliche Microsoft .NET Framework-Version. Diese erforderliche Komponente ist nur für die PowerShell Desktop-Edition gültig. +# DotNetFrameworkVersion = '' + +# Die für dieses Modul mindestens erforderliche Version der CLR (Common Language Runtime). Diese erforderliche Komponente ist nur für die PowerShell Desktop-Edition gültig. +# CLRVersion = '' + +# Die für dieses Modul erforderliche Prozessorarchitektur ("Keine", "X86", "Amd64"). +# ProcessorArchitecture = '' + +# Die Module, die vor dem Importieren dieses Moduls in die globale Umgebung geladen werden müssen +# RequiredModules = @() + +# Die Assemblys, die vor dem Importieren dieses Moduls geladen werden müssen +# RequiredAssemblies = @() + +# Die Skriptdateien (PS1-Dateien), die vor dem Importieren dieses Moduls in der Umgebung des Aufrufers ausgeführt werden. +# ScriptsToProcess = @() + +# Die Typdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# TypesToProcess = @() + +# Die Formatdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# FormatsToProcess = @() + +# Die Module, die als geschachtelte Module des in "RootModule/ModuleToProcess" angegebenen Moduls importiert werden sollen. +# NestedModules = @() + +# Aus diesem Modul zu exportierende Funktionen. Um optimale Leistung zu erzielen, verwenden Sie keine Platzhalter und löschen den Eintrag nicht. Verwenden Sie ein leeres Array, wenn keine zu exportierenden Funktionen vorhanden sind. +FunctionsToExport = @() + +# Aus diesem Modul zu exportierende Cmdlets. Um optimale Leistung zu erzielen, verwenden Sie keine Platzhalter und löschen den Eintrag nicht. Verwenden Sie ein leeres Array, wenn keine zu exportierenden Cmdlets vorhanden sind. +CmdletsToExport = @() + +# Die aus diesem Modul zu exportierenden Variablen +# VariablesToExport = @() + +# Aus diesem Modul zu exportierende Aliase. Um optimale Leistung zu erzielen, verwenden Sie keine Platzhalter und löschen den Eintrag nicht. Verwenden Sie ein leeres Array, wenn keine zu exportierenden Aliase vorhanden sind. +AliasesToExport = @() + +# Aus diesem Modul zu exportierende DSC-Ressourcen +DscResourcesToExport = 'TfsAgent' + +# Liste aller Module in diesem Modulpaket +# ModuleList = @() + +# Liste aller Dateien in diesem Modulpaket +# FileList = @() + +# Die privaten Daten, die an das in "RootModule/ModuleToProcess" angegebene Modul übergeben werden sollen. Diese können auch eine PSData-Hashtabelle mit zusätzlichen von PowerShell verwendeten Modulmetadaten enthalten. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo-URI dieses Moduls +# HelpInfoURI = '' + +# Standardpräfix für Befehle, die aus diesem Modul exportiert werden. Das Standardpräfix kann mit "Import-Module -Prefix" überschrieben werden. +# DefaultCommandPrefix = '' + +} + diff --git a/src/xtfsDscAgent/xTfsDscAgent.psm1 b/src/xtfsDscAgent/xTfsDscAgent.psm1 index 91d822c..e8c5aa8 100644 --- a/src/xtfsDscAgent/xTfsDscAgent.psm1 +++ b/src/xtfsDscAgent/xTfsDscAgent.psm1 @@ -9,13 +9,15 @@ enum AuthMode{ ALT } [DscResource()] -class xTfsDscAgent { +class TfsAgent { [DscProperty(Key)] [string]$AgentFolder; [DscProperty(Mandatory)] [Ensure]$Ensure; [DscProperty(Mandatory)] [string] $serverUrl; + [DscProperty(Mandatory)] + [string] $CollectionName; [DscProperty()] [string] $filePath; # 2.117.2 @@ -32,7 +34,7 @@ class xTfsDscAgent { [DscProperty()] [bool] $AgentRunAsService = $false; [DscProperty()] - [string] $WorkFolder = "_work"; + [string] $WorkFolder = "_work"; [DscProperty()] [PsCredential] $AgentUser; [DscProperty()] @@ -45,10 +47,10 @@ class xTfsDscAgent { # thanks to the help from here: https://stackoverflow.com/questions/41618766/powershell-invoke-webrequest-fails-with-ssl-tls-secure-channel [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor ` [Net.SecurityProtocolType]::Tls11 -bor ` - [Net.SecurityProtocolType]::Tls - } + [Net.SecurityProtocolType]::Tls + } [void] Set() { - $this.prepearePowershell(); + $this.prepearePowershell(); $this.ToStringVerbose(); if ($this.Ensure -eq [Ensure]::Present) { $testResult = $this.getTestResult(); @@ -58,7 +60,7 @@ class xTfsDscAgent { if (!(Test-Path $this.AgentFolder) -or (Get-ChildItem $this.AgentFolder).Length -eq 0) { #install $zipPath = $this.AgentFolder + "\agent.zip"; - $downloadUri = $this.getAgentDownLoadUri($this.serverUrl, $this.AgentVersion, $this.AgentPlatform); + $downloadUri = $this.getAgentDownLoadUri("$($this.serverUrl)/$($this.CollectionName)/", $this.AgentVersion, $this.AgentPlatform); if (!$this.filePath) { $this.downloadAgent($downloadUri, $zipPath); } @@ -67,34 +69,36 @@ class xTfsDscAgent { Copy-Item -Path $this.filePath -Destination $zipPath; } $this.unpackAgentZip($zipPath); - $this.installAgent($this.getConfigurationString()); + $this.installAgent($this.getConfigurationString()); } } elseif (!$testResult.AgentVersionOkay) { - Write-Verbose ("The Agent Version isn't " + $this.AgentVersion); + Write-Verbose ("The Agent Version isn't " + $this.AgentVersion); #first uninstall current Agentversion $this.installAgent($this.getRemoveString()); Remove-Item $this.AgentFolder -Recurse -Force; - #then install again + #then install again $this.Set(); - } + } elseif (!$testResult.AgentNameOkay -or !$testResult.AgentWorkFolderOkay -or !$testResult.AgentUrlOkay) { Write-Verbose ("The Agent isn't configured rigth."); #here we must remove the agent $this.installAgent($this.getRemoveString()); - $this.installAgent($this.getConfigurationString()); - } + $this.installAgent($this.getConfigurationString()); + } #If the agent is configure as Service the agent starting after config automatic if (!$this.AgentRunAsService) { Write-Verbose "Try to start agent, because it isn't a Windows service." - $this.startAgent(); + $this.startAgent(); } else { Write-Verbose "Don't start the agent, because the windows service start automatic."; - } + } } else { #uninstall + $removestring = $this.getRemoveString(); + Write-Verbose $removestring; $this.installAgent($this.getRemoveString()); Remove-Item $this.AgentFolder -Recurse -Force; } @@ -104,6 +108,7 @@ class xTfsDscAgent { $this.ToStringVerbose(); $testResult = $this.getTestResult(); $isPresent = $true; + $currentConfig = $this.Get(); if (!$testResult.AgentFolderOkay) { Write-Verbose "The AgentFolder doesn't exsists"; $isPresent = $false; @@ -120,8 +125,12 @@ class xTfsDscAgent { Write-Verbose ("The Agent Workfolder isn't " + $this.WorkFolder); $isPresent = $false; } + if([string]::IsNullOrWhiteSpace($currentConfig.serverUrl) -eq $true){ + Write-Verbose "The agent is extracted but not registered." + $isPresent = $false; + } if (!$testResult.AgentUrlOkay) { - Write-Verbose ("The Agent hasn't the '" + $this.serverUrl + "' as TFS / VSTS Url configured."); + Write-Verbose ("The Agent hasn't the '" + $this.serverUrl + "' as TFS / VSTS Url configured. It has $($currentConfig.serverUrl)"); $isPresent = $false; } @@ -132,12 +141,12 @@ class xTfsDscAgent { return -not $isPresent; } } - [xTfsDscAgent]Get() { + [TfsAgent]Get() { $this.prepearePowershell(); $this.ToStringVerbose(); - $result = [xTfsDscAgent]::new(); - $result.AgentFolder = $this.AgentFolder - $result.ReplaceAgent = $false + $result = [TfsAgent]::new(); + $result.AgentFolder = $this.AgentFolder + $result.ReplaceAgent = $false $agentJsonpath = $this.AgentFolder + "\.agent"; if (Test-Path $agentJsonpath) { $agentJsonFile = ConvertFrom-Json -InputObject (Get-Content $agentJsonpath -Raw); @@ -152,10 +161,10 @@ class xTfsDscAgent { } return $result; } - [void]ToStringVerbose(){ - $propertyNames = $this | Get-Member | ?{$_.MemberType -like "Property"} | %{$_.Name}; - $propertyNames | %{Write-Verbose $_}; - $propertyNames | %{Select-Object -InputObject $this -ExpandProperty $_} | %{Write-Verbose $_ -Verbose}; + [void]ToStringVerbose(){ + $propertyNames = $this | Get-Member | Where-Object{$_.MemberType -like "Property"} | ForEach-Object{$_.Name}; + $propertyNames | ForEach-Object{Write-Verbose $_}; + $propertyNames | ForEach-Object{Select-Object -InputObject $this -ExpandProperty $_} | ForEach-Object{Write-Verbose $_ -Verbose}; } [PSCustomObject] getTestResult() { $result = [PSCustomObject]@{ @@ -174,22 +183,30 @@ class xTfsDscAgent { } else { $result.AgentNameOkay = $getResult.AgentName -eq $this.AgentName; - } + } $result.AgentWorkFolderOkay = $this.WorkFolder -eq $getResult.WorkFolder; + if($this.serverUrl[$this.serverUrl.Length -1] -ne "/"){ + Write-Verbose "We change the serverURL and add an '/'."; + $this.serverUrl = "$($this.serverUrl)/"; + } $result.AgentUrlOkay = $this.serverUrl -eq $getResult.serverUrl; - Write-Verbose ("The Version: " + $getResult.AgentVersion + " Should be: " + $this.AgentVersion); + if ($this.AgentVersion -eq "latest") { - if ($this.serverUrl.Length -gt 0) { - $versionObject = $this.getLatestVersion($this.getAllAgentThatAreAvabiled($this.serverUrl), $this.AgentPlatform).version; + if ("$($this.serverUrl)/$($this.CollectionName)/".Length -gt 2) { + $versionObject = $this.getLatestVersion($this.getAllAgentThatAreAvabiled("$($this.serverUrl)/$($this.CollectionName)/"), $this.AgentPlatform).version; $version = $versionObject.major.ToString() + "." + $versionObject.minor.ToString() + "." + $versionObject.patch.ToString(); - $result.AgentVersionOkay = $getResult.AgentVersion -eq $version; + $result.AgentVersionOkay = $getResult.AgentVersion.Trim() -like $version.Trim(); + if($result -eq $false){ + Write-Verbose "The agent version isn't $version. It is $($getResult.AgentVersion)"; + } } else { $result.AgentVersionOkay = $false; - } + } } else { - # this doesn't working. Why? + Write-Verbose ("The Version: " + $getResult.AgentVersion + " Should be: " + $this.AgentVersion); + # this doesn't working. Why? $result.AgentVersionOkay = $this.AgentVersion -eq $getResult.AgentVersion; } return $result; @@ -197,17 +214,14 @@ class xTfsDscAgent { [void] installAgent([string] $configureString) { $configureStringPasswordObfuscated = $configureString -replace '(?<=--windowslogonpassword \s*).*?(?= --)', '*****' - Write-Verbose ("Configure Agent with this parameters:" + $configureStringPasswordObfuscated); - $fullString = ($this.AgentFolder + "\config.cmd") +" " + $configureString; - $bytes = [System.Text.Encoding]::Unicode.GetBytes($fullString) - #$encodedCommand = [Convert]::ToBase64String($bytes) + Write-Verbose ("Configure Agent with this parameters:" + $configureStringPasswordObfuscated); Write-Verbose ("Start installation: " + (Get-Date)); cmd /c "$($this.AgentFolder)\config.cmd $configureString"; Write-Verbose ("Installation completed: $(Get-Date)"); } - [void] startAgent() { - $startProgrammPath = $this.AgentFolder + "run.cmd"; - Invoke-Command -ScriptBlock {Start-Process $args[0]} -ArgumentList $startProgrammPath -InDisconnectedSession -ComputerName localhost + [void] startAgent() { + $startProgrammPath = $this.AgentFolder + "run.cmd"; + Invoke-Command -ScriptBlock {Start-Process $args[0]} -ArgumentList $startProgrammPath -InDisconnectedSession -ComputerName localhost Write-Verbose "Start sucess"; } [string] getRemoveString() { @@ -218,7 +232,7 @@ class xTfsDscAgent { } [string] getConfigurationString() { $configstring = ""; - $configstring += (" --url " + $this.serverUrl); + $configstring += (" --url " + "$($this.serverUrl)/$($this.CollectionName)/"); $configstring += (" --pool " + $this.AgentPool); $configstring += (" --work " + $this.WorkFolder); $configstring += $this.authString(); @@ -280,45 +294,45 @@ class xTfsDscAgent { [bool] checkIfCurrentAgentVersionIsInstalled() { $version = $this.AgentVersion; if ($this.AgentVersion -eq "latest") { - $versionObject = $this.getLatestVersion($this.getAllAgentThatAreAvabiled($this.serverUrl)).version; + $versionObject = $this.getLatestVersion($this.getAllAgentThatAreAvabiled("$($this.serverUrl)/$($this.CollectionName)/")).version; $version = $versionObject.major + "." + $versionObject.minor + "." + $versionObject.patch; } return $false; # we must find a way to do this! } - + [void] unpackAgentZip([string] $zipPath) { Expand-Archive -Path $zipPath -DestinationPath $this.AgentFolder Remove-Item $zipPath } - [void] downloadAgent([string] $url, [string] $zipPath) { + [void] downloadAgent([string] $url, [string] $zipPath) { Invoke-WebRequest $url -OutFile $zipPath -UseBasicParsing -Verbose; } - [string] getAgentDownLoadUri([string] $serverUrl, [string] $version, [string] $platfrom) { + [string] getAgentDownLoadUri([string] $serverUrl, [string] $version, [string] $platfrom) { $allagents = $this.getAllAgentThatAreAvabiled($serverUrl); if ($version -eq "latest") { - return $this.getLatestVersion($allagents, $platfrom).downloadUrl; + return $this.getLatestVersion($allagents, $platfrom).downloadUrl; } else { return $this.getspecifivVersion($allagents, $version, $platfrom).downloadUrl; - } + } } [PsCustomObject] getLatestVersion([PSCustomObject] $agents, [string] $platfrom) { - return ($agents | - Where-Object {$_.type -eq "agent" -and $_.platform -eq $platfrom} | + return ($agents | + Where-Object {$_.type -eq "agent" -and $_.platform -eq $platfrom} | Sort-Object createdOn -Descending)[0]; } [PsCustomObject] getspecifivVersion([PsCustomObject] $agents, [string] $version, [string] $platform) { $splitedVersion = $version.Split(".") - [Array]$result = $agents | + [Array]$result = $agents | Where-Object {$_.type -eq "agent" -and $_.platform -eq $platfrom -and $_.version.major -eq $splitedVersion[0] -and $_.version.minor -eq $splitedVersion[1] -and $_.version.patch -eq $splitedVersion[2] }; if ($result.Length -eq 0) { throw "version are not found! Maybe it is not compatible with your TFS version!"; } - return ($result | Sort-Object createdOn -Descending)[0]; + return ($result | Sort-Object createdOn -Descending)[0]; } [PSCustomObject] getAllAgentThatAreAvabiled([string] $serverUrl) { - $agentVersionsUrl = $serverUrl + "_apis/distributedTask/packages/agent"; + $agentVersionsUrl = "$($this.serverUrl)/$($this.CollectionName)/" + "_apis/distributedTask/packages/agent"; $webResult = Invoke-WebRequest $agentVersionsUrl -Credential $this.AgentUser -UseBasicParsing; $agentJson = ConvertFrom-Json -InputObject $webResult.Content; return $agentJson.value;