From b3d80b35aca76d9708f4c3d84896652eceabde06 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 22:26:44 +0530 Subject: [PATCH 01/34] feat(nrdot-mssql): Added the automation cli for mssql otel --- .../nrdot/mssql-otel/windows.yml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml new file mode 100644 index 000000000..853032334 --- /dev/null +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -0,0 +1,67 @@ +# Visit our schema definition for additional information on this file format. +# https://github.com/newrelic/open-install-library/blob/main/docs/recipe-spec/recipe-spec.md#schema-definition + +name: nrdot-collector-mssql +displayName: NRDOT MSSQL (Windows) +description: New Relic install recipe for MSSQL monitoring via NRDOT Collector on Windows self-hosted environments +repository: https://github.com/newrelic/nrdot-collector-releases + +installTargets: + - type: host + os: windows + +keywords: + - MSSQL + - SQL Server + - NRDOT + - OpenTelemetry + - OTel + - Database + - Windows + +processMatch: + - sqlservr + +preInstall: + discoveryMode: + - targeted + info: | + To capture data from SQL Server, we need to create/authorize a monitoring identity + (a SQL login, a Windows account, or a gMSA account depending on the authentication + method you choose). This requires administrative access to your SQL Server instance + (sysadmin role or equivalent) on the account running this installation. + +inputVars: + - name: NR_CLI_MSSQL_CONFIG_PRESET + prompt: "NRDOT configuration - 1) Standard 2) Full-feature: " + default: "1" + - name: NR_CLI_MSSQL_AUTH_MODE + prompt: "SQL Server authentication - 1) SQL Server Auth 2) Windows Auth 3) gMSA: " + default: "1" + - name: NR_CLI_MSSQL_SERVER + prompt: "SQL Server host (e.g. localhost): " + default: "localhost" + - name: NR_CLI_MSSQL_PORT + prompt: "SQL Server port: " + default: "1433" + - name: NR_CLI_MSSQL_SA_PASSWORD + prompt: "SQL Server 'sa' password (mode 1 only, used once to create the newrelic login; leave blank otherwise): " + secret: true + - name: NR_CLI_MSSQL_WIN_ACCOUNT + prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " + - name: NR_CLI_MSSQL_WIN_PASSWORD + prompt: "Password for that Windows account (mode 2 only; leave blank otherwise): " + secret: true + - name: NR_CLI_MSSQL_GMSA_ACCOUNT + prompt: "gMSA account, DOMAIN\\gMSAName$ (mode 3 only; leave blank otherwise): " + +validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'sqlserver.%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" + +successLinkConfig: + type: EXPLORER + +install: + version: "3" + silent: true + + tasks: {} \ No newline at end of file From ba296530f25ab76419f00875d4979dd29bd2e301 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 23:13:24 +0530 Subject: [PATCH 02/34] add windows.yml preflight tasks (admin/sqlcmd check, auth input validation, SQL Server version gate) --- .../nrdot/mssql-otel/windows.yml | 92 ++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 853032334..219bf0702 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -64,4 +64,94 @@ install: version: "3" silent: true - tasks: {} \ No newline at end of file + tasks: + write_recipe_metadata: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $metadata = '{"Metadata":{"CapturedCliOutput":"true"}}' + try { $metadata | Set-Content {{.NR_CLI_OUTPUT}} } catch {} + PSEOF + + assert_pre_req: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) + $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + if (-not $isAdmin) { + Write-Host -ForegroundColor Red "This newrelic install must be run in an Administrator PowerShell session." + exit 131 + } + + $sqlcmdPath = Get-Command sqlcmd.exe -ErrorAction SilentlyContinue + if (-not $sqlcmdPath) { + Write-Host -ForegroundColor Red "sqlcmd is required to configure SQL Server monitoring. Install the sqlcmd utility (https://learn.microsoft.com/sql/tools/sqlcmd/sqlcmd-utility) and re-run this recipe." + exit 16 + } + PSEOF + + assert_auth_inputs: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + + switch ($mode) { + "1" { + if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_SA_PASSWORD}}")) { + Write-Host -ForegroundColor Red "NR_CLI_MSSQL_SA_PASSWORD is required for SQL Server Auth (mode 1)." + exit 1 + } + } + "2" { + if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_WIN_ACCOUNT}}") -or [string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_WIN_PASSWORD}}")) { + Write-Host -ForegroundColor Red "NR_CLI_MSSQL_WIN_ACCOUNT and NR_CLI_MSSQL_WIN_PASSWORD are both required for Windows Auth (mode 2)." + exit 1 + } + } + "3" { + if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}")) { + Write-Host -ForegroundColor Red "NR_CLI_MSSQL_GMSA_ACCOUNT is required for gMSA (mode 3)." + exit 1 + } + } + default { + Write-Host -ForegroundColor Red "NR_CLI_MSSQL_AUTH_MODE must be 1, 2, or 3." + exit 1 + } + } + PSEOF + + assert_sql_server_version: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $server = "{{.NR_CLI_MSSQL_SERVER}}" + $port = "{{.NR_CLI_MSSQL_PORT}}" + + if ($mode -eq "1") { + $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}") + } else { + $connArgs = @("-S", "$server,$port", "-E") + } + + $query = "SET NOCOUNT ON; SELECT SERVERPROPERTY('ProductMajorVersion');" + $result = & sqlcmd @connArgs -h -1 -Q $query 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to connect to SQL Server to check its version:" + Write-Host $result + exit 1 + } + + $majorVersionLine = ($result | Where-Object { $_ -match '^\s*\d+\s*$' } | Select-Object -First 1) + $majorVersion = if ($majorVersionLine) { [int]$majorVersionLine.Trim() } else { $null } + + if (-not $majorVersion -or $majorVersion -lt 14) { + Write-Host -ForegroundColor Red "SQL Server version $majorVersion is not supported. SQL Server 2017 or later (major version 14+) is required." + exit 1 + } + + Write-Host "SQL Server major version $majorVersion detected - supported." + PSEOF \ No newline at end of file From a3b93e9bcff1b648d163aca026d3a550f4ad6640 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 23:16:31 +0530 Subject: [PATCH 03/34] add windows.yml install_nrdot task --- .../nrdot/mssql-otel/windows.yml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 219bf0702..57d738bb4 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -154,4 +154,71 @@ install: } Write-Host "SQL Server major version $majorVersion detected - supported." + PSEOF + + install_nrdot: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + [Net.ServicePointManager]::SecurityProtocol = "tls12, tls" + + $CollectorService = "nrdot-collector" + + $LatestVersion = (Invoke-RestMethod -Uri "https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest").tag_name + if (-not $LatestVersion) { + Write-Host -ForegroundColor Red "Failed to fetch the latest release version from GitHub. Please check your internet connection." + exit 1 + } + + $InstalledVersion = $null + $UninstallKeys = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -ErrorAction SilentlyContinue + foreach ($Key in $UninstallKeys) { + $KeyData = Get-ItemProperty -Path $Key.PSPath -ErrorAction SilentlyContinue + if ($KeyData.DisplayName -match "nrdot-collector") { + $InstalledVersion = $KeyData.DisplayVersion + break + } + } + + if ($InstalledVersion -and ("v$InstalledVersion" -eq $LatestVersion -or $InstalledVersion -eq $LatestVersion)) { + Write-Host "nrdot-collector $LatestVersion is already installed. Skipping download." + exit 0 + } elseif ($InstalledVersion) { + Write-Host "Upgrading nrdot-collector from $InstalledVersion to $LatestVersion" + } else { + Write-Host "Installing nrdot-collector version: $LatestVersion" + } + + $MsiPath = "$env:TEMP\nrdot-collector.msi" + $LogPath = "$env:TEMP\nrdot_install.log" + $DownloadUrl = "https://github.com/newrelic/nrdot-collector-releases/releases/download/$LatestVersion/nrdot-collector_${LatestVersion}_windows_x64.msi" + Write-Host "Downloading from: $DownloadUrl" + + $WebClient = New-Object System.Net.WebClient + $WebClient.Headers.Add("User-Agent", "Mozilla/5.0") + if ($env:HTTPS_PROXY) { + $WebClient.Proxy = New-Object System.Net.WebProxy($env:HTTPS_PROXY, $true) + } + try { + $WebClient.DownloadFile($DownloadUrl, $MsiPath) + } catch { + Write-Host -ForegroundColor Red "Failed to download the nrdot-collector package: $_" + exit 1 + } + + $Process = Start-Process -FilePath "msiexec.exe" -ArgumentList "/i `"$MsiPath`" /qn /norestart /L*V `"$LogPath`"" -Wait -NoNewWindow -PassThru + if ($Process.ExitCode -ne 0) { + Write-Host -ForegroundColor Red "msiexec failed with exit code $($Process.ExitCode). See $LogPath for details." + exit $Process.ExitCode + } + + Remove-Item -Path $MsiPath -ErrorAction SilentlyContinue + + $Service = Get-Service -Name $CollectorService -ErrorAction SilentlyContinue + if (-not $Service) { + Write-Host -ForegroundColor Red "nrdot-collector service was not found after installation." + exit 1 + } + + Write-Host "nrdot-collector installed successfully." PSEOF \ No newline at end of file From ceb7bddcfadf7c498cc5e23a219582a82390dfe8 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 23:19:37 +0530 Subject: [PATCH 04/34] add windows.yml configure_database_user task (SQL Auth, Windows Auth, gMSA) --- .../nrdot/mssql-otel/windows.yml | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 57d738bb4..b284a47b6 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -221,4 +221,161 @@ install: } Write-Host "nrdot-collector installed successfully." + PSEOF + + configure_database_user: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $server = "{{.NR_CLI_MSSQL_SERVER}}" + $port = "{{.NR_CLI_MSSQL_PORT}}" + $sqlFile = "$env:TEMP\nr-mssql-grant.sql" + $pwFile = "$env:TEMP\nr-mssql-newrelic-pw.tmp" + + function Invoke-GrantScript($connArgs, $file) { + $result = & sqlcmd @connArgs -i $file 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "SQL script failed:" + Write-Host $result + exit 1 + } + Write-Host $result + } + + if ($mode -eq "1") { + $chars = (48..57) + (65..90) + (97..122) + $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) + $NrPassword | Set-Content -Path $pwFile -NoNewline + + $sql = @" + USE [master]; + GO + IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = 'newrelic') + CREATE LOGIN [newrelic] WITH PASSWORD = '$NrPassword'; + ELSE + ALTER LOGIN [newrelic] WITH PASSWORD = '$NrPassword'; + GO + GRANT VIEW SERVER STATE TO [newrelic]; + GRANT VIEW ANY DEFINITION TO [newrelic]; + GRANT VIEW ANY DATABASE TO [newrelic]; + GO + DECLARE @name SYSNAME; + DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR + SELECT [name] + FROM [master].[sys].[databases] + WHERE [name] NOT IN ('master', 'msdb', 'model', 'rdsadmin', 'distribution') + AND [state] = 0; + OPEN db_cursor; + FETCH NEXT FROM db_cursor INTO @name; + WHILE @@FETCH_STATUS = 0 + BEGIN + BEGIN TRY + EXEC('USE [' + @name + ']; + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''newrelic'') + BEGIN + CREATE USER [newrelic] FOR LOGIN [newrelic]; + END; + GRANT VIEW DATABASE STATE TO [newrelic];'); + END TRY + BEGIN CATCH + PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); + END CATCH + FETCH NEXT FROM db_cursor INTO @name; + END + CLOSE db_cursor; + DEALLOCATE db_cursor; + GO + "@ + $sql | Set-Content -Path $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}") $sqlFile + } + elseif ($mode -eq "2") { + $account = "{{.NR_CLI_MSSQL_WIN_ACCOUNT}}" + $sql = @" + USE [master]; + GO + GRANT VIEW SERVER STATE TO [$account]; + GRANT VIEW ANY DEFINITION TO [$account]; + GRANT VIEW ANY DATABASE TO [$account]; + GO + DECLARE @name SYSNAME; + DECLARE @sql NVARCHAR(MAX); + DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR + SELECT [name] + FROM [master].[sys].[databases] + WHERE [name] NOT IN ('master', 'msdb', 'model', 'rdsadmin', 'distribution') + AND [state] = 0; + OPEN db_cursor; + FETCH NEXT FROM db_cursor INTO @name; + WHILE @@FETCH_STATUS = 0 + BEGIN + BEGIN TRY + SET @sql = ' + USE [' + @name + ']; + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''$account'') + BEGIN + CREATE USER [$account] FOR LOGIN [$account]; + END; + GRANT VIEW DATABASE STATE TO [$account]; + GRANT VIEW DEFINITION TO [$account]; + ALTER ROLE db_datareader ADD MEMBER [$account];'; + EXEC sp_executesql @sql; + END TRY + BEGIN CATCH + PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); + END CATCH + FETCH NEXT FROM db_cursor INTO @name; + END + CLOSE db_cursor; + DEALLOCATE db_cursor; + GO + "@ + $sql | Set-Content -Path $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-E") $sqlFile + } + else { + $gmsa = "{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}" + $sql = @" + USE master; + GO + IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = '$gmsa') + CREATE LOGIN [$gmsa] FROM WINDOWS; + GO + GRANT VIEW SERVER STATE TO [$gmsa]; + GRANT VIEW ANY DEFINITION TO [$gmsa]; + GRANT VIEW ANY DATABASE TO [$gmsa]; + GO + DECLARE @name SYSNAME; + DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR + SELECT [name] FROM [master].[sys].[databases] + WHERE [name] NOT IN ('master','msdb','model','rdsadmin','distribution') + AND [state] = 0; + OPEN db_cursor; + FETCH NEXT FROM db_cursor INTO @name; + WHILE @@FETCH_STATUS = 0 + BEGIN + BEGIN TRY + EXEC('USE [' + @name + ']; + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''$gmsa'') + BEGIN + CREATE USER [$gmsa] FOR LOGIN [$gmsa]; + END; + GRANT VIEW DATABASE STATE TO [$gmsa];'); + END TRY + BEGIN CATCH + PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); + END CATCH + FETCH NEXT FROM db_cursor INTO @name; + END + CLOSE db_cursor; + DEALLOCATE db_cursor; + GO + "@ + $sql | Set-Content -Path $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-E") $sqlFile + } + + Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue + Write-Host "SQL Server monitoring identity configured successfully." PSEOF \ No newline at end of file From f8269c1b5b31df794f4d88b5c36e47f33327ed07 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 23:24:54 +0530 Subject: [PATCH 05/34] add windows.yml configure_service_identity task --- .../nrdot/mssql-otel/windows.yml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index b284a47b6..bba97fe0d 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -378,4 +378,38 @@ install: Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue Write-Host "SQL Server monitoring identity configured successfully." + PSEOF + + configure_service_identity: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $service = "nrdot-collector" + + if ($mode -eq "1") { + Write-Host "SQL Server Auth selected - service logon account left unchanged (LocalSystem)." + exit 0 + } + + if ($mode -eq "2") { + $account = "{{.NR_CLI_MSSQL_WIN_ACCOUNT}}" + $password = "{{.NR_CLI_MSSQL_WIN_PASSWORD}}" + } else { + $account = "{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}" + $password = "" + } + + Stop-Service -Name $service -Force -ErrorAction SilentlyContinue + + cmd /c "sc config `"$service`" obj= `"$account`" password= `"$password`"" + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to configure the nrdot-collector service to run as $account." + exit 1 + } + + $svcInfo = Get-WmiObject Win32_Service -Filter "Name='$service'" + Write-Host "nrdot-collector service logon account set to: $($svcInfo.StartName)" + + Start-Service -Name $service PSEOF \ No newline at end of file From 17c106e430223650ec26cfddbb84fc34cdebe80a Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 23:39:49 +0530 Subject: [PATCH 06/34] add windows.yml create_collector_config task (Standard/Full-feature x 3 auth modes) --- .../nrdot/mssql-otel/windows.yml | 526 ++++++++++++++++++ 1 file changed, 526 insertions(+) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index bba97fe0d..a1610e8c6 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -412,4 +412,530 @@ install: Write-Host "nrdot-collector service logon account set to: $($svcInfo.StartName)" Start-Service -Name $service + PSEOF + + create_collector_config: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $preset = "{{.NR_CLI_MSSQL_CONFIG_PRESET}}" + $server = "{{.NR_CLI_MSSQL_SERVER}}" + $port = "{{.NR_CLI_MSSQL_PORT}}" + $region = "{{.NEW_RELIC_REGION}}" + $licenseKey = "{{.NEW_RELIC_LICENSE_KEY}}" + $configDir = "C:\Program Files\nrdot-collector" + $configPath = "$configDir\mssql-config.yaml" + $pwFile = "$env:TEMP\nr-mssql-newrelic-pw.tmp" + + New-Item -Path $configDir -ItemType Directory -Force | Out-Null + + $interval = if ($preset -eq "2") { "30s" } else { "15s" } + + if ($mode -eq "1") { + $NrPassword = (Get-Content -Path $pwFile -Raw) + $receiverFields = " collection_interval: $interval`n username: newrelic`n password: $NrPassword`n server: $server`n port: $port" + } else { + $receiverFields = " collection_interval: $interval`n datasource: `"server=$server;port=$port;integrated security=true;encrypt=true;TrustServerCertificate=true;`"" + } + + switch ($region) { + "staging" { $otlpEndpoint = "https://staging-otlp.nr-data.net" } + "EU" { $otlpEndpoint = "https://otlp.eu01.nr-data.net" } + "JP" { $otlpEndpoint = "https://otlp.jp.nr-data.net" } + default { $otlpEndpoint = "https://otlp.nr-data.net" } + } + + if ($preset -eq "2") { + $template = @' + extensions: + health_check: + + receivers: + otlp: + protocols: + grpc: + http: + + host_metrics: + collection_interval: 30s + scrapers: + cpu: + metrics: + system.cpu.time: + enabled: false + system.cpu.utilization: + enabled: true + load: + memory: + metrics: + system.memory.utilization: + enabled: true + paging: + metrics: + system.paging.utilization: + enabled: false + system.paging.faults: + enabled: false + disk: + metrics: + system.disk.merged: + enabled: false + system.disk.pending_operations: + enabled: false + system.disk.weighted_io_time: + enabled: false + network: + metrics: + system.network.connections: + enabled: false + processes: + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_user_error: true + mute_process_io_error: true + metrics: + process.cpu.utilization: + enabled: true + process.memory.utilization: + enabled: true + + nrsqlserver: + __RECEIVER_FIELDS__ + metrics: + sqlserver.database.count: + enabled: true + sqlserver.database.file.size: + enabled: true + sqlserver.database.io: + enabled: true + sqlserver.database.latency: + enabled: true + sqlserver.database.operations: + enabled: true + sqlserver.database.transactions.active: + enabled: true + sqlserver.database.backup_or_restore.rate: + enabled: true + sqlserver.database.execution.errors: + enabled: true + sqlserver.database.full_scan.rate: + enabled: true + sqlserver.database.tempdb.space: + enabled: true + sqlserver.database.tempdb.version_store.size: + enabled: true + sqlserver.lock.timeout.rate: + enabled: true + sqlserver.lock.wait.count: + enabled: true + sqlserver.deadlock.rate: + enabled: true + sqlserver.transaction.delay: + enabled: true + sqlserver.transaction.longest_running_time: + enabled: true + sqlserver.transaction.version_cleanup.rate: + enabled: true + sqlserver.transaction.version_generation.rate: + enabled: true + sqlserver.memory.area: + enabled: true + sqlserver.memory.cache.object.count: + enabled: true + sqlserver.memory.grants.pending.count: + enabled: true + sqlserver.memory.page.count: + enabled: true + sqlserver.memory.target: + enabled: true + sqlserver.memory.usage: + enabled: true + sqlserver.os.memory.usage: + enabled: true + sqlserver.os.memory.utilization: + enabled: true + sqlserver.os.scheduler.runnable_tasks.count: + enabled: true + sqlserver.os.wait.duration: + enabled: true + sqlserver.os.wait.tasks.count: + enabled: true + sqlserver.page.buffer_cache.free_list.stalls.rate: + enabled: true + sqlserver.page.lookup.rate: + enabled: true + sqlserver.batch.compilation.utilization: + enabled: true + sqlserver.batch.page_split.utilization: + enabled: true + sqlserver.recompilation.ratio: + enabled: true + sqlserver.parameterization.rate: + enabled: true + sqlserver.plan.execution.rate: + enabled: true + sqlserver.attention.rate: + enabled: true + sqlserver.process.count: + enabled: true + sqlserver.processes.blocked: + enabled: true + sqlserver.login.rate: + enabled: true + sqlserver.logout.rate: + enabled: true + sqlserver.thread_pool.tasks.count: + enabled: true + sqlserver.thread_pool.workers.count: + enabled: true + sqlserver.thread_pool.workers.max: + enabled: true + sqlserver.thread_pool.workers.utilization: + enabled: true + sqlserver.tempdb.allocation.wait_time.total: + enabled: true + sqlserver.tempdb.contention.waiters.count: + enabled: true + sqlserver.tempdb.data_files.count: + enabled: true + sqlserver.tempdb.file.size: + enabled: true + sqlserver.tempdb.space.usage: + enabled: true + sqlserver.latch.superlatch.count: + enabled: true + sqlserver.latch.superlatch.transition.rate: + enabled: true + sqlserver.latch.wait.rate: + enabled: true + sqlserver.latch.wait_time.avg: + enabled: true + sqlserver.latch.wait_time.total: + enabled: true + sqlserver.index.search.rate: + enabled: true + sqlserver.resource_pool.disk.operations: + enabled: true + sqlserver.resource_pool.disk.throttled.read.rate: + enabled: true + sqlserver.resource_pool.disk.throttled.write.rate: + enabled: true + sqlserver.replica.data.rate: + enabled: true + sqlserver.failover_cluster.ag.cluster_type: + enabled: true + sqlserver.failover_cluster.ag.failure_condition_level: + enabled: true + sqlserver.failover_cluster.ag.health_check_timeout: + enabled: true + sqlserver.failover_cluster.ag.required_sync_secondaries: + enabled: true + sqlserver.failover_cluster.replica.database.queue_size: + enabled: true + sqlserver.failover_cluster.replica.database.redo.rate: + enabled: true + sqlserver.failover_cluster.replica.flow_control_time: + enabled: true + sqlserver.failover_cluster.replica.role: + enabled: true + sqlserver.failover_cluster.replica.synchronization_health: + enabled: true + sqlserver.computer.uptime: + enabled: true + sqlserver.cpu.count: + enabled: true + sqlserver.table.count: + enabled: true + sqlserver.kill_connection.error.rate: + enabled: true + + events: + db.server.query_sample: + enabled: true + db.server.top_query: + enabled: true + + top_query_collection: + lookback_time: 60s + max_query_sample_count: 500 + top_query_count: 200 + collection_interval: 60s + + collect_full_query_text: true + allowed_comment_keys: + - nr_service_guid + + query_sample_collection: + max_rows_per_query: 100 + + processors: + metrics_transform: + transforms: + - include: system.cpu.utilization + action: update + operations: + - action: aggregate_labels + label_set: [state] + aggregation_type: mean + - include: system.paging.operations + action: update + operations: + - action: aggregate_labels + label_set: [direction] + aggregation_type: sum + + filter/exclude_cpu_utilization: + metrics: + datapoint: + - 'metric.name == "system.cpu.utilization" and attributes["state"] == "interrupt"' + - 'metric.name == "system.cpu.utilization" and attributes["state"] == "nice"' + - 'metric.name == "system.cpu.utilization" and attributes["state"] == "softirq"' + + filter/exclude_memory_utilization: + metrics: + datapoint: + - 'metric.name == "system.memory.utilization" and attributes["state"] == "slab_unreclaimable"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "inactive"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "cached"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "buffered"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "slab_reclaimable"' + + filter/exclude_memory_usage: + metrics: + datapoint: + - 'metric.name == "system.memory.usage" and attributes["state"] == "slab_unreclaimable"' + - 'metric.name == "system.memory.usage" and attributes["state"] == "inactive"' + + filter/exclude_filesystem_utilization: + metrics: + datapoint: + - 'metric.name == "system.filesystem.utilization" and attributes["type"] == "squashfs"' + + filter/exclude_filesystem_usage: + metrics: + datapoint: + - 'metric.name == "system.filesystem.usage" and attributes["type"] == "squashfs"' + - 'metric.name == "system.filesystem.usage" and attributes["state"] == "reserved"' + + filter/exclude_filesystem_inodes_usage: + metrics: + datapoint: + - 'metric.name == "system.filesystem.inodes.usage" and attributes["type"] == "squashfs"' + - 'metric.name == "system.filesystem.inodes.usage" and attributes["state"] == "reserved"' + + filter/exclude_system_disk: + metrics: + datapoint: + - 'metric.name == "system.disk.operations" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.merged" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.io" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.io_time" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.operation_time" and IsMatch(attributes["device"], "^loop.*") == true' + + filter/exclude_system_paging: + metrics: + datapoint: + - 'metric.name == "system.paging.usage" and attributes["state"] == "cached"' + - 'metric.name == "system.paging.operations" and attributes["type"] == "cached"' + + filter/exclude_network: + metrics: + datapoint: + - 'IsMatch(metric.name, "^system.network.*") == true and attributes["device"] == "lo"' + + attributes/exclude_system_paging: + include: + match_type: strict + metric_names: + - system.paging.operations + actions: + - key: type + action: delete + + cumulativetodelta: + + transform/host: + metric_statements: + - context: metric + statements: + - set(metric.description, "") + - set(metric.unit, "") + + batch: + + resource_detection: + detectors: ["system"] + system: + hostname_sources: ["os"] + resource_attributes: + host.id: + enabled: true + + resource_detection/cloud: + detectors: ["gcp", "ec2", "azure"] + timeout: 2s + override: true + + resource_detection/env: + detectors: ["env"] + timeout: 2s + override: true + + exporters: + otlphttp: + endpoint: __OTLP_ENDPOINT__ + headers: + api-key: __LICENSE_KEY__ + tls: + insecure: false + compression: gzip + + service: + telemetry: + metrics: + level: none + + extensions: [health_check] + + pipelines: + metrics/host: + receivers: [host_metrics, nrsqlserver] + processors: + - metrics_transform + - filter/exclude_cpu_utilization + - filter/exclude_memory_utilization + - filter/exclude_memory_usage + - filter/exclude_filesystem_utilization + - filter/exclude_filesystem_usage + - filter/exclude_filesystem_inodes_usage + - filter/exclude_system_disk + - filter/exclude_network + - attributes/exclude_system_paging + - transform/host + - resource_detection + - resource_detection/cloud + - resource_detection/env + - cumulativetodelta + - batch + exporters: [otlphttp] + + logs/host: + receivers: [nrsqlserver] + processors: + - resource_detection + - resource_detection/cloud + - resource_detection/env + - batch + exporters: [otlphttp] + + traces: + receivers: [otlp] + processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch] + exporters: [otlphttp] + + metrics: + receivers: [otlp] + processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch] + exporters: [otlphttp] + + logs: + receivers: [otlp] + processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch] + exporters: [otlphttp] + '@ + } else { + $template = @' + receivers: + nrsqlserver: + __RECEIVER_FIELDS__ + metrics: + sqlserver.database.count: + enabled: true + sqlserver.database.io: + enabled: true + sqlserver.database.latency: + enabled: true + sqlserver.database.operations: + enabled: true + sqlserver.database.tempdb.space: + enabled: true + sqlserver.database.tempdb.version_store.size: + enabled: true + sqlserver.deadlock.rate: + enabled: true + sqlserver.os.wait.duration: + enabled: true + sqlserver.processes.blocked: + enabled: true + sqlserver.memory.grants.pending.count: + enabled: true + sqlserver.database.file.size: + enabled: true + sqlserver.memory.area: + enabled: true + + events: + db.server.query_sample: + enabled: true + db.server.top_query: + enabled: true + + top_query_collection: + lookback_time: 60s + max_query_sample_count: 1000 + top_query_count: 250 + collection_interval: 60s + + collect_full_query_text: true + allowed_comment_keys: + - nr_service_guid + + query_sample_collection: + max_rows_per_query: 100 + + processors: + memory_limiter: + check_interval: ${env:NR_MEM_LIMITER_CHECK_INTERVAL:-1s} + limit_mib: ${env:NR_MEM_LIMITER_LIMIT_MIB:-200} + spike_limit_mib: ${env:NR_MEM_LIMITER_SPIKE_MIB:-50} + + batch: + + exporters: + otlphttp: + endpoint: __OTLP_ENDPOINT__ + headers: + api-key: __LICENSE_KEY__ + tls: + insecure: false + compression: gzip + + service: + telemetry: + metrics: + level: none + + pipelines: + metrics: + receivers: [nrsqlserver] + processors: [memory_limiter, batch] + exporters: [otlphttp] + + logs: + receivers: [nrsqlserver] + processors: [memory_limiter, batch] + exporters: [otlphttp] + '@ + } + + $configText = $template.Replace('__RECEIVER_FIELDS__', $receiverFields).Replace('__OTLP_ENDPOINT__', $otlpEndpoint).Replace('__LICENSE_KEY__', $licenseKey) + $configText | Set-Content -Path $configPath -Encoding utf8 + + if ($mode -eq "1") { + Remove-Item -Path $pwFile -ErrorAction SilentlyContinue + } + + Write-Host "MSSQL OTel config written to $configPath" PSEOF \ No newline at end of file From 55cce3b8e23dc0e6736bdbc4ea04ec02b4c17472 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 20 Jul 2026 23:43:02 +0530 Subject: [PATCH 07/34] add configure_service/restart/verify tasks and postInstall --- .../nrdot/mssql-otel/windows.yml | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index a1610e8c6..e0260a2bc 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -938,4 +938,81 @@ install: } Write-Host "MSSQL OTel config written to $configPath" - PSEOF \ No newline at end of file + PSEOF + + configure_service: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $service = "nrdot-collector" + $exePath = "C:\Program Files\nrdot-collector\nrdot-collector.exe" + $configPath = "C:\Program Files\nrdot-collector\mssql-config.yaml" + $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\nrdot-collector" + + $existingImagePath = (Get-ItemProperty -Path $regPath -Name "ImagePath" -ErrorAction SilentlyContinue).ImagePath + + if ($existingImagePath -and $existingImagePath -match [regex]::Escape("mssql-config.yaml")) { + Write-Host "nrdot-collector service is already configured to use mssql-config.yaml." + } else { + $newImagePath = "`"$exePath`" --config `"$configPath`"" + Set-ItemProperty -Path $regPath -Name "ImagePath" -Value $newImagePath + Write-Host "nrdot-collector service ImagePath updated to use mssql-config.yaml." + } + + $validateOutput = & $exePath validate --config="$configPath" 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "mssql-config.yaml failed validation:" + Write-Host $validateOutput + exit 1 + } + Write-Host "mssql-config.yaml validated successfully." + PSEOF + + restart_nrdot: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + net stop nrdot-collector + net start nrdot-collector + PSEOF + + assert_nrdot_status_ok: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $maxRetries = 30 + $tries = 0 + Write-Host "Waiting for NRDOT Collector to start..." + while ($tries -lt $maxRetries) { + $tries++ + $service = Get-Service -Name "nrdot-collector" -ErrorAction SilentlyContinue + if ($service -and $service.Status -eq "Running") { + Write-Host "NRDOT Collector is running." + exit 0 + } + Start-Sleep -Seconds 2 + } + Write-Host -ForegroundColor Red "NRDOT Collector did not start in time. Install log:" + Get-Content "$env:TEMP\nrdot_install.log" -ErrorAction SilentlyContinue | Select-Object -Last 50 + exit 31 + PSEOF + + default: + cmds: + - task: write_recipe_metadata + - task: assert_pre_req + - task: assert_auth_inputs + - task: assert_sql_server_version + - task: install_nrdot + - task: configure_database_user + - task: configure_service_identity + - task: create_collector_config + - task: configure_service + - task: restart_nrdot + - task: assert_nrdot_status_ok + +postInstall: + info: |2 + MSSQL OTel config: C:\Program Files\nrdot-collector\mssql-config.yaml + Service status: Get-Service nrdot-collector + Restart service: net stop nrdot-collector; net start nrdot-collector \ No newline at end of file From 532bc3b4dd8f81b2f8e560e0b327bb1931b343ed Mon Sep 17 00:00:00 2001 From: rreddy Date: Tue, 21 Jul 2026 12:28:49 +0530 Subject: [PATCH 08/34] trust SQL Server certificate in sqlcmd calls (-C) --- .../infrastructure/nrdot/mssql-otel/windows.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index e0260a2bc..5be90efac 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -132,9 +132,9 @@ install: $port = "{{.NR_CLI_MSSQL_PORT}}" if ($mode -eq "1") { - $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}") + $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}", "-C") } else { - $connArgs = @("-S", "$server,$port", "-E") + $connArgs = @("-S", "$server,$port", "-E", "-C") } $query = "SET NOCOUNT ON; SELECT SERVERPROPERTY('ProductMajorVersion');" @@ -288,7 +288,7 @@ install: GO "@ $sql | Set-Content -Path $sqlFile - Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}") $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}", "-C") $sqlFile } elseif ($mode -eq "2") { $account = "{{.NR_CLI_MSSQL_WIN_ACCOUNT}}" @@ -332,7 +332,7 @@ install: GO "@ $sql | Set-Content -Path $sqlFile - Invoke-GrantScript @("-S", "$server,$port", "-E") $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } else { $gmsa = "{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}" @@ -373,7 +373,7 @@ install: GO "@ $sql | Set-Content -Path $sqlFile - Invoke-GrantScript @("-S", "$server,$port", "-E") $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue From f647d521354dc4307d7e97044b6fbecb3659d218 Mon Sep 17 00:00:00 2001 From: rreddy Date: Tue, 21 Jul 2026 16:00:28 +0530 Subject: [PATCH 09/34] don't auto-upgrade existing collector; allow custom SQL login name --- .../nrdot/mssql-otel/windows.yml | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 5be90efac..17f322496 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -47,6 +47,9 @@ inputVars: - name: NR_CLI_MSSQL_SA_PASSWORD prompt: "SQL Server 'sa' password (mode 1 only, used once to create the newrelic login; leave blank otherwise): " secret: true + - name: NR_CLI_MSSQL_LOGIN_NAME + prompt: "Monitoring username: " + default: "newrelic" - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " - name: NR_CLI_MSSQL_WIN_PASSWORD @@ -180,15 +183,14 @@ install: } } - if ($InstalledVersion -and ("v$InstalledVersion" -eq $LatestVersion -or $InstalledVersion -eq $LatestVersion)) { - Write-Host "nrdot-collector $LatestVersion is already installed. Skipping download." + if ($InstalledVersion) { + Write-Host -ForegroundColor Yellow "nrdot-collector is already installed (version $InstalledVersion). Skipping installation - this recipe does not modify an existing nrdot-collector install, since some environments intentionally pin a specific collector version." + Write-Host -ForegroundColor Yellow "To manually upgrade to the latest version ($LatestVersion), follow the install/upgrade steps at: https://docs.newrelic.com/docs/opentelemetry/database/mssql/windows-hosted/" exit 0 - } elseif ($InstalledVersion) { - Write-Host "Upgrading nrdot-collector from $InstalledVersion to $LatestVersion" - } else { - Write-Host "Installing nrdot-collector version: $LatestVersion" } + Write-Host "Installing nrdot-collector version: $LatestVersion" + $MsiPath = "$env:TEMP\nrdot-collector.msi" $LogPath = "$env:TEMP\nrdot_install.log" $DownloadUrl = "https://github.com/newrelic/nrdot-collector-releases/releases/download/$LatestVersion/nrdot-collector_${LatestVersion}_windows_x64.msi" @@ -244,6 +246,7 @@ install: } if ($mode -eq "1") { + $loginName = "{{.NR_CLI_MSSQL_LOGIN_NAME}}" $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) $NrPassword | Set-Content -Path $pwFile -NoNewline @@ -251,14 +254,14 @@ install: $sql = @" USE [master]; GO - IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = 'newrelic') - CREATE LOGIN [newrelic] WITH PASSWORD = '$NrPassword'; + IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = '$loginName') + CREATE LOGIN [$loginName] WITH PASSWORD = '$NrPassword'; ELSE - ALTER LOGIN [newrelic] WITH PASSWORD = '$NrPassword'; + ALTER LOGIN [$loginName] WITH PASSWORD = '$NrPassword'; GO - GRANT VIEW SERVER STATE TO [newrelic]; - GRANT VIEW ANY DEFINITION TO [newrelic]; - GRANT VIEW ANY DATABASE TO [newrelic]; + GRANT VIEW SERVER STATE TO [$loginName]; + GRANT VIEW ANY DEFINITION TO [$loginName]; + GRANT VIEW ANY DATABASE TO [$loginName]; GO DECLARE @name SYSNAME; DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR @@ -272,11 +275,11 @@ install: BEGIN BEGIN TRY EXEC('USE [' + @name + ']; - IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''newrelic'') + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''$loginName'') BEGIN - CREATE USER [newrelic] FOR LOGIN [newrelic]; + CREATE USER [$loginName] FOR LOGIN [$loginName]; END; - GRANT VIEW DATABASE STATE TO [newrelic];'); + GRANT VIEW DATABASE STATE TO [$loginName];'); END TRY BEGIN CATCH PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); @@ -433,8 +436,9 @@ install: $interval = if ($preset -eq "2") { "30s" } else { "15s" } if ($mode -eq "1") { + $loginName = "{{.NR_CLI_MSSQL_LOGIN_NAME}}" $NrPassword = (Get-Content -Path $pwFile -Raw) - $receiverFields = " collection_interval: $interval`n username: newrelic`n password: $NrPassword`n server: $server`n port: $port" + $receiverFields = " collection_interval: $interval`n username: $loginName`n password: $NrPassword`n server: $server`n port: $port" } else { $receiverFields = " collection_interval: $interval`n datasource: `"server=$server;port=$port;integrated security=true;encrypt=true;TrustServerCertificate=true;`"" } From ab790199aaf012d54d950ffbd343e416e8ba98d5 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 27 Jul 2026 12:37:17 +0530 Subject: [PATCH 10/34] allow custom monitoring password; fix install detection bug --- .../infrastructure/nrdot/mssql-otel/windows.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 17f322496..4259c723b 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -50,6 +50,9 @@ inputVars: - name: NR_CLI_MSSQL_LOGIN_NAME prompt: "Monitoring username: " default: "newrelic" + - name: NR_CLI_MSSQL_LOGIN_PASSWORD + prompt: "Password for the monitoring login (mode 1 only; leave blank to auto-generate a random password): " + secret: true - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " - name: NR_CLI_MSSQL_WIN_PASSWORD @@ -177,7 +180,7 @@ install: $UninstallKeys = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -ErrorAction SilentlyContinue foreach ($Key in $UninstallKeys) { $KeyData = Get-ItemProperty -Path $Key.PSPath -ErrorAction SilentlyContinue - if ($KeyData.DisplayName -match "nrdot-collector") { + if ($KeyData.DisplayName -match "NRDOT Collector") { $InstalledVersion = $KeyData.DisplayVersion break } @@ -247,8 +250,12 @@ install: if ($mode -eq "1") { $loginName = "{{.NR_CLI_MSSQL_LOGIN_NAME}}" - $chars = (48..57) + (65..90) + (97..122) - $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) + if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_LOGIN_PASSWORD}}")) { + $chars = (48..57) + (65..90) + (97..122) + $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) + } else { + $NrPassword = "{{.NR_CLI_MSSQL_LOGIN_PASSWORD}}" + } $NrPassword | Set-Content -Path $pwFile -NoNewline $sql = @" From 0d6f769e678211fe54337561fb8ef37c9a5e91c4 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 27 Jul 2026 13:03:03 +0530 Subject: [PATCH 11/34] full stop with manual-steps message when nrdot-collector exists --- .../nrdot/mssql-otel/windows.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 4259c723b..72b975c32 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -187,9 +187,21 @@ install: } if ($InstalledVersion) { - Write-Host -ForegroundColor Yellow "nrdot-collector is already installed (version $InstalledVersion). Skipping installation - this recipe does not modify an existing nrdot-collector install, since some environments intentionally pin a specific collector version." - Write-Host -ForegroundColor Yellow "To manually upgrade to the latest version ($LatestVersion), follow the install/upgrade steps at: https://docs.newrelic.com/docs/opentelemetry/database/mssql/windows-hosted/" - exit 0 + Write-Host -ForegroundColor Yellow "NRDOT Collector is already installed." + Write-Host "" + Write-Host "Please follow the steps below to complete the setup:" + Write-Host "" + Write-Host " 1. Create mssql-config.yaml" + Write-Host " if not present. Refer to:" + Write-Host " https://docs.newrelic.com/docs/opentelemetry/database/mssql/windows-hosted/" + Write-Host "" + Write-Host " 2. Make sure the monitoring user is created" + Write-Host " and has the required permissions granted." + Write-Host "" + Write-Host " 3. After making the above changes, restart the NRDOT Collector:" + Write-Host " net stop nrdot-collector; net start nrdot-collector" + Write-Host "" + exit 131 } Write-Host "Installing nrdot-collector version: $LatestVersion" From cba3a1d7e46fe33fdea351b57d8cfebad9532333 Mon Sep 17 00:00:00 2001 From: rreddy Date: Mon, 27 Jul 2026 17:38:30 +0530 Subject: [PATCH 12/34] fix(mssql-otel-windows): remove manual password entry, auto-generate only --- .../infrastructure/nrdot/mssql-otel/windows.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 72b975c32..771369eed 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -50,9 +50,6 @@ inputVars: - name: NR_CLI_MSSQL_LOGIN_NAME prompt: "Monitoring username: " default: "newrelic" - - name: NR_CLI_MSSQL_LOGIN_PASSWORD - prompt: "Password for the monitoring login (mode 1 only; leave blank to auto-generate a random password): " - secret: true - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " - name: NR_CLI_MSSQL_WIN_PASSWORD @@ -262,12 +259,8 @@ install: if ($mode -eq "1") { $loginName = "{{.NR_CLI_MSSQL_LOGIN_NAME}}" - if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_LOGIN_PASSWORD}}")) { - $chars = (48..57) + (65..90) + (97..122) - $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) - } else { - $NrPassword = "{{.NR_CLI_MSSQL_LOGIN_PASSWORD}}" - } + $chars = (48..57) + (65..90) + (97..122) + $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) $NrPassword | Set-Content -Path $pwFile -NoNewline $sql = @" From 1373ef2e310318e20481c54b47a425bb1bd25e4b Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 31 Jul 2026 10:37:35 +0530 Subject: [PATCH 13/34] add CI test definition for Windows NRDOT MSSQL recipe --- .../nrdot/mssql-otel/mssql-windows2019.json | 49 +++++++++++++++++++ .../roles/onafterstart/tasks/main.yml | 6 +++ .../install/roles/configure/tasks/main.yml | 48 ++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 test/definitions/nrdot/mssql-otel/mssql-windows2019.json create mode 100644 test/deploy/windows/nrdot-mssql/install/roles/configure/tasks/main.yml diff --git a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json new file mode 100644 index 000000000..c95c0d681 --- /dev/null +++ b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json @@ -0,0 +1,49 @@ +{ + "global_tags": { + "owning_team": "database-integrations", + "Environment": "development", + "Department": "product", + "Product": "database-integrations" + }, + "resources": [ + { + "id": "host1", + "provider": "aws", + "type": "ec2", + "size": "t3.xlarge", + "is_windows": true, + "ami_name": "Windows_Server-2019-English-Full-SQL_2019_Standard-*", + "user_name": "Administrator" + } + ], + "services": [ + { + "id": "mssql1", + "destinations": [ + "host1" + ], + "source_repository": "https://github.com/newrelic/open-install-library.git", + "deploy_script_path": "test/deploy/windows/nrdot-mssql/install/roles", + "port": 1433 + } + ], + "instrumentations": { + "resources": [ + { + "id": "nr_nrdot_mssql_windows2019", + "resource_ids": [ + "host1" + ], + "provider": "newrelic", + "source_repository": "https://github.com/newrelic/open-install-library.git", + "deploy_script_path": "test/deploy/windows/newrelic-cli/install-recipe/roles", + "params": { + "recipe_content_url": "https://raw.githubusercontent.com/newrelic/open-install-library/main/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml", + "recipe_targeted": "nrdot-collector-mssql", + "validate_output": "NRDOT MSSQL \\(Windows\\)\\s+\\(installed\\)", + "env_var": "$env:NR_CLI_MSSQL_CONFIG_PRESET=\"1\"; $env:NR_CLI_MSSQL_AUTH_MODE=\"1\"; $env:NR_CLI_MSSQL_SERVER=\"localhost\"; $env:NR_CLI_MSSQL_PORT=\"1433\"; $env:NR_CLI_MSSQL_SA_PASSWORD=\"YourStrong@Passw0rd\"; $env:NR_CLI_MSSQL_LOGIN_NAME=\"newrelic\"" + } + } + ] + } +} diff --git a/test/deploy/windows/newrelic-cli/install-recipe/roles/onafterstart/tasks/main.yml b/test/deploy/windows/newrelic-cli/install-recipe/roles/onafterstart/tasks/main.yml index 3c02d69d5..1a8239ae2 100644 --- a/test/deploy/windows/newrelic-cli/install-recipe/roles/onafterstart/tasks/main.yml +++ b/test/deploy/windows/newrelic-cli/install-recipe/roles/onafterstart/tasks/main.yml @@ -11,6 +11,7 @@ # newrelic_organization_id: (optional) The NewRelic Organization ID if use_organization_id is set to True. # nr_host_fleet_id: (optional) A NewRelic host fleet id to add if NR_CLI_FLEET_ID should be added to env_vars. # use_system_identity_auth: (optional) A boolean value to control if system identity authentication should be used. False by default. +# env_var: (optional) A ";"-joined string of PowerShell $env: assignments (e.g. '$env:FOO="bar"; $env:BAZ="qux"') injected into the environment before running newrelic install. - name: Prepare command_should_fail option when: command_should_fail is not defined @@ -47,6 +48,11 @@ set_fact: env_vars: "" +- name: Get environment variables from "env_var" + set_fact: + env_vars: "{{ env_vars }}; {{ env_var }}" + when: env_var is defined + - name: Adding newrelic accountId environment variable set_fact: env_vars: "{{ env_vars }}; $env:NEW_RELIC_ACCOUNT_ID=\"{{ newrelic_account_id }}\"" diff --git a/test/deploy/windows/nrdot-mssql/install/roles/configure/tasks/main.yml b/test/deploy/windows/nrdot-mssql/install/roles/configure/tasks/main.yml new file mode 100644 index 000000000..94661981b --- /dev/null +++ b/test/deploy/windows/nrdot-mssql/install/roles/configure/tasks/main.yml @@ -0,0 +1,48 @@ +--- + +- name: Verify SQL Server is installed and responsive + win_shell: | + sqlcmd -S localhost -I -Q "SELECT GETDATE()"; + register: output + +- fail: + msg: "SqlServer is not installed on the host" + when: output is failed + +- name: Set SqlServer Authentication mode to Mixed to allow username/password login + win_shell: | + Import-Module -Name SQLPS + $sql = [Microsoft.SqlServer.Management.Smo.Server]::new("localhost") + $sql.Settings.LoginMode = 'Mixed' + $sql.Alter() + Get-Service -Name 'MSSQLSERVER' | Restart-Service -Force + +- name: Wait for SQL Server to accept connections after restart + win_shell: | + sqlcmd -S localhost -I -Q "SELECT 1"; + register: output + retries: 30 + delay: 2 + until: output is not failed + +- fail: + msg: "SQL Server did not come back up after switching to Mixed Mode auth" + when: output is failed + +- name: Enable sa login and set its password + win_shell: | + sqlcmd -S localhost -I -Q "ALTER LOGIN sa WITH PASSWORD='YourStrong@Passw0rd'; ALTER LOGIN sa ENABLE;"; + register: output + +- fail: + msg: "Failed to enable/set password for the sa login" + when: output is failed + +- name: Verify sa login works + win_shell: | + sqlcmd -S localhost -U sa -P "YourStrong@Passw0rd" -Q "SELECT GETDATE()"; + register: output + +- fail: + msg: "Unable to authenticate as sa with the configured password" + when: output is failed From a5e925c39c5881284e7332fde50ccf05c1f6b89b Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 31 Jul 2026 11:10:34 +0530 Subject: [PATCH 14/34] add placeholder env vars for unused auth-mode inputs --- test/definitions/nrdot/mssql-otel/mssql-windows2019.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json index c95c0d681..cd1eef647 100644 --- a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json +++ b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json @@ -41,7 +41,7 @@ "recipe_content_url": "https://raw.githubusercontent.com/newrelic/open-install-library/main/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml", "recipe_targeted": "nrdot-collector-mssql", "validate_output": "NRDOT MSSQL \\(Windows\\)\\s+\\(installed\\)", - "env_var": "$env:NR_CLI_MSSQL_CONFIG_PRESET=\"1\"; $env:NR_CLI_MSSQL_AUTH_MODE=\"1\"; $env:NR_CLI_MSSQL_SERVER=\"localhost\"; $env:NR_CLI_MSSQL_PORT=\"1433\"; $env:NR_CLI_MSSQL_SA_PASSWORD=\"YourStrong@Passw0rd\"; $env:NR_CLI_MSSQL_LOGIN_NAME=\"newrelic\"" + "env_var": "$env:NR_CLI_MSSQL_CONFIG_PRESET=\"1\"; $env:NR_CLI_MSSQL_AUTH_MODE=\"1\"; $env:NR_CLI_MSSQL_SERVER=\"localhost\"; $env:NR_CLI_MSSQL_PORT=\"1433\"; $env:NR_CLI_MSSQL_SA_PASSWORD=\"YourStrong@Passw0rd\"; $env:NR_CLI_MSSQL_LOGIN_NAME=\"newrelic\"; $env:NR_CLI_MSSQL_WIN_ACCOUNT=\"unused\"; $env:NR_CLI_MSSQL_WIN_PASSWORD=\"unused\"; $env:NR_CLI_MSSQL_GMSA_ACCOUNT=\"unused\"" } } ] From aa7a1a541bfb31f263bc62bdbc7b2349c1cbdd61 Mon Sep 17 00:00:00 2001 From: rreddy Date: Tue, 4 Aug 2026 15:16:08 +0530 Subject: [PATCH 15/34] read SA_PASSWORD/SERVER/PORT/LOGIN_NAME from env, not task templates --- .../nrdot/mssql-otel/windows.yml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 771369eed..7defe50f9 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -102,7 +102,7 @@ install: switch ($mode) { "1" { - if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_SA_PASSWORD}}")) { + if ([string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_SA_PASSWORD)) { Write-Host -ForegroundColor Red "NR_CLI_MSSQL_SA_PASSWORD is required for SQL Server Auth (mode 1)." exit 1 } @@ -131,11 +131,11 @@ install: - | powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" - $server = "{{.NR_CLI_MSSQL_SERVER}}" - $port = "{{.NR_CLI_MSSQL_PORT}}" + $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } + $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } if ($mode -eq "1") { - $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}", "-C") + $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", $env:NR_CLI_MSSQL_SA_PASSWORD, "-C") } else { $connArgs = @("-S", "$server,$port", "-E", "-C") } @@ -242,8 +242,8 @@ install: - | powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" - $server = "{{.NR_CLI_MSSQL_SERVER}}" - $port = "{{.NR_CLI_MSSQL_PORT}}" + $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } + $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } $sqlFile = "$env:TEMP\nr-mssql-grant.sql" $pwFile = "$env:TEMP\nr-mssql-newrelic-pw.tmp" @@ -258,7 +258,7 @@ install: } if ($mode -eq "1") { - $loginName = "{{.NR_CLI_MSSQL_LOGIN_NAME}}" + $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) $NrPassword | Set-Content -Path $pwFile -NoNewline @@ -303,7 +303,7 @@ install: GO "@ $sql | Set-Content -Path $sqlFile - Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", "{{.NR_CLI_MSSQL_SA_PASSWORD}}", "-C") $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", $env:NR_CLI_MSSQL_SA_PASSWORD, "-C") $sqlFile } elseif ($mode -eq "2") { $account = "{{.NR_CLI_MSSQL_WIN_ACCOUNT}}" @@ -435,8 +435,8 @@ install: powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" $preset = "{{.NR_CLI_MSSQL_CONFIG_PRESET}}" - $server = "{{.NR_CLI_MSSQL_SERVER}}" - $port = "{{.NR_CLI_MSSQL_PORT}}" + $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } + $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } $region = "{{.NEW_RELIC_REGION}}" $licenseKey = "{{.NEW_RELIC_LICENSE_KEY}}" $configDir = "C:\Program Files\nrdot-collector" @@ -448,7 +448,7 @@ install: $interval = if ($preset -eq "2") { "30s" } else { "15s" } if ($mode -eq "1") { - $loginName = "{{.NR_CLI_MSSQL_LOGIN_NAME}}" + $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } $NrPassword = (Get-Content -Path $pwFile -Raw) $receiverFields = " collection_interval: $interval`n username: $loginName`n password: $NrPassword`n server: $server`n port: $port" } else { From dc8126f828c247a76d2645b5b613d66032722ca4 Mon Sep 17 00:00:00 2001 From: rreddy Date: Wed, 5 Aug 2026 19:05:27 +0530 Subject: [PATCH 16/34] read WIN_ACCOUNT/WIN_PASSWORD/GMSA_ACCOUNT from env, not task templates --- .../infrastructure/nrdot/mssql-otel/windows.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 7defe50f9..6b8e2053f 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -108,13 +108,13 @@ install: } } "2" { - if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_WIN_ACCOUNT}}") -or [string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_WIN_PASSWORD}}")) { + if ([string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_WIN_ACCOUNT) -or [string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_WIN_PASSWORD)) { Write-Host -ForegroundColor Red "NR_CLI_MSSQL_WIN_ACCOUNT and NR_CLI_MSSQL_WIN_PASSWORD are both required for Windows Auth (mode 2)." exit 1 } } "3" { - if ([string]::IsNullOrWhiteSpace("{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}")) { + if ([string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_GMSA_ACCOUNT)) { Write-Host -ForegroundColor Red "NR_CLI_MSSQL_GMSA_ACCOUNT is required for gMSA (mode 3)." exit 1 } @@ -306,7 +306,7 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", $env:NR_CLI_MSSQL_SA_PASSWORD, "-C") $sqlFile } elseif ($mode -eq "2") { - $account = "{{.NR_CLI_MSSQL_WIN_ACCOUNT}}" + $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT $sql = @" USE [master]; GO @@ -350,7 +350,7 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } else { - $gmsa = "{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}" + $gmsa = $env:NR_CLI_MSSQL_GMSA_ACCOUNT $sql = @" USE master; GO @@ -408,16 +408,16 @@ install: } if ($mode -eq "2") { - $account = "{{.NR_CLI_MSSQL_WIN_ACCOUNT}}" - $password = "{{.NR_CLI_MSSQL_WIN_PASSWORD}}" + $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $password = $env:NR_CLI_MSSQL_WIN_PASSWORD } else { - $account = "{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}" + $account = $env:NR_CLI_MSSQL_GMSA_ACCOUNT $password = "" } Stop-Service -Name $service -Force -ErrorAction SilentlyContinue - cmd /c "sc config `"$service`" obj= `"$account`" password= `"$password`"" + & sc.exe config "$service" obj= "$account" password= "$password" if ($LASTEXITCODE -ne 0) { Write-Host -ForegroundColor Red "Failed to configure the nrdot-collector service to run as $account." exit 1 From a38edc8407d945aa1010d99e066b76ddefae2c25 Mon Sep 17 00:00:00 2001 From: rreddy Date: Wed, 5 Aug 2026 19:13:14 +0530 Subject: [PATCH 17/34] restrict ACL on mssql-config.yaml to service account only --- .../infrastructure/nrdot/mssql-otel/windows.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 6b8e2053f..e4e307efb 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -949,6 +949,19 @@ install: $configText = $template.Replace('__RECEIVER_FIELDS__', $receiverFields).Replace('__OTLP_ENDPOINT__', $otlpEndpoint).Replace('__LICENSE_KEY__', $licenseKey) $configText | Set-Content -Path $configPath -Encoding utf8 + $serviceAccount = if ($mode -eq "1") { $null } + elseif ($mode -eq "2") { $env:NR_CLI_MSSQL_WIN_ACCOUNT } + else { $env:NR_CLI_MSSQL_GMSA_ACCOUNT } + + $grants = @("SYSTEM:(F)", "BUILTIN\Administrators:(F)") + if ($serviceAccount) { $grants += "${serviceAccount}:(R)" } + + icacls $configPath /inheritance:r /grant:r $grants | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to restrict permissions on $configPath." + exit 1 + } + if ($mode -eq "1") { Remove-Item -Path $pwFile -ErrorAction SilentlyContinue } From 6c5a635964328b36a75b77a65c9f7f36f7abb9a4 Mon Sep 17 00:00:00 2001 From: rreddy Date: Thu, 6 Aug 2026 09:30:34 +0530 Subject: [PATCH 18/34] restrict ACL and guarantee cleanup for mssql temp files on Windows --- .../nrdot/mssql-otel/windows.yml | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index e4e307efb..90a7a7cc6 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -257,11 +257,23 @@ install: Write-Host $result } + function Protect-TempFile($path) { + icacls $path /inheritance:r /grant:r "SYSTEM:(F)" "BUILTIN\Administrators:(F)" | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to restrict permissions on $path." + exit 1 + } + } + + $grantSucceeded = $false + + try { if ($mode -eq "1") { $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) $NrPassword | Set-Content -Path $pwFile -NoNewline + Protect-TempFile $pwFile $sql = @" USE [master]; @@ -303,6 +315,7 @@ install: GO "@ $sql | Set-Content -Path $sqlFile + Protect-TempFile $sqlFile Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", $env:NR_CLI_MSSQL_SA_PASSWORD, "-C") $sqlFile } elseif ($mode -eq "2") { @@ -347,6 +360,7 @@ install: GO "@ $sql | Set-Content -Path $sqlFile + Protect-TempFile $sqlFile Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } else { @@ -388,11 +402,19 @@ install: GO "@ $sql | Set-Content -Path $sqlFile + Protect-TempFile $sqlFile Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } - Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue + $grantSucceeded = $true Write-Host "SQL Server monitoring identity configured successfully." + } + finally { + Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue + if (-not $grantSucceeded) { + Remove-Item -Path $pwFile -ErrorAction SilentlyContinue + } + } PSEOF configure_service_identity: From 6c3edf566122e7c9da93f3600783fb0a2cc089b5 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 11:36:47 +0530 Subject: [PATCH 19/34] updated test for windows --- test/definitions/nrdot/mssql-otel/mssql-windows2019.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json index cd1eef647..0d59060e2 100644 --- a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json +++ b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json @@ -41,7 +41,7 @@ "recipe_content_url": "https://raw.githubusercontent.com/newrelic/open-install-library/main/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml", "recipe_targeted": "nrdot-collector-mssql", "validate_output": "NRDOT MSSQL \\(Windows\\)\\s+\\(installed\\)", - "env_var": "$env:NR_CLI_MSSQL_CONFIG_PRESET=\"1\"; $env:NR_CLI_MSSQL_AUTH_MODE=\"1\"; $env:NR_CLI_MSSQL_SERVER=\"localhost\"; $env:NR_CLI_MSSQL_PORT=\"1433\"; $env:NR_CLI_MSSQL_SA_PASSWORD=\"YourStrong@Passw0rd\"; $env:NR_CLI_MSSQL_LOGIN_NAME=\"newrelic\"; $env:NR_CLI_MSSQL_WIN_ACCOUNT=\"unused\"; $env:NR_CLI_MSSQL_WIN_PASSWORD=\"unused\"; $env:NR_CLI_MSSQL_GMSA_ACCOUNT=\"unused\"" + "env_var": "$env:NR_CLI_MSSQL_CONFIG_PRESET=\"1\"; $env:NR_CLI_MSSQL_AUTH_MODE=\"1\"; $env:NR_CLI_MSSQL_SERVER=\"localhost\"; $env:NR_CLI_MSSQL_PORT=\"1433\"; $env:NR_CLI_MSSQL_SA_PASSWORD=\"YourStrong@Passw0rd\"; $env:NR_CLI_MSSQL_LOGIN_NAME=\"newrelic\"; $env:NR_CLI_MSSQL_WIN_ACCOUNT=\"\"; $env:NR_CLI_MSSQL_WIN_PASSWORD=\"\"; $env:NR_CLI_MSSQL_GMSA_ACCOUNT=\"\"" } } ] From dd4d43f66199a8c9cab822ea7a52112ed3373ee5 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 12:04:00 +0530 Subject: [PATCH 20/34] updated test for windows added available for NRDOT already installed --- test/definitions/nrdot/mssql-otel/mssql-windows2019.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json index 0d59060e2..dd95c9fda 100644 --- a/test/definitions/nrdot/mssql-otel/mssql-windows2019.json +++ b/test/definitions/nrdot/mssql-otel/mssql-windows2019.json @@ -40,7 +40,7 @@ "params": { "recipe_content_url": "https://raw.githubusercontent.com/newrelic/open-install-library/main/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml", "recipe_targeted": "nrdot-collector-mssql", - "validate_output": "NRDOT MSSQL \\(Windows\\)\\s+\\(installed\\)", + "validate_output": "NRDOT MSSQL \\(Windows\\)\\s+\\((installed|available)\\)", "env_var": "$env:NR_CLI_MSSQL_CONFIG_PRESET=\"1\"; $env:NR_CLI_MSSQL_AUTH_MODE=\"1\"; $env:NR_CLI_MSSQL_SERVER=\"localhost\"; $env:NR_CLI_MSSQL_PORT=\"1433\"; $env:NR_CLI_MSSQL_SA_PASSWORD=\"YourStrong@Passw0rd\"; $env:NR_CLI_MSSQL_LOGIN_NAME=\"newrelic\"; $env:NR_CLI_MSSQL_WIN_ACCOUNT=\"\"; $env:NR_CLI_MSSQL_WIN_PASSWORD=\"\"; $env:NR_CLI_MSSQL_GMSA_ACCOUNT=\"\"" } } From a8fb93f5e8fa159bcd9e63c3eb59337c16a11c94 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 12:42:30 +0530 Subject: [PATCH 21/34] add inputVars default --- recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 90a7a7cc6..25c7bd2f0 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -47,16 +47,20 @@ inputVars: - name: NR_CLI_MSSQL_SA_PASSWORD prompt: "SQL Server 'sa' password (mode 1 only, used once to create the newrelic login; leave blank otherwise): " secret: true + default: "" - name: NR_CLI_MSSQL_LOGIN_NAME prompt: "Monitoring username: " default: "newrelic" - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " + default: "" - name: NR_CLI_MSSQL_WIN_PASSWORD prompt: "Password for that Windows account (mode 2 only; leave blank otherwise): " secret: true + default: "" - name: NR_CLI_MSSQL_GMSA_ACCOUNT prompt: "gMSA account, DOMAIN\\gMSAName$ (mode 3 only; leave blank otherwise): " + default: "" validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'sqlserver.%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" From 04c62829ec95beb5b07ed99be895635647fc016c Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 15:23:05 +0530 Subject: [PATCH 22/34] add inputVars --- .../newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 25c7bd2f0..08807dc0a 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -47,20 +47,20 @@ inputVars: - name: NR_CLI_MSSQL_SA_PASSWORD prompt: "SQL Server 'sa' password (mode 1 only, used once to create the newrelic login; leave blank otherwise): " secret: true - default: "" + default: " " - name: NR_CLI_MSSQL_LOGIN_NAME prompt: "Monitoring username: " default: "newrelic" - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " - default: "" + default: " " - name: NR_CLI_MSSQL_WIN_PASSWORD prompt: "Password for that Windows account (mode 2 only; leave blank otherwise): " secret: true - default: "" + default: " " - name: NR_CLI_MSSQL_GMSA_ACCOUNT prompt: "gMSA account, DOMAIN\\gMSAName$ (mode 3 only; leave blank otherwise): " - default: "" + default: " " validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'sqlserver.%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" From b2e699cc1771d73f6575b6521277da28623f880e Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 16:04:41 +0530 Subject: [PATCH 23/34] pass sa password via SQLCMDPASSWORD instead of sqlcmd -P arg --- .../newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 08807dc0a..2f3fc2033 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -139,7 +139,8 @@ install: $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } if ($mode -eq "1") { - $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", $env:NR_CLI_MSSQL_SA_PASSWORD, "-C") + $env:SQLCMDPASSWORD = $env:NR_CLI_MSSQL_SA_PASSWORD + $connArgs = @("-S", "$server,$port", "-U", "sa", "-C") } else { $connArgs = @("-S", "$server,$port", "-E", "-C") } @@ -320,7 +321,8 @@ install: "@ $sql | Set-Content -Path $sqlFile Protect-TempFile $sqlFile - Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", $env:NR_CLI_MSSQL_SA_PASSWORD, "-C") $sqlFile + $env:SQLCMDPASSWORD = $env:NR_CLI_MSSQL_SA_PASSWORD + Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-C") $sqlFile } elseif ($mode -eq "2") { $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT From f3875be495bb01afbcb90795c3799031df8bb17a Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 16:20:57 +0530 Subject: [PATCH 24/34] change assert_auth_inputs for password --- .../nrdot/mssql-otel/windows.yml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 2f3fc2033..0692d79f9 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -47,20 +47,20 @@ inputVars: - name: NR_CLI_MSSQL_SA_PASSWORD prompt: "SQL Server 'sa' password (mode 1 only, used once to create the newrelic login; leave blank otherwise): " secret: true - default: " " + default: "" - name: NR_CLI_MSSQL_LOGIN_NAME prompt: "Monitoring username: " default: "newrelic" - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " - default: " " + default: "" - name: NR_CLI_MSSQL_WIN_PASSWORD prompt: "Password for that Windows account (mode 2 only; leave blank otherwise): " secret: true - default: " " + default: "" - name: NR_CLI_MSSQL_GMSA_ACCOUNT prompt: "gMSA account, DOMAIN\\gMSAName$ (mode 3 only; leave blank otherwise): " - default: " " + default: "" validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'sqlserver.%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" @@ -106,25 +106,32 @@ install: switch ($mode) { "1" { - if ([string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_SA_PASSWORD)) { - Write-Host -ForegroundColor Red "NR_CLI_MSSQL_SA_PASSWORD is required for SQL Server Auth (mode 1)." + $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD + if ([string]::IsNullOrWhiteSpace($saPassword) -or $saPassword.Trim() -eq "") { + Write-Host -ForegroundColor Red "Error: SQL Server 'sa' password is required for SQL Server Auth (mode 1)." + Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid SA password." exit 1 } } "2" { - if ([string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_WIN_ACCOUNT) -or [string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_WIN_PASSWORD)) { - Write-Host -ForegroundColor Red "NR_CLI_MSSQL_WIN_ACCOUNT and NR_CLI_MSSQL_WIN_PASSWORD are both required for Windows Auth (mode 2)." + $winAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $winPassword = $env:NR_CLI_MSSQL_WIN_PASSWORD + if ([string]::IsNullOrWhiteSpace($winAccount) -or $winAccount.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($winPassword) -or $winPassword.Trim() -eq "") { + Write-Host -ForegroundColor Red "Error: Windows account and password are both required for Windows Auth (mode 2)." + Write-Host -ForegroundColor Red "Please re-run the installation and provide valid credentials." exit 1 } } "3" { - if ([string]::IsNullOrWhiteSpace($env:NR_CLI_MSSQL_GMSA_ACCOUNT)) { - Write-Host -ForegroundColor Red "NR_CLI_MSSQL_GMSA_ACCOUNT is required for gMSA (mode 3)." + $gmsaAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + if ([string]::IsNullOrWhiteSpace($gmsaAccount) -or $gmsaAccount.Trim() -eq "") { + Write-Host -ForegroundColor Red "Error: gMSA account is required for gMSA Auth (mode 3)." + Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid gMSA account." exit 1 } } default { - Write-Host -ForegroundColor Red "NR_CLI_MSSQL_AUTH_MODE must be 1, 2, or 3." + Write-Host -ForegroundColor Red "Error: NR_CLI_MSSQL_AUTH_MODE must be 1, 2, or 3." exit 1 } } From 77ab9fd0b16e834cac7362401628d8c7cf878862 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 16:28:00 +0530 Subject: [PATCH 25/34] add debug for password --- recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 0692d79f9..d7b19016b 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -107,7 +107,8 @@ install: switch ($mode) { "1" { $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD - if ([string]::IsNullOrWhiteSpace($saPassword) -or $saPassword.Trim() -eq "") { + Write-Host "DEBUG: Password length = $($saPassword.Length), IsNullOrWhiteSpace = $([string]::IsNullOrWhiteSpace($saPassword))" + if ([string]::IsNullOrWhiteSpace($saPassword) -or $saPassword.Length -eq 0) { Write-Host -ForegroundColor Red "Error: SQL Server 'sa' password is required for SQL Server Auth (mode 1)." Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid SA password." exit 1 From 7affd691900447732cd1dc6c8f700a87a580ced7 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 16:31:41 +0530 Subject: [PATCH 26/34] Remove debug output from validation --- recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index d7b19016b..a27821535 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -107,7 +107,6 @@ install: switch ($mode) { "1" { $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD - Write-Host "DEBUG: Password length = $($saPassword.Length), IsNullOrWhiteSpace = $([string]::IsNullOrWhiteSpace($saPassword))" if ([string]::IsNullOrWhiteSpace($saPassword) -or $saPassword.Length -eq 0) { Write-Host -ForegroundColor Red "Error: SQL Server 'sa' password is required for SQL Server Auth (mode 1)." Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid SA password." From 8f856e94848b0a6153357a33ec7d4ac9bfd31061 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 16:37:13 +0530 Subject: [PATCH 27/34] Use environment variable instead of template variable for SA password --- .../newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index a27821535..b2226c3cc 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -146,8 +146,8 @@ install: $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } if ($mode -eq "1") { - $env:SQLCMDPASSWORD = $env:NR_CLI_MSSQL_SA_PASSWORD - $connArgs = @("-S", "$server,$port", "-U", "sa", "-C") + $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD + $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", $saPassword, "-C") } else { $connArgs = @("-S", "$server,$port", "-E", "-C") } @@ -328,8 +328,8 @@ install: "@ $sql | Set-Content -Path $sqlFile Protect-TempFile $sqlFile - $env:SQLCMDPASSWORD = $env:NR_CLI_MSSQL_SA_PASSWORD - Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-C") $sqlFile + $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD + Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", $saPassword, "-C") $sqlFile } elseif ($mode -eq "2") { $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT From 499fdbb0c662d34f61879bb8658b168b0cd304f3 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 16:54:29 +0530 Subject: [PATCH 28/34] drop -P from the args entirely --- .../infrastructure/nrdot/mssql-otel/windows.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index b2226c3cc..1bf465777 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -147,7 +147,12 @@ install: if ($mode -eq "1") { $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD - $connArgs = @("-S", "$server,$port", "-U", "sa", "-P", $saPassword, "-C") + if ([string]::IsNullOrEmpty($saPassword)) { + Write-Host -ForegroundColor Red "SA password is empty when preparing to connect to SQL Server - this should not happen." + exit 1 + } + $env:SQLCMDPASSWORD = $saPassword + $connArgs = @("-S", "$server,$port", "-U", "sa", "-C") } else { $connArgs = @("-S", "$server,$port", "-E", "-C") } @@ -329,7 +334,12 @@ install: $sql | Set-Content -Path $sqlFile Protect-TempFile $sqlFile $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD - Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-P", $saPassword, "-C") $sqlFile + if ([string]::IsNullOrEmpty($saPassword)) { + Write-Host -ForegroundColor Red "SA password is empty when preparing to connect to SQL Server - this should not happen." + exit 1 + } + $env:SQLCMDPASSWORD = $saPassword + Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-C") $sqlFile } elseif ($mode -eq "2") { $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT From a789c06369a5cb453c140bb208a321d955c32646 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 17:22:52 +0530 Subject: [PATCH 29/34] read secret input vars via template substitution, not --- .../infrastructure/nrdot/mssql-otel/windows.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 1bf465777..484bb10f2 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -106,7 +106,7 @@ install: switch ($mode) { "1" { - $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD + $saPassword = '{{.NR_CLI_MSSQL_SA_PASSWORD}}' if ([string]::IsNullOrWhiteSpace($saPassword) -or $saPassword.Length -eq 0) { Write-Host -ForegroundColor Red "Error: SQL Server 'sa' password is required for SQL Server Auth (mode 1)." Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid SA password." @@ -115,7 +115,7 @@ install: } "2" { $winAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT - $winPassword = $env:NR_CLI_MSSQL_WIN_PASSWORD + $winPassword = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' if ([string]::IsNullOrWhiteSpace($winAccount) -or $winAccount.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($winPassword) -or $winPassword.Trim() -eq "") { Write-Host -ForegroundColor Red "Error: Windows account and password are both required for Windows Auth (mode 2)." Write-Host -ForegroundColor Red "Please re-run the installation and provide valid credentials." @@ -146,7 +146,7 @@ install: $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } if ($mode -eq "1") { - $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD + $saPassword = '{{.NR_CLI_MSSQL_SA_PASSWORD}}' if ([string]::IsNullOrEmpty($saPassword)) { Write-Host -ForegroundColor Red "SA password is empty when preparing to connect to SQL Server - this should not happen." exit 1 @@ -333,7 +333,7 @@ install: "@ $sql | Set-Content -Path $sqlFile Protect-TempFile $sqlFile - $saPassword = $env:NR_CLI_MSSQL_SA_PASSWORD + $saPassword = '{{.NR_CLI_MSSQL_SA_PASSWORD}}' if ([string]::IsNullOrEmpty($saPassword)) { Write-Host -ForegroundColor Red "SA password is empty when preparing to connect to SQL Server - this should not happen." exit 1 @@ -454,7 +454,7 @@ install: if ($mode -eq "2") { $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT - $password = $env:NR_CLI_MSSQL_WIN_PASSWORD + $password = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' } else { $account = $env:NR_CLI_MSSQL_GMSA_ACCOUNT $password = "" From 8adabc5653ec09578acd9a09f0af3be1ea7aade0 Mon Sep 17 00:00:00 2001 From: rreddy Date: Fri, 7 Aug 2026 17:59:13 +0530 Subject: [PATCH 30/34] mssql-otel windows recipe against re-install and cross-task failures --- .../nrdot/mssql-otel/windows.yml | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 484bb10f2..2a6754ea1 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -47,20 +47,20 @@ inputVars: - name: NR_CLI_MSSQL_SA_PASSWORD prompt: "SQL Server 'sa' password (mode 1 only, used once to create the newrelic login; leave blank otherwise): " secret: true - default: "" + default: " " - name: NR_CLI_MSSQL_LOGIN_NAME prompt: "Monitoring username: " default: "newrelic" - name: NR_CLI_MSSQL_WIN_ACCOUNT prompt: "Windows account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " - default: "" + default: " " - name: NR_CLI_MSSQL_WIN_PASSWORD prompt: "Password for that Windows account (mode 2 only; leave blank otherwise): " secret: true - default: "" + default: " " - name: NR_CLI_MSSQL_GMSA_ACCOUNT prompt: "gMSA account, DOMAIN\\gMSAName$ (mode 3 only; leave blank otherwise): " - default: "" + default: " " validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'sqlserver.%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" @@ -190,17 +190,9 @@ install: exit 1 } - $InstalledVersion = $null - $UninstallKeys = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -ErrorAction SilentlyContinue - foreach ($Key in $UninstallKeys) { - $KeyData = Get-ItemProperty -Path $Key.PSPath -ErrorAction SilentlyContinue - if ($KeyData.DisplayName -match "NRDOT Collector") { - $InstalledVersion = $KeyData.DisplayVersion - break - } - } + $ExistingService = Get-Service -Name $CollectorService -ErrorAction SilentlyContinue - if ($InstalledVersion) { + if ($ExistingService) { Write-Host -ForegroundColor Yellow "NRDOT Collector is already installed." Write-Host "" Write-Host "Please follow the steps below to complete the setup:" @@ -262,7 +254,6 @@ install: $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } $sqlFile = "$env:TEMP\nr-mssql-grant.sql" - $pwFile = "$env:TEMP\nr-mssql-newrelic-pw.tmp" function Invoke-GrantScript($connArgs, $file) { $result = & sqlcmd @connArgs -i $file 2>&1 @@ -282,15 +273,11 @@ install: } } - $grantSucceeded = $false - try { if ($mode -eq "1") { $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) - $NrPassword | Set-Content -Path $pwFile -NoNewline - Protect-TempFile $pwFile $sql = @" USE [master]; @@ -429,14 +416,10 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } - $grantSucceeded = $true Write-Host "SQL Server monitoring identity configured successfully." } finally { Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue - if (-not $grantSucceeded) { - Remove-Item -Path $pwFile -ErrorAction SilentlyContinue - } } PSEOF @@ -486,7 +469,6 @@ install: $licenseKey = "{{.NEW_RELIC_LICENSE_KEY}}" $configDir = "C:\Program Files\nrdot-collector" $configPath = "$configDir\mssql-config.yaml" - $pwFile = "$env:TEMP\nr-mssql-newrelic-pw.tmp" New-Item -Path $configDir -ItemType Directory -Force | Out-Null @@ -494,7 +476,15 @@ install: if ($mode -eq "1") { $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } - $NrPassword = (Get-Content -Path $pwFile -Raw) + $chars = (48..57) + (65..90) + (97..122) + $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) + $env:SQLCMDPASSWORD = '{{.NR_CLI_MSSQL_SA_PASSWORD}}' + $alterResult = & sqlcmd -S "$server,$port" -U sa -C -Q "ALTER LOGIN [$loginName] WITH PASSWORD = '$NrPassword';" 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to set the monitoring login's password:" + Write-Host $alterResult + exit 1 + } $receiverFields = " collection_interval: $interval`n username: $loginName`n password: $NrPassword`n server: $server`n port: $port" } else { $receiverFields = " collection_interval: $interval`n datasource: `"server=$server;port=$port;integrated security=true;encrypt=true;TrustServerCertificate=true;`"" @@ -994,9 +984,12 @@ install: $configText = $template.Replace('__RECEIVER_FIELDS__', $receiverFields).Replace('__OTLP_ENDPOINT__', $otlpEndpoint).Replace('__LICENSE_KEY__', $licenseKey) $configText | Set-Content -Path $configPath -Encoding utf8 - $serviceAccount = if ($mode -eq "1") { $null } - elseif ($mode -eq "2") { $env:NR_CLI_MSSQL_WIN_ACCOUNT } - else { $env:NR_CLI_MSSQL_GMSA_ACCOUNT } + $serviceAccount = $null + if ($mode -eq "2") { + $serviceAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + } elseif ($mode -eq "3") { + $serviceAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + } $grants = @("SYSTEM:(F)", "BUILTIN\Administrators:(F)") if ($serviceAccount) { $grants += "${serviceAccount}:(R)" } @@ -1007,10 +1000,6 @@ install: exit 1 } - if ($mode -eq "1") { - Remove-Item -Path $pwFile -ErrorAction SilentlyContinue - } - Write-Host "MSSQL OTel config written to $configPath" PSEOF From a4d59338e7f3c59d9289fdd8ab6de26fe663f9c0 Mon Sep 17 00:00:00 2001 From: rreddy Date: Thu, 13 Aug 2026 12:03:38 +0530 Subject: [PATCH 31/34] NRDOT MSSQL recipe for AWS RDS on Windows --- .../nrdot/mssql-otel/windows-rds.yml | 1087 +++++++++++++++++ 1 file changed, 1087 insertions(+) create mode 100644 recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml new file mode 100644 index 000000000..4b5667c5f --- /dev/null +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml @@ -0,0 +1,1087 @@ +# Visit our schema definition for additional information on this file format. +# https://github.com/newrelic/open-install-library/blob/main/docs/recipe-spec/recipe-spec.md#schema-definition + +name: nrdot-collector-mssql-rds +displayName: NRDOT MSSQL RDS (Windows) +description: New Relic install recipe for MSSQL monitoring via NRDOT Collector on Windows connecting to AWS RDS SQL Server instances +repository: https://github.com/newrelic/nrdot-collector-releases + +installTargets: + - type: host + os: windows + +keywords: + - MSSQL + - SQL Server + - NRDOT + - OpenTelemetry + - OTel + - Database + - Windows + - RDS + - AWS + +processMatch: [] + +preInstall: + discoveryMode: + - targeted + info: | + To capture data from your AWS RDS SQL Server instance, we need to create/authorize + a monitoring identity (a SQL login, a Windows domain account, or a gMSA account + depending on the authentication method you choose). For SQL Server Auth this + requires your RDS master user (or a user with the rds_superuser role); for Windows + Domain Auth / gMSA this requires administrative access to your RDS instance. This + host also needs network connectivity to the RDS endpoint (inbound access on the + SQL Server port in the RDS security group). + +inputVars: + - name: NR_CLI_MSSQL_CONFIG_PRESET + prompt: "NRDOT configuration - 1) Standard 2) Full-feature: " + default: "1" + - name: NR_CLI_MSSQL_AUTH_MODE + prompt: "SQL Server authentication - 1) SQL Server Auth 2) Windows Domain Auth 3) gMSA: " + default: "1" + - name: NR_CLI_MSSQL_SERVER + prompt: "RDS SQL Server endpoint (e.g. mydb.xxxxxxxxxx.us-east-1.rds.amazonaws.com): " + - name: NR_CLI_MSSQL_PORT + prompt: "SQL Server port: " + default: "1433" + - name: NR_CLI_MSSQL_MASTER_USER + prompt: "RDS master username (mode 1 only; leave blank otherwise): " + default: " " + - name: NR_CLI_MSSQL_MASTER_PASSWORD + prompt: "RDS master user password (mode 1 only, used once to create the monitoring login; leave blank otherwise): " + secret: true + default: " " + - name: NR_CLI_MSSQL_LOGIN_NAME + prompt: "Monitoring username: " + default: "newrelic" + - name: NR_CLI_MSSQL_WIN_ACCOUNT + prompt: "Windows domain account to grant permissions to, DOMAIN\\username (mode 2 only; leave blank otherwise): " + default: " " + - name: NR_CLI_MSSQL_WIN_PASSWORD + prompt: "Password for that domain account (mode 2 only; leave blank otherwise): " + secret: true + default: " " + - name: NR_CLI_MSSQL_GMSA_ACCOUNT + prompt: "gMSA account, DOMAIN\\gMSAName$ (mode 3 only; leave blank otherwise): " + default: " " + +validationNrql: "SELECT count(*) FROM Metric WHERE metricName LIKE 'sqlserver.%' AND instrumentation.provider = 'opentelemetry' SINCE 10 minutes ago" + +successLinkConfig: + type: EXPLORER + +install: + version: "3" + silent: true + + tasks: + write_recipe_metadata: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $metadata = '{"Metadata":{"CapturedCliOutput":"true"}}' + try { $metadata | Set-Content {{.NR_CLI_OUTPUT}} } catch {} + PSEOF + + assert_pre_req: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) + $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + if (-not $isAdmin) { + Write-Host -ForegroundColor Red "This newrelic install must be run in an Administrator PowerShell session." + exit 131 + } + + $sqlcmdPath = Get-Command sqlcmd.exe -ErrorAction SilentlyContinue + if (-not $sqlcmdPath) { + Write-Host -ForegroundColor Red "sqlcmd is required to configure SQL Server monitoring. Install the sqlcmd utility (https://learn.microsoft.com/sql/tools/sqlcmd/sqlcmd-utility) and re-run this recipe." + exit 16 + } + PSEOF + + assert_auth_inputs: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + + switch ($mode) { + "1" { + $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $masterPassword = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' + if ([string]::IsNullOrWhiteSpace($masterUser) -or $masterUser.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($masterPassword) -or $masterPassword.Trim() -eq "") { + Write-Host -ForegroundColor Red "Error: RDS master username and password are both required for SQL Server Auth (mode 1)." + Write-Host -ForegroundColor Red "Please re-run the installation and provide valid RDS master credentials." + exit 1 + } + } + "2" { + $winAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $winPassword = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' + if ([string]::IsNullOrWhiteSpace($winAccount) -or $winAccount.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($winPassword) -or $winPassword.Trim() -eq "") { + Write-Host -ForegroundColor Red "Error: Windows domain account and password are both required for Windows Domain Auth (mode 2)." + Write-Host -ForegroundColor Red "Please re-run the installation and provide valid credentials." + exit 1 + } + } + "3" { + $gmsaAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + if ([string]::IsNullOrWhiteSpace($gmsaAccount) -or $gmsaAccount.Trim() -eq "") { + Write-Host -ForegroundColor Red "Error: gMSA account is required for gMSA Auth (mode 3)." + Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid gMSA account." + exit 1 + } + } + default { + Write-Host -ForegroundColor Red "Error: NR_CLI_MSSQL_AUTH_MODE must be 1, 2, or 3." + exit 1 + } + } + PSEOF + + assert_sql_server_version: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $server = $env:NR_CLI_MSSQL_SERVER + $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + + if ($mode -eq "1") { + $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $masterPassword = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' + if ([string]::IsNullOrEmpty($masterPassword)) { + Write-Host -ForegroundColor Red "RDS master password is empty when preparing to connect to SQL Server - this should not happen." + exit 1 + } + $env:SQLCMDPASSWORD = $masterPassword + $connArgs = @("-S", "$server,$port", "-U", $masterUser, "-C") + } else { + $connArgs = @("-S", "$server,$port", "-E", "-C") + } + + $query = "SET NOCOUNT ON; SELECT SERVERPROPERTY('ProductMajorVersion');" + $result = & sqlcmd @connArgs -h -1 -Q $query 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to connect to SQL Server to check its version:" + Write-Host $result + exit 1 + } + + $majorVersionLine = ($result | Where-Object { $_ -match '^\s*\d+\s*$' } | Select-Object -First 1) + $majorVersion = if ($majorVersionLine) { [int]$majorVersionLine.Trim() } else { $null } + + if (-not $majorVersion -or $majorVersion -lt 14) { + Write-Host -ForegroundColor Red "SQL Server version $majorVersion is not supported. SQL Server 2017 or later (major version 14+) is required." + exit 1 + } + + Write-Host "SQL Server major version $majorVersion detected - supported." + PSEOF + + install_nrdot: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + [Net.ServicePointManager]::SecurityProtocol = "tls12, tls" + + $CollectorService = "nrdot-collector" + + $LatestVersion = (Invoke-RestMethod -Uri "https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest").tag_name + if (-not $LatestVersion) { + Write-Host -ForegroundColor Red "Failed to fetch the latest release version from GitHub. Please check your internet connection." + exit 1 + } + + $ExistingService = Get-Service -Name $CollectorService -ErrorAction SilentlyContinue + + if ($ExistingService) { + Write-Host -ForegroundColor Yellow "NRDOT Collector is already installed." + Write-Host "" + Write-Host "Please follow the steps below to complete the setup:" + Write-Host "" + Write-Host " 1. Create mssql-config.yaml" + Write-Host " if not present. Refer to:" + Write-Host " https://docs.newrelic.com/docs/opentelemetry/database/mssql/windows-rds/" + Write-Host "" + Write-Host " 2. Make sure the monitoring user is created" + Write-Host " and has the required permissions granted." + Write-Host "" + Write-Host " 3. After making the above changes, restart the NRDOT Collector:" + Write-Host " net stop nrdot-collector; net start nrdot-collector" + Write-Host "" + exit 131 + } + + Write-Host "Installing nrdot-collector version: $LatestVersion" + + $MsiPath = "$env:TEMP\nrdot-collector.msi" + $LogPath = "$env:TEMP\nrdot_install.log" + $DownloadUrl = "https://github.com/newrelic/nrdot-collector-releases/releases/download/$LatestVersion/nrdot-collector_${LatestVersion}_windows_x64.msi" + Write-Host "Downloading from: $DownloadUrl" + + $WebClient = New-Object System.Net.WebClient + $WebClient.Headers.Add("User-Agent", "Mozilla/5.0") + if ($env:HTTPS_PROXY) { + $WebClient.Proxy = New-Object System.Net.WebProxy($env:HTTPS_PROXY, $true) + } + try { + $WebClient.DownloadFile($DownloadUrl, $MsiPath) + } catch { + Write-Host -ForegroundColor Red "Failed to download the nrdot-collector package: $_" + exit 1 + } + + $Process = Start-Process -FilePath "msiexec.exe" -ArgumentList "/i `"$MsiPath`" /qn /norestart /L*V `"$LogPath`"" -Wait -NoNewWindow -PassThru + if ($Process.ExitCode -ne 0) { + Write-Host -ForegroundColor Red "msiexec failed with exit code $($Process.ExitCode). See $LogPath for details." + exit $Process.ExitCode + } + + Remove-Item -Path $MsiPath -ErrorAction SilentlyContinue + + $Service = Get-Service -Name $CollectorService -ErrorAction SilentlyContinue + if (-not $Service) { + Write-Host -ForegroundColor Red "nrdot-collector service was not found after installation." + exit 1 + } + + Write-Host "nrdot-collector installed successfully." + PSEOF + + configure_database_user: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $server = $env:NR_CLI_MSSQL_SERVER + $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $sqlFile = "$env:TEMP\nr-mssql-grant.sql" + + function Invoke-GrantScript($connArgs, $file) { + $result = & sqlcmd @connArgs -i $file 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "SQL script failed:" + Write-Host $result + exit 1 + } + Write-Host $result + } + + function Protect-TempFile($path) { + icacls $path /inheritance:r /grant:r "SYSTEM:(F)" "BUILTIN\Administrators:(F)" | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to restrict permissions on $path." + exit 1 + } + } + + try { + if ($mode -eq "1") { + $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } + $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $chars = (48..57) + (65..90) + (97..122) + $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) + + $sql = @" + USE [master]; + GO + IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = '$loginName') + CREATE LOGIN [$loginName] WITH PASSWORD = '$NrPassword'; + ELSE + ALTER LOGIN [$loginName] WITH PASSWORD = '$NrPassword'; + GO + GRANT VIEW SERVER STATE TO [$loginName]; + GRANT VIEW ANY DEFINITION TO [$loginName]; + GRANT VIEW ANY DATABASE TO [$loginName]; + GO + DECLARE @name SYSNAME; + DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR + SELECT [name] + FROM [master].[sys].[databases] + WHERE [name] NOT IN ('master', 'msdb', 'model', 'rdsadmin', 'distribution') + AND [state] = 0; + OPEN db_cursor; + FETCH NEXT FROM db_cursor INTO @name; + WHILE @@FETCH_STATUS = 0 + BEGIN + BEGIN TRY + EXEC('USE [' + @name + ']; + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''$loginName'') + BEGIN + CREATE USER [$loginName] FOR LOGIN [$loginName]; + END; + GRANT VIEW DATABASE STATE TO [$loginName];'); + END TRY + BEGIN CATCH + PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); + END CATCH + FETCH NEXT FROM db_cursor INTO @name; + END + CLOSE db_cursor; + DEALLOCATE db_cursor; + GO + "@ + $sql | Set-Content -Path $sqlFile + Protect-TempFile $sqlFile + $masterPassword = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' + if ([string]::IsNullOrEmpty($masterPassword)) { + Write-Host -ForegroundColor Red "RDS master password is empty when preparing to connect to SQL Server - this should not happen." + exit 1 + } + $env:SQLCMDPASSWORD = $masterPassword + Invoke-GrantScript @("-S", "$server,$port", "-U", $masterUser, "-C") $sqlFile + } + elseif ($mode -eq "2") { + $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $sql = @" + USE [master]; + GO + IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = '$account') + CREATE LOGIN [$account] FROM WINDOWS; + GO + GRANT VIEW SERVER STATE TO [$account]; + GRANT VIEW ANY DEFINITION TO [$account]; + GRANT VIEW ANY DATABASE TO [$account]; + GO + DECLARE @name SYSNAME; + DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR + SELECT [name] + FROM [master].[sys].[databases] + WHERE [name] NOT IN ('master', 'msdb', 'model', 'rdsadmin', 'distribution') + AND [state] = 0; + OPEN db_cursor; + FETCH NEXT FROM db_cursor INTO @name; + WHILE @@FETCH_STATUS = 0 + BEGIN + BEGIN TRY + EXEC('USE [' + @name + ']; + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''$account'') + BEGIN + CREATE USER [$account] FOR LOGIN [$account]; + END; + GRANT VIEW DATABASE STATE TO [$account];'); + END TRY + BEGIN CATCH + PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); + END CATCH + FETCH NEXT FROM db_cursor INTO @name; + END + CLOSE db_cursor; + DEALLOCATE db_cursor; + GO + "@ + $sql | Set-Content -Path $sqlFile + Protect-TempFile $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile + } + else { + $gmsa = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $sql = @" + USE master; + GO + IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = '$gmsa') + CREATE LOGIN [$gmsa] FROM WINDOWS; + GO + GRANT VIEW SERVER STATE TO [$gmsa]; + GRANT VIEW ANY DEFINITION TO [$gmsa]; + GRANT VIEW ANY DATABASE TO [$gmsa]; + GO + DECLARE @name SYSNAME; + DECLARE db_cursor CURSOR READ_ONLY FORWARD_ONLY FOR + SELECT [name] FROM [master].[sys].[databases] + WHERE [name] NOT IN ('master','msdb','model','rdsadmin','distribution') + AND [state] = 0; + OPEN db_cursor; + FETCH NEXT FROM db_cursor INTO @name; + WHILE @@FETCH_STATUS = 0 + BEGIN + BEGIN TRY + EXEC('USE [' + @name + ']; + IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''$gmsa'') + BEGIN + CREATE USER [$gmsa] FOR LOGIN [$gmsa]; + END; + GRANT VIEW DATABASE STATE TO [$gmsa];'); + END TRY + BEGIN CATCH + PRINT 'Error on ' + @name + ': ' + ERROR_MESSAGE(); + END CATCH + FETCH NEXT FROM db_cursor INTO @name; + END + CLOSE db_cursor; + DEALLOCATE db_cursor; + GO + "@ + $sql | Set-Content -Path $sqlFile + Protect-TempFile $sqlFile + Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile + } + + Write-Host "SQL Server monitoring identity configured successfully." + } + finally { + Remove-Item -Path $sqlFile -ErrorAction SilentlyContinue + } + PSEOF + + configure_service_identity: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $service = "nrdot-collector" + + if ($mode -eq "1") { + Write-Host "SQL Server Auth selected - service logon account left unchanged (LocalSystem)." + exit 0 + } + + if ($mode -eq "2") { + $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $password = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' + } else { + $account = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $password = "" + } + + Stop-Service -Name $service -Force -ErrorAction SilentlyContinue + + & sc.exe config "$service" obj= "$account" password= "$password" + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to configure the nrdot-collector service to run as $account." + exit 1 + } + + $svcInfo = Get-WmiObject Win32_Service -Filter "Name='$service'" + Write-Host "nrdot-collector service logon account set to: $($svcInfo.StartName)" + + Start-Service -Name $service + PSEOF + + create_collector_config: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" + $preset = "{{.NR_CLI_MSSQL_CONFIG_PRESET}}" + $server = $env:NR_CLI_MSSQL_SERVER + $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $region = "{{.NEW_RELIC_REGION}}" + $licenseKey = "{{.NEW_RELIC_LICENSE_KEY}}" + $configDir = "C:\Program Files\nrdot-collector" + $configPath = "$configDir\mssql-config.yaml" + + New-Item -Path $configDir -ItemType Directory -Force | Out-Null + + $interval = if ($preset -eq "2") { "30s" } else { "15s" } + + if ($mode -eq "1") { + $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } + $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $chars = (48..57) + (65..90) + (97..122) + $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) + $env:SQLCMDPASSWORD = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' + $alterResult = & sqlcmd -S "$server,$port" -U $masterUser -C -Q "ALTER LOGIN [$loginName] WITH PASSWORD = '$NrPassword';" 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to set the monitoring login's password:" + Write-Host $alterResult + exit 1 + } + $receiverFields = " collection_interval: $interval`n username: $loginName`n password: $NrPassword`n server: $server`n port: $port" + } else { + $receiverFields = " collection_interval: $interval`n datasource: `"server=$server;port=$port;integrated security=true;encrypt=true;TrustServerCertificate=true;`"" + } + + switch ($region) { + "staging" { $otlpEndpoint = "https://staging-otlp.nr-data.net" } + "EU" { $otlpEndpoint = "https://otlp.eu01.nr-data.net" } + "JP" { $otlpEndpoint = "https://otlp.jp.nr-data.net" } + default { $otlpEndpoint = "https://otlp.nr-data.net" } + } + + if ($preset -eq "2") { + $template = @' + extensions: + health_check: + + receivers: + otlp: + protocols: + grpc: + http: + + host_metrics: + collection_interval: 30s + scrapers: + cpu: + metrics: + system.cpu.time: + enabled: false + system.cpu.utilization: + enabled: true + load: + memory: + metrics: + system.memory.utilization: + enabled: true + paging: + metrics: + system.paging.utilization: + enabled: false + system.paging.faults: + enabled: false + disk: + metrics: + system.disk.merged: + enabled: false + system.disk.pending_operations: + enabled: false + system.disk.weighted_io_time: + enabled: false + network: + metrics: + system.network.connections: + enabled: false + processes: + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_user_error: true + mute_process_io_error: true + metrics: + process.cpu.utilization: + enabled: true + process.memory.utilization: + enabled: true + + nrsqlserver: + __RECEIVER_FIELDS__ + metrics: + sqlserver.database.count: + enabled: true + sqlserver.database.file.size: + enabled: true + sqlserver.database.io: + enabled: true + sqlserver.database.latency: + enabled: true + sqlserver.database.operations: + enabled: true + sqlserver.database.transactions.active: + enabled: true + sqlserver.database.backup_or_restore.rate: + enabled: true + sqlserver.database.full_scan.rate: + enabled: true + sqlserver.database.tempdb.space: + enabled: true + sqlserver.database.tempdb.version_store.size: + enabled: true + sqlserver.lock.timeout.rate: + enabled: true + sqlserver.lock.wait.count: + enabled: true + sqlserver.deadlock.rate: + enabled: true + sqlserver.transaction.delay: + enabled: true + sqlserver.transaction.longest_running_time: + enabled: true + sqlserver.transaction.version_cleanup.rate: + enabled: true + sqlserver.transaction.version_generation.rate: + enabled: true + sqlserver.memory.area: + enabled: true + sqlserver.memory.cache.object.count: + enabled: true + sqlserver.memory.grants.pending.count: + enabled: true + sqlserver.memory.page.count: + enabled: true + sqlserver.memory.target: + enabled: true + sqlserver.memory.usage: + enabled: true + sqlserver.os.memory.usage: + enabled: true + sqlserver.os.memory.utilization: + enabled: true + sqlserver.os.scheduler.runnable_tasks.count: + enabled: true + sqlserver.os.wait.duration: + enabled: true + sqlserver.os.wait.tasks.count: + enabled: true + sqlserver.page.buffer_cache.free_list.stalls.rate: + enabled: true + sqlserver.page.lookup.rate: + enabled: true + sqlserver.batch.compilation.utilization: + enabled: true + sqlserver.batch.page_split.utilization: + enabled: true + sqlserver.recompilation.ratio: + enabled: true + sqlserver.parameterization.rate: + enabled: true + sqlserver.plan.execution.rate: + enabled: true + sqlserver.attention.rate: + enabled: true + sqlserver.process.count: + enabled: true + sqlserver.processes.blocked: + enabled: true + sqlserver.login.rate: + enabled: true + sqlserver.logout.rate: + enabled: true + sqlserver.thread_pool.tasks.count: + enabled: true + sqlserver.thread_pool.workers.count: + enabled: true + sqlserver.thread_pool.workers.max: + enabled: true + sqlserver.thread_pool.workers.utilization: + enabled: true + sqlserver.tempdb.allocation.wait_time.total: + enabled: true + sqlserver.tempdb.contention.waiters.count: + enabled: true + sqlserver.tempdb.data_files.count: + enabled: true + sqlserver.tempdb.file.size: + enabled: true + sqlserver.tempdb.space.usage: + enabled: true + sqlserver.latch.superlatch.count: + enabled: true + sqlserver.latch.superlatch.transition.rate: + enabled: true + sqlserver.latch.wait.rate: + enabled: true + sqlserver.latch.wait_time.avg: + enabled: true + sqlserver.latch.wait_time.total: + enabled: true + sqlserver.index.search.rate: + enabled: true + sqlserver.resource_pool.disk.operations: + enabled: true + sqlserver.resource_pool.disk.throttled.read.rate: + enabled: true + sqlserver.resource_pool.disk.throttled.write.rate: + enabled: true + sqlserver.replica.data.rate: + enabled: true + sqlserver.failover_cluster.ag.cluster_type: + enabled: true + sqlserver.failover_cluster.ag.failure_condition_level: + enabled: true + sqlserver.failover_cluster.ag.health_check_timeout: + enabled: true + sqlserver.failover_cluster.ag.required_sync_secondaries: + enabled: true + sqlserver.failover_cluster.replica.database.queue_size: + enabled: true + sqlserver.failover_cluster.replica.database.redo.rate: + enabled: true + sqlserver.failover_cluster.replica.flow_control_time: + enabled: true + sqlserver.failover_cluster.replica.role: + enabled: true + sqlserver.failover_cluster.replica.synchronization_health: + enabled: true + sqlserver.computer.uptime: + enabled: true + sqlserver.cpu.count: + enabled: true + sqlserver.table.count: + enabled: true + sqlserver.kill_connection.error.rate: + enabled: true + + events: + db.server.query_sample: + enabled: true + db.server.top_query: + enabled: true + + top_query_collection: + lookback_time: 60s + max_query_sample_count: 500 + top_query_count: 200 + collection_interval: 60s + + collect_full_query_text: true + allowed_comment_keys: + - nr_service_guid + + query_sample_collection: + max_rows_per_query: 100 + + processors: + metrics_transform: + transforms: + - include: system.cpu.utilization + action: update + operations: + - action: aggregate_labels + label_set: [state] + aggregation_type: mean + - include: system.paging.operations + action: update + operations: + - action: aggregate_labels + label_set: [direction] + aggregation_type: sum + + filter/exclude_cpu_utilization: + metrics: + datapoint: + - 'metric.name == "system.cpu.utilization" and attributes["state"] == "interrupt"' + - 'metric.name == "system.cpu.utilization" and attributes["state"] == "nice"' + - 'metric.name == "system.cpu.utilization" and attributes["state"] == "softirq"' + + filter/exclude_memory_utilization: + metrics: + datapoint: + - 'metric.name == "system.memory.utilization" and attributes["state"] == "slab_unreclaimable"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "inactive"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "cached"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "buffered"' + - 'metric.name == "system.memory.utilization" and attributes["state"] == "slab_reclaimable"' + + filter/exclude_memory_usage: + metrics: + datapoint: + - 'metric.name == "system.memory.usage" and attributes["state"] == "slab_unreclaimable"' + - 'metric.name == "system.memory.usage" and attributes["state"] == "inactive"' + + filter/exclude_filesystem_utilization: + metrics: + datapoint: + - 'metric.name == "system.filesystem.utilization" and attributes["type"] == "squashfs"' + + filter/exclude_filesystem_usage: + metrics: + datapoint: + - 'metric.name == "system.filesystem.usage" and attributes["type"] == "squashfs"' + - 'metric.name == "system.filesystem.usage" and attributes["state"] == "reserved"' + + filter/exclude_filesystem_inodes_usage: + metrics: + datapoint: + - 'metric.name == "system.filesystem.inodes.usage" and attributes["type"] == "squashfs"' + - 'metric.name == "system.filesystem.inodes.usage" and attributes["state"] == "reserved"' + + filter/exclude_system_disk: + metrics: + datapoint: + - 'metric.name == "system.disk.operations" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.merged" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.io" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.io_time" and IsMatch(attributes["device"], "^loop.*") == true' + - 'metric.name == "system.disk.operation_time" and IsMatch(attributes["device"], "^loop.*") == true' + + filter/exclude_system_paging: + metrics: + datapoint: + - 'metric.name == "system.paging.usage" and attributes["state"] == "cached"' + - 'metric.name == "system.paging.operations" and attributes["type"] == "cached"' + + filter/exclude_network: + metrics: + datapoint: + - 'IsMatch(metric.name, "^system.network.*") == true and attributes["device"] == "lo"' + + attributes/exclude_system_paging: + include: + match_type: strict + metric_names: + - system.paging.operations + actions: + - key: type + action: delete + + cumulativetodelta: + + transform/host: + metric_statements: + - context: metric + statements: + - set(metric.description, "") + - set(metric.unit, "") + + batch: + + resource_detection: + detectors: ["system"] + system: + hostname_sources: ["os"] + resource_attributes: + host.id: + enabled: true + + resource_detection/cloud: + detectors: ["gcp", "ec2", "azure"] + timeout: 2s + override: true + + resource_detection/env: + detectors: ["env"] + timeout: 2s + override: true + + exporters: + otlphttp: + endpoint: __OTLP_ENDPOINT__ + headers: + api-key: __LICENSE_KEY__ + tls: + insecure: false + compression: gzip + + service: + telemetry: + metrics: + level: none + + extensions: [health_check] + + pipelines: + metrics/host: + receivers: [host_metrics, nrsqlserver] + processors: + - metrics_transform + - filter/exclude_cpu_utilization + - filter/exclude_memory_utilization + - filter/exclude_memory_usage + - filter/exclude_filesystem_utilization + - filter/exclude_filesystem_usage + - filter/exclude_filesystem_inodes_usage + - filter/exclude_system_disk + - filter/exclude_network + - attributes/exclude_system_paging + - transform/host + - resource_detection + - resource_detection/cloud + - resource_detection/env + - cumulativetodelta + - batch + exporters: [otlphttp] + + logs/host: + receivers: [nrsqlserver] + processors: + - resource_detection + - resource_detection/cloud + - resource_detection/env + - batch + exporters: [otlphttp] + + traces: + receivers: [otlp] + processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch] + exporters: [otlphttp] + + metrics: + receivers: [otlp] + processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch] + exporters: [otlphttp] + + logs: + receivers: [otlp] + processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch] + exporters: [otlphttp] + '@ + } else { + $template = @' + receivers: + nrsqlserver: + __RECEIVER_FIELDS__ + metrics: + sqlserver.database.count: + enabled: true + sqlserver.database.io: + enabled: true + sqlserver.database.latency: + enabled: true + sqlserver.database.operations: + enabled: true + sqlserver.database.tempdb.space: + enabled: true + sqlserver.database.tempdb.version_store.size: + enabled: true + sqlserver.deadlock.rate: + enabled: true + sqlserver.os.wait.duration: + enabled: true + sqlserver.processes.blocked: + enabled: true + sqlserver.memory.grants.pending.count: + enabled: true + sqlserver.database.file.size: + enabled: true + sqlserver.memory.area: + enabled: true + + events: + db.server.query_sample: + enabled: true + db.server.top_query: + enabled: true + + top_query_collection: + lookback_time: 60s + max_query_sample_count: 1000 + top_query_count: 250 + collection_interval: 60s + + collect_full_query_text: true + allowed_comment_keys: + - nr_service_guid + + query_sample_collection: + max_rows_per_query: 100 + + processors: + memory_limiter: + check_interval: ${env:NR_MEM_LIMITER_CHECK_INTERVAL:-1s} + limit_mib: ${env:NR_MEM_LIMITER_LIMIT_MIB:-200} + spike_limit_mib: ${env:NR_MEM_LIMITER_SPIKE_MIB:-50} + + batch: + + exporters: + otlphttp: + endpoint: __OTLP_ENDPOINT__ + headers: + api-key: __LICENSE_KEY__ + tls: + insecure: false + compression: gzip + + service: + telemetry: + metrics: + level: none + + pipelines: + metrics: + receivers: [nrsqlserver] + processors: [memory_limiter, batch] + exporters: [otlphttp] + + logs: + receivers: [nrsqlserver] + processors: [memory_limiter, batch] + exporters: [otlphttp] + '@ + } + + $configText = $template.Replace('__RECEIVER_FIELDS__', $receiverFields).Replace('__OTLP_ENDPOINT__', $otlpEndpoint).Replace('__LICENSE_KEY__', $licenseKey) + $configText | Set-Content -Path $configPath -Encoding utf8 + + $serviceAccount = $null + if ($mode -eq "2") { + $serviceAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + } elseif ($mode -eq "3") { + $serviceAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + } + + $grants = @("SYSTEM:(F)", "BUILTIN\Administrators:(F)") + if ($serviceAccount) { $grants += "${serviceAccount}:(R)" } + + icacls $configPath /inheritance:r /grant:r $grants | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "Failed to restrict permissions on $configPath." + exit 1 + } + + Write-Host "MSSQL OTel config written to $configPath" + PSEOF + + configure_service: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $service = "nrdot-collector" + $exePath = "C:\Program Files\nrdot-collector\nrdot-collector.exe" + $configPath = "C:\Program Files\nrdot-collector\mssql-config.yaml" + $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\nrdot-collector" + + $existingImagePath = (Get-ItemProperty -Path $regPath -Name "ImagePath" -ErrorAction SilentlyContinue).ImagePath + + if ($existingImagePath -and $existingImagePath -match [regex]::Escape("mssql-config.yaml")) { + Write-Host "nrdot-collector service is already configured to use mssql-config.yaml." + } else { + $newImagePath = "`"$exePath`" --config `"$configPath`"" + Set-ItemProperty -Path $regPath -Name "ImagePath" -Value $newImagePath + Write-Host "nrdot-collector service ImagePath updated to use mssql-config.yaml." + } + + $validateOutput = & $exePath validate --config="$configPath" 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host -ForegroundColor Red "mssql-config.yaml failed validation:" + Write-Host $validateOutput + exit 1 + } + Write-Host "mssql-config.yaml validated successfully." + PSEOF + + restart_nrdot: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + net stop nrdot-collector + net start nrdot-collector + PSEOF + + assert_nrdot_status_ok: + cmds: + - | + powershell -NoProfile -Command - <<'PSEOF' + $maxRetries = 30 + $tries = 0 + Write-Host "Waiting for NRDOT Collector to start..." + while ($tries -lt $maxRetries) { + $tries++ + $service = Get-Service -Name "nrdot-collector" -ErrorAction SilentlyContinue + if ($service -and $service.Status -eq "Running") { + Write-Host "NRDOT Collector is running." + exit 0 + } + Start-Sleep -Seconds 2 + } + Write-Host -ForegroundColor Red "NRDOT Collector did not start in time. Install log:" + Get-Content "$env:TEMP\nrdot_install.log" -ErrorAction SilentlyContinue | Select-Object -Last 50 + exit 31 + PSEOF + + default: + cmds: + - task: write_recipe_metadata + - task: assert_pre_req + - task: assert_auth_inputs + - task: assert_sql_server_version + - task: install_nrdot + - task: configure_database_user + - task: configure_service_identity + - task: create_collector_config + - task: configure_service + - task: restart_nrdot + - task: assert_nrdot_status_ok + +postInstall: + info: |2 + MSSQL OTel config: C:\Program Files\nrdot-collector\mssql-config.yaml + Service status: Get-Service nrdot-collector + Restart service: net stop nrdot-collector; net start nrdot-collector From e5f8d189a4e9efa08a5967cb315790942e0a00c4 Mon Sep 17 00:00:00 2001 From: rreddy Date: Thu, 13 Aug 2026 16:30:54 +0530 Subject: [PATCH 32/34] eplaced with '{{.NR_CLI_MSSQL_*}}' template substitution --- .../nrdot/mssql-otel/windows-rds.yml | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml index 4b5667c5f..ff6eae84e 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml @@ -112,7 +112,7 @@ install: switch ($mode) { "1" { - $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $masterUser = '{{.NR_CLI_MSSQL_MASTER_USER}}' $masterPassword = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' if ([string]::IsNullOrWhiteSpace($masterUser) -or $masterUser.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($masterPassword) -or $masterPassword.Trim() -eq "") { Write-Host -ForegroundColor Red "Error: RDS master username and password are both required for SQL Server Auth (mode 1)." @@ -121,7 +121,7 @@ install: } } "2" { - $winAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $winAccount = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' $winPassword = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' if ([string]::IsNullOrWhiteSpace($winAccount) -or $winAccount.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($winPassword) -or $winPassword.Trim() -eq "") { Write-Host -ForegroundColor Red "Error: Windows domain account and password are both required for Windows Domain Auth (mode 2)." @@ -130,7 +130,7 @@ install: } } "3" { - $gmsaAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $gmsaAccount = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' if ([string]::IsNullOrWhiteSpace($gmsaAccount) -or $gmsaAccount.Trim() -eq "") { Write-Host -ForegroundColor Red "Error: gMSA account is required for gMSA Auth (mode 3)." Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid gMSA account." @@ -149,11 +149,11 @@ install: - | powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" - $server = $env:NR_CLI_MSSQL_SERVER - $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $server = '{{.NR_CLI_MSSQL_SERVER}}' + $port = '{{.NR_CLI_MSSQL_PORT}}' if ($mode -eq "1") { - $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $masterUser = '{{.NR_CLI_MSSQL_MASTER_USER}}' $masterPassword = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' if ([string]::IsNullOrEmpty($masterPassword)) { Write-Host -ForegroundColor Red "RDS master password is empty when preparing to connect to SQL Server - this should not happen." @@ -259,8 +259,8 @@ install: - | powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" - $server = $env:NR_CLI_MSSQL_SERVER - $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $server = '{{.NR_CLI_MSSQL_SERVER}}' + $port = '{{.NR_CLI_MSSQL_PORT}}' $sqlFile = "$env:TEMP\nr-mssql-grant.sql" function Invoke-GrantScript($connArgs, $file) { @@ -283,8 +283,8 @@ install: try { if ($mode -eq "1") { - $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } - $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $loginName = '{{.NR_CLI_MSSQL_LOGIN_NAME}}' + $masterUser = '{{.NR_CLI_MSSQL_MASTER_USER}}' $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) @@ -338,7 +338,7 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-U", $masterUser, "-C") $sqlFile } elseif ($mode -eq "2") { - $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $account = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' $sql = @" USE [master]; GO @@ -381,7 +381,7 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } else { - $gmsa = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $gmsa = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' $sql = @" USE master; GO @@ -443,10 +443,10 @@ install: } if ($mode -eq "2") { - $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $account = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' $password = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' } else { - $account = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $account = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' $password = "" } @@ -470,8 +470,8 @@ install: powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" $preset = "{{.NR_CLI_MSSQL_CONFIG_PRESET}}" - $server = $env:NR_CLI_MSSQL_SERVER - $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $server = '{{.NR_CLI_MSSQL_SERVER}}' + $port = '{{.NR_CLI_MSSQL_PORT}}' $region = "{{.NEW_RELIC_REGION}}" $licenseKey = "{{.NEW_RELIC_LICENSE_KEY}}" $configDir = "C:\Program Files\nrdot-collector" @@ -482,8 +482,8 @@ install: $interval = if ($preset -eq "2") { "30s" } else { "15s" } if ($mode -eq "1") { - $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } - $masterUser = $env:NR_CLI_MSSQL_MASTER_USER + $loginName = '{{.NR_CLI_MSSQL_LOGIN_NAME}}' + $masterUser = '{{.NR_CLI_MSSQL_MASTER_USER}}' $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) $env:SQLCMDPASSWORD = '{{.NR_CLI_MSSQL_MASTER_PASSWORD}}' @@ -992,9 +992,9 @@ install: $serviceAccount = $null if ($mode -eq "2") { - $serviceAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $serviceAccount = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' } elseif ($mode -eq "3") { - $serviceAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $serviceAccount = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' } $grants = @("SYSTEM:(F)", "BUILTIN\Administrators:(F)") From 4bf768331577dddc87d3b85c88d576937ed04c01 Mon Sep 17 00:00:00 2001 From: rreddy Date: Thu, 13 Aug 2026 18:17:43 +0530 Subject: [PATCH 33/34] atch sqlcmd/service-config failures caused by : inputVar reads in windows.yml --- .../nrdot/mssql-otel/windows.yml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index 2a6754ea1..f2eac5995 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -114,7 +114,7 @@ install: } } "2" { - $winAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $winAccount = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' $winPassword = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' if ([string]::IsNullOrWhiteSpace($winAccount) -or $winAccount.Trim() -eq "" -or [string]::IsNullOrWhiteSpace($winPassword) -or $winPassword.Trim() -eq "") { Write-Host -ForegroundColor Red "Error: Windows account and password are both required for Windows Auth (mode 2)." @@ -123,7 +123,7 @@ install: } } "3" { - $gmsaAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $gmsaAccount = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' if ([string]::IsNullOrWhiteSpace($gmsaAccount) -or $gmsaAccount.Trim() -eq "") { Write-Host -ForegroundColor Red "Error: gMSA account is required for gMSA Auth (mode 3)." Write-Host -ForegroundColor Red "Please re-run the installation and provide a valid gMSA account." @@ -142,8 +142,8 @@ install: - | powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" - $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } - $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $server = '{{.NR_CLI_MSSQL_SERVER}}' + $port = '{{.NR_CLI_MSSQL_PORT}}' if ($mode -eq "1") { $saPassword = '{{.NR_CLI_MSSQL_SA_PASSWORD}}' @@ -251,8 +251,8 @@ install: - | powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" - $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } - $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $server = '{{.NR_CLI_MSSQL_SERVER}}' + $port = '{{.NR_CLI_MSSQL_PORT}}' $sqlFile = "$env:TEMP\nr-mssql-grant.sql" function Invoke-GrantScript($connArgs, $file) { @@ -275,7 +275,7 @@ install: try { if ($mode -eq "1") { - $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } + $loginName = '{{.NR_CLI_MSSQL_LOGIN_NAME}}' $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) @@ -329,7 +329,7 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-U", "sa", "-C") $sqlFile } elseif ($mode -eq "2") { - $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $account = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' $sql = @" USE [master]; GO @@ -374,7 +374,7 @@ install: Invoke-GrantScript @("-S", "$server,$port", "-E", "-C") $sqlFile } else { - $gmsa = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $gmsa = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' $sql = @" USE master; GO @@ -436,10 +436,10 @@ install: } if ($mode -eq "2") { - $account = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $account = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' $password = '{{.NR_CLI_MSSQL_WIN_PASSWORD}}' } else { - $account = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $account = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' $password = "" } @@ -463,8 +463,8 @@ install: powershell -NoProfile -Command - <<'PSEOF' $mode = "{{.NR_CLI_MSSQL_AUTH_MODE}}" $preset = "{{.NR_CLI_MSSQL_CONFIG_PRESET}}" - $server = if ($env:NR_CLI_MSSQL_SERVER) { $env:NR_CLI_MSSQL_SERVER } else { "localhost" } - $port = if ($env:NR_CLI_MSSQL_PORT) { $env:NR_CLI_MSSQL_PORT } else { "1433" } + $server = '{{.NR_CLI_MSSQL_SERVER}}' + $port = '{{.NR_CLI_MSSQL_PORT}}' $region = "{{.NEW_RELIC_REGION}}" $licenseKey = "{{.NEW_RELIC_LICENSE_KEY}}" $configDir = "C:\Program Files\nrdot-collector" @@ -475,7 +475,7 @@ install: $interval = if ($preset -eq "2") { "30s" } else { "15s" } if ($mode -eq "1") { - $loginName = if ($env:NR_CLI_MSSQL_LOGIN_NAME) { $env:NR_CLI_MSSQL_LOGIN_NAME } else { "newrelic" } + $loginName = '{{.NR_CLI_MSSQL_LOGIN_NAME}}' $chars = (48..57) + (65..90) + (97..122) $NrPassword = -join ($chars | Get-Random -Count 24 | ForEach-Object {[char]$_}) $env:SQLCMDPASSWORD = '{{.NR_CLI_MSSQL_SA_PASSWORD}}' @@ -986,9 +986,9 @@ install: $serviceAccount = $null if ($mode -eq "2") { - $serviceAccount = $env:NR_CLI_MSSQL_WIN_ACCOUNT + $serviceAccount = '{{.NR_CLI_MSSQL_WIN_ACCOUNT}}' } elseif ($mode -eq "3") { - $serviceAccount = $env:NR_CLI_MSSQL_GMSA_ACCOUNT + $serviceAccount = '{{.NR_CLI_MSSQL_GMSA_ACCOUNT}}' } $grants = @("SYSTEM:(F)", "BUILTIN\Administrators:(F)") From 67b45e1e5cf5359e772a976d8c12d74f1d9d34c6 Mon Sep 17 00:00:00 2001 From: rreddy Date: Thu, 13 Aug 2026 21:41:52 +0530 Subject: [PATCH 34/34] drop two metric keys the released nrdot-collector rejects at config validate --- .../newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml | 4 ---- recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml index ff6eae84e..350751a48 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows-rds.yml @@ -605,8 +605,6 @@ install: enabled: true sqlserver.memory.page.count: enabled: true - sqlserver.memory.target: - enabled: true sqlserver.memory.usage: enabled: true sqlserver.os.memory.usage: @@ -705,8 +703,6 @@ install: enabled: true sqlserver.table.count: enabled: true - sqlserver.kill_connection.error.rate: - enabled: true events: db.server.query_sample: diff --git a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml index f2eac5995..ca325ef4f 100644 --- a/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml +++ b/recipes/newrelic/infrastructure/nrdot/mssql-otel/windows.yml @@ -599,8 +599,6 @@ install: enabled: true sqlserver.memory.page.count: enabled: true - sqlserver.memory.target: - enabled: true sqlserver.memory.usage: enabled: true sqlserver.os.memory.usage: @@ -699,8 +697,6 @@ install: enabled: true sqlserver.table.count: enabled: true - sqlserver.kill_connection.error.rate: - enabled: true events: db.server.query_sample: