Share via


Under the hood tour of Azure AD Connect Health: AD FS Diagnostics Module

Hello there, my name is Ramiro Calderon, and I am an engineering manager in the Active Directory team. In this post, I want to walk you through the AD FS Diagnostics PowerShell module, which is deployed to the AD FS Servers as part of Azure Active Directory Connect Health agent. This PowerShell module has cmdlets that are executed by the health agent on a regular basis and uploads the results to the cloud for visualization in the Azure Portal and alert generation.

While this module is used by the Azure AD Connect Health Agent, we have also published a copy of it in the AD Script Center. This way if you are not using the Azure AD Connect Health service, you have a way to run this locally on your AD FS servers.

The module file name is ADFSDiagnostics.psm1, is located under "%programfiles%\Microsoft AD Health Agent\Microsoft AD Diagnostics Service". Note that it requires elevated access, and PowerShell 4.0 to run. Below are the cmdlets available in the module:

PS C:\Program Files\Microsoft AD Health Agent\Microsoft AD Diagnostics Service> Get-Command -Module ADFSDiagnostics

 

CommandType Name ModuleName

----------- ---- ----------

Function Get-AdfsServerConfiguration ADFSDiagnostics

Function Get-AdfsServerTrace ADFSDiagnostics

Function Get-AdfsSystemInformation ADFSDiagnostics

Function Get-AdfsVersionEx ADFSDiagnostics

Function Receive-AdfsServerTrace ADFSDiagnostics

Function Set-ADFSDiagTestMode ADFSDiagnostics

Function Start-AdfsServerTrace ADFSDiagnostics

Function Test-AdfsServerHealth ADFSDiagnostics

Function Test-AdfsServerHealthSingleCheck ADFSDiagnostics

Function Test-AdfsServerToken ADFSDiagnostics

 

Let's take a look at the most important usage scenarios for the module. If you want to learn more about a specific cmdlet, you can use the Get-Help cmdlet to see detailed description and examples.

Performing AD FS Server Health Checks

This is perhaps the most useful during troubleshooting. The Test-ADFSServerHealth cmdlet contains a series of health checks for the most common AD FS issues with configuration that we have observed over the years.

We have different kinds of checks:

  • Configuration/Best practices: Examples include SPN, DNS configuration, service account properties, etc.
  • Certificate Properties: For example, whether a certificate is about to expire, CRL failed, etc. You can also do inspection on the relying party trust and claims provider trust certificates by setting the parameter "-VerifyTrustCerts" to $true (it is $false by default).
  • Synthetic Checks: For example, retrieve federation metadata, and get a token from the STS). One thing worth mentioning here is that in order to have the synthetic transaction that gets a token from the STS using the actual server where the cmdlet is running, you should consider tweaking the host file to point to itself (either with IPv4 127.0.0.1 or IPv6 ::1) when resolving the AD FS host farm name. Otherwise, the synthetic transaction will go through the usual DNS resolution where another server will actually serve the request.
  • Office 365 Specific Checks: For example, verify that endpoints required for office 365 to work are enabled, Relying Party trusts are configured, etc.. You can skip the office 365 checks by setting the parameter "-VerifyO365" to false (it is true by default).

Each result has the following properties:

  • Name: Name of the test being run
  • Result: This is the result of the test and can have a value of 'Pass', 'Fail' and 'NotRun'. A test might not run for a number of reasons such as OS version (IIS related checks only apply to ADFS 2.x), or role (Some tests only apply to a primary node on a WID farm).
  • Detail: Explanation of the 'Fail' and 'NotRun' result. It is typically empty when the check passes.
  • Output: Data collected for the specific test. It is a hashtable that contains a list of key value pairs with relevant data in the context of the test (for example, in the SPN test we add the service account name, the SPNs found in the service account, etc.,).
  • ExceptionMessage: If the test encountered an exception, this property contains the exception message.

See an example below.

Test-ADFSServerHealth | ft Name,Result -AutoSize

 

Name Result

---- ------

