Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not possible to add user/pass as global to invoke PowerShell script with specific credentials #448

Closed
LLHogia opened this issue Oct 16, 2023 · 4 comments
Labels
feature request Categorizes issue or PR as related to a new feature or enhancement.

Comments

@LLHogia
Copy link

LLHogia commented Oct 16, 2023

Description

I have tried the below YML to execute a PowerShell script with a specific user and password:

integrations:
  - name: nri-flex
    config:
      name: TEST_winPerfMon
      global:
        user: testdomain\MonitorUser
        pass: HIDDEN
      apis:
        - event_type: perfmonCounters
          shell: powershell
          commands:
            - run: "& \"C:/Program Files/New Relic/newrelic-infra/integrations.d/test_windows-perfmon-script.ps1\""

I'm not 100% this is the correct syntax. I have tried putting the globals below "name: nri-flex" as well. Same result!

Error message in New Relic:
image

No errors in the newrelic-infra.log file. The integrations returns "Integration health check finished with success".

Expected Behavior

Should be possible to pass a username and password to a PowerShell script to invoke that script with the specified credentials.

Steps to Reproduce

  1. Use the YML provided in the description
  2. Use this PowerShell script: https://github.com/newrelic/nri-flex/blob/master/examples/windows/windows-perfmon-script.ps1
  3. Enable PSRemoting on the target machine you want to fetch performance counters from.
  4. Edit the script (windows-perfmon-script.ps1) to get data from a specific machine. This can be done by adding \\COMPUTERNAME\PerformanceCounterPath to the CounterPath attribut. Or just add the computer name as a parameter to the Get-Counter method, like: (Get-Counter -MaxSamples 1 -Counter $c.CounterPath -ComputerName COMPUTERNAME).CounterSamples

Example 1

$results = @()

$counters = @(
    [ PSCustomObject ]@{ CounterName = "percentProcessorTime"; CounterPath = "\Processor(*)\% Processor Time" }
    [ PSCustomObject ]@{ CounterName = "memoryPercentCommitedBytes"; CounterPath = "\Memory\% Committed Bytes In Use" }
    [ PSCustomObject ]@{ CounterName = "diskWritesPerSecond"; CounterPath = "\PhysicalDisk(*)\Disk Writes/sec" }
)

# Iterate through our target counters and grab our results
foreach( $c in $counters ) {
    $query = (Get-Counter -MaxSamples 1 -Counter $c.CounterPath -ComputerName MRAPP01).CounterSamples
    foreach( $q in $query ) {       
        $item = @(
            [ PSCustomObject ]@{ CounterName = $c.CounterName; CounterPath = $c.CounterPath; CounterInstance = $q.InstanceName; CounterValue = $q.CookedValue }
        )

        $results += $item 
    }
}

$results | ConvertTo-Json

Example 2

$results = @()

$counters = @(
    [ PSCustomObject ]@{ CounterName = "percentProcessorTime"; CounterPath = "\\COMPUTERNAME\Processor(*)\% Processor Time" }
    [ PSCustomObject ]@{ CounterName = "memoryPercentCommitedBytes"; CounterPath = "\\COMPUTERNAME\Memory\% Committed Bytes In Use" }
    [ PSCustomObject ]@{ CounterName = "diskWritesPerSecond"; CounterPath = "\\COMPUTERNAME\PhysicalDisk(*)\Disk Writes/sec" }
)

# Iterate through our target counters and grab our results
foreach( $c in $counters ) {
    $query = (Get-Counter -MaxSamples 1 -Counter $c.CounterPath).CounterSamples
    foreach( $q in $query ) {       
        $item = @(
            [ PSCustomObject ]@{ CounterName = $c.CounterName; CounterPath = $c.CounterPath; CounterInstance = $q.InstanceName; CounterValue = $q.CookedValue }
        )

        $results += $item 
    }
}

$results | ConvertTo-Json

Workaround

I want to be able to get performance counters from different machines. This is because we're using a Windows Failover Cluster. So we don't work with unique server names. All performance counters should be targeted via the cluster node name.

  1. Enable PSRemoting on each machine within the cluster.
  2. Create a TXT file with the below examples:
