Using a DSC report server
Applies To: Windows PowerShell 5.0
Important
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. we would like you to know that a newer version of DSC is now generally available, managed by a feature of Azure Policy named guest configuration. The guest configuration service combines features of DSC Extension, Azure Automation State Configuration, and the most commonly requested features from customer feedback. Guest configuration also includes hybrid machine support through Arc-enabled servers.
The Local Configuration Manager (LCM) of a node can be configured to send reports about its configuration status to a pull server that 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.
[DSCLocalConfigurationManager()]
configuration ReportClientConfig
{
Node localhost
{
Settings
{
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/PSDSCPullServer.svc'
RegistrationKey = 'ba39daaa-96c5-4f2f-9149-f95c46460faa'
AllowUnsecureConnection = $true
}
}
}
ReportClientConfig
The following configuration configures a node to use a single server for configurations, resources, and reporting.
[DSCLocalConfigurationManager()]
configuration PullClientConfig
{
Node localhost
{
Settings
{
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'
}
}
}
PullClientConfig
Note
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
{
param
(
$AgentId = "$((glcm).AgentId)",
$serviceURL = "http://CONTOSO-REPORT:8080/PSDSCPullServer.svc"
)
$invokeWebRequestSplat = @{
Uri = "$serviceURL/Nodes(AgentId= '$AgentId')/Reports"
ContentType = "application/json;odata=minimalmetadata;streaming=true;charset=utf-8"
UseBasicParsing = $true
Headers = @{Accept = "application/json"; ProtocolVersion = "2.0"}
ErrorAction = 'SilentlyContinue'
ErrorVariable = 'ev'
}
$request = Invoke-WebRequest @invokeWebRequestSplat
$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
$reports[1]
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
:283b%12","::2000:0:0:0","::1","::2000:0:0:0"],"DurationInSeconds":"21","JobID":"{019DFBE5-F99F-11E5-80C6-001DD8B8065C}","Curren
tChecksum":"A7797571CB9C3AF4D74C39A0FDA11DAF33273349E1182385528FFC1E47151F7F","MetaData":"Author: configAuthor; Name:
Sample_ArchiveFirewall; Version: 2.0.0; GenerationDate: 04/01/2016 15:23:30; GenerationHost: CONTOSO-PullSrv;","RebootRequested":"False
","Status":"Success","IPV4Addresses":["10.240.179.151","127.0.0.1"],"LCMVersion":"2.0","ResourcesNotInDesiredState":[{"SourceInf
o":"C:\\ReportTest\\Sample_xFirewall_AddFirewallRule.ps1::23::9::xFirewall","ModuleName":"xNetworking","DurationInSeconds":"8.785",
"InstanceName":"Firewall","StartDate":"2016-04-03T06:21:56.4650000-07:00","ResourceName":"xFirewall","ModuleVersion":"2.7.0.0","
RebootRequested":"False","ResourceId":"[xFirewall]Firewall","ConfigurationName":"Sample_ArchiveFirewall","InDesiredState":"False
"}],"NumberOfResources":"2","Type":"Consistency","HostName":"CONTOSO-PULLCLI","ResourcesInDesiredState":[{"SourceInfo":"C:\\ReportTest\\Sample_xFirewall_AddFirewallRule.ps1::16::9::Archive","ModuleName":"PSDesiredStateConfiguration","DurationInSeconds":"1.848",
"InstanceName":"ArchiveExample","StartDate":"2016-04-03T06:21:56.4650000-07:00","ResourceName":"Archive","ModuleVersion":"1.1","
RebootRequested":"False","ResourceId":"[Archive]ArchiveExample","ConfigurationName":"Sample_ArchiveFirewall","InDesiredState":"T
rue"}],"MACAddresses":["00-1D-D8-B8-06-5C","00-00-00-00-00-00-00-E0"],"MetaConfiguration":{"AgentId":"52DA826D-00DE-4166-8ACB-73F2B46A7E00",
"ConfigurationDownloadManagers":[{"SourceInfo":"C:\\ReportTest\\LCMConfig.ps1::14::9::ConfigurationRepositoryWeb","A
llowUnsecureConnection":"True","ServerURL":"http://CONTOSO-PullSrv:8080/PSDSCPullServer.svc","RegistrationKey":"","ResourceId":"[Config
urationRepositoryWeb]CONTOSO-PullSrv","ConfigurationNames":["ClientConfig"]}],"ActionAfterReboot":"ContinueConfiguration","LCMCo
mpatibleVersions":["1.0","2.0"],"LCMState":"Idle","ResourceModuleManagers":[],"ReportManagers":[{"AllowUnsecureConnection":"True
","RegistrationKey":"","ServerURL":"http://CONTOSO-PullSrv:8080/PSDSCPullServer.svc","ResourceId":"[ReportServerWeb]CONTOSO-PullSrv","S
ourceInfo":"C:\\ReportTest\\LCMConfig.ps1::24::9::ReportServerWeb"}],"StatusRetentionTimeInDays":"10","LCMVersion":"2.0","Config
urationMode":"ApplyAndMonitor","RefreshFrequencyMins":"30","RebootNodeIfNeeded":"True","RefreshMode":"Pull","DebugMode":["NONE"]
,"LCMStateDetail":"","AllowModuleOverwrite":"False","ConfigurationModeFrequencyMins":"15"},"Locale":"en-US","Mode":"Pull"}}
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
$statusData
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:
CONTOSO-PullSrv;
RebootRequested : False
Status : Success
IPV4Addresses : {10.240.179.151, 127.0.0.1}
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=2.7.0.0; RebootRequested=False; ResourceId=[xFirewall]Firewall; ConfigurationName=Sample_ArchiveFirewall;
InDesiredState=False}}
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;
InDesiredState=True}}
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:
$statusData.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
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