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="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://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 = http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid
AttributeValue = S-1-5-21-2474419537-1598421116-3968361607-513
}
class 0:assertion#Attribute
{
Name = http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid
AttributeValue = S-1-5-21-2474419537-1598421116-3968361607-500
}
…
class 0:assertion#Attribute
{
Name = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
AttributeValue = ONEADHEALTHTEST\healthadmin
}
class 0:assertion#Attribute
{
Name = http://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!