Using a DSC report server

Applies To: Windows PowerShell 5.0


The Pull Server (Windows Feature DSC-Service) is a supported component of Windows Server however there are no plans to offer new features or capabilities. It is recommended to begin transitioning managed clients to Azure Automation DSC (includes features beyond Pull Server on Windows Server) or one of the community solutions listed here.

Note The report server described in this topic is not available in PowerShell 4.0.

The Local Configuration Manager (LCM) of a node can be configured to send reports about its configuration status to a pull server, which can then be queried to retrieve that data. Each time the node checks and applies a configuration, it sends a report to the report server. These reports are stored in a database on the server, and can be retrieved by calling the reporting web service. Each report contains information such as what configurations were applied and whether they succeeded, the resources used, any errors that were thrown, and start and finish times.

Configuring a node to send reports

You tell a node to send reports to a server by using a ReportServerWeb block in the node's LCM configuration (for information about configuring the LCM, see Configuring the Local Configuration Manager). The server to which the node sends reports must be set up as a web pull server (you cannot send reports to an SMB share). For information about setting up a pull server, see Setting up a DSC web pull server. The report server can be the same service from which the node pulls configurations and gets resources, or it can be a different service.

In the ReportServerWeb block, you specify the URL of the pull service and a registration key that is known to the server.

The following configuration configures a node to pull configurations from one service, and send reports to a service on a different server.

configuration ReportClientConfig
    Node localhost
            RefreshMode          = 'Pull'
            RefreshFrequencyMins = 30
            RebootNodeIfNeeded   = $true

        ConfigurationRepositoryWeb CONTOSO-PullSrv
            ServerURL          = 'https://CONTOSO-PULL:8080/PSDSCPullServer.svc'
            RegistrationKey    = 'bbb9778f-43f2-47de-b61e-a0daff474c6d'
            ConfigurationNames = @('ClientConfig')

        ReportServerWeb CONTOSO-ReportSrv
            ServerURL               = 'http://CONTOSO-REPORT:8080/PSDSCReportServer.svc'
            RegistrationKey         = 'ba39daaa-96c5-4f2f-9149-f95c46460faa'
            AllowUnsecureConnection = $true


The following configuration configures a node to use a single server for configurations, resources, and reporting.

configuration PullClientConfig
    Node localhost
            RefreshMode = 'Pull'
            RefreshFrequencyMins = 30
            RebootNodeIfNeeded = $true

        ConfigurationRepositoryWeb CONTOSO-PullSrv
            ServerURL = 'https://CONTOSO-PullSrv:8080/PSDSCPullServer.svc'
            RegistrationKey = 'fbc6ef09-ad98-4aad-a062-92b0e0327562'

        ReportServerWeb CONTOSO-ReportSrv
            ServerURL = 'https://CONTOSO-PullSrv:8080/PSDSCPullServer.svc'


You can name the web service whatever you want when you set up a pull server, but the ServerURL property must match the service name.

Getting report data

Reports sent to the pull server are entered into a database on the server. The reports are available through calls to the web service. To retrieve reports for a specific node, send an HTTP request to the report web service in the following form: http://CONTOSO-REPORT:8080/PSDSCReportServer.svc/Nodes(AgentId='MyNodeAgentId')/Reports where MyNodeAgentId is the AgentId of the node for which you want to get reports. You can get the AgentID for a node by calling Get-DscLocalConfigurationManager on that node.

The reports are returned as an array of JSON objects.

The following script returns the reports for the node on which it is run:

function GetReport
        $AgentId = "$((glcm).AgentId)", 
        $serviceURL = "http://CONTOSO-REPORT:8080/PSDSCPullServer.svc"

    $requestUri = "$serviceURL/Nodes(AgentId= '$AgentId')/Reports"
    $request = Invoke-WebRequest -Uri $requestUri  -ContentType "application/json;odata=minimalmetadata;streaming=true;charset=utf-8" `
               -UseBasicParsing -Headers @{Accept = "application/json";ProtocolVersion = "2.0"} `
               -ErrorAction SilentlyContinue -ErrorVariable ev
    $object = ConvertFrom-Json $request.content
    return $object.value

Viewing report data

If you set a variable to the result of the GetReport function, you can view the individual fields in an element of the array that is returned:

