├── .gitignore ├── Makefile ├── PowerShellExporter.sln ├── PowerShellExporter ├── App.config ├── Metric.cs ├── MetricsCollector.cs ├── MetricsConfig.cs ├── PowerShellExporter.csproj ├── Program.cs ├── grafana-dashboard.json ├── metrics.psm1 └── metrics.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin/ 3 | obj/ 4 | dist/ 5 | tmp/ 6 | *.user 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | dist: dist/PowerShellExporter.zip 2 | 3 | dist/PowerShellExporter.zip: dist/PowerShellExporter.exe dist/metrics.yml dist/metrics.psm1 4 | cd dist && \ 5 | rm -f PowerShellExporter.zip && \ 6 | zip -9 PowerShellExporter.zip \ 7 | PowerShellExporter.exe{,.config} \ 8 | metrics.yml \ 9 | metrics.psm1 && \ 10 | unzip -l PowerShellExporter.zip && \ 11 | sha256sum PowerShellExporter.zip 12 | 13 | dist/PowerShellExporter.exe: PowerShellExporter/bin/Release/net461/PowerShellExporter.exe tmp/libz.exe 14 | mkdir -p dist 15 | # NB to be able to load Serilog.Sinks.File from .config we need to use Scenario 4 as 16 | # described at https://github.com/MiloszKrajewski/LibZ/blob/master/doc/scenarios.md 17 | cd PowerShellExporter/bin/Release/net461 && \ 18 | ../../../../tmp/libz add --libz PowerShellExporter.libz --include '*.dll' --move && \ 19 | ../../../../tmp/libz inject-libz --assembly PowerShellExporter.exe --libz PowerShellExporter.libz --move && \ 20 | ../../../../tmp/libz instrument --assembly PowerShellExporter.exe --libz-resources 21 | cp PowerShellExporter/bin/Release/net461/PowerShellExporter.exe* dist 22 | 23 | dist/metrics.yml: PowerShellExporter/metrics.yml 24 | mkdir -p dist 25 | cp $< $@ 26 | 27 | dist/metrics.psm1: PowerShellExporter/metrics.psm1 28 | mkdir -p dist 29 | cp $< $@ 30 | 31 | tmp/libz.exe: 32 | mkdir -p tmp 33 | wget -Otmp/libz-1.2.0.0-tool.zip https://github.com/MiloszKrajewski/LibZ/releases/download/1.2.0.0/libz-1.2.0.0-tool.zip 34 | unzip -d tmp tmp/libz-1.2.0.0-tool.zip 35 | 36 | PowerShellExporter/bin/Release/net461/PowerShellExporter.exe: PowerShellExporter/* 37 | MSBuild.exe -m -p:Configuration=Release -t:restore -t:build 38 | 39 | clean: 40 | MSBuild.exe -m -p:Configuration=Release -t:clean 41 | rm -rf tmp dist 42 | 43 | .PHONY: dist clean 44 | -------------------------------------------------------------------------------- /PowerShellExporter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellExporter", "PowerShellExporter\PowerShellExporter.csproj", "{BB54512E-69E4-4189-B4A6-34157AB6B073}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {BB54512E-69E4-4189-B4A6-34157AB6B073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {BB54512E-69E4-4189-B4A6-34157AB6B073}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {BB54512E-69E4-4189-B4A6-34157AB6B073}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {BB54512E-69E4-4189-B4A6-34157AB6B073}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {BC817DC6-6CB6-4E2D-8342-76D70FFAAFF5} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /PowerShellExporter/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /PowerShellExporter/Metric.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Text; 3 | 4 | namespace PowerShellExporter 5 | { 6 | public class Metric 7 | { 8 | public Metric(double value, Hashtable labels) 9 | { 10 | Value = value; 11 | Labels = labels; 12 | } 13 | 14 | public double Value { get; private set; } 15 | 16 | public Hashtable Labels { get; private set; } 17 | 18 | public override string ToString() 19 | { 20 | var labels = new StringBuilder(); 21 | 22 | if (Labels.Count != 0) 23 | { 24 | foreach (DictionaryEntry entry in Labels) 25 | { 26 | if (labels.Length > 0) 27 | { 28 | labels.Append(", "); 29 | } 30 | labels.AppendFormat("{0}={1}", entry.Key, entry.Value); 31 | } 32 | } 33 | 34 | return string.Format("{0}{1}", Labels.Count != 0 ? string.Format("{{{0}}}", labels) : null, Value); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /PowerShellExporter/MetricsCollector.cs: -------------------------------------------------------------------------------- 1 | using Prometheus; 2 | using Prometheus.Advanced; 3 | using Serilog; 4 | using System; 5 | using System.Linq; 6 | using System.Management.Automation; 7 | using System.Management.Automation.Runspaces; 8 | 9 | namespace PowerShellExporter 10 | { 11 | class MetricsCollector : IOnDemandCollector, IDisposable 12 | { 13 | private readonly MetricsConfig _config; 14 | private readonly string _metricsPowerShellModulePath; 15 | private ICollectorRegistry _registry; 16 | private Runspace _rs; 17 | 18 | public MetricsCollector(MetricsConfig config, string metricsPowerShellModulePath) 19 | { 20 | _config = config; 21 | _metricsPowerShellModulePath = metricsPowerShellModulePath; 22 | } 23 | 24 | public void Dispose() 25 | { 26 | if (_rs != null) 27 | { 28 | _rs.Dispose(); 29 | } 30 | } 31 | 32 | public void RegisterMetrics(ICollectorRegistry registry) 33 | { 34 | _registry = registry; 35 | 36 | var iss = InitialSessionState.CreateDefault(); 37 | iss.ImportPSModule(new[] { _metricsPowerShellModulePath }); 38 | iss.Types.Add(new SessionStateTypeEntry(new TypeData(typeof(Metric)), false)); 39 | 40 | _rs = RunspaceFactory.CreateRunspace(iss); 41 | 42 | // NB if this errors out with something alike: 43 | // runspace cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies 44 | // you need to change the PowerShell (64-bit and 32-bit) execution policies with: 45 | // PowerShell.exe -Command Set-ExecutionPolicy Unrestricted 46 | // c:\windows\syswow64\WindowsPowerShell\v1.0\PowerShell.exe -Command Set-ExecutionPolicy Unrestricted 47 | _rs.Open(); 48 | 49 | Log.Information("PowerShell v{PowerShellVersion}", _rs.Version); 50 | } 51 | 52 | public void UpdateMetrics() 53 | { 54 | foreach (var m in _config.Metrics) 55 | { 56 | Log.Debug("Executing {Cmdlet}", m.Cmdlet); 57 | 58 | try 59 | { 60 | using (var ps = PowerShell.Create()) 61 | { 62 | ps.Runspace = _rs; 63 | ps.AddCommand(m.Cmdlet); 64 | 65 | var results = ps.Invoke(); 66 | 67 | if (ps.HadErrors) 68 | { 69 | foreach (var e in ps.Streams.Error) 70 | { 71 | Log.Error("{Cmdlet} error {Error}", m.Cmdlet, e); 72 | } 73 | } 74 | else 75 | { 76 | foreach (var r in results) 77 | { 78 | var labels = r.Labels.Keys.Cast().ToArray(); 79 | var labelValues = r.Labels.Values.Cast().Select(v => v.ToString()).ToArray(); 80 | 81 | var gauge = Metrics.WithCustomRegistry(_registry).CreateGauge(m.Name, m.Help, labels); 82 | gauge.Labels(labelValues).Set(r.Value); 83 | } 84 | } 85 | } 86 | } 87 | catch (Exception e) 88 | { 89 | Log.Error(e, "Unhandled exception invoking {Cmdlet}", m.Cmdlet); 90 | } 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /PowerShellExporter/MetricsConfig.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using YamlDotNet.Serialization; 3 | using YamlDotNet.Serialization.NamingConventions; 4 | 5 | namespace PowerShellExporter 6 | { 7 | public class MetricsConfig 8 | { 9 | public MetricConfig[] Metrics { get; set; } 10 | 11 | internal static MetricsConfig Load(string path) 12 | { 13 | using (var f = File.OpenText(path)) 14 | { 15 | var deserializer = new DeserializerBuilder() 16 | .WithNamingConvention(new UnderscoredNamingConvention()) 17 | .Build(); 18 | return deserializer.Deserialize(f); 19 | } 20 | } 21 | } 22 | 23 | public class MetricConfig 24 | { 25 | public string Name { get; set; } 26 | public string Cmdlet { get; set; } 27 | public string Help { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PowerShellExporter/PowerShellExporter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {BB54512E-69E4-4189-B4A6-34157AB6B073} 4 | Exe 5 | net461 6 | true 7 | 0.0.3 8 | PowerShell Exporter 9 | Exports the results of PowerShell cmdlets as Prometheus Gauge Metrics 10 | ruilopes.com 11 | Copyright ruilopes.com 12 | 13 | 14 | 15 | False 16 | C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll 17 | False 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | 32 | 33 | 2.1.3 34 | 35 | 36 | 2.7.1 37 | 38 | 39 | 2.1.2 40 | 41 | 42 | 3.1.1 43 | 44 | 45 | 4.0.0 46 | 47 | 48 | 4.1.0 49 | 50 | 51 | 4.1.0 52 | 53 | 54 | 5.1.0 55 | 56 | 57 | -------------------------------------------------------------------------------- /PowerShellExporter/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using Prometheus; 3 | using Prometheus.Advanced; 4 | using Serilog; 5 | using System; 6 | using System.IO; 7 | using System.Reflection; 8 | using Topshelf; 9 | 10 | namespace PowerShellExporter 11 | { 12 | class PowerShellExporterService 13 | { 14 | private MetricServer _metricServer; 15 | 16 | public PowerShellExporterService(Uri url, string metricsConfigPath, string metricsPowerShellModulePath) 17 | { 18 | var metricsConfig = MetricsConfig.Load(metricsConfigPath); 19 | 20 | DefaultCollectorRegistry.Instance.Clear(); 21 | DefaultCollectorRegistry.Instance.RegisterOnDemandCollectors(new MetricsCollector(metricsConfig, metricsPowerShellModulePath)); 22 | 23 | _metricServer = new MetricServer( 24 | hostname: url.Host, 25 | port: url.Port, 26 | url: url.AbsolutePath.Trim('/') + '/', 27 | useHttps: url.Scheme == "https"); 28 | } 29 | 30 | public void Start() 31 | { 32 | _metricServer.Start(); 33 | } 34 | 35 | public void Stop() 36 | { 37 | _metricServer.Stop(); 38 | } 39 | } 40 | 41 | class Program 42 | { 43 | static int Main(string[] args) 44 | { 45 | ConfigureSerilog(); 46 | 47 | var exitCode = HostFactory.Run(hc => 48 | { 49 | hc.UseSerilog(); 50 | 51 | var url = new Uri("http://localhost:9360/metrics"); // see https://github.com/prometheus/prometheus/wiki/Default-port-allocations 52 | var metricsConfigPath = @".\metrics.yml"; 53 | 54 | hc.AddCommandLineDefinition("url", value => url = new Uri(value)); 55 | hc.AddCommandLineDefinition("metrics", value => metricsConfigPath = value); 56 | 57 | hc.AfterInstall((ihs) => 58 | { 59 | // add service parameters. 60 | using (var services = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services")) 61 | using (var service = services.OpenSubKey(ihs.ServiceName, true)) 62 | { 63 | service.SetValue( 64 | "ImagePath", 65 | string.Format( 66 | "{0} -url \"{1}\" -metrics \"{2}\"", 67 | service.GetValue("ImagePath"), 68 | url, 69 | Path.GetFullPath(metricsConfigPath))); 70 | } 71 | }); 72 | 73 | hc.Service(sc => 74 | { 75 | sc.ConstructUsing(settings => 76 | { 77 | Log.Information("{product} v{version}", 78 | Assembly.GetExecutingAssembly().GetCustomAttribute().Product, 79 | Assembly.GetExecutingAssembly().GetCustomAttribute().Version); 80 | Log.Information("Configuration url: {url}", url); 81 | Log.Information("Configuration metrics: {metrics}", metricsConfigPath); 82 | 83 | return new PowerShellExporterService(url, metricsConfigPath, metricsConfigPath.Replace(".yml", ".psm1")); 84 | }); 85 | sc.WhenStarted(service => service.Start()); 86 | sc.WhenStopped(service => service.Stop()); 87 | }); 88 | 89 | hc.EnableServiceRecovery(rc => 90 | { 91 | rc.RestartService(1); // first failure: restart after 1 minute 92 | rc.RestartService(1); // second failure: restart after 1 minute 93 | rc.RestartService(1); // subsequent failures: restart after 1 minute 94 | }); 95 | 96 | hc.StartAutomatically(); 97 | 98 | hc.RunAsLocalSystem(); 99 | 100 | hc.SetDescription(Assembly.GetExecutingAssembly().GetCustomAttribute().Description); 101 | hc.SetDisplayName(Assembly.GetExecutingAssembly().GetCustomAttribute().Title); 102 | hc.SetServiceName(Assembly.GetExecutingAssembly().GetCustomAttribute().Product); 103 | }); 104 | 105 | return (int)Convert.ChangeType(exitCode, exitCode.GetTypeCode()); 106 | } 107 | 108 | private static void ConfigureSerilog() 109 | { 110 | if (!Environment.UserInteractive) 111 | { 112 | Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); 113 | } 114 | 115 | var configuration = new LoggerConfiguration(); 116 | 117 | if (Environment.UserInteractive) 118 | { 119 | configuration = configuration 120 | .MinimumLevel.Debug() 121 | .WriteTo.Console(); 122 | } 123 | else 124 | { 125 | configuration = configuration 126 | .ReadFrom.AppSettings(); 127 | } 128 | 129 | Log.Logger = configuration.CreateLogger(); 130 | 131 | if (Environment.UserInteractive) 132 | { 133 | Log.Information("Running in UserInteractive mode"); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /PowerShellExporter/grafana-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_PROMETHEUS", 5 | "label": "Prometheus", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "prometheus", 9 | "pluginName": "Prometheus" 10 | } 11 | ], 12 | "__requires": [ 13 | { 14 | "type": "grafana", 15 | "id": "grafana", 16 | "name": "Grafana", 17 | "version": "5.2.2" 18 | }, 19 | { 20 | "type": "panel", 21 | "id": "graph", 22 | "name": "Graph", 23 | "version": "5.0.0" 24 | }, 25 | { 26 | "type": "datasource", 27 | "id": "prometheus", 28 | "name": "Prometheus", 29 | "version": "5.0.0" 30 | } 31 | ], 32 | "annotations": { 33 | "list": [ 34 | { 35 | "builtIn": 1, 36 | "datasource": "-- Grafana --", 37 | "enable": true, 38 | "hide": true, 39 | "iconColor": "rgba(0, 211, 255, 1)", 40 | "name": "Annotations & Alerts", 41 | "type": "dashboard" 42 | } 43 | ] 44 | }, 45 | "editable": true, 46 | "gnetId": null, 47 | "graphTooltip": 1, 48 | "id": null, 49 | "iteration": 1534191396287, 50 | "links": [], 51 | "panels": [ 52 | { 53 | "aliasColors": {}, 54 | "bars": false, 55 | "dashLength": 10, 56 | "dashes": false, 57 | "datasource": "${DS_PROMETHEUS}", 58 | "fill": 1, 59 | "gridPos": { 60 | "h": 4, 61 | "w": 12, 62 | "x": 0, 63 | "y": 0 64 | }, 65 | "id": 4, 66 | "legend": { 67 | "alignAsTable": false, 68 | "avg": false, 69 | "current": false, 70 | "max": false, 71 | "min": false, 72 | "rightSide": false, 73 | "show": true, 74 | "total": false, 75 | "values": false 76 | }, 77 | "lines": true, 78 | "linewidth": 1, 79 | "links": [], 80 | "nullPointMode": "null", 81 | "percentage": false, 82 | "pointradius": 5, 83 | "points": false, 84 | "renderer": "flot", 85 | "seriesOverrides": [], 86 | "spaceLength": 10, 87 | "stack": false, 88 | "steppedLine": false, 89 | "targets": [ 90 | { 91 | "expr": "sum(pse_tcp_connections{instance=\"$instance\"}) by (state)", 92 | "format": "time_series", 93 | "intervalFactor": 1, 94 | "legendFormat": "{{state}}", 95 | "refId": "A" 96 | } 97 | ], 98 | "thresholds": [], 99 | "timeFrom": null, 100 | "timeRegions": [], 101 | "timeShift": null, 102 | "title": "TCP Connections", 103 | "tooltip": { 104 | "shared": true, 105 | "sort": 0, 106 | "value_type": "individual" 107 | }, 108 | "type": "graph", 109 | "xaxis": { 110 | "buckets": null, 111 | "mode": "time", 112 | "name": null, 113 | "show": true, 114 | "values": [] 115 | }, 116 | "yaxes": [ 117 | { 118 | "format": "none", 119 | "label": null, 120 | "logBase": 1, 121 | "max": null, 122 | "min": null, 123 | "show": true 124 | }, 125 | { 126 | "format": "short", 127 | "label": null, 128 | "logBase": 1, 129 | "max": null, 130 | "min": null, 131 | "show": true 132 | } 133 | ], 134 | "yaxis": { 135 | "align": false, 136 | "alignLevel": null 137 | } 138 | } 139 | ], 140 | "refresh": "30s", 141 | "schemaVersion": 16, 142 | "style": "dark", 143 | "tags": [], 144 | "templating": { 145 | "list": [ 146 | { 147 | "allValue": null, 148 | "current": {}, 149 | "datasource": "${DS_PROMETHEUS}", 150 | "hide": 0, 151 | "includeAll": false, 152 | "label": "Instance", 153 | "multi": false, 154 | "name": "instance", 155 | "options": [], 156 | "query": "label_values(pce_system_up_time, instance)", 157 | "refresh": 1, 158 | "regex": "", 159 | "skipUrlSync": false, 160 | "sort": 0, 161 | "tagValuesQuery": "", 162 | "tags": [], 163 | "tagsQuery": "", 164 | "type": "query", 165 | "useTags": false 166 | } 167 | ] 168 | }, 169 | "time": { 170 | "from": "now-6h", 171 | "to": "now" 172 | }, 173 | "timepicker": { 174 | "refresh_intervals": [ 175 | "5s", 176 | "10s", 177 | "30s", 178 | "1m", 179 | "5m", 180 | "15m", 181 | "30m", 182 | "1h", 183 | "2h", 184 | "1d" 185 | ], 186 | "time_options": [ 187 | "5m", 188 | "15m", 189 | "1h", 190 | "6h", 191 | "12h", 192 | "24h", 193 | "2d", 194 | "7d", 195 | "30d" 196 | ] 197 | }, 198 | "timezone": "browser", 199 | "title": "PowerShellExporter", 200 | "version": 1 201 | } 202 | -------------------------------------------------------------------------------- /PowerShellExporter/metrics.psm1: -------------------------------------------------------------------------------- 1 | function Get-TcpConnectionsMetrics { 2 | Get-NetTCPConnection ` 3 | | Where-Object {$_.RemotePort -ne 0} ` 4 | | Where-Object {$_.LocalAddress -ne '127.0.0.1' -and $_.LocalAddress -ne '::1'} ` 5 | | Group-Object -Property 'LocalAddress','LocalPort','RemoteAddress','RemotePort','State' ` 6 | | ForEach-Object { 7 | [PowerShellExporter.Metric]::new($_.Count, @{ 8 | 'local_address' = $_.Group[0].LocalAddress 9 | 'local_port' = $_.Group[0].LocalPort 10 | 'remote_address' = $_.Group[0].RemoteAddress 11 | 'remote_port' = $_.Group[0].RemotePort 12 | 'state' = $_.Group[0].State 13 | }) 14 | } 15 | } -------------------------------------------------------------------------------- /PowerShellExporter/metrics.yml: -------------------------------------------------------------------------------- 1 | # * try to follow the metrics and label name best practices as described at: 2 | # * https://prometheus.io/docs/practices/naming/ 3 | # * https://prometheus.io/docs/concepts/jobs_instances/ 4 | # * https://prometheus.io/docs/concepts/data_model/ 5 | 6 | metrics: 7 | # sample promql: 8 | # sum(pse_tcp_connections) by (state) 9 | # sum(pse_tcp_connections) by (remote_address) 10 | - name: pse_tcp_connections 11 | cmdlet: Get-TcpConnectionsMetrics 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This PowerShell Exporter lets you export Prometheus Gauge Metrics from the result of a PowerShell cmdlet. 2 | 3 | **WARNING this is still a PoC; things will change for sure** 4 | 5 | 6 | # Usage 7 | 8 | The cmdlets are defined inside `metrics.psm1` and are loaded at the application startup, e.g.: 9 | 10 | ```powershell 11 | # count the number tcp connections grouped by their remote address, port, and state. 12 | # NB this must return PowerShellExporter.Metric objects. 13 | function Get-TcpConnectionsMetrics { 14 | Get-NetTCPConnection ` 15 | | Where-Object {$_.RemotePort -ne 0} ` 16 | | Where-Object {$_.LocalAddress -ne '127.0.0.1' -and $_.LocalAddress -ne '::1'} ` 17 | | Group-Object -Property 'RemoteAddress','RemotePort','State' ` 18 | | ForEach-Object { 19 | [PowerShellExporter.Metric]::new( 20 | # metric value 21 | $_.Count, 22 | # metric labels 23 | @{ 24 | 'remote_address' = $_.Group[0].RemoteAddress 25 | 'remote_port' = $_.Group[0].RemotePort 26 | 'state' = $_.Group[0].State 27 | }) 28 | } 29 | } 30 | ``` 31 | 32 | The metrics are defined inside `metrics.yml` and are evaluated at every prometheus scrape, e.g.: 33 | 34 | ```yml 35 | metrics: 36 | # sample promql: 37 | # sum(pse_tcp_connections) by (state) 38 | # sum(pse_tcp_connections) by (remote_address) 39 | - name: pse_tcp_connections 40 | cmdlet: Get-TcpConnectionsMetrics 41 | ``` 42 | 43 | Prometheus can be configured with something alike: 44 | 45 | ```yml 46 | global: 47 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 48 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 49 | # scrape_timeout is set to the global default (10s). 50 | ... 51 | scrape_configs: 52 | ... 53 | - job_name: pse 54 | static_configs: 55 | - targets: 56 | - localhost:9360 57 | ``` 58 | 59 | This exporter can be installed as a Windows service with something alike: 60 | 61 | ```powershell 62 | .\PowerShellExporter help # show help. 63 | .\PowerShellExporter install # install with default settings. 64 | Start-Service PowerShellExporter # start the service. 65 | ``` 66 | 67 | **NB** you can modify the default listening url `http://localhost:9360/metrics` with the `-url` command line argument, e.g., `-url http://localhost:9360/pse/metrics`. 68 | 69 | **NB** you need to make sure this exporter metrics are not taking too long to complete by observing the scrape durations with the promql `scrape_duration_seconds{job="pse"}`. 70 | 71 | 72 | # Build 73 | 74 | Type `make` and use what ends-up in the `dist` sub-directory. 75 | 76 | If the PowerShell `System.Management.Automation.dll` assembly is not found, you need to get its location with the `[PSObject].Assembly.Location` snippet and modify the `PowerShellExporter.csproj` ``. 77 | --------------------------------------------------------------------------------