NewRelicPS.InfraAgent.psm1

Using module '.\NewRelicPS.Agents.psm1'
Using module '.\NewRelicPS.Configuration.GetFromYAML.psm1'

<#
.Synopsis
    Installs the New Relic Infrastructure Agent
.Description
    Installs the New Relic Infrastructure Agent
.Example
    Install-NRInfraAgent -LicenseKey <LicenseKey>
    Installs, licenses and starts the New Relic Infrastructure agent.
.Example
    Install-NRInfraAgent -LicenseKey <LicenseKey> -Version '1.9.7'
    Installs, licenses and starts the New Relic Infrastructure agent version 1.9.7
.Parameter LicenseKey
    New Relic License Key that links the agent to the account.
.Parameter Version
    If provided, attempts to install a specific version of the agent otherwise falls back to the default managed by New Relic.
#>

Function Install-NRInfraAgent {
    [CMDLetBinding()]
    Param (
        [Parameter (Mandatory = $true)]
        [string] $LicenseKey,
        [Parameter (Mandatory = $false)]
        [string] $Version = ''
    )
    # Must run as an admin to install and start the service...
    Confirm-RunAsAdmin

    $AgentRootURL = 'https://download.newrelic.com/infrastructure_agent/windows/'

    if ($Version -ne '') {
        Write-Output "Using Version: $Version"
        $AgentName = "newrelic-infra.$Version.msi"
    }
    else {
        # Use Default version updated by New Relic
        $AgentName = 'newrelic-infra.msi'
    }

    $AgentURL = "$AgentRootURL$AgentName"
    $LocalPath = $env:Temp

    # Check for and create temp folder
    If (!(Test-Path $LocalPath)) {
        Throw "Temp path $LocalPath not found..."
    }

    # Download
    Write-Output "Downloading Agent: $AgentURL"
    Invoke-RestMethod -ContentType 'application/octet-stream' -Uri $AgentURL -OutFile "$LocalPath\$AgentName"

    # Install and Wait
    Write-Output "Installing Agent: $AgentName"
    Start-Process 'C:\Windows\System32\msiexec.exe' -ArgumentList "/qn /i $LocalPath\$AgentName GENERATE_CONFIG=true LICENSE_KEY=$LicenseKey" -wait

    # Start Service
    Start-Service newrelic-infra

    # Remove Temp Agent File
    Write-Output "Cleaning up Agent Files: $LocalPath\$AgentName"
    Remove-Item "$LocalPath\$AgentName" -Force

    # Write output when finished
    Write-Output 'Finished Install'
}

<#
.Synopsis
    Updates the New Relic Infrastructure Agent
.Description
    Updates the New Relic Infrastructure Agent to a specific version
.Example
    Update-NRInfraAgent -Version '1.55.0'
    Checks the existing agent version and if it doesn't match updates to 1.55.0
.Parameter Version
    Installs a specific version of the agent. Please review the release notes before updating.
#>

Function Update-NRInfraAgent {
    [CMDLetBinding(SupportsShouldProcess = $true)]
    Param (
        [Parameter (Mandatory = $true)]
        [string] $Version
    )
    # Must run as an admin to install and start the service...
    Confirm-RunAsAdmin

    $AgentRootURL = 'https://download.newrelic.com/infrastructure_agent/windows/'

    if ($Version -ne '') {
        Write-Output "Updating to Version: $Version"
        $AgentName = "newrelic-infra.$Version.msi"
    }
    else {
        Throw 'You must provide a valid version to update'
    }

    $AgentURL = "$AgentRootURL$AgentName"
    $LocalPath = $env:Temp

    # Check for and create temp folder
    If (!(Test-Path $LocalPath)) {
        Throw "Temp path $LocalPath not found..."
    }

    $agentDetails = Get-NRInfraAgent

    if($agentDetails.'New Relic Infrastructure Agent version' -eq $Version){
        Write-Output "Version $Version already installed..."
    }else{
        Write-Output "Currently running version: $($agentDetails.'New Relic Infrastructure Agent version')"
        if ($PSCmdlet.ShouldProcess('New Relic Infrastructure Agent',"Update to $Version")) {
            # Download
            Write-Output "Downloading Agent: $AgentURL"
            Invoke-RestMethod -ContentType 'application/octet-stream' -Uri $AgentURL -OutFile "$LocalPath\$AgentName"

            # Install and Wait
            Write-Output "Installing Agent: $AgentName"
            Start-Process 'C:\Windows\System32\msiexec.exe' -ArgumentList "/qn /i $LocalPath\$AgentName" -wait

            # Start Service
            Start-Service newrelic-infra

            # Remove Temp Agent File
            Write-Output "Cleaning up Agent Files: $LocalPath\$AgentName"
            Remove-Item "$LocalPath\$AgentName" -Force

            # Write output when finished
            Write-Output 'Finished Updating'
        }
    }
}

