DscResources/VstsAgent/VstsAgent.psm1


function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [ValidateSet("Present", "Absent")]
        [string]$Ensure = "Present",

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ServerUrl,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$VstsPool,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$PoolId,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$VstsAccount,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$RunAsAccount,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Token,

        [ValidateSet("Running", "Stopped")]
        [string]$State = "Running",

        [string]$Version
    )

    if ((Test-Path "$Path/.agent") -Or ($Ensure -eq "Absent")) {
        &"$Path/config" remove --unattended --auth pat --token $Token
    }

    if ($Ensure -eq "Present") {
        Write-Verbose "Downloading Agent..."
        DownloadAgent $Version $Path
        UnzipAgent "$Path\vsts-agent-$Version.zip"

        Write-Verbose "Installing Agent..."
        InstallAgent $Path $Name $ServerUrl $VstsPool $RunAsAccount $Token
    }

    if ($State -ne "Running") {
        Stop-Service "VSTS Agent ($VstsAccount.$Name)"
    }
}

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        [ValidateSet("Present", "Absent")]
        [string]$Ensure = "Present",

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ServerUrl,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$VstsPool,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$PoolId,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$VstsAccount,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$RunAsAccount,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Token,

        [ValidateSet("Running", "Stopped")]
        [string]$State = "Running",

        [string]$Version
    )

    if (Test-Path "$Path/.agent") {
        $agentFile = ConvertFrom-Json -InputObject (Get-Content "$Path/.agent" -Raw)
    }

    $agent = Get-Wmiobject win32_service | Where-Object { $_.Name -eq "VSTS Agent ($VstsAccount.$Name)" }
    
    if ($agent -eq $null) {
        $agentStatus = "Stopped"
    }
    else {
        $agentStatus = $agent.State
    }

    return @{
        Path         = $Path
        RunAsAccount = $agent.StartName
        Version      = (&"$Path/config" --version)
        PoolId       = $agentFile.poolId
        ServerUrl    = $agentFile.serverUrl
        Name         = $agentFile.agentName
    }
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [ValidateSet("Present", "Absent")]
        [string]$Ensure = "Present",

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ServerUrl,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$VstsPool,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$PoolId,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$VstsAccount,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$RunAsAccount,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Token,

        [ValidateSet("Running", "Stopped")]
        [string]$State = "Running",

        [string]$Version
    )

    $agent = Get-Wmiobject win32_service | Where-Object { $_.Name -eq "vstsagent.$VstsAccount.$Name" }

    if (($agent -ne $null) -And (Test-Path "$Path/.agent")) {
        $agentFile = ConvertFrom-Json -InputObject (Get-Content "$Path/.agent" -Raw)

        if ($agent.State -ne $State) {
            Write-Verbose "Agent $($agentFile.agentName) found but service is not $($State)."
            return $false
        } 
        elseif ($agent.StartName -ne $RunAsAccount) {
            Write-Verbose "Agent $($agentFile.agentName) found but service account is not $($RunAsAccount)."
            return $false
        }
        elseif ($agentFile.poolId -ne $PoolId) {
            Write-Verbose "Agent $($agentFile.agentName) found but agent pool is not $($VstsPool)."
            return $false
        }
        elseif ($agentFile.agentName -ne $Name) {
            Write-Verbose "Agent exists but agent name is not $($Name)."
            return $false
        }
        elseif ($agentFile.serverUrl -ne $ServerUrl) {
            Write-Verbose "Agent $($agentFile.agentName) found but server url is not $($ServerUrl)."
            return $false
        }
        elseif ([String]::IsNullOrWhiteSpace($Version)) {
            Write-Verbose "Agent $($agentFile.agentName) found with matching settings."
            return $true
        }
        else {
            return ($Version -eq (&"$Path/config" --version))
        } 
    }

    Write-Verbose "Agent $($agentFile.agentName) not found."
    return $false
}

function DownloadAgent($Version, $Path)
{
    if (!(Test-Path $Path)) {
        [void](New-Item $Path -ItemType "Directory")
    }

    $VstsAgentDownloadUrl = "https://vstsagentpackage.azureedge.net/agent/{0}/vsts-agent-win-x64-{0}.zip"

    add-type `
@"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@

    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

    $Null = Invoke-WebRequest -Uri [String]::Format($VstsAgentDownloadUrl, $Version), "$($Path)\vsts-agent-$($Version).zip" -OutFile "$($Path)\vsts-agent-$($Version).zip"
}

function UnzipAgent($Path)
{
    Add-Type -AssemblyName System.IO.Compression.FileSystem 
    Get-ChildItem -Path (Split-Path $Path -Parent) -Exclude (Split-Path $Path -Leaf) | Remove-Item -Recurse -Force
    [System.IO.Compression.ZipFile]::ExtractToDirectory($Path, (Split-Path $Path -Parent))
}

function InstallAgent($Path, $Name, $ServerUrl, $AgentPool, $ServiceAccount, $Token)
{
    &"$Path\config.cmd" --unattended --url $ServerUrl --auth pat --token $Token --pool $AgentPool --agent $Name --runAsService --windowsLogonAccount $ServiceAccount
}