IsAdfsRunning Pass

IsWidRunning Pass

PingFederationMetadata Pass

CheckAdfsSslBindings Pass

Test-Certificate-Service-Communications-Primary-NotFoundInStore Pass

Test-Certificate-Service-Communications-Primary-IsSelfSigned Pass

Test-Certificate-Service-Communications-Primary-PrivateKeyAbsent Pass

Test-Certificate-SSL-Primary-AboutToExpire Pass

CheckFarmDNSHostResolution Pass

CheckDuplicateSPN Pass

TestServiceAccountProperties Pass

TestAppPoolIDMatchesServiceID Pass

TestComputerNameEqFarmName Pass

TestSSLUsingADFSPort Pass

TestSSLCertSubjectContainsADFSFarmName Pass

TestAdfsAuditPolicyEnabled Pass

TestAdfsRequestToken Pass

CheckOffice365Endpoints Pass

TestADFSO365RelyingParty Pass

TestNtlmOnlySupportedClientAtProxyEnabled Pass

 

Gathering deployment information

The Get-AdfsSystemInformation cmdlet gathers general information about the server (OS, hardware, hotfixes installed, etc.). In the example below, I used a custom formatter so I can see some of the properties right out of the console output, which I find very useful when dealing with object in the pipeline that have complex data types:

Get-AdfsSystemInformation | fc -Depth 1

class PSCustomObject

{

OSVersion =

class Version

{

Major = 6

Minor = 1

Build = 7601

Revision = 65536

MajorRevision = 1

MinorRevision = 0

}

OSName = Microsoft Windows Server 2008 R2 Datacenter

MachineDomain = oneadhealthtest.com

IPAddress = 10.0.0.9

..

}

 

Similarly, the Get-ADFSServerConfiguration cmdlet gets more detailed information for AD FS Farm (Certificates, endpoints, etc.):

Get-AdfsServerConfiguration | fc –Depth 1

 

class PSCustomObject

{

ADFSSyncProperties =

class SyncPropertiesBase

{

Role = PrimaryComputer

}

ADFSAttributeStore =

class AttributeStore

{

}

ADFSCertificate =

[

@{Certificate=…

 

; CertificateType=Service-Communications; IsPrimary=True; StoreName=My; StoreLocation=LocalMachine;

Thumbprint=4AF5D7FD8934C08D1CF7C3D9C8394EB586606AA5}

@{Certificate=…

; CertificateType=Token-Decrypting; IsPrimary=True; StoreName=My; StoreLocation=LocalMachine;

Thumbprint=4AF5D7FD8934C08D1CF7C3D9C8394EB586606AA5}

AutoCertificateRollover = False

CertificateCriticalThreshold = 2

CertificateDuration = 365

CertificateGenerationThreshold = 20

CertificatePromotionThreshold = 5

CertificateRolloverInterval = 720

CertificateSharingContainer = CN=f056c184-835a-4fb8-9e0b-ebb421e8530f,CN=ADFS,CN=Microsoft,CN=Program

Data,DC=oneadhealthtest,DC=com

CertificateThresholdMultiplier = 1440

ClientCertRevocationCheck = None

}

ADFSRelyingPartyTrustCount = 5

ADFSClaimsProviderTrustCount = 1

..

 

}

 

Getting a token with specific credentials

We also added the cmdlet Test-ADFSServerToken, which you can use to get a token against the federation service. In the example below, we get a token against the "fs.contoso.com" AD FS farm, for the Relying Party identified by "urn:federation:MicrosoftOnline", providing a credential for the user we want to get the token for:

Test-AdfsServerToken -federationServer fs.contoso.com -appliesTo urn:federation:MicrosoftOnline -credential (Get-Credential)

 

cmdlet Get-Credential at command pipeline position 1

Supply values for the following parameters:

Credential

<s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:a="https://www.w3.org/2005/08/addressing" xmlns:u="h

ttp://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">…<…</s:Envelope>

 