<#
.Synopsis
    Gets details on the New Relic infrastructure agent running on the machine.
.Description
    Gets details on the New Relic infrastructure agent running on the machine.
.Example
    Get-NRInfraAgent
    Returns details on the infrastructure agent
#>

Function Get-NRInfraAgent {
    # Doesn't use direct user input. Needs to be rewritten but native functionality for getting output from a process is limited.
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '')]
    [CMDLetBinding()]
    [OutputType([System.Collections.Hashtable])]
    Param (
    )
    $Agent = "$ENV:ProgramFiles\New Relic\newrelic-infra\newrelic-infra.exe"

    # Validate Agent is present
    if(!(Test-Path $Agent)){
        Throw "Agent not found at: $Agent"
    }

    # Query Agent
    $response = Invoke-Expression "& `"$Agent`" --version"

    # Check Output
    if($null -eq $response){
        Throw "Failed to get output from agent at: $Agent"
    }

    # Response Object
    $data = @{}

    # Parse out the response into Key Value Pairs
    $kvs = $response.split(',')
    foreach ($kv in $kvs) {
        $parts = $kv.split(':')
        $key = $parts[0].Trim()
        $value = ($parts[1..($parts.count -1)] -join '').Trim()
        $data.Add($key, $value)
    }
    return $data
}

<#
.Synopsis
    Gets the settings used by the New Relic Infrastructure agent from its yml file.
.Description
    Gets the settings used by the New Relic Infrastructure agent from its yml file.
.Example
    Get-NRInfraAgentSetting
    Gets the current settings from the yml file.
#>

Function Get-NRInfraAgentSetting {
    [CMDLetBinding()]
    Param (
    )
    $configFile = "$ENV:ProgramFiles\New Relic\newrelic-infra\newrelic-infra.yml"

    if(!(Test-Path $configFile)){
        Throw "New Relic Infrastructure Agent Config file not found at $configFile. Make sure the agent is installed!"
    }

    return Get-NRConfigFromYAML $configFile
}

<#
.Synopsis
    Sets the settings used by the New Relic Infrastructure agent in its yml file.
.Description
    Sets the settings used by the New Relic Infrastructure agent in its yml file. Merges with existing settings, adds or updates matching.
.Example
    $Settings = @{}
    $Settings.Add('enable_process_metrics', $true)
    Set-NRInfraAgentSetting -Settings $Settings
.Parameter Settings
    Hashtable of settings to update.
#>

Function Set-NRInfraAgentSetting {
    [CMDLetBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    Param (
        [Parameter (Mandatory = $true)]
        [Hashtable] $Settings
    )
    # Must run as an admin to install and start the service...
    Confirm-RunAsAdmin

    # Get Current Settings
    $config = Get-NRInfraAgentSetting

    # Combine Settings
    $newConfig = Join-HashTable -Base $config -Override $Settings -Verbose

    # Save Settings
    $configFile = "$ENV:ProgramFiles\New Relic\newrelic-infra\newrelic-infra.yml"
    $Content = ConvertTo-Yaml $newConfig

    if ($PSCmdlet.ShouldProcess('Infra Agent Settings', 'Save')) {
        Set-Content -Path $configFile -Value $Content
    }

    # Restart Agent to pick up settings
    Restart-Service 'newrelic-infra'
}