├── .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