Sometimes you may have a need to examine the contents of a token. For example, this could be to validate the issuance rules that you have configured for a specific relying party trust. You can use some PowerShell XML constructs to get the token as an object in the pipeline:

$token = Test-AdfsServerToken -federationServer localhost -appliesTo urn:federation:MicrosoftOnline -credential (Get-Credential)

$tokenXml = [Xml]$token

$tokenXml.Envelope.Body.RequestSecurityTokenResponse.RequestedSecurityToken.Assertion.AttributeStatement | fc -Depth 2

class 0:assertion#AttributeStatement

{

Attribute =

[

class 0:assertion#Attribute

{

Name = https://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid

AttributeValue = S-1-5-21-2474419537-1598421116-3968361607-513

}

class 0:assertion#Attribute

{

Name = https://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid

AttributeValue = S-1-5-21-2474419537-1598421116-3968361607-500

}

class 0:assertion#Attribute

{

Name = https://schemas.xmlsoap.org/ws/2005/05/identity/claims/name

AttributeValue = ONEADHEALTHTEST\healthadmin

}

class 0:assertion#Attribute

{

Name = https://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname

AttributeValue = ONEADHEALTHTEST\healthadmin

}

]

 

}

 

Getting events based on Activity ID

AD FS (especially 2012R2) does a great job of writing events into the Windows event logs. When you hit an error (see image below), you may see an activity ID. Think of this as a transaction identifier and multiple events can be generated with the same activity ID. This gives an account of what happened on the AD FS servers when it processed the token request. When tracing is enabled, the # of events increase with more detail.

When troubleshooting issues and you have an activity ID handy, it is currently a challenge to go into each AD FS server and filter events looking for this activity ID. The script now makes it super simple for you.

You can run the Get-ADFSServerTrace cmdlet to find all the events related to that activity ID, across the different AD FS logs (Admin, Audits, and Trace). You can run this across multiple servers in just one command! Just populate the ComputerName parameter as an array of machine names, provided that you have enabled remote event log reading on them.

This example shows getting the activity Id from the error page above in the local AD FS server. Note below the "Source" property showing whether the event comes from audit or admin logs.

PS C:\adfsdiag> Get-AdfsServerTrace -ActivityId 00000000-0000-0000-886e-0080000000df

ComputerName : localhost

Source : Audits

TimeCreated : 2/12/2015 5:40:07 PM

EventId : 403

Message : An HTTP request was received.

Activity ID: 00000000-0000-0000-886e-0080000000df

 

Request Details:

Date And Time: 2015-02-12 17:40:07

Client IP: ::1

ComputerName : localhost

Source : Admin

TimeCreated : 2/12/2015 5:40:07 PM

EventId : 364

Message : Encountered error during federation passive request.

Additional Data

Protocol Name:

Relying Party:

Exception details:

Microsoft.IdentityServer.RequestFailedException: MSIS7065: There are no registered protocol handlers on

path /adfs/ls to process the incoming request.

ComputerName : localhost

Source : Audits

TimeCreated : 2/12/2015 5:40:07 PM

EventId : 404

Message : An HTTP response was dispatched with:

Activity ID: 00000000-0000-0000-886e-0080000000df

Response Details:

Date And Time: 2015-02-12 17:40:07

Status Code: 200

Status Description: OK

 

Sometimes it is useful to have it in a table format. For that, use the parameter OutHtmlFilePath, and the cmdlet will format the output to an HTML file and opens up the browser:

Get-AdfsServerTrace -ActivityId 00000000-0000-0000-ce70-0080000000df -OutHtmlFilePath .\report.htm

Report Generated at .\report.htm

 

 

Conclusion

The ADFSDiagnostics module is another tool in your toolbox to troubleshoot issues with your Hybrid environment. We are looking forward to your valuable feedback, either by signing up to the Azure Active Directory Connect Health or download the module stand-alone in the AD Script Center.
As we learn more about your scenarios and issues, and get feedback from you we will add more health checks, gather more data, and add more functionality to make your life easier and your Hybrid Deployment in good shape!