$reports = GetReport
JobId                : 019dfbe5-f99f-11e5-80c6-001dd8b8065c
OperationType        : Consistency
RefreshMode          : Pull
Status               : Success
ReportFormatVersion  : 2.0
ConfigurationVersion : 2.0.0
StartTime            : 04/03/2016 06:21:43
EndTime              : 04/03/2016 06:22:04
RebootRequested      : False
Errors               : {}
StatusData           : {{"StartDate":"2016-04-03T06:21:43.7220000-07:00","IPV6Addresses":["2001:4898:d8:f2f2:852b:b255:b071:283b","fe80::852b:b255:b071
                       tChecksum":"A7797571CB9C3AF4D74C39A0FDA11DAF33273349E1182385528FFC1E47151F7F","MetaData":"Author: configAuthor; Name:
                       Sample_ArchiveFirewall; Version: 2.0.0; GenerationDate: 04/01/2016 15:23:30; GenerationHost: CONTOSO-PullSrv;","RebootRequested":"False
AdditionalData       : {}

By default, the reports are sorted by JobID. To get the most recent report, you can sort the reports by descending StartTime property, and then get the first element of the array:

$reportsByStartTime = $reports | Sort-Object {$_."StartTime" -as [DateTime] } -Descending
$reportMostRecent = $reportsByStartTime[0]

Notice that the StatusData property is an object with a number of properties. This is where much of the reporting data is. Let's look at the individual fields of the StatusData property for the most recent report:

$statusData = $reportMostRecent.StatusData | ConvertFrom-Json
StartDate                  : 2016-04-04T11:21:41.2990000-07:00
IPV6Addresses              : {2001:4898:d8:f2f2:852b:b255:b071:283b, fe80::852b:b255:b071:283b%12, ::2000:0:0:0, ::1...}
DurationInSeconds          : 25
JobID                      : {135D230E-FA92-11E5-80C6-001DD8B8065C}
CurrentChecksum            : A7797571CB9C3AF4D74C39A0FDA11DAF33273349E1182385528FFC1E47151F7F
MetaData                   : Author: configAuthor; Name: Sample_ArchiveFirewall; Version: 2.0.0; GenerationDate: 04/01/2016 15:23:30; GenerationHost:
RebootRequested            : False
Status                     : Success
IPV4Addresses              : {,}
LCMVersion                 : 2.0
ResourcesNotInDesiredState : {@{SourceInfo=C:\ReportTest\Sample_xFirewall_AddFirewallRule.ps1::23::9::xFirewall; ModuleName=xNetworking;
                             DurationInSeconds=10.725; InstanceName=Firewall; StartDate=2016-04-04T11:21:55.7200000-07:00; ResourceName=xFirewall;
                             ModuleVersion=; RebootRequested=False; ResourceId=[xFirewall]Firewall; ConfigurationName=Sample_ArchiveFirewall;
NumberOfResources          : 2
Type                       : Consistency
HostName                   : CONTOSO-PULLCLI
ResourcesInDesiredState    : {@{SourceInfo=C:\ReportTest\Sample_xFirewall_AddFirewallRule.ps1::16::9::Archive; ModuleName=PSDesiredStateConfiguration;
                             DurationInSeconds=2.672; InstanceName=ArchiveExample; StartDate=2016-04-04T11:21:55.7200000-07:00; ResourceName=Archive;
                             ModuleVersion=1.1; RebootRequested=False; ResourceId=[Archive]ArchiveExample; ConfigurationName=Sample_ArchiveFirewall;
MACAddresses               : {00-1D-D8-B8-06-5C, 00-00-00-00-00-00-00-E0}
MetaConfiguration          : @{AgentId=52DA826D-00DE-4166-8ACB-73F2B46A7E00; ConfigurationDownloadManagers=System.Object[];
                             ActionAfterReboot=ContinueConfiguration; LCMCompatibleVersions=System.Object[]; LCMState=Idle;
                             ResourceModuleManagers=System.Object[]; ReportManagers=System.Object[]; StatusRetentionTimeInDays=10; LCMVersion=2.0;
                             ConfigurationMode=ApplyAndMonitor; RefreshFrequencyMins=30; RebootNodeIfNeeded=True; RefreshMode=Pull;
                             DebugMode=System.Object[]; LCMStateDetail=; AllowModuleOverwrite=False; ConfigurationModeFrequencyMins=15}
Locale                     : en-US
Mode                       : Pull

Among other things, this shows that the most recent configuration called two resources, and that one of them was in the desired state, and one of them was not. You can get a more readable output of just the ResourcesNotInDesiredState property:

SourceInfo        : C:\ReportTest\Sample_xFirewall_AddFirewallRule.ps1::16::9::Archive
ModuleName        : PSDesiredStateConfiguration
DurationInSeconds : 2.672
InstanceName      : ArchiveExample
StartDate         : 2016-04-04T11:21:55.7200000-07:00
ResourceName      : Archive
ModuleVersion     : 1.1
RebootRequested   : False
ResourceId        : [Archive]ArchiveExample
ConfigurationName : Sample_ArchiveFirewall
InDesiredState    : True

Note that these examples are meant to give you an idea of what you can do with report data. For an introduction on working with JSON in PowerShell, see Playing with JSON and PowerShell.

See Also

Configuring the Local Configuration Manager

Setting up a DSC web pull server

Setting up a pull client using configuration names