CLUSTERROLENAME2,\pti.a.RDA_PTPERF.roi(RDA_PTPERF)\FrequencyOfReceivedMessages
CLUSTERROLENAME1,\UDPv4\Datagrams Received/sec
  1. Create a standard YML file for the integrations and call the PowerShell script. You can use the YML from the description (remove global).

  2. Create the PowerShell script you're calling from the YML file. Since New Relic Infrastructure Agent is running as Local System we need to invoke a command with other credentials. This is why I want to be able to pass the username and password via the YML file so I don't need to invoke from the PowerShell script. See our example below:

$username = 'testdomain\MonitorUser'
$password = 'HIDDEN'

# Construct the PSCredential object
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword

# Define the path to the folder containing TXT files
$folderPath = "C:\Program Files\New Relic\newrelic-infra\integrations.d\Performance Counters"

# Get all TXT files within the folder
$txtFiles = Get-ChildItem $folderPath -Filter *.txt

# Initialize the result array
$results = @()

# Loop through each TXT file
foreach ($txtFile in $txtFiles) {
    # Read the content of the file and split it into lines
    $counterList = Get-Content $txtFile.FullName

    # Loop through each line in the file
    foreach ($line in $counterList) {
        # Split the line into hostname and counter path
        $hostname, $counterPath = $line -split ','

        # Construct the ScriptBlock with the dynamic counter path
        $scriptBlock = {
            param (
                [string]$counterPath
            )
            (Get-Counter -Counter $counterPath).CounterSamples[0].RawValue
        }

        # Invoke the command with the dynamic values
        $result = Invoke-Command -ComputerName $hostname -ScriptBlock $scriptBlock -Credential $credential -ArgumentList $counterPath

        # Append the result to the results array
        $results += [PSCustomObject]@{
            CounterCluster = $hostname
            CounterPath = $counterPath
            CounterValue = $result[0]
            CounterFileName = $txtFile.Name  # Add the filename to the output
        }
    }
}

# Convert the results array to JSON
$results | ConvertTo-Json

Your Environment

Operating System: Windows Server 2019 OS Build 17763.4851
New Relic Infrastructure Agent: 1.47.2

Additional context

N/A

@LLHogia LLHogia added the bug Categorizes issue or PR as related to a bug. label Oct 16, 2023
@workato-integration
Copy link

@LLHogia
Copy link
Author

LLHogia commented Oct 18, 2023

One solution could be to add native support for performance counters in YML.

Something like this:

integrations:
  - name: nri-flex
    interval: 120s
    config:
      name: getPerformanceCounter
      global:
        user: WindowsOrDomainUserWithAdminAccess
        pass: password123
      apis:
        - event_type: getPerformanceCounter
          performance_counters:
            - \\ServerA\UDPv4\Datagrams No Port/sec
            - \\ServerB\UDPv4\Datagrams Sent/sec

Or something like this:

integrations:
  - name: nri-flex
    interval: 120s
    config:
      name: getPerformanceCounter
      global:
        user: WindowsOrDomainUserWithAdminAccess
        pass: password123
      apis:
        - event_type: getPerformanceCounter
          performance_counters:
            - name: Datagrams No Port/sec
              counter: \\ServerA\UDPv4\Datagrams No Port/sec
              
            - name: Datagrams Sent/sec
              counter: \\ServerB\UDPv4\Datagrams Sent/sec

The problem with my workaround is the execution time. Getting 20 counters from different machines takes 15-20 seconds. I assume this is a lot faster if not doing it via an external PowerShell file which also invokes a command as another user.

@josemore josemore added feature request Categorizes issue or PR as related to a new feature or enhancement. and removed bug Categorizes issue or PR as related to a bug. labels Nov 14, 2023
@josemore
Copy link
Contributor

There's no plan to include perfcounters in Flex. An alternative is the OTel collector receiver: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/windowsperfcountersreceiver that can be used to push metrics directly to the OTLP endpoint.

@LLHogia
Copy link
Author

LLHogia commented Oct 10, 2024

There's no plan to include perfcounters in Flex. An alternative is the OTel collector receiver: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/windowsperfcountersreceiver that can be used to push metrics directly to the OTLP endpoint.

@josemore
Thanks for your suggestion. However, we still would like to pass credentials from the Flex script. I mean, Flex should be able to invoke a command as a specific user.

OTel collector receiver is just a workaround for the Performance Counter example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Categorizes issue or PR as related to a new feature or enhancement.
Projects
None yet
Development

No branches or pull requests

2 participants