├── LICENSE ├── jp └── readme.md ├── README.md └── ADFS-tracing.ps1 /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Copyright (c) Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /jp/readme.md: -------------------------------------------------------------------------------- 1 | # AD FS 診断トレース 2 | 3 | ## 重要事項 4 | 5 | 本スクリプト (ADFS-tracing.ps1) は、Active Directory Federation Services または Web Application Proxy サーバーに関して問題が生じた際に、Microsoft カスタマー サービス & サポート (CSS) がトラブルシューティングを目的として有効となる情報を収集するためのものです。収集されたデータには、IP アドレス、コンピューター名、ユーザー名など、個人を特定できる情報 (PII) や機密データが含まれる場合があります。 6 | 7 | スクリプトによって生成されたすべてのデータは、最初の実行時にユーザーにより指定されたフォルダーに保存されます。トレースが完了すると、スクリプトはサーバー名、日付、時刻を含むアーカイブ ファイル (zip ファイル) を自動的に生成します。アーカイブ処理が失敗した場合、すべてのデータは指定されたフォルダー内のサブフォルダー (/temporary) に保存されます。 8 | >[!NOTE] 9 | > このスクリプトはインターネット接続を必要とせず、また Microsoft に自動的にデータを送信することはありません。このスクリプトで収集されたデータは、**アクティブなサポート契約にて支援しているお問い合わせがある場合のみ** Microsoft に送信ください。Microsoft にデータを送信する際は、**必ず** セキュアなファイル転送を通じて実施ください。 10 | > 11 | > 転送ツールへのアクセスは、サポート インシデントに割り当てられた Microsoft サポート担当者から提供されます。ツールをご利用の際に懸念事項がある場合は、サポート エンジニアにご相談ください。 12 | > 13 | > https://privacy.microsoft.com/en-us/privacy 14 | 15 | ## スクリプトの使用方法 16 | 17 | ### 必要条件: 18 | 19 | - スクリプトは Windows Server 2012R2 / Windows Server 2016 / Windows Server 2019 / Windows Server 2022 and Windows Server 2025 OS 上の AD FS をサポートします。 20 | - スクリプトを実行するにはローカル管理者権限が必要です。 21 | - 可能であれば、アカウントはドメイン ユーザーを利用ください。 22 | - 長時間トレースを実行する場合は、対象フォルダーのあるディスクに最低 5GB の空きディスク容量が必要です。 23 | - Windows Server 2012 R2 では、Windows Management Framework (WMF) 5.1 (PowerShell 5.0) のインストールが推奨されます (必須ではない) 。WMF はこちらから入手可能です: https://www.microsoft.com/en-us/download/details.aspx?id=54616 24 | 25 | Zip ファイルダウンロードするには、緑色の「code」ボタンを押下し、トレース対象のサーバー上の任意の場所に解凍ください。スクリプト ファイルと「helpermodules」フォルダーが指定した場所にあることを確認ください。 26 | 27 | スクリプトは PowerShell コンソールから実行することを推奨します。 28 | 29 | ### トレース スクリプトを対話モードで実行する場合: 30 | 31 | パラメーターなしでスクリプトを実行すると、フォーム画面が表示され、以下のオプションが提供されます: 32 | 33 | | オプション | 説明 | 34 | | :--------: | :--------- | 35 | | Configuration Only | このモードでは静的なデータのみがエクスポートされます。もっとも一般的なシナリオです。 | 36 | | Runtime Tracing | このモードでは Configuration Only のデータに加え、http.sys、schannel、kerberos/ntlm のデバッグ トレース、および AD FS、DRS のイベント ログを有効化して取得します。 | 37 | | Include Network Traces | ネットワーク トレースを収集します。Runtime Tracing のオプションとの併用時にのみ使用できます。 | 38 | | Include Performance Counters | AD FS のパフォーマンス カウンターを収集します。Runtime Tracing のオプションとの併用時にのみ使用できます。 | 39 | | LDAP Traces | LDAP のデバッグ ログを有効化します。AD FS のセットアップや LDAP 関連の問題のデバッグに使用します。
本オプションは Web Application Proxy サーバーでは利用不可です。
警告: このオプションを開始すると AD FS サービスが再起動する場合があります | 40 | | WAP Traces | Web Application Proxy サーバー コアの高度なデバッグ ログを有効化します。Exchange OWA、SharePoint オンプレミスなどの WAP 公開アプリケーションの問題をトラブルシュートするのに利用します。
Web Application Proxy サーバーでのみ利用可能なオプションです。 | 41 | | Textbox/Browse | データ保存先のフォルダーのパスを指定するのに使用します。ファイルの保存先はファイル エクスプローラーをご利用いただいても結構です。 | 42 | 43 | ### コンソールからスクリプトを実行する場合: 44 | 45 | スクリプトは画面から実行した場合と同様に 4 つのパラメーターを受け付けます: 46 | 47 | | オプション | 説明 | 48 | | :-------- | :--------- | 49 | | -Path | ファイルを保存するフォルダーの絶対パスを指定します。
このパラメーターが省略された場合、スクリプトは対話モードで実行され、他のオプションは無視されます。 | 50 | | -Tracing | 省略された場合、スクリプトは Configuration Only のモードで実行され、オプションで指定されたトレースは無視されます。 | 51 | | -NetworkTracing | ネットワーク トレースを有効化します。-Tracing が指定されている場合のみ動作します。 | 52 | | -PerfTracing | パフォーマンス カウンターの収集を有効化します。-Tracing が指定されている場合のみ動作します。 | 53 | | -LDAPTracing | LDAP デバッグ トレースを有効化します。-Tracing が指定され、AD FS サーバー上で実行される場合のみ動作します。 | 54 | | -WAPTracing | WAP デバッグ トレースを有効化します。-Tracing が指定され、WAP サーバー上で実行される場合のみ動作します。 | 55 | 56 | --- 57 | 58 | 事象の再現中に実行する場合、特にトレースを取得するシナリオの場合は、スクリプトは最初に静的データの収集を開始します。複数のマシンでトレースが必要な場合、他のサーバーでも設定に時間をかけられるようスクリプトは一旦動作を停止します。 59 | 60 | すべてのサーバーが準備できたら、画面の指示に従って CTRL+Y を押すか、PowerShell ISE では OK をクリックしてトレースを再開します。その後、データ収集/トレースが実行中であることを示すメッセージが表示されます。この時点で、キャプチャしたい問題を再現ください。 61 | >[!TIP] 62 | > データのサイズを最小限にするため、できるだけ素早く問題を再現ください。 63 | 64 | 問題を再現したら、CTRL+Y (コンソールの場合) または OK (ISE の場合) で収集を停止します。残りのデータ収集とデバッグ トレースのコンパイルには時間がかかる場合があります。**時間がかかっていても中断せず、しばらくお待ちください。** 65 | 66 | スクリプトが終了したら、サポート エンジニアにより提供されたワークスペース (DTM) に圧縮したファイルをアップロードください。 67 | 68 | ### 出力されるファイルに関する参考情報: 69 | 70 | | ファイル名 | 説明 | 71 | | ----------- | ----------- | 72 | | AD FS Tracing-Debug.evtx | AD FS の詳細診断/デバッグ イベント ログ | 73 | | AD FS-Admin.evtx | AD FS の管理ログ (エラーや情報などのイベント ログ) | 74 | | Application.evtx | Windows OS のアプリケーション イベント ログ | 75 | | DRS-Admin.evtx | デバイス登録サービスのイベント ログ | 76 | | Device Registration Service Tracing-Debug.evtx | デバイス登録サービスの診断イベント ログ | 77 | | Microsoft-Windows-CAPI2-Operational.evtx | 証明書の検証に関する問題を分析するための Crypto API イベント ログ | 78 | | Security.evtx | OS のセキュリティ イベント ログ (サイズは最大で 1 時間またはトレースを取得した期間になります) | 79 | | Microsoft-Windows-WebApplicationProxy-Session.evtx | WAP のデバッグ イベント ログ | 80 | | Microsoft-Windows-WebApplicationProxy-Admin.evtx | WAP の管理イベント ログ | 81 | | System.evtx | システム イベント ログ | 82 | | Hostname--perf_.blg | トレース期間中のパフォーマンス カウンター情報 | 83 | | Hostname-ADFS-DatabaseStatus.txt | AD FS データベースの状態に関する基本的な健全性テスト (WID を利用の場合のみ) | 84 | | Hostname-ADFS-fileversions.txt | 現在インストールされている AD FS バイナリ ファイルのバージョン | 85 | | Hostname-Certificates-CA.txt | コンピューターの中間認証局証明書ストアの一覧 | 86 | | Hostname-Certificates-My.txt | コンピューターの個人証明書ストアの一覧 | 87 | | Hostname-Certificates-Root.txt | コンピューターのルート CA 証明書ストアの一覧 | 88 | | Hostname-Certificates-NTAuth.txt | コンピューターの NTAuth ストアの一覧 | 89 | | Hostname-Certificates-ADFSTrustedDevices.txt | トレース後に収集された ADFSTrustedDevices ストアの一覧 | 90 | | Hostname-Certificates-CliAuthIssuer.txt | ClientAuthIssuers ストアの一覧 (AD FS HTTP バインディングで CTL ストアが構成されている場合) | 91 | | Hostname-environment-variables.txt | 登録されているシステム環境変数 | 92 | | Hostname-GPReport.html | スクリプトの実行ユーザーとコンピューターに適用されたグループ ポリシー | 93 | | Hostname-hosts.txt | ホスト ファイルのエントリの一覧 | 94 | | Hostname-ipconfig-all.txt | ネットワーク アダプターの TCP/IP の構成 | 95 | | Hostname-Microsoft.IdentityServer.ServiceHost.Exe.Config | AD FS サービスの構成ファイル | 96 | | Hostname-sysinfo.txt | システムに関する基本情報 | 97 | | Hostname-netsh-dnsclient-show-state.txt | DNSSEC および DirectAccess の構成情報 | 98 | | Hostname-DNSClient-Cache.txt | DNS クライアントのキャッシュ エントリ | 99 | | Hostname-netsh-http-show-cacheparam.txt | HTTP のキャッシュ構成 | 100 | | Hostname-netsh-http-show-cachestate.txt | HTTP のキャッシュ状態 | 101 | | Hostname-netsh-http-show-iplisten.txt | HTTP IP のリスナー構成 | 102 | | Hostname-netsh-http-show-servicestate.txt | 登録されている Web アプリケーション エンドポイントの一覧 | 103 | | Hostname-netsh-http-show-sslcert.txt | HTTP バインディングの構成 | 104 | | Hostname-netsh-http-show-timeout.txt | HTTP ドライバのタイムアウト設定 | 105 | | Hostname-netsh-http-show-urlacl.txt | HTTP における URL 予約 | 106 | | Hostname-netsh-int-advf-show-global.txt | グローバルのファイアウォール設定 | 107 | | Hostname-netsh-int-ipv4-show-dynamicport-tcp.txt | IPv4 TCP ポート範囲の定義 | 108 | | Hostname-netsh-int-ipv4-show-dynamicport-udp.txt | IPv4 UDP ポート範囲の定義 | 109 | | Hostname-netsh-int-ipv6-show-dynamicport-tcp.txt | IPv6 TCP ポート範囲の定義 | 110 | | Hostname-netsh-int-ipv6-show-dynamicport-udp.txt | IPv6 UDP ポート範囲の定義 | 111 | | Hostname-netsh-winhttp-proxy.txt | システム プロキシ構成の出力 | 112 | | Hostname-NetTCPConnection.txt | 現在確立されているネットワーク接続の一覧 | 113 | | Hostname-network.etl | トレース セッション中に収集されたネットワーク トレース | 114 | | Hostname-nltest-trusted_domains.txt | AD FS ドメインが信頼するドメインの一覧 | 115 | | Hostname-reg-ciphers_policy_registry.txt | GPO によって展開された TLS 暗号設定 | 116 | | Hostname-reg-Cryptography_registry.txt | TLS/SSL 暗号化構成のレジストリ出力 | 117 | | Hostname-reg-NETLOGON-port-and-other-params.txt | Netlogon サービスのレジストリ設定 | 118 | | Hostname-reg-NTDS-port-and-other-params.txt | NTDS 設定プロパティのレジストリ出力 | 119 | | Hostname-reg-schannel.txt | SCHannel 構成パラメータ (TLS/SSL 関連) | 120 | | Hostname-DotNetFramework.txt | .NET Framework のバージョンと TLS プロトコルのサポート | 121 | | Hostname-route-print.txt | ローカル マシンの IP ルーティング構成 | 122 | | Hostname-services-running.txt | 現在実行中のサービス一覧 | 123 | | Hostname-tasklist.txt | 実行中のタスク一覧 | 124 | | Hostname-WindowsPatches.htm | インストール済み Windows 更新プログラム情報 | 125 | | dcloc_krb_ntlmauth.etl | Kerberos および NTLM デバッグトレース (バイナリ形式) | 126 | | http_trace.etl | HTTP ドライバ トレース (バイナリ形式) | 127 | | schannel.etl | Schannel (TLS/SSL プロバイダー) デバッグ ファイル (バイナリ形式) | 128 | | ldap.etl | LDAP デバッグ トレース ファイル (バイナリ形式) | 129 | | wap_trace.etl | Web アプリケーションのコア デバッグ トレース (バイナリ形式) | 130 | | Get-AdfsAccessControlPolicy.txt | 現在定義されているすべてのアクセス制御ポリシーの一覧 | 131 | | Get-AdfsAdditionalAuthenticationRule.txt | グローバル MFA クレーム ルールの詳細 (構成されている場合) | 132 | | Get-AdfsApplicationGroup.txt | 構成済み OAuth 2.0 および OpenID Connect アプリケーション グループの概要 | 133 | | Get-AdfsApplicationPermission.txt | OAuth 2.0 および OpenID Connect クライアント アプリの構成済みアプリケーション権限の一覧 | 134 | | Get-AdfsAttributeStore.txt | 構成済み属性ストア (AD/LDAP/SQL またはカスタム属性ストア プロバイダー) の一覧 | 135 | | Get-AdfsAuthenticationProvider.txt | インストールされている認証プロバイダーの一覧 | 136 | | Get-AdfsAuthenticationProviderWebContent.txt | 認証プロバイダーの Web カスタマイズ (構成されている場合) | 137 | | Get-ADFSAzureMfaAdapterconfig.txt | Azure MFA アダプター構成のエクスポート (構成されている場合) | 138 | | Get-AdfsCertificate.txt | トークンの署名/復号およびサービス通信に使用される証明書の詳細 | 139 | | Get-AdfsCertificateAuthority.txt | WHfB シナリオにおける AD FS 証明書登録機関の構成 | 140 | | Get-AdfsClaimDescription.txt | すべてのクレーム記述の一覧 | 141 | | Get-AdfsClaimsProviderTrust.txt | 構成されたクレーム プロバイダーの詳細構成情報 | 142 | | Get-AdfsClaimsProviderTrustsGroup.txt | (構成されている場合) クレーム プロバイダー信頼グループの一覧 | 143 | | Get-AdfsClient.txt | 現在登録されている OAuth 2.0 クライアントの一覧 | 144 | | Get-AdfsDeviceRegistration.txt | デバイス登録設定の詳細 | 145 | | Get-AdfsDeviceRegistrationUpnSuffix.txt | 登録されたデバイス登録ドメイン サフィックスの一覧 (Get-AdfsRegistrationHosts と同様) | 146 | | Get-AdfsDirectoryProperties.txt | 認証を許可された UPN サフィックス/NetBIOS 名の一覧 (2019+) | 147 | | Get-AdfsEndpoint.txt | AD FS エンドポイントの構成一覧 (有効 / 無効含む) | 148 | | Get-AdfsFarmInformation.txt | AD FS ファーム ノードの一覧 | 149 | | Get-AdfsGlobalAuthenticationPolicy.txt | AD FS の認証ハンドラーの構成 | 150 | | Get-AdfsGlobalWebContent.txt | 共通した AD FS の Web カスタマイズ設定に関する情報 | 151 | | Get-AdfsLocalClaimsProviderTrust.txt | ローカル クレーム プロバイダー (AD 組み込みおよび LDAP クレーム プロバイダー) の一覧 | 152 | | Get-AdfsNativeClientApplication.txt | 構成された OAuth 2.0 および OpenID Connect ネイティブ クライアント アプリの一覧 | 153 | | Get-AdfsNonClaimsAwareRelyingPartyTrust.txt | WAP に公開される可能性のある非クレーム アプリの一覧 | 154 | | Get-AdfsProperties.txt | AD FS サービス構成プロパティの一覧 | 155 | | Get-AdfsRegistrationHosts.txt | 登録されたデバイス登録ドメイン サフィックスの一覧 | 156 | | Get-AdfsRelyingPartyTrust.txt | 現在構成されているすべての証明書利用者信頼の出力 | 157 | | Get-AdfsRelyingPartyTrustsGroup.txt | 証明書利用者信頼のグループ構成の一覧 | 158 | | Get-AdfsRelyingPartyWebContent.txt | 構成された証明書利用者信頼の Web コンテンツ カスタマイズの一覧 | 159 | | Get-AdfsRelyingPartyWebTheme.txt | 証明書利用者信頼に関連付けられた Web テーマの一覧 | 160 | | Get-AdfsScopeDescription.txt | OpenID Connect スコープ定義 | 161 | | Get-AdfsServerApplication.txt | OAuth 2.0 サーバー アプリケーションの構成詳細 | 162 | | Get-AdfsSslCertificate.txt | HTTP にバインドされている SSL 証明書 | 163 | | Get-AdfsSyncProperties.txt | WID (Windows Internal Database) 展開における AD FS のデータベースの同期状態 | 164 | | Get-AdfsTrustedFederationPartner.txt | 信頼されたフェデレーション パートナーの一覧 | 165 | | Get-AdfsWebApiApplication.txt | OAuth 2.0 および OpenID Connect の Web API 構成設定 | 166 | | Get-AdfsWebApplicationProxyRelyingPartyTrust.txt | WAP で事前認証を構成している証明書利用者信頼の構成の出力 | 167 | | Get-AdfsWebConfig.txt | 現在アクティブなデフォルト Web テーマと Cookie 設定 (HomeRealmDiscovery の自動化用) | 168 | | Get-AdfsWebTheme.txt | 構成された AD FS Web テーマの一覧 | 169 | | Get-ServiceAccountDetails.txt | AD DS 内の AD FS サービス アカウント構成の詳細と使用される Kerberos 暗号化の情報 | 170 | | netlogon.bak | Netlogon デバッグ ログのバックアップ ファイル (ログ ファイルが長期間のトレースで 100MB を超えた場合に作成) | 171 | | netlogon.log | Netlogon デバッグ ログ情報 | 172 | | Get-WebApplicationProxyApplication.txt | 公開されたアプリケーションの一覧 | 173 | | Get-WebApplicationProxyAvailableADFSRelyingParty.txt | フェデレーション サーバーで構成された利用可能な証明書利用者信頼の一覧 | 174 | | Get-WebApplicationProxyConfiguration.txt | Web Application Proxy サーバーの全体の設定 | 175 | | Get-WebApplicationProxyHealth.txt | Web Application Proxy サーバーの健全性状態 | 176 | | Get-WebApplicationProxySslCertificate.txt | フェデレーション サーバー プロキシ用の SSL 証明書のバインディング情報 | 177 | | Get-WebApplicationProxyAdfsTimeSkew.txt | バックエンドの AD FS への呼び出しを行った際に生じた時間差の確認結果 | 178 | | HOSTNAME-Microsoft.IdentityServer.ProxyService.exe.config | プロキシ サービスの構成ファイル | 179 | | transscript_output.txt | スクリプト実行に関する診断/テレメトリ情報 | 180 | | Wid \ error.log | WID エラーログ (WID 展開時のみ収集、ファイルの累積サイズが 10MB 以下の場合) | 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADFS Diagnostic Tracing 2 | 3 | ### Important notices 4 | --- 5 | The script ( ADFS-tracing.ps1 ) is designed to collect information that will help Microsoft Customer Support Services (CSS) troubleshoot an issue you may be experiencing with Active Directory Federation Services or Web Application Proxy Server. 6 | The collected data may contain Personally Identifiable Information (PII) and/or sensitive data, such as (but not limited to) IP addresses; computer names and/or user names. 7 | 8 | All data generated by the script will be stored in a designated folder specified by the user at initialization. 9 | Once the tracing has completed, the script will automatically generate a archive file (zip file), with the server name, date and time. Should for some reasons the archive process fail, all data is located in a subfolder (/temporary) in the specified destination folder. 10 | >[!NOTE] 11 | > This Script does not require an Internet Connection and will at no point automatically send data to Microsoft. 12 | > Any data collected by this script **must only** be sent to Microsoft as part of an active Support Engagement. 13 | > Any data you want to send to Microsoft **must only** be transfered through a Secure File Transfer. 14 | > 15 | > Access to such transfer tools should be provided by the Microsoft support professional assigned to your support incident. 16 | > Please discuss this with your support professional and also any concerns you may have. 17 | > 18 | > https://privacy.microsoft.com/en-us/privacy 19 | 20 | ### Script usage 21 | --- 22 | ##### Requirements: 23 | - the Script supports ADFS on Windows Server 2012R2 / Windows Server 2016 / Windows Server 2019 / Windows Server 2022 and Windows Server 2025 24 | - local administrator privileges are required to run the scripts at minimum. 25 | - Preferably the account is also a Domain User 26 | - a miminum of 5GB of free diskspace on the volume for the target folder when running the tracing for a longer period 27 | - on Windows Server 2012R2 it is recommended (not mandatory) to have the Windows Management Framework 5.1 (WMF) aka Powershell 5.0 installed 28 | You can get the WMF from https://www.microsoft.com/en-us/download/details.aspx?id=54616 29 | 30 | 31 | Download the zip file via the green "code" button and extract the Zip file content to a location on the servers you want to trace. 32 | Ensure that you have the Scriptfile and the folder "helpermodules" in the target location prior from executing the script. 33 | 34 | It is recommended to run the script from Powershell Console. 35 | 36 | ##### Running the Tracing Script interactively: 37 | When executing the Script without any parameters the script will Render a Forms UI and providing you with the following Options: 38 | 39 | | Options | Description 40 | | :--------: | :--------- | 41 | | Configuration Only | In this mode only static data will be exported. This is the default scenario even if not enabled| 42 | | Runtime Tracing | In this mode the script will collect all data available in Configuration Only and enables additional debug traces for http.sys, schannel, kerberos/ntlm and ADFS and DRS Debug Event Tracing | 43 | | include Network Traces | This option is only available for a runtime trace and you can opt-in if you want to collect a network trace | 44 | | include Performance Counters | This option is only available for a runtime trace and you can opt-in to collect ADFS performance counters for the duration of the tracing | 45 | | LDAP Traces | Enables Debug logging for LDAP and may be used when debugging ADFS Setup issues and other LDAP dependent activities.
This option is unavailable on Web Application Proxy Servers
Warning:This option may cause a service restart when started | 46 | | WAP Traces | Enables Advanced Debug logging for Web Application Proxy Server Core to troubleshoot issues to WAP published applications
like Exchange OWA, Sharepoint On-premise, etc...
Only available on Web Application Proxy Servers | 47 | | Textbox/Browse | Provide the path to the Destination folder or alternatively you can use the Filebrowser to select the folder where the data will be stored | 48 | 49 | 50 | ##### Running the script from console: 51 | The script accepts four parameters similar to the UI 52 | | Options | Description 53 | | :-------- | :--------- | 54 | | -Path | Provide the absolute path to the folder where the files should be stored.
If this parameter is omitted the script will automatically run in interactive mode ignoring other switch parameters | 55 | | -Tracing | if omitted the script will run in a config only mode ignoring any one of the optional trace switches | 56 | | -NetworkTracing | set this switch to enable network tracing; this only works if -Tracing is provided | 57 | | -PerfTracing | set this switch to enable performance counter collection; this only works if -Tracing is provided | 58 | | -LDAPTracing | set this switch to enable LDAP debug tracing; this only works if -Tracing is provided and when executed on ADFS Servers| 59 | | -WAPTracing | set this switch to enable LDAP debug tracing; this only works if -Tracing is provided and when executed on WAP Servers| 60 | 61 | During runtime and in particular the trace scenario the script will begin pulling initial static data. 62 | It will Pause the execution to give you the time to configure the other servers, if tracing on multiple machines is required. 63 | 64 | Once all servers are prepared for tracing you can resume tracing on each of the servers by following the onscreen instructions by pressing the Key combination CTRL+Y 65 | or should you run the script in Powershell ISE a Dialog popup should occur. Click OK here. 66 | 67 | The script will then display another message to inform you that the data collection/tracing is running. 68 | At this time perform the steps to REPRODUCE THE ISSUE you want to capture. 69 | >[!TIP] 70 | >Try to reproduce the issue as fast you can to keep the size of the data as small as possible. 71 | 72 | Once the problem has been reproduced you can stop the collection by pressing CTRL+Y (console) or by clicking OK (ISE) 73 | 74 | At this point it will take some time collect the remaining data and to compile the debug traces (if Tracing was enabled). 75 | So **please be patient** and do not abort the script through Task Manager 76 | 77 | 78 | 79 | When the scripts finished, you can upload the compressed file to the workspace provided by the support engineer. 80 | 81 | 82 | 83 | ##### Output File Reference: 84 | 85 | | Filename | Description | 86 | | ----------- | ----------- | 87 | | AD FS Tracing-Debug.evtx | ADFS verbose diagnostics/debug events | 88 | | AD FS-Admin.evtx | ADFS Administrative logs containing high level error and informative events | 89 | | Application.evtx | Windows OS Application eventlogs | 90 | | DRS-Admin.evtx | Device Registration Service event logs | 91 | | Device Registration Service Tracing-Debug.evtx | Device Registration Service diagnostic events | 92 | | Microsoft-Windows-CAPI2-Operational.evtx | Crypto API Events allowing to analyze Certificate Validation issues | 93 | | Security.evtx | Security Eventlogs of the Operating System. Size is limited to maximum 1hour or for the duration of a trace session | 94 | | Microsoft-Windows-WebApplicationProxy-Session.evtx | WAP Debug Event logs* | 95 | | Microsoft-Windows-WebApplicationProxy-Admin.evtx | WAP Admin Event logs* | 96 | | System.evtx | System Event logs | 97 | | Hostname--perf_.blg | Performance Counter information's for the duration of a trace. | 98 | | Hostname-ADFS-DatabaseStatus.txt | basic health test on the ADFS database status (WID-Only) | 99 | | Hostname-ADFS-fileversions.txt | readout of the ADFS binary file versions currently installed | 100 | | Hostname-Certificates-CA.txt | enumeration of the Intermediate Authentication Certificate Store of the computer | 101 | | Hostname-Certificates-My.txt | enumeration of the Personal CertificateStore of the computer | 102 | | Hostname-Certificates-Root.txt | enumeration of the Root CA CertificateStore of the computer | 103 | | Hostname-Certificates-NTAuth.txt | enumeration of the NTAuth Store of the computer | 104 | | Hostname-Certificates-ADFSTrustedDevices.txt | enumeration of the ADFSTrustedDevices Store of the computer collected after a trace | 105 | | Hostname-Certificates-CliAuthIssuer.txt | enumeration of the ClientAuthIssuers Store (only if configured CTL store on an ADFS HTTP Binding) | 106 | | Hostname-environment-variables.txt | Current System Environment Variables registered | 107 | | Hostname-GPReport.html | Group Policies applied to the user running script and the Computer | 108 | | Hostname-hosts.txt | list Hostfile entries | 109 | | Hostname-ipconfig-all.txt | contains TCP/IP configuration of the network adapters | 110 | | Hostname-Microsoft.IdentityServer.ServiceHost.Exe.Config | ADFS Service Configuration file | 111 | | Hostname-sysinfo.txt | contains base informations about the system | 112 | | Hostname-netsh-dnsclient-show-state.txt | informations about DNSSEC and DirectAccess configuration | 113 | | Hostname-DNSClient-Cache.txt | DNS CLient cache entries for DNS resolved resource | 114 | | Hostname-netsh-http-show-cacheparam.txt | contains http configuration for caching | 115 | | Hostname-netsh-http-show-cachestate.txt | contains http caching status | 116 | | Hostname-netsh-http-show-iplisten.txt | contains http ip listeners if configured | 117 | | Hostname-netsh-http-show-servicestate.txt | contains a list of currently registered web application endpoints | 118 | | Hostname-netsh-http-show-sslcert.txt | HTTP Binding configuration | 119 | | Hostname-netsh-http-show-timeout.txt | HTTP driver timeout settings | 120 | | Hostname-netsh-http-show-urlacl.txt | URL Reservations in HTTP | 121 | | Hostname-netsh-int-advf-show-global.txt | global firewall setting | 122 | | Hostname-netsh-int-ipv4-show-dynamicport-tcp.txt | IPv4 TCP Port range definition | 123 | | Hostname-netsh-int-ipv4-show-dynamicport-udp.txt | IPv4 UDP Port range definition | 124 | | Hostname-netsh-int-ipv6-show-dynamicport-tcp.txt | IPv6 TCP Port range definition | 125 | | Hostname-netsh-int-ipv6-show-dynamicport-udp.txt | IPv6 TCP Port range definition | 126 | | Hostname-netsh-winhttp-proxy.txt | output of System Proxy configuration | 127 | | Hostname-NetTCPConnection.txt | contains a list of currently established network connections | 128 | | Hostname-network.etl | contains a network trace collected during a trace session | 129 | | Hostname-nltest-trusted_domains.txt | list of trusted domains the adfs domain trusts | 130 | | Hostname-reg-ciphers_policy_registry.txt | TLS Cipher Configuration deployed via GPOs | 131 | | Hostname-reg-Cryptography_registry.txt | registry export of the TLS/SSL Cryptography config | 132 | | Hostname-reg-NETLOGON-port-and-other-params.txt | export of the netlogon service registry settings | 133 | | Hostname-reg-NTDS-port-and-other-params.txt | an registry export of NTDS settings properties | 134 | | Hostname-reg-schannel.txt | SCHannel configuration parameters; related to TLS/SSL configuration | 135 | | Hostname-DotNetFramework.txt | .NetFramework version and TLS protocol support | 136 | | Hostname-route-print.txt | ip routing configuration of the local machine | 137 | | Hostname-services-running.txt | a list of all currently running services | 138 | | Hostname-tasklist.txt | a list of all running tasks | 139 | | Hostname-WindowsPatches.htm | contains informations about installed Windows Updates | 140 | | dcloc_krb_ntlmauth.etl | contains kerberos and NTLM debug traces in a binary format | 141 | | http_trace.etl | http driver trace in binary format | 142 | | schannel.etl | schannel (TLS/SSL provider) debug file in a binary format | 143 | | ldap.etl | LDAP debug tracing file in a binary format | 144 | | wap_trace.etl | Web Application Core debug tracing in a binary format| 145 | | Get-AdfsAccessControlPolicy.txt | contains list of all Access Control Policies currently defined in ADFS | 146 | | Get-AdfsAdditionalAuthenticationRule.txt | Contains details of global MFA claim Rules if configured | 147 | | Get-AdfsApplicationGroup.txt | summary of configured OAUTH2/OpenID application groups | 148 | | Get-AdfsApplicationPermission.txt | a list of configured application permissions fror Oauth2/OpenID client apps | 149 | | Get-AdfsAttributeStore.txt | a list of configured Attribute stores (AD/LDAP/SQL or custom attribute store providers | 150 | | Get-AdfsAuthenticationProvider.txt | a list of al installed authentication providers | 151 | | Get-AdfsAuthenticationProviderWebContent.txt | contains web customization for Authentication providers if configured | 152 | | Get-ADFSAzureMfaAdapterconfig.txt | an export of the Azure MFA Adaper configuration if configured | 153 | | Get-AdfsCertificate.txt | details of the currently configured Certificates for TokenSigning/Decryption and ServiceCommunication | 154 | | Get-AdfsCertificateAuthority.txt | contains the configuration of the ADFS Certificate Enrollment authority in WHFB scenarios | 155 | | Get-AdfsClaimDescription.txt | a list of all Claims descriptions | 156 | | Get-AdfsClaimsProviderTrust.txt | detailed configuration information of configured Claims Provider | 157 | | Get-AdfsClaimsProviderTrustsGroup.txt | lists Claims Provider trust groups if configured | 158 | | Get-AdfsClient.txt | lists currently registered Oauth2 CLients | 159 | | Get-AdfsDeviceRegistration.txt | details of the Device Registration settings | 160 | | Get-AdfsDeviceRegistrationUpnSuffix.txt | contains lists of registered Device Registration Domain Suffixes identically to Get-AdfsRegistrationHosts | 161 | | Get-AdfsDirectoryProperties.txt | a list of discovered UPN Suffixes/Netbios Names allowed to authenticate (with 2019+) | 162 | | Get-AdfsEndpoint.txt | a list of ADFS endpoints enabled/disabled | 163 | | Get-AdfsFarmInformation.txt | a list of all ADFS Farmnodes in a 2016/2019 Farm deployment | 164 | | Get-AdfsGlobalAuthenticationPolicy.txt | Authentication Handler configuration in ADFS | 165 | | Get-AdfsGlobalWebContent.txt | contains informations about the common ADFS Web customization settings | 166 | | Get-AdfsLocalClaimsProviderTrust.txt | a list of local claims provider (AD builtin and LDAP claims provider) | 167 | | Get-AdfsNativeClientApplication.txt | a list of configured OAuth2/OpenID native client apps | 168 | | Get-AdfsNonClaimsAwareRelyingPartyTrust.txt | a list of non-claims apps that may be published in WAP | 169 | | Get-AdfsProperties.txt | lists the ADFS Service configuration properties | 170 | | Get-AdfsRegistrationHosts.txt | contains lists of registered Device Registration Domain Suffixes | 171 | | Get-AdfsRelyingPartyTrust.txt | output of all relying party trust applications currently configured | 172 | | Get-AdfsRelyingPartyTrustsGroup.txt | lists the Relying Party Trust Group configuration | 173 | | Get-AdfsRelyingPartyWebContent.txt | lists all Relying Party configured web content customizations | 174 | | Get-AdfsRelyingPartyWebTheme.txt | contains a list of Relying party associated web themes | 175 | | Get-AdfsScopeDescription.txt | Openid Scope definitions | 176 | | Get-AdfsServerApplication.txt | OAUTH2 Server Application configuration detauls | 177 | | Get-AdfsSslCertificate.txt | currently bound SSL certificates in HTTP | 178 | | Get-AdfsSyncProperties.txt | Contains information about the ADFS Database Sync status in WID deployments | 179 | | Get-AdfsTrustedFederationPartner.txt | * | 180 | | Get-AdfsWebApiApplication.txt | Oauth2/OpenID web API configuration settings | 181 | | Get-AdfsWebApplicationProxyRelyingPartyTrust.txt | output for the WAP Pre-Authentication relying party configuration | 182 | | Get-AdfsWebConfig.txt | shows currently active default web theme and cookie settings for HomeRealmDiscovery automation | 183 | | Get-AdfsWebTheme.txt | a list of configured ADFS Web Themes | 184 | | Get-ServiceAccountDetails.txt | Contains details about the ADFS Service Account configuration in AD DS and predicts Kerberos Encryption used | 185 | | netlogon.bak | netlogon debug log backup file (usually created if the log file itself exceeds 100mb during a longer tracing period) | 186 | | netlogon.log | netlogon debug log informations | 187 | | Get-WebApplicationProxyApplication.txt | Lists the published applications | 188 | | Get-WebApplicationProxyAvailableADFSRelyingParty.txt | list of available relying parties configured on a federation server| 189 | | Get-WebApplicationProxyConfiguration.txt | Global Web Application Proxy settings | 190 | | Get-WebApplicationProxyHealth.txt | Health status of the Web Application Proxy server | 191 | | Get-WebApplicationProxySslCertificate.txt | binding information for the SSL certificate for federation server proxy | 192 | | Get-WebApplicationProxyAdfsTimeSkew.txt | the script attempts to call the backend ADFS for testing on potential timeskews | 193 | | HOSTNAME-Microsoft.IdentityServer.ProxyService.exe.config | Proxy Service Configuration file | 194 | | transscript_output.txt | diagnostics/telemetry about the execution of the script | 195 | | Wid \ error.log | WID error logs (only collected if WID Deployments and if the cummulative size of the files is <=10mb) | 196 | -------------------------------------------------------------------------------- /ADFS-tracing.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################################################ 2 | # ADFS troubleshooting - Data Collection 3 | # Supported OS versions: Windows Server 2012 to Server 2025 4 | # Supported role: ADFS on 2012 to 2022, ADFS proxy server (2012) and Web Application Proxy (2012 R2 to 2022) 5 | ############################################################################################################ 6 | 7 | param ( 8 | [Parameter(Mandatory=$false)] 9 | [string] $Path, 10 | 11 | [Parameter(Mandatory=$false)] 12 | [switch]$Tracing, 13 | 14 | [Parameter(Mandatory=$false)] 15 | [switch]$NetworkTracing, 16 | 17 | [Parameter(Mandatory=$false)] 18 | [switch]$LDAPTracing, 19 | 20 | [Parameter(Mandatory=$false)] 21 | [switch]$WAPTracing, 22 | 23 | [Parameter(Mandatory=$false)] 24 | [switch]$PerfTracing 25 | ) 26 | 27 | 28 | ########################################################################## 29 | #region Assembly Depencies 30 | Add-Type -AssemblyName System.ServiceProcess 31 | Add-Type -AssemblyName System.Windows.Forms 32 | Add-Type -AssemblyName System.IO.Compression.FileSystem 33 | 34 | #region Parameters 35 | [Version]$WinVer = [System.Environment]::OSVersion.Version 36 | $IsProxy = ((Get-WindowsFeature -name ADFS-Proxy).Installed -or (Get-WindowsFeature -name Web-Application-Proxy).Installed) 37 | 38 | # Event logs 39 | $ADFSDebugEvents = "Microsoft-Windows-CAPI2/Operational","AD FS Tracing/Debug","Device Registration Service Tracing/Debug" 40 | $WAPDebugEvents = "Microsoft-Windows-CAPI2/Operational","AD FS Tracing/Debug","Microsoft-Windows-WebApplicationProxy/Session" 41 | 42 | $ADFSExportEvents = 'System','Application','Security','AD FS Tracing/Debug','AD FS/Admin','Microsoft-Windows-CAPI2/Operational','Device Registration Service Tracing/Debug','DRS/Admin' 43 | $WAPExportEvents = 'System','Application','Security','AD FS Tracing/Debug','AD FS/Admin','Microsoft-Windows-CAPI2/Operational','Microsoft-Windows-WebApplicationProxy/Admin','Microsoft-Windows-WebApplicationProxy/Session' 44 | 45 | #Definition Netlogon Debug Logging 46 | $setDBFlag = 'DBFlag' 47 | $setvaltype = [Microsoft.Win32.RegistryValueKind]::String 48 | $setvalue = "0x2fffffff" 49 | 50 | # Netlogon increase size to 100MB = 102400000Bytes = 0x61A8000) 51 | $setNLMaxLogSize = 'MaximumLogFileSize' 52 | $setvaltype2 = [Microsoft.Win32.RegistryValueKind]::DWord 53 | $setvalue2 = 0x061A8000 54 | 55 | # Store the original values to revert the config after collection 56 | $orgdbflag = (get-itemproperty -PATH "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters").$setDBFlag 57 | $orgNLMaxLogSize = (get-itemproperty -PATH "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters").$setNLMaxLogSize 58 | 59 | #ETW Trace providers for SSL,kerberos,ntlm,http.sys 60 | $LogmanOn = 'logman.exe create trace "schannel" -ow -o .\schannel.etl -p {37D2C3CD-C5D4-4587-8531-4696C44244C8} 0xffffffffffffffff 0xff -nb 16 16 -bs 1024 -mode Circular -f bincirc -max 1024 -ets',` 61 | 'logman create trace "dcloc" -ow -o .\dcloc_krb_ntlmauth.etl -p "Microsoft-Windows-DCLocator" 0xffffffffffffffff 0xff -nb 16 16 -bs 1024 -mode Circular -f bincirc -max 1024 -ets',` 62 | 'logman update trace "dcloc" -p {6B510852-3583-4E2D-AFFE-A67F9F223438} 0xffffffffffffffff 0xff -ets',` 63 | 'logman update trace "dcloc" -p {5BBB6C18-AA45-49B1-A15F-085F7ED0AA90} 0xffffffffffffffff 0xff -ets',` 64 | 'logman create trace "minio_http" -ow -o .\http_trace.etl -p "Microsoft-Windows-HttpService" 0xffffffffffffffff 0xff -nb 16 16 -bs 1024 -mode Circular -f bincirc -max 2048 -ets',` 65 | 'logman update trace "minio_http" -p "Microsoft-Windows-HttpEvent" 0xffffffffffffffff 0xff -ets',` 66 | 'logman update trace "minio_http" -p "Microsoft-Windows-Http-SQM-Provider" 0xffffffffffffffff 0xff -ets',` 67 | 'logman update trace "minio_http" -p {B3A7698A-0C45-44DA-B73D-E181C9B5C8E6} 0xffffffffffffffff 0xff -ets' 68 | 69 | $LogmanOff = 'logman stop "schannel" -ets',` 70 | 'logman stop "minio_http" -ets',` 71 | 'logman stop "dcloc" -ets' 72 | 73 | #ldap debug traces; process filters are set in the function to enable ldap tracing 74 | $ldapetlOn='logman create trace "adfs_ldap" -ow -o .\ldap.etl -p "Microsoft-Windows-ADSI" 0xffffffffffffffff 0xff -nb 16 16 -bs 1024 -mode Circular -f bincirc -max 4096 -ets',` 75 | 'logman update trace "adfs_ldap" -p "Microsoft-Windows-LDAP-Client" 0xffffffffffffffff 0xff -ets' 76 | 77 | $ldapetlOff= 'logman stop "adfs_ldap" -ets' 78 | 79 | #Web Application Proxy Traces 80 | $WAPTraceOn = 'logman create trace "WebAppProxy" -ow -o .\wap_trace.etl -p {66C13383-C691-4CF7-B404-7E172E2DC0C2} 0xffffffffffffffff 0xff -nb 16 16 -bs 1024 -mode Circular -f bincirc -max 4096 -ets ',` 81 | 'logman update trace "WebAppProxy" -p {7B879E0C-83A7-4DCA-8492-063A257D4288} 0xffffffffffffffff 0xff -ets',` 82 | 'logman update trace "WebAppProxy" -p {DBD9121B-9FC9-4725-B35D-EC411FC28196} 0xffffffffffffffff 0xff -ets',` 83 | 'logman update trace "WebAppProxy" -p {2C7484EA-F1AC-4A4F-8FF0-39222A187F0D} 0xffffffffffffffff 0xff -ets',` 84 | 'logman update trace "WebAppProxy" -p {6519B1CA-2DD1-45D8-A53A-34D03B24EF58} 0xffffffffffffffff 0xff -ets' 85 | $WapTraceOff = "logman -stop WebAppProxy -ets" 86 | 87 | #NetworkCapture+genericInternetTraffic 88 | $EnableNetworkTracer = 'netsh trace start scenario=internetServer capture=yes report=disabled overwrite=yes maxsize=500 tracefile=.\%COMPUTERNAME%-network.etl' 89 | $DisableNetworkTracer = 'netsh trace stop' 90 | 91 | #Performance Counters 92 | $perfcnt = @{ 93 | ADFSMain = @{ CounterName = "\AD FS\*"; Type = "ADFSBackend" } 94 | ADFSCrypto = @{ CounterName = "\AD FS Cryptographic Counters(*)\*"; Type = "ADFSBackend" } 95 | ADFSAttStore = @{ CounterName = "\AD FS Attribute Store Counters(*)\*"; Type = "ADFSBackend" } 96 | ADFSDomCount = @{ CounterName = "\AD FS Domain Connection Counters\*"; Type = "ADFSBackend" } 97 | ADFSExtAuth = @{ CounterName = "\AD FS External Authentication Provider Counters\*"; Type = "ADFSBackend" } 98 | ADFSNode2Node = @{ CounterName = "\AD FS Inter-node Communication Counters(*)\*"; Type = "ADFSBackend" } 99 | ADFSLocalClaims = @{ CounterName = "\AD FS Local Claims Provider Connections(*)\*"; Type = "ADFSBackend" } 100 | WIDDBCounter = @{ CounterName = '\MSSQL$MICROSOFT##WID:Databases(*)\*'; Type = "WID" } 101 | ADFSProxy = @{ CounterName = "\AD FS Proxy\*"; Type="ADFSProxy"} 102 | WAPPerf = @{ CounterName = "\Web Application Proxy\*"; Type="ADFSProxy"} 103 | TCPCounter = @{ CounterName = "\TCPv4\*"; Type="General"} 104 | Memory = @{ CounterName = "\Memory\*"; Type="General"} 105 | Processor = @{ CounterName = "\Processor(*)\*"; Type="General"} 106 | Process = @{ CounterName = "\Process(*)\*"; Type="General"} 107 | } 108 | 109 | #Collection for Additional Files 110 | $Filescollector = 'copy /y %windir%\debug\netlogon.* ',` 111 | 'ipconfig /all > %COMPUTERNAME%-ipconfig-all.txt',` 112 | 'nltest /trusted_domains > %COMPUTERNAME%-nltest-trusted_domains.txt',` 113 | 'netsh dnsclient show state > %COMPUTERNAME%-netsh-dnsclient-show-state.txt',` 114 | 'route print > %COMPUTERNAME%-route-print.txt',` 115 | 'netsh advfirewall show global > %COMPUTERNAME%-netsh-int-advf-show-global.txt',` 116 | 'netsh int ipv4 show dynamicport tcp > %COMPUTERNAME%-netsh-int-ipv4-show-dynamicport-tcp.txt',` 117 | 'netsh int ipv4 show dynamicport udp > %COMPUTERNAME%-netsh-int-ipv4-show-dynamicport-udp.txt',` 118 | 'netsh int ipv6 show dynamicport tcp > %COMPUTERNAME%-netsh-int-ipv6-show-dynamicport-tcp.txt',` 119 | 'netsh int ipv6 show dynamicport udp > %COMPUTERNAME%-netsh-int-ipv6-show-dynamicport-udp.txt',` 120 | 'netsh http show cacheparam > %COMPUTERNAME%-netsh-http-show-cacheparam.txt',` 121 | 'netsh http show cachestate > %COMPUTERNAME%-netsh-http-show-cachestate.txt',` 122 | 'netsh http show sslcert > %COMPUTERNAME%-netsh-http-show-sslcert.txt',` 123 | 'netsh http show iplisten > %COMPUTERNAME%-netsh-http-show-iplisten.txt',` 124 | 'netsh http show servicestate > %COMPUTERNAME%-netsh-http-show-servicestate.txt',` 125 | 'netsh http show timeout > %COMPUTERNAME%-netsh-http-show-timeout.txt',` 126 | 'netsh http show urlacl > %COMPUTERNAME%-netsh-http-show-urlacl.txt',` 127 | 'GPResult /f /h %COMPUTERNAME%-GPReport.html',` 128 | 'systeminfo > %COMPUTERNAME%-sysinfo.txt',` 129 | 'regedit /e %COMPUTERNAME%-reg-NTDS-port-and-other-params.txt HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\parameters',` 130 | 'regedit /e %COMPUTERNAME%-reg-NETLOGON-port-and-other-params.txt HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\parameters',` 131 | 'regedit /e %COMPUTERNAME%-reg-schannel.txt HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL',` 132 | 'regedit /e %COMPUTERNAME%-reg-Cryptography_registry.txt HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography',` 133 | 'regedit /e %COMPUTERNAME%-reg-ciphers_policy_registry.txt HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL' 134 | 135 | #Enum forDotNetReleases 136 | #https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#version_table 137 | #https://learn.microsoft.com/de-de/lifecycle/products/microsoft-net-framework 138 | $fxversions = @{ 139 | ".NET Framework 4.5 (all OS)" = 378389 140 | ".NET Framework 4.5.1 (2012R2)" = 378675 141 | ".NET Framework 4.5.1 (Windows other)" = 378758 142 | ".NET Framework 4.5.2 (all OS)" = 379893 143 | ".NET Framework 4.6 (Win 10)" = 393295 144 | ".NET Framework 4.6 (other OS)" = 393297 145 | ".NET Framework 4.6.1 (Win 10 1511)" = 394254 146 | ".NET Framework 4.6.1 (all OS)" = 394271 147 | ".NET Framework 4.6.2 (RS1/2016)" = 394802 148 | ".NET Framework 4.6.2 (all OS)" = 394806 149 | ".NET Framework 4.7 (Win 10 1703/RS2)" = 460798 150 | ".NET Framework 4.7 (all OS)" = 460805 151 | ".NET Framework 4.7.1 (Win 10 1709/RS3)"= 461308 152 | ".NET Framework 4.7.1 (all Other)" = 461310 153 | ".NET Framework 4.7.2 (Win10 1803)" = 461808 154 | ".NET Framework 4.7.2 (all OS)" = 461814 155 | ".NET Framework 4.8 (Win 10 19H1/19H2)" = 528040 156 | ".NET Framework 4.8 (Win 10 20H1-22H2)" = 528372 157 | ".NET Framework 4.8 (Win 11/Server22)" = 528449 158 | ".NET Framework 4.8 (other OS)" = 528049 159 | ".NET Framework 4.8.1 (Win11 2022)" = 533320 160 | ".NET Framework 4.8.1 (other OS) " = 533325 161 | } 162 | 163 | #TypeDefinition for interop with native APIs 164 | Add-Type -TypeDefinition @" 165 | using System; 166 | using System.Text.RegularExpressions; 167 | using System.Runtime.InteropServices; 168 | 169 | public enum AccessType 170 | { 171 | DefaultProxy = 0, 172 | NoProxy = 1, 173 | NamedProxy = 3, 174 | AutomaticProxy = 4 175 | } 176 | 177 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 178 | public struct WINHTTP_PROXY_INFO 179 | { 180 | public AccessType AccessType; 181 | public string Proxy; 182 | public string Bypass; 183 | } 184 | 185 | public struct WinhttpCurrentUserIeProxyConfig 186 | { 187 | [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)] 188 | public bool AutoDetect; 189 | [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 190 | public string AutoConfigUrl; 191 | [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 192 | public string Proxy; 193 | [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 194 | public string ProxyBypass; 195 | } 196 | 197 | public class WinHttp 198 | { 199 | [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] 200 | public static extern bool WinHttpGetDefaultProxyConfiguration(ref WINHTTP_PROXY_INFO config); 201 | [DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)] 202 | public static extern bool WinHttpGetIEProxyConfigForCurrentUser(ref WinhttpCurrentUserIeProxyConfig pProxyConfig); 203 | } 204 | [Flags] 205 | public enum EncTypes 206 | { 207 | NULL_DEFAULTS_TO_RC4_HMAC = 0x0, 208 | DES_CBC_CRC = 0x01, 209 | DES_CBC_MD5 = 0x02, 210 | RC4_HMAC = 0x04, 211 | AES128_CTS_HMAC_SHA1_96 = 0x08, 212 | AES256_CTS_HMAC_SHA1_96 = 0x10, 213 | FAST_Supported = 0x10000, 214 | CompoundIdentity = 0x20000, 215 | Claims_Supported = 0x40000, 216 | Sid_Compression_Disabled = 0x80000 217 | } 218 | 219 | public class KrbEnum 220 | { 221 | public static string[] EnumerateKrb(int encType) 222 | { 223 | EncTypes type = (EncTypes)encType; 224 | string[] result = type.ToString().Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries); 225 | return result; 226 | } 227 | } 228 | 229 | public class ServiceConfigHelper 230 | { 231 | 232 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 233 | public struct QUERY_SERVICE_CONFIG 234 | { 235 | public int dwServiceType; 236 | public int dwStartType; 237 | public int dwErrorControl; 238 | [MarshalAs(UnmanagedType.LPWStr)] 239 | public string lpBinaryPathName; 240 | public int dwTagId; 241 | [MarshalAs(UnmanagedType.LPWStr)] 242 | public string lpLoadOrderGroup; 243 | public int dwDependencies; 244 | [MarshalAs(UnmanagedType.LPWStr)] 245 | public string lpServiceStartName; // This is the service account name 246 | public IntPtr lpDisplayName; 247 | } 248 | 249 | [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 250 | public static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, uint dwDesiredAccess); 251 | 252 | [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 253 | public static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); 254 | 255 | [DllImport("advapi32.dll", SetLastError = true)] 256 | public static extern bool CloseServiceHandle(IntPtr hSCObject); 257 | 258 | [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 259 | public static extern bool QueryServiceConfig(IntPtr hService, IntPtr lpServiceConfig, int cbBufSize, out int pcbBytesNeeded); 260 | 261 | const uint SC_MANAGER_ALL_ACCESS = 0xF003F; 262 | const uint SERVICE_QUERY_CONFIG = 0x0001; 263 | 264 | // Method to get the service account name 265 | public static string GetServiceAccount(string serviceName) 266 | { 267 | IntPtr scmHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); 268 | if (scmHandle == IntPtr.Zero) 269 | { 270 | return "OpenSCManager_failed"; 271 | } 272 | 273 | IntPtr serviceHandle = OpenService(scmHandle, serviceName, SERVICE_QUERY_CONFIG); 274 | if (serviceHandle == IntPtr.Zero) 275 | { 276 | CloseServiceHandle(scmHandle); 277 | return "OpenService_failed"; 278 | } 279 | 280 | int bytesNeeded = 0; 281 | QueryServiceConfig(serviceHandle, IntPtr.Zero, 0, out bytesNeeded); // Find out how much memory is needed 282 | 283 | IntPtr queryConfigBuffer = Marshal.AllocHGlobal(bytesNeeded); 284 | bool success = QueryServiceConfig(serviceHandle, queryConfigBuffer, bytesNeeded, out bytesNeeded); 285 | if (!success) 286 | { 287 | Marshal.FreeHGlobal(queryConfigBuffer); 288 | CloseServiceHandle(serviceHandle); 289 | CloseServiceHandle(scmHandle); 290 | return "QueryServiceConfig_failed"; 291 | } 292 | 293 | QUERY_SERVICE_CONFIG qsc = (QUERY_SERVICE_CONFIG)Marshal.PtrToStructure(queryConfigBuffer, typeof(QUERY_SERVICE_CONFIG)); 294 | string serviceAccount = qsc.lpServiceStartName; 295 | 296 | Marshal.FreeHGlobal(queryConfigBuffer); 297 | CloseServiceHandle(serviceHandle); 298 | CloseServiceHandle(scmHandle); 299 | 300 | return serviceAccount; 301 | } 302 | } 303 | "@ 304 | #endregion 305 | ########################################################################## 306 | #region UI 307 | function filepathvalidformat { 308 | param ( 309 | $path 310 | ) 311 | # Regular expression to match a valid filesystem path format 312 | $filepathreg = '^(?:[a-zA-Z]:\\|\\\\[\d\D]|\.{1,2}\\)([^<>:"\\|?*]+\\)*[^<>:"\\|?*]*$' #'^(?:[a-zA-Z]:\\|\\\\|\.{1,2}\\)([^<>:"\\|?*]+\\)*[^<>:"\\|?*]*$' 313 | return [regex]::IsMatch($path, $filepathreg) 314 | } 315 | 316 | $DisplayText = @( 317 | @{ Text = "This ADFS Tracing script is designed to gather detailed information about your ADFS configuration and related Windows settings. `nIt also offers the ability to collect various debug logs at runtime for issues that need to be actively reproduced or that are not easily detectable through other means. 318 | The collected data can be provided to a Microsoft support technician for further analysis."} 319 | @{ Text = "`nWhen performing a Debug/Runtime Trace, you have the option to include Network Traces and/or Performance Counters in the data collection if needed to troubleshoot a specific issue."} 320 | @{ Text = "`nThe script will prepare to capture data and will notify the Administrator when the data collection process is ready to begin.`nIt will pause and you can setup the tracing in the same way."} 321 | @{ Text = "By pressing 'CTRL + Y' the data collecting process on the server. When Tracing multiple servers repeat the procedure and start the tracing on the other nodes as well"} 322 | @{ Text = "The script will display another message to confirm that it is actively capturing data. `nPress 'CTRL + Y' again to stop the data capture.`n"} 323 | @{ Text = "`nNote:"; Style="bold"} 324 | @{ Text = "The Script is not designed to run for extended periods."} 325 | @{ Text = "In most cases, the script will require between 4GB to 10GB of diskspace, depending on the workload and the duration of the trace and size of the eventlogs."} 326 | @{ Text = "The script will capture multiple traces in circular buffers and will use a temporary folder at the specified path (e.g., C:\tracing\temporary)."} 327 | @{ Text = "It's advisable to capture data during periods of low activity in your ADFS environment to minimize impact."} 328 | @{ Text = "The temporary folder will be later compressed into a .zip file and stored at the selected path."} 329 | ) 330 | 331 | function WriteRichBoxText { 332 | param ( 333 | [Array]$textElements 334 | ) 335 | $Description.text="" 336 | foreach ($element in $textElements) { 337 | $Description.SelectionStart = $Description.TextLength 338 | $Description.SelectionLength = 0 339 | if ($element.Style -eq "bold") { 340 | $font = New-Object System.Drawing.Font('Arial', 10, [System.Drawing.FontStyle]::Bold) 341 | } 342 | else { 343 | $font = New-Object System.Drawing.Font('Arial', 10, [System.Drawing.FontStyle]::Regular) 344 | } 345 | $Description.SelectionFont = $font 346 | if (!$element.Color) { 347 | $element.Color= [System.Drawing.Color]::FromName('black') 348 | } 349 | $Description.SelectionColor = [System.Drawing.Color]::FromName($element.Color) 350 | $Description.AppendText($element.Text + [Environment]::NewLine) 351 | $Description.SelectionStart = 0 352 | } 353 | } 354 | 355 | Function RunDialog { 356 | [System.Windows.Forms.Application]::EnableVisualStyles() 357 | 358 | $Form = New-Object system.Windows.Forms.Form 359 | $Form.ClientSize = '800,600' 360 | $Form.text = "ADFS Trace Collector" 361 | $Form.TopMost = $false 362 | $Form.StartPosition = 'CenterScreen' 363 | $Form.MaximizeBox = $false 364 | $Form.MinimizeBox = $false 365 | $Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D 366 | 367 | # Text field 368 | $Description = New-Object system.Windows.Forms.RichTextBox 369 | $Description.Size = new-object System.Drawing.Size(770, 360) 370 | $Description.multiline = $true 371 | $Description.location = New-Object System.Drawing.Point(15,0) 372 | $Description.Font = 'Arial,10' 373 | $Description.ScrollBars = 'Vertical' 374 | $Description.ReadOnly = $true 375 | $Description.DetectUrls = $true 376 | WriteRichBoxText $DisplayText 377 | 378 | $checkBoxWidth = 180 # Width for each checkbox 379 | $xOffset = 10 # Initial X offset for checkbox 380 | $yOffset = 20 # Y offset for aligning checkboxes 381 | 382 | $Scenario = New-Object System.Windows.Forms.GroupBox 383 | $Scenario.Text = "Scenario" 384 | $Scenario.Location = New-Object System.Drawing.Point(15, 375) # Positioned below the RichTextBox 385 | $Scenario.Size = new-object System.Drawing.Size(770, 50) 386 | $cScenario = @("Configuration only", "Runtime Tracing") 387 | 388 | for ($i = 0; $i -lt $cScenario.Length; $i++) { 389 | $checkBox = New-Object System.Windows.Forms.CheckBox 390 | $checkBox.Text = $cScenario[$i] 391 | $checkBox.AutoSize = $true 392 | $checkBox.Location = New-Object System.Drawing.Point(($xOffset + ($i * $checkBoxWidth)), $yOffset) 393 | 394 | switch -Wildcard ($cScenario[$i]) { 395 | "Configuration only" { Set-Variable -Name cfgonly -Value $checkBox -Force } 396 | "Runtime Tracing" { Set-Variable -Name TracingMode -Value $checkBox -Force } 397 | } 398 | $Scenario.Controls.Add($checkBox) 399 | } 400 | # Options GroupBox 401 | $Options = New-Object System.Windows.Forms.GroupBox 402 | $Options.Text = "Options" 403 | $Options.Location = New-Object System.Drawing.Point(15, 430) # Positioned below the ScenarioGroup 404 | $Options.Size = new-object System.Drawing.Size(480, 50) # Adjust the size as needed 780,50 405 | 406 | $cOptions = @("include Network Traces", "include Performance Counter") 407 | 408 | for ($i = 0; $i -lt $cOptions.Length; $i++) { 409 | $checkBox = New-Object System.Windows.Forms.CheckBox 410 | $checkBox.Text = $cOptions[$i] 411 | $checkBox.AutoSize = $true 412 | $checkBox.Enabled= $false 413 | $checkBox.Location = New-Object System.Drawing.Point(($xOffset + ($i * $checkBoxWidth)), $yOffset) 414 | 415 | switch -Wildcard ($cOptions[$i]) { 416 | "include Network Traces" { Set-Variable -Name NetTrace -Value $checkBox -Force } 417 | "include Performance Counter" { Set-Variable -Name perfc -Value $checkBox -Force } 418 | } 419 | $Options.Controls.Add($checkBox) 420 | } 421 | ##### Advanced Options GroupBox 422 | $aOptions = New-Object System.Windows.Forms.GroupBox 423 | $aOptions.Text = if(!$IsProxy){ "advanced Options (can cause service restarts)"} else { "advanced Options" } 424 | $aOptions.Location = New-Object System.Drawing.Point(500, 430) # Positioned below the ScenarioGroup 425 | $aOptions.Size = new-object System.Drawing.Size(285, 50) # Adjust the size as needed 426 | 427 | $caOptions= if(!$IsProxy){ "LDAP Traces" } else { "WAP Traces" } 428 | 429 | for ($i = 0; $i -lt $caOptions.count; $i++) { 430 | $checkBox = New-Object System.Windows.Forms.CheckBox 431 | $checkBox.Text = $caOptions 432 | $checkBox.AutoSize = $true 433 | $checkBox.Enabled= $false 434 | $checkBox.Location = New-Object System.Drawing.Point(($xOffset + ($i * $checkBoxWidth)), $yOffset) 435 | 436 | switch -Wildcard ($caOptions) { 437 | "LDAP Traces" { Set-Variable -Name ldapt -Value $checkBox -Force } 438 | "WAP Traces" { Set-Variable -Name wapt -Value $checkBox -Force } 439 | } 440 | $aOptions.Controls.Add($checkBox) 441 | } 442 | 443 | ##### 444 | $label = New-Object System.Windows.Forms.GroupBox 445 | $label.Text = 'Type a path to the Destination Folder or Click "Browse..." to select the Folder' 446 | $label.Location = New-Object System.Drawing.Point(15,480) # Positioned below the ScenarioGroup 447 | $label.Size = new-object System.Drawing.Size(585, 60) # Adjust the size as needed 448 | 449 | #Text Field for the Export Path to store the results 450 | $TargetFolder = New-Object system.Windows.Forms.TextBox 451 | $TargetFolder.text = "" 452 | $TargetFolder.Size = new-object System.Drawing.Size(470, 30) 453 | $TargetFolder.location = New-Object System.Drawing.Point(10,20) 454 | $TargetFolder.Font = 'Arial,13' 455 | 456 | $SelFolder = New-Object system.Windows.Forms.Button 457 | $SelFolder.text = "Browse..." 458 | $SelFolder.Size = new-object System.Drawing.Size(90, 29) 459 | $SelFolder.location = New-Object System.Drawing.Point(486,20) 460 | $SelFolder.Font = 'Arial,10' 461 | 462 | $label.Controls.AddRange(@($TargetFolder,$SelFolder)) 463 | 464 | $Okbtn = New-Object system.Windows.Forms.Button 465 | $Okbtn.text = "OK" 466 | $Okbtn.Size = new-object System.Drawing.Size(70, 30) 467 | $Okbtn.location = New-Object System.Drawing.Point(620,540) 468 | $Okbtn.Font = 'Arial,10' 469 | $Okbtn.DialogResult = [System.Windows.Forms.DialogResult]::OK 470 | $Okbtn.Enabled = $false 471 | 472 | $cnlbtn = New-Object system.Windows.Forms.Button 473 | $cnlbtn.text = "Cancel" 474 | $cnlbtn.Size = new-object System.Drawing.Size(70, 30) 475 | $cnlbtn.location = New-Object System.Drawing.Point(700,540) 476 | $cnlbtn.Font = 'Arial,10' 477 | $cnlbtn.DialogResult = [System.Windows.Forms.DialogResult]::Cancel 478 | 479 | $Form.controls.AddRange(@($Description,$Scenario,$Options,$aOptions,$Okbtn,$cnlbtn,$label)) 480 | 481 | $cfgonly.Add_CheckStateChanged({ if ($cfgonly.checked) { 482 | $TracingMode.Enabled = $false; 483 | $NetTrace.Enabled = $false; 484 | $perfc.Enabled = $false; 485 | if(!$IsProxy){ $ldapt.Enabled=$false}else {$wapt.Enabled = $false} 486 | } 487 | else { 488 | $TracingMode.Enabled = $true; $NetTrace.Enabled = $false 489 | } 490 | }) 491 | 492 | $TracingMode.Add_CheckStateChanged({ if ($TracingMode.checked){ 493 | $cfgonly.Enabled = $false; 494 | $NetTrace.Enabled = $true; 495 | $NetTrace.Checked = $true; 496 | $perfc.Enabled = $true; 497 | if(!$IsProxy){ $ldapt.Enabled=$true}else {$wapt.Enabled = $true} 498 | } 499 | else { 500 | $cfgonly.Enabled = $true; 501 | $NetTrace.Checked = $false; 502 | $NetTrace.Enabled = $false; 503 | $perfc.Checked = $false; 504 | $perfc.Enabled = $false; 505 | if(!$IsProxy) { 506 | $ldapt.Checked = $false; 507 | $ldapt.Enabled = $false; 508 | } 509 | else { 510 | $wapt.Checked = $false; 511 | $wapt.Enabled = $false; 512 | } 513 | } 514 | }) 515 | 516 | #For future Versions we may add addional dependencies to the Network Trace. 517 | #$NetTrace.Add_CheckedChanged({ }) 518 | $Description.add_LinkClicked({ Start-Process -FilePath $_.LinkText }) 519 | 520 | $SelFolder.Add_Click({ $FolderDialog = New-Object windows.forms.FolderBrowserDialog 521 | $FolderDialog.RootFolder = "Desktop" 522 | $FolderDialog.ShowDialog() 523 | $TargetFolder.text = $FolderDialog.SelectedPath 524 | }) 525 | 526 | $TargetFolder.Add_TextChanged({ $Okbtn.Enabled = filepathvalidformat $TargetFolder.Text; }) 527 | $FormsCompleted = $Form.ShowDialog() 528 | 529 | if ($FormsCompleted -eq [System.Windows.Forms.DialogResult]::OK) { 530 | return New-Object psobject -Property @{ 531 | Path = $TargetFolder.text 532 | TraceEnabled = $TracingMode.Checked 533 | NetTraceEnabled = $NetTrace.Checked 534 | ConfigOnly = $cfgonly.Checked 535 | PerfCounter =$perfc.Checked 536 | LdapTraceEnabled=$ldapt.Checked 537 | WAPTraceEnabled=$wapt.Checked 538 | } 539 | $Form.dispose() 540 | } 541 | elseif($FormsCompleted -eq [System.Windows.Forms.DialogResult]::Cancel) { 542 | Write-host "Script was canceled by User" -ForegroundColor Red 543 | $Form.dispose() 544 | exit 545 | } 546 | } 547 | 548 | Function Pause { param([String]$Message,[String]$MessageTitle,[String]$MessageC) 549 | # "ReadKey" not supported in PowerShell ISE. 550 | If ($psISE) { 551 | # Show MessageBox UI instead 552 | $Shell = New-Object -ComObject "WScript.Shell" 553 | $Shell.Popup($Message, 0, $MessageTitle, 0)|Out-Null 554 | Return 555 | } 556 | #If not ISE we prompt for key stroke 557 | Write-Host -NoNewline $MessageC -ForegroundColor Yellow 558 | do {$keyInfo = [Console]::ReadKey($false)} until ($keyInfo.Key -eq 'Y' -and $keyInfo.Modifiers -eq 'Control') 559 | } 560 | #endregion 561 | ########################################################################## 562 | #region Functions 563 | Function IsAdminAccount { 564 | return ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 565 | } 566 | 567 | function LDAPQuery { 568 | param( 569 | [string]$filter, 570 | [string[]]$att, 571 | [string]$conn, 572 | [string]$basedn 573 | ) 574 | [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols") | Out-Null 575 | [System.Reflection.Assembly]::LoadWithPartialName("System.Net")| Out-Null 576 | 577 | $c = New-Object System.DirectoryServices.Protocols.LdapConnection ($conn) 578 | 579 | $c.SessionOptions.SecureSocketLayer = $false; 580 | $c.SessionOptions.Sealing = $true 581 | $c.SessionOptions.Signing = $true 582 | $c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Kerberos 583 | $c.Bind(); 584 | #rather timeout than waiting for too long... 585 | $c.Timeout=[timespan]::FromSeconds(45) 586 | 587 | if([string]::IsNullOrEmpty($basedn)) { 588 | $basedn = (New-Object System.DirectoryServices.DirectoryEntry("LDAP://$conn/RootDSE")).DefaultNamingContext 589 | } 590 | 591 | $scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree 592 | $r = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$att 593 | 594 | $re = try { $c.SendRequest($r) } 595 | catch { $_.Exception.InnerException } 596 | 597 | $c.Dispose() 598 | 599 | return $re 600 | } 601 | 602 | function get-Certificatesfromstore { 603 | param( 604 | [string]$StoreName 605 | ) 606 | 607 | $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($StoreName,[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine) 608 | $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly) 609 | $certcollection = $store.Certificates 610 | $store.Close() 611 | 612 | return $certcollection 613 | } 614 | 615 | function Get-CertificatesByStore { 616 | param ( 617 | [Parameter(Mandatory=$true)] 618 | [ValidateSet("My", "Root", "CA", "NtAuth", "ADFSTrustedDevices", "ClientAuthIssuer")] 619 | [string]$StoreName 620 | ) 621 | 622 | $storePath = switch ($StoreName) { 623 | "My" { [System.Security.Cryptography.X509Certificates.StoreName]::My } 624 | "Root" { [System.Security.Cryptography.X509Certificates.StoreName]::Root } 625 | "CA" { 'CA' } # [System.Security.Cryptography.X509Certificates.StoreName]::CertificateAuthority seems to be not working so using the Alias CA instead 626 | "NtAuth" { 'NtAuth' } 627 | "ADFSTrustedDevices" { 'ADFSTrustedDevices' } 628 | "ClientAuthIssuer" { 'ClientAuthIssuer' } 629 | } 630 | 631 | $certs = get-Certificatesfromstore $storePath 632 | $mycert = @() 633 | 634 | foreach ($cert in $certs) { 635 | $obj = New-Object -TypeName PSObject 636 | $obj | Add-Member -MemberType NoteProperty -Name "Issuer" -Value $cert.Issuer 637 | if(($cert.FriendlyName)){$obj | Add-Member -MemberType NoteProperty -Name "FriendlyName" -Value $cert.FriendlyName} 638 | $obj | Add-Member -MemberType NoteProperty -Name "Subject" -Value $cert.Subject 639 | $obj | Add-Member -MemberType NoteProperty -Name "NotAfter" -Value $cert.NotAfter 640 | $obj | Add-Member -MemberType NoteProperty -Name "NotBefore" -Value $cert.NotBefore 641 | $obj | Add-Member -MemberType NoteProperty -Name "SerialNumber" -Value $cert.SerialNumber 642 | $obj | Add-Member -MemberType NoteProperty -Name "ThumbPrint" -Value $cert.Thumbprint 643 | 644 | # PrivateKey and related properties for MY store 645 | if ($StoreName -eq "My") { 646 | $obj | Add-Member -MemberType NoteProperty -Name "PrivateKey" -Value $cert.HasPrivateKey 647 | $obj | Add-Member -MemberType NoteProperty -Name "Exportable" -Value $cert.PrivateKey.CspKeyContainerInfo.Exportable 648 | $obj | Add-Member -MemberType NoteProperty -Name "ProviderName" -Value $cert.PrivateKey.CspKeyContainerInfo.ProviderName 649 | 650 | $keyspec = (($cert.PrivateKey).CspKeyContainerInfo).KeyNumber 651 | $keyspecName = switch ($keyspec) { 652 | "Exchange" { "AT_EXCHANGE" } 653 | "Signature" { "AT_SIGNATURE" } 654 | default { "CNG" } 655 | } 656 | $obj | Add-Member -MemberType NoteProperty -Name "Keyspec" -Value $keyspecName 657 | } 658 | 659 | # Root/Non-Root check and origin determination for other stores 660 | if ($StoreName -ne "My") { 661 | if ($cert.Subject -ne $cert.Issuer) { 662 | $obj | Add-Member -MemberType NoteProperty -Name "IsRoot" -Value 'Non-Root' 663 | } else { 664 | $obj | Add-Member -MemberType NoteProperty -Name "IsRoot" -Value 'Root' 665 | } 666 | 667 | # Determine the origin based on store type 668 | $certsrc = $null 669 | if ($StoreName -eq "NtAuth") { $obj | Add-Member -MemberType NoteProperty -Name "Origin" -Value 'DirectoryService' } 670 | elseif ($StoreName -eq "ADFSTrustedDevices") { $obj | Add-Member -MemberType NoteProperty -Name "Origin" -Value 'ADFS' } 671 | elseif ($StoreName -eq "ClientAuthIssuer") { $obj | Add-Member -MemberType NoteProperty -Name "Origin" -Value 'Registry' } 672 | else { 673 | $ds = "HKLM:\SOFTWARE\Microsoft\EnterpriseCertificates\$StoreName\Certificates\" + $cert.Thumbprint 674 | $gpo = "HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\$StoreName\Certificates\" + $cert.Thumbprint 675 | 676 | if ([bool](Test-Path $ds)) { $certsrc = "DirectoryService" } 677 | if ([bool](Test-Path $gpo)) { $certsrc = "GroupPolicy" } 678 | if (![string]::IsNullOrEmpty($certsrc)) { $obj | Add-Member -MemberType NoteProperty -Name "Origin" -Value $certsrc } 679 | else { $obj | Add-Member -MemberType NoteProperty -Name "Origin" -Value 'Registry' } 680 | } 681 | } 682 | 683 | $mycert += $obj 684 | } 685 | 686 | # We check ClientAuthIssuer only if a bidning was configured. An empty store can cause issues so warn. 687 | if ($StoreName -eq "ClientAuthIssuer" -and $mycert.Count -eq 0) { 688 | $mycert = "WARNING: ClientAuthIssuers is configured on an ADFS related binding but the Certificate store is empty. This can break Certificate Based authentication for users" 689 | } 690 | 691 | return $mycert 692 | } 693 | 694 | 695 | function Test-IsWID { 696 | # Try to get the SecurityTokenService object and its configuration DB connection string 697 | $sts = Get-WmiObject -Namespace root\ADFS -Class SecurityTokenService -ErrorAction SilentlyContinue 698 | $connectionString = $sts.ConfigurationDatabaseConnectionString 699 | # Determine if it's using WID or SSEE 700 | $result = $connectionString -match "##wid" -or $connectionString -match "##ssee" 701 | #if Wid get the service status and service account name 702 | if ($result) { 703 | $svc = new-object System.ServiceProcess.ServiceController('MSSQL$MICROSOFT##WID') 704 | $widaccount = [ServiceConfigHelper]::GetServiceAccount($svc.Name) 705 | } 706 | 707 | return [PSCustomObject]@{ 708 | IsWID = $result 709 | ConfigurationDatabaseConnectionString = $connectionString 710 | IsWIDStarted = $svc.Status 711 | WIDServiceAccount = $widaccount 712 | } 713 | } 714 | 715 | function Get-ADFSDBStateFromWID { 716 | 717 | $dbconfig = Test-IsWID 718 | $dbstates = @{} 719 | 720 | #skip if not WID exit.we shouldnt get this ever since we check before calling this function 721 | if (!$dbconfig.IsWID) { 722 | break 723 | } 724 | 725 | #Verify Service Account for WIDService 726 | $svcstatustmpl= @' 727 | ================ WID Service - Status ================= 728 | Service Status : {0} 729 | WID Service Account : {1} 730 | '@ 731 | $expectedAccount = 'NT SERVICE\MSSQL$MICROSOFT##WID' 732 | if (!($dbconfig.WIDServiceAccount.IndexOf('_failed') -eq -1)){ 733 | $serviceacc=[string]::Format("Error: An error occurred whilst querying the ServiceAccountName for the Windows Internal Database service. Error Code: {0}`r`n", $dbconfig.WIDServiceAccount) 734 | } elseif ([string]::Compare($expectedAccount, $dbconfig.WIDServiceAccount, $true) -eq 0 ) { 735 | $serviceacc=[string]::Format("{0} - Test passed`r`n", $dbconfig.WIDServiceAccount) 736 | } else { 737 | $serviceacc=[string]::Format("{0} - Test failed. Expected Account is: {1}`r`n", $dbconfig.WIDServiceAccount, $expectedAccount) 738 | } 739 | 740 | [string]::Format($svcstatustmpl, 741 | $(switch ([int]$dbconfig.IsWIDStarted) { 742 | ([int][System.ServiceProcess.ServiceControllerStatus]::Running) { "Running - Test passed" } 743 | ([int][System.ServiceProcess.ServiceControllerStatus]::Stopped) { "Stopped - Test failed" } 744 | ([int][System.ServiceProcess.ServiceControllerStatus]::Paused) { "Paused - Test failed" } 745 | ([int][System.ServiceProcess.ServiceControllerStatus]::StartPending) { "Start Pending - Test failed" } 746 | ([int][System.ServiceProcess.ServiceControllerStatus]::StopPending) { "Stop Pending - Test failed" } 747 | default { "Unknown Status - Test failed" } 748 | }), 749 | $serviceacc 750 | ) 751 | 752 | #check if service is running and if then attempt the query 753 | if ($dbconfig.IsWIDStarted -eq [System.ServiceProcess.ServiceControllerStatus]::Running ) { 754 | 755 | #query basic DB states also retrieve the owner of the DB. 756 | #general reference on the properties https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-databases-transact-sql?view=sql-server-ver17 757 | $query=@" 758 | SELECT 759 | d.name AS DatabaseName, 760 | suser_sname(d.owner_sid) AS DatabaseOwner, 761 | d.state_desc AS DatabaseState, 762 | d.recovery_model_desc AS RecoveryModel, 763 | d.is_read_only AS IsReadOnly, 764 | d.is_broker_enabled AS IsBrokerEnabled, 765 | d.user_access_desc AS AccessMode 766 | FROM sys.databases AS d 767 | WHERE d.name LIKE 'ADFS%' 768 | ORDER BY d.name; 769 | "@ 770 | 771 | $dbstatustemplate= @' 772 | ============= {0} - Status ============ 773 | Database Access : {1} 774 | Database State : {2} 775 | Broker enabled : {3} 776 | Database Owner : {4} 777 | 778 | '@ 779 | 780 | $failed = "Test failed" 781 | $success= "Test passed" 782 | $padding=16 783 | 784 | try { 785 | $connection = new-object system.data.SqlClient.SqlConnection($dbconfig.ConfigurationDatabaseConnectionString); 786 | $connection.Open() 787 | 788 | $sqlcmd = $connection.CreateCommand(); 789 | $sqlcmd.CommandText = $query; 790 | $result = $sqlcmd.ExecuteReader(); 791 | $table = new-object "System.Data.DataTable" 792 | $table.Load($result) 793 | 794 | foreach ($row in $table.Rows) { 795 | $dbstates[$row.DatabaseName] = @{ 796 | DatabaseState = $row.DatabaseState 797 | RecoveryModel = $row.RecoveryModel 798 | IsReadOnly = $row.IsReadOnly 799 | AccessMode = $row.AccessMode 800 | BrokerEnabled = $row.IsBrokerEnabled 801 | DBOwner = $row.DatabaseOwner 802 | } 803 | } 804 | } catch { 805 | return [string]::Format($errtemplate, $_.Exception.InnerException) 806 | } finally { 807 | # Close and dispose the connection if it exists 808 | if ($connection.State -eq 'Open') { 809 | $connection.Close() 810 | } 811 | $connection.Dispose() 812 | } 813 | } else { 814 | 815 | $errtemplate= @" 816 | Error: Failed to query ADFS database informations from WID. 817 | Details: {0} 818 | "@ 819 | return $([string]::Format($errtemplate, 'WID (Windows Internal Database) service is not running.')) 820 | } 821 | 822 | #loop through the results and format the output 823 | foreach ($dbname in $dbstates.keys) { 824 | #access mode is expected to be MULTI_USER. if SINGLE_USER or RESTRICTED_USER is found we show it as failed 825 | $accmode= switch ($dbstates[$dbname].AccessMode) { 826 | MULTI_USER { "$($dbstates[$dbname].AccessMode.PadRight($padding)) - $($success)"} 827 | SINGLE_USER { "$($dbstates[$dbname].AccessMode.PadRight($padding)) - $($failed)" } 828 | RESTRICTED_USER { "$($dbstates[$dbname].AccessMode.PadRight($padding)) - $($failed)" } 829 | } 830 | # we expect the DB to be online. if any other value is found we show it as failed test 831 | $dbstate= switch ($dbstates[$dbname].DatabaseState -eq "Online" ) { 832 | true {"$($dbstates[$dbname].DatabaseState.PadRight($padding)) - $($success)"} 833 | false {"$($dbstates[$dbname].DatabaseState.PadRight($padding)) - $($failed)" } 834 | } 835 | #broker is expected to be enabled. Else the service may not reload its config after a change was detected (eg after sync) 836 | $broker = switch ([bool]$dbstates[$dbname].BrokerEnabled) { 837 | true {"$($dbstates[$dbname].BrokerEnabled.tostring().PadRight($padding)) - $($success)"} 838 | false {"$($dbstates[$dbname].BrokerEnabled.tostring().PadRight($padding)) - $($failed)"} 839 | } 840 | 841 | #format the output 842 | [string]::Format($dbstatustemplate, 843 | ($dbname.PadRight(19)), 844 | $accmode, 845 | $dbstate, 846 | $broker, 847 | $dbstates[$dbname].DBOwner 848 | ) 849 | } 850 | } 851 | 852 | function get-servicesettingsfromdb { 853 | param( 854 | [Parameter(Mandatory=$true)] 855 | [string]$DBConnectionString 856 | ) 857 | # Validate input 858 | if ([string]::IsNullOrEmpty($DBConnectionString)) { 859 | $errMsg = "Error: Database connection string is null or empty." 860 | throw [System.ArgumentException]::new($errMsg) 861 | } 862 | 863 | #basic validation of the connection string format 864 | try { 865 | # Attempt to parse the connection string 866 | $dbstring = New-Object System.Data.SqlClient.SqlConnectionStringBuilder $DBConnectionString 867 | } 868 | catch { 869 | $errMsg = "Error: Invalid connection string format" 870 | throw [System.ArgumentException]::new($errMsg,$($_.Exception)) 871 | } 872 | #Create SQL Connection 873 | try { 874 | $connection = new-object system.data.SqlClient.SqlConnection($dbstring.ConnectionString); 875 | $connection.Open() 876 | 877 | $query = "SELECT * FROM IdentityServerPolicy.ServiceSettings" 878 | $sqlcmd = $connection.CreateCommand(); 879 | $sqlcmd.CommandText = $query; 880 | $result = $sqlcmd.ExecuteReader(); 881 | $table = new-object "System.Data.DataTable" 882 | $table.Load($result) 883 | [XML]$SSD= $table.ServiceSettingsData 884 | } catch { 885 | $errMsg = "Error: Failed to connect to or query from ADFS Configuration database" 886 | throw [System.Exception]::new($errMsg,$($_.Exception)) 887 | 888 | } finally { 889 | if ($connection.State -eq 'Open') { 890 | $connection.Close() 891 | } 892 | $connection.Dispose() 893 | } 894 | 895 | return $SSD 896 | } 897 | 898 | Function Test-WiaSupportedUseragents { 899 | # Unsupported user agents may cause issues with WIA authentication on various platforms if internal. In particular mobile apps using webview controls or devices that dont support wia per se 900 | # usually this is due to using too generic user agents. Update the list as needed. We will extend this list as needed 901 | $unsupported=@("Mozilla/5.0","Mozilla/4.0","Chrome","FireFox","Safari","Opera","OPR","Edg/*","Edge/*","Edg/","Edge/","Webkit/","=~Windows\s*NT.*Edge") 902 | $agents = (get-adfsproperties).WiasupportedUseragents 903 | $commonItems=@() 904 | 905 | if (!($null -eq $agents) -and !($null -eq $unsupported)) { 906 | $commonItems = [System.Linq.Enumerable]::Intersect( 907 | [System.Collections.Generic.List[object]]@($unsupported), 908 | [System.Collections.Generic.List[object]]@($agents) 909 | ) 910 | } 911 | 912 | if ($commonItems.Count -gt 0) { 913 | $commonItemsArray = [System.Linq.Enumerable]::ToArray( 914 | [System.Collections.Generic.IEnumerable[object]]$commonItems 915 | ) 916 | 917 | $sb = New-Object System.Text.StringBuilder 918 | for ($i = 0; $i -lt $commonItemsArray.Length; $i++) { 919 | [void]$sb.Append(" - ") 920 | [void]$sb.AppendLine([string]$commonItemsArray[$i]) 921 | } 922 | 923 | $msgvalue = [String]::Format( 924 | "Warning: The following WiaSupportUseragents are known to cause unexpected signin issues with certain device platforms:`r`n{0}`The agent string(s) listed are either too generic, lacking device platforms identifiers 925 | or are generally outdated and no longer applicable",$sb) 926 | 927 | $message = New-Object -TypeName PSObject 928 | $message | Add-Member -NotePropertyName 'Test-WiaSupportedUseragents' -NotePropertyValue $msgvalue 929 | } else { 930 | 931 | $msgvalue = [String]::Format("Informational: WiasupportedUseragents seems to be configured correctly. 932 | If a misconfiguration exists it is currently not known by this script.") 933 | $message = New-Object -TypeName PSObject 934 | $message | Add-Member -NotePropertyName 'Test-WiaSupportedUseragents' -NotePropertyValue $msgvalue 935 | } 936 | 937 | return $message 938 | } 939 | 940 | 941 | function Get-AzureMFAConfig { 942 | $dbconfig = Test-IsWID 943 | #skip if WID is not started as it would definitely fail the query 944 | if ($dbconfig.IsWID -and ($dbconfig.IsWIDStarted -ne [System.ServiceProcess.ServiceControllerStatus]::Running )) { 945 | $errMsg = "Error: WID (Windows Internal Database) service is not running." 946 | throw [System.Exception]::new($errMsg) 947 | } 948 | 949 | try { 950 | $ssd = get-servicesettingsfromdb -DBConnectionString $dbconfig.ConfigurationDatabaseConnectionString 951 | } catch { 952 | $errMsg = "Error: Reading Azure MFA Configuration failed." 953 | throw [System.Exception]::new($errMsg,$($_.Exception)) 954 | } 955 | if(!$null -eq $ssd) { #loop through the AuthAdapters and find the config for AzureMFAAdapter; we might expand this for other adapters if necessary 956 | foreach ($AmD in $ssd.ServiceSettingsData.SecurityTokenService.AuthenticationMethods.AuthenticationMethodDescriptor) { 957 | if ($AmD.Identifier -eq "AzureMfaAuthentication" -and (!$AmD.ConfigurationData.IsEmpty)) { 958 | return [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($AmD.ConfigurationData)) 959 | } 960 | } 961 | } 962 | } 963 | 964 | function Get-ADFSAzureMfaAdapterconfig { 965 | #exception format template 966 | $errmsgformatter=@" 967 | Error: An error occurred whilst attempting to read the MFA Adapter Configuration. 968 | {0} 969 | {1} 970 | {2} 971 | "@ 972 | #try to get config and handle the exception if it occurs try to provide as much info as possible and break on error 973 | Try { 974 | $MFAraw= Get-AzureMFAConfig 975 | } Catch { 976 | #cycle through the exception chain to provide as much info as possible 977 | $errstr= [string]::Format($errmsgformatter, 978 | $_.Exception.Message, 979 | $_.Exception.InnerException.Message, 980 | $_.Exception.InnerException.InnerException 981 | ) 982 | Return $errstr.TrimEnd() 983 | break 984 | } 985 | 986 | #try to process the config if it was retrieved 987 | if($null -ne $MFAraw) { 988 | $obj = [PSCustomObject]@{} 989 | $obj| Add-Member -MemberType NoteProperty -Name 'AdapterConfig' -Value $MFAraw 990 | 991 | if(($MFAraw -as [XML]).ChildNodes.ClientId -ne '981f26a1-7f43-403b-a875-f8b09b8cd720') { 992 | $obj| Add-Member -MemberType NoteProperty -Name 'Error' -Value 'The configured ClientId is incorrect and does not match the Azure AD MFA ClientId required. Re-run the MFA AdapterConfig and update the ClientID' 993 | } 994 | 995 | $mfacert= (get-childitem Cert:\LocalMachine\my | where-object {$_.Subject -contains "CN="+ ($MFAraw -as [XML]).ChildNodes.TenantId +", OU=Microsoft AD FS Azure MFA"}) 996 | 997 | if(![string]::IsNullOrEmpty($mfacert)) { 998 | $obj| Add-Member -MemberType NoteProperty -Name 'Information' -Value 'A suitable Azure MFA Certificate was found in the store. Verify that the certificate referenced below is properly registered in AzureAD' 999 | $obj| Add-Member -MemberType NoteProperty -Name 'Subject' -Value $mfacert.Subject 1000 | $obj| Add-Member -MemberType NoteProperty -Name 'Thumbprint' -Value $mfacert.Thumbprint 1001 | $obj| Add-Member -MemberType NoteProperty -Name 'NotAfter' -Value $mfacert.NotAfter 1002 | $obj| Add-Member -MemberType NoteProperty -Name 'NotBefore' -Value $mfacert.NotBefore 1003 | } 1004 | else { 1005 | $mfacert= get-childitem Cert:\LocalMachine\my | where-object { $_.Subject -match 'OU=Microsoft AD FS Azure MFA' } 1006 | if($mfacert.count -eq '0') { 1007 | $obj| Add-Member -MemberType NoteProperty -Name 'Critical' -Value 'There are no Azure MFA Certificates existing in the local machines store' 1008 | } 1009 | 1010 | if($mfacert.count -eq '1') { 1011 | $obj| Add-Member -MemberType NoteProperty -Name 'Warning' -Value 'A Certificate was found in store but it does not match the TenantId in the configuration' 1012 | $obj| Add-Member -MemberType NoteProperty -Name 'Subject' -Value $mfacert.Subject 1013 | $obj| Add-Member -MemberType NoteProperty -Name 'Thumbprint' -Value $mfacert.Thumbprint 1014 | } 1015 | 1016 | if($mfacert.count -gt '1') { 1017 | $obj| Add-Member -MemberType NoteProperty -Name 'Warning' -Value 'More than one suitable Certificate was found in store but none of them matches the TenantId in the configuration' 1018 | } 1019 | } 1020 | 1021 | $adfsreg = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SOFTWARE\Microsoft\ADFS") 1022 | 1023 | if ($null -ne $adfsreg) { 1024 | $MFAREG = @('StsUrl','SasUrl','ResourceUri') 1025 | $foundKeys = @() 1026 | 1027 | foreach ($key in $MFAREG) { 1028 | if ($null -ne $adfsreg.GetValue($key)) { 1029 | $foundKeys += $key 1030 | } 1031 | } 1032 | 1033 | if ($foundKeys.Count -eq 0) { 1034 | $obj | Add-Member -MemberType NoteProperty -Name 'TenantEnvironment' -Value 'Azure MFA has not been configured for Azure Government and will use the default Public environment.' 1035 | } 1036 | else { 1037 | $obj | Add-Member -MemberType NoteProperty -Name 'TenantEnvironment' -Value 'Registry Entries for Azure Government have been found. Please review the registry' 1038 | 1039 | foreach ($key in $MFAREG) { 1040 | $value = $adfsreg.GetValue($key) 1041 | $obj | Add-Member -MemberType NoteProperty -Name $key -Value $(if ($null -ne $value) { $value } else { 'Key/Value not found' }) 1042 | } 1043 | } 1044 | #never forget to close the registry handle 1045 | $adfsreg.Close() 1046 | } 1047 | else { 1048 | $obj | Add-Member -MemberType NoteProperty -Name 'TenantEnvironment' -Value 'Error: ADFS registry key not found' 1049 | } 1050 | return $obj 1051 | } 1052 | else { return "Information: AzureMFA is not configured in this ADFS Farm." } 1053 | } 1054 | 1055 | function Get-ProxySettings { 1056 | $proxycfg = [PSCustomObject]@{} 1057 | 1058 | $IEProxyConfig = New-Object WinhttpCurrentUserIeProxyConfig 1059 | [WinHttp]::WinHttpGetIEProxyConfigForCurrentUser([ref]$IEProxyConfig) |Out-Null 1060 | 1061 | $WINHTTPPROXY = New-Object WINHTTP_PROXY_INFO 1062 | [WinHttp]::WinHttpGetDefaultProxyConfiguration([ref]$WINHTTPPROXY) |Out-Null 1063 | 1064 | $proxycfg = @" 1065 | IE ProxySetting of current CurrentUser: [$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)] 1066 | ============================================================= 1067 | AutoDetect: $($IEProxyConfig.AutoDetect) 1068 | AutoConfigUrl: $($IEProxyConfig.AutoConfigUrl) 1069 | ProxyName: $($IEProxyConfig.Proxy) 1070 | ProxyBypass: $($IEProxyConfig.ProxyBypass) 1071 | 1072 | WinHTTP Proxy Setting 1073 | ============================================================= 1074 | AutoDetect: $($WINHTTPPROXY.AccessType) 1075 | ProxyName: $($WINHTTPPROXY.Proxy) 1076 | ProxyBypass: $($WINHTTPPROXY.Bypass) 1077 | "@ 1078 | 1079 | return $proxycfg 1080 | } 1081 | 1082 | Function EnableDebugEvents ($events) { 1083 | if($TraceEnabled) { 1084 | ForEach ($evt in $events) { 1085 | $TraceLog = New-Object System.Diagnostics.Eventing.Reader.EventlogConfiguration $evt 1086 | $TraceLog.IsEnabled = $false 1087 | $TraceLog.SaveChanges() 1088 | 1089 | if ($TraceLog.LogName -like "*Tracing/Debug*") { 1090 | $TraceLog.ProviderLevel = 5 1091 | $TraceLog.IsEnabled = $true 1092 | $TraceLog.SaveChanges() 1093 | } 1094 | elseif($TraceLog.IsEnabled -eq $false) { 1095 | $tracelog.MaximumSizeInBytes = '50000000' 1096 | $TraceLog.IsEnabled = $true 1097 | $TraceLog.SaveChanges() 1098 | } 1099 | } 1100 | } 1101 | else 1102 | { Write-Host "Debug Event Logging skipped due to selected scenario" -ForegroundColor DarkCyan } 1103 | } 1104 | 1105 | Function LogManStart { 1106 | if($TraceEnabled) { 1107 | Push-Location $TraceDir 1108 | ForEach ($ets in $LogmanOn) 1109 | { 1110 | cmd /c $ets |Out-Null 1111 | } 1112 | Pop-Location 1113 | } 1114 | else { Write-Host "ETW Tracing skipped due to selected scenario" -ForegroundColor DarkCyan } 1115 | } 1116 | 1117 | Function EnableNetlogonDebug { 1118 | if($TraceEnabled) { 1119 | $key = (get-item -PATH "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon") 1120 | $subkey = $key.OpenSubKey("Parameters",$true) 1121 | Write-host "Enabling Netlogon Debug Logging" -ForegroundColor DarkCyan 1122 | 1123 | $subkey.SetValue($setDBFlag,$setvalue,$setvaltype) 1124 | 1125 | Write-host "Increasing Netlogon Debug Size to 100 MB" -ForegroundColor DarkCyan 1126 | $subkey.SetValue($setNLMaxLogSize,$setvalue2,$setvaltype2) 1127 | 1128 | #cleanup and close the write handle 1129 | $key.Close() 1130 | } 1131 | else { Write-Host "Netlogon Logging skipped due to scenario" -ForegroundColor DarkCyan } 1132 | } 1133 | 1134 | Function LogManStop { 1135 | if($TraceEnabled) { 1136 | Push-Location $TraceDir 1137 | ForEach ($log in $LogmanOff) { 1138 | cmd.exe /c $log |Out-Null 1139 | } 1140 | Pop-Location 1141 | } 1142 | else { Write-host "ETW Tracing was not enabled" -ForegroundColor DarkCyan } 1143 | } 1144 | 1145 | Function DisableNetlogonDebug { 1146 | if($TraceEnabled) { 1147 | $key = (get-item -PATH "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon") 1148 | $subkey = $key.OpenSubKey("Parameters",$true) 1149 | # Configure Keys based on initial configuration; if the keys did not exist we are also removing the keys again. else we set the old value 1150 | if ([string]::IsNullOrEmpty($orgdbflag)) { $subkey.deleteValue($setDBFlag) } 1151 | else { $subkey.SetValue($setDBFlag,$orgdbflag,$setvaltype) } 1152 | 1153 | if ([string]::IsNullOrEmpty($orgNLMaxLogSize)) { $subkey.deleteValue($setNLMaxLogSize) } 1154 | else { $subkey.SetValue($setNLMaxLogSize,$orgNLMaxLogSize,$setvaltype2) } 1155 | $key.Close() 1156 | } 1157 | else { Write-host "Netlogon logging was not enabled" -ForegroundColor DarkCyan } 1158 | } 1159 | 1160 | Function DisableDebugEvents ($events) { 1161 | if($TraceEnabled) { 1162 | ForEach ($evt in $events) { 1163 | $TraceLog = New-Object System.Diagnostics.Eventing.Reader.EventlogConfiguration $evt 1164 | if ($TraceLog.IsEnabled -eq $true) { 1165 | $TraceLog.IsEnabled = $false 1166 | $TraceLog.SaveChanges() 1167 | } 1168 | } 1169 | } 1170 | else { Write-host "Debug Tracing Eventlogs were not enabled" -ForegroundColor DarkCyan } 1171 | } 1172 | 1173 | Function ExportEventLogs { 1174 | Param( 1175 | [parameter(Position=0)] 1176 | $events, 1177 | [parameter(Position=1)] 1178 | $RuntimeInMsec 1179 | ) 1180 | 1181 | Push-Location $TraceDir 1182 | ForEach ($evts in $events) { 1183 | $expfilter= '*' #default filter 1184 | #Sec events can be very large; in tracing mode we only care about the events whilst the trace ran 1185 | #query filter for export is timebased and calculated on the time the trace collection started and ended + an offset of 5 minutes 1186 | if ($evts -eq 'Security') { 1187 | if($TraceEnabled) 1188 | { 1189 | #"create export filter with : "+$RuntimeInMsec 1190 | $expfilter= '' + '' 1191 | } 1192 | else {#only export the last 60 minutes; 1193 | $expfilter= '' + '' 1194 | } 1195 | } 1196 | # Replace slashes in the event filename before building the export paths 1197 | $evtx = [regex]::Replace($evts,"/","-") 1198 | $evttarget = $TraceDir +"\"+ $evtx+".evtx" 1199 | $EventSession = New-Object System.Diagnostics.Eventing.Reader.EventLogSession 1200 | $EventSession.ExportLog($evts,'Logname',$expfilter,$evttarget) 1201 | } 1202 | Pop-Location 1203 | } 1204 | 1205 | function widlogs { 1206 | $widlog="$env:windir\WID\Log" 1207 | $wid = $TraceDir + "\Wid" 1208 | #for the time being we only want to collect the error logs from wid if the cummulative size is less then 25MB was 10 initially 1209 | if ([math]::Round(((Get-ChildItem $widlog -Filter *.log)| Measure-Object -Property Length -sum).sum / 1Mb ,1) -le 25) { 1210 | New-Item -ItemType directory -Path $wid -Force | Out-Null 1211 | foreach ($file in (Get-ChildItem -Path $widlog -Filter *.log) ) { 1212 | Copy-Item ($file.fullname) -Destination $wid 1213 | } 1214 | } 1215 | } 1216 | 1217 | Function GatherTheRest { 1218 | Push-Location $TraceDir 1219 | ForEach ($logfile in $Filescollector) { 1220 | cmd.exe /c $logfile | out-null 1221 | } 1222 | Get-ProxySettings | out-file $env:COMPUTERNAME-ProxySettings.txt 1223 | Get-CertificatesByStore MY| out-file $env:COMPUTERNAME-Certificates-My.txt 1224 | Get-CertificatesByStore Root| out-file $env:COMPUTERNAME-Certificates-Root.txt 1225 | Get-CertificatesByStore CA| out-file $env:COMPUTERNAME-Certificates-CA.txt 1226 | Get-CertificatesByStore NTAuth| out-file $env:COMPUTERNAME-Certificates-NTAuth.txt 1227 | Get-CertificatesByStore ADFSTrustedDevices| out-file $env:COMPUTERNAME-Certificates-ADFSTrustedDevices.txt 1228 | 1229 | if(!$IsProxy) { 1230 | Get-Adfssslcertificate|foreach-object {if($_.CtlStoreName -eq "ClientAuthIssuer" ) {Get-CertificatesByStore ClientAuthIssuer| out-file $env:COMPUTERNAME-Certificates-CliAuthIssuer.txt }} 1231 | 1232 | if ( (Test-IsWID).IsWID) { 1233 | widlogs 1234 | Get-ADFSDBStateFromWID | out-file $env:COMPUTERNAME-ADFS-DatabaseStatus.txt 1235 | } 1236 | } 1237 | else { 1238 | Get-WebApplicationProxySslCertificate|foreach-object { if($_.CtlStoreName -eq "ClientAuthIssuer" ) {Get-ClientAuthIssuerCertificates| out-file $env:COMPUTERNAME-Certificates-CliAuthIssuer.txt} } 1239 | } 1240 | 1241 | Get-DnsClientCache |Sort-Object -Property Entry |format-list |Out-File $env:COMPUTERNAME-DNSClient-Cache.txt 1242 | Get-ChildItem env: |Format-Table Key,Value -Wrap |Out-File $env:COMPUTERNAME-environment-variables.txt 1243 | Get-NetTCPConnection|Sort-Object -Property LocalAddress |out-file $env:COMPUTERNAME-NetTCPConnection.txt 1244 | get-service|Sort-Object -Property Status -Descending |Format-Table DisplayName,Status,StartType -autosize | out-file $env:COMPUTERNAME-services-running.txt 1245 | get-process |Sort-Object Id |Format-Table Name,Id, SessionId,WorkingSet -AutoSize |out-file $env:COMPUTERNAME-tasklist.txt 1246 | Get-Content $env:windir\system32\drivers\etc\hosts |out-file $env:COMPUTERNAME-hosts.txt 1247 | ((get-childitem $env:Windir\adfs\* -include *.dll,*.exe).VersionInfo |Sort-Object -Property FileVersion |Format-Table FileName, FileVersion) |out-file $env:COMPUTERNAME-ADFS-fileversions.txt 1248 | VerifyNetFX |format-list | out-file $env:COMPUTERNAME-DotNetFramework.txt 1249 | Get-WindowsUpdateHTMLReport | out-file $env:COMPUTERNAME-WindowsPatches.html 1250 | 1251 | Pop-Location 1252 | } 1253 | 1254 | function Enable-ADFSPerfcounters { 1255 | 1256 | Param( 1257 | [Parameter(Mandatory=$false)] 1258 | [ValidateSet("ADFSProxy", "ADFSBackend")] 1259 | [string]$Scenario, 1260 | 1261 | [Parameter(Mandatory=$false)] 1262 | [ValidateSet("Create", "Enable", "Disable", "Delete")] 1263 | [string]$Action 1264 | 1265 | ) 1266 | 1267 | #sanity checks 1268 | if (-not $PSBoundParameters.ContainsKey('Scenario')) { 1269 | "Missing Parameter. You must supply a Scenario. Allowed values: ADFSProxy, ADFSBackend."; 1270 | break; 1271 | } 1272 | 1273 | if (-not $PSBoundParameters.ContainsKey('Action')) { 1274 | "Missing Parameter. You must supply a Scenario. Allowed values: Create, Enable, Disable, Delete"; 1275 | break; 1276 | } 1277 | 1278 | #Action logic 1279 | switch ($Action) { 1280 | 1281 | "Create" { # create the perfcounter collection, we distinguish between WAP and ADFS scenario but always add general counters. 1282 | $joined="" 1283 | foreach ($Counter in $perfcnt.Keys) { 1284 | 1285 | # add role based counters 1286 | if ($perfcnt[$Counter].Type -eq $Scenario) { 1287 | $format = [string]::Format('"{0}" ',$perfcnt[$Counter].CounterName) 1288 | $joined +=$format 1289 | } 1290 | 1291 | #add WID counters if WID is used and we are in ADFSBackend scenario 1292 | if ($perfcnt[$Counter].Type -eq 'WID' -and ( (Test-IsWID).IsWID ) -and ($Scenario -eq 'ADFSBackend')) { 1293 | $format = [string]::Format('"{0}" ',$perfcnt[$Counter].CounterName) 1294 | $joined +=$format 1295 | } 1296 | 1297 | ## always add general perf counters 1298 | if ($perfcnt[$Counter].Type -eq 'General') { 1299 | $format = [string]::Format('"{0}" ',$perfcnt[$Counter].CounterName) 1300 | $joined +=$format 1301 | } 1302 | } 1303 | #build the string and return it 1304 | $result = [String]::Format('Logman.exe create counter {0} -o ".\%COMPUTERNAME%-{0}-perf.blg" -f bincirc -max 512 -v mmddhhmm -c {1} -si 00:00:05',$Scenario,$joined.TrimEnd()) 1305 | return $result 1306 | } 1307 | "Enable" { #Enable the perfcounter collection setting it to running state 1308 | return [string]::Format('Logman.exe start {0}',$Scenario) 1309 | } 1310 | 1311 | "Disable" { #Disable the perfcounter collection setting it to stopped state 1312 | return [string]::Format('Logman.exe stop {0}',$Scenario) 1313 | } 1314 | 1315 | "Delete" { #Delete the perfcounter collection 1316 | return [string]::Format('Logman.exe delete {0}',$Scenario) 1317 | } 1318 | } 1319 | } 1320 | 1321 | Function EnablePerfCounter { 1322 | if ($TraceEnabled -and $PerfCounter) { 1323 | 1324 | Write-host "Enabling PerfCounter" -ForegroundColor DarkCyan 1325 | 1326 | if ($IsProxy) { $Scenario='ADFSProxy' } else {$Scenario='ADFSBackend' } 1327 | 1328 | Push-Location $TraceDir 1329 | cmd /c $(Enable-ADFSPerfcounters $Scenario Create) |Out-Null 1330 | cmd /c $(Enable-ADFSPerfcounters $Scenario Enable) |Out-Null 1331 | Pop-Location 1332 | } 1333 | else { Write-Host "Performance Monitoring will not be sampled due to selected scenario" -ForegroundColor DarkCyan } 1334 | } 1335 | 1336 | Function DisablePerfCounter { 1337 | if ($TraceEnabled -and $PerfCounter) { 1338 | 1339 | Write-Host "Stopping Performance Monitoring" -ForegroundColor DarkCyan 1340 | 1341 | if ($IsProxy) { $Scenario='ADFSProxy' } else {$Scenario='ADFSBackend' } 1342 | Push-Location $TraceDir 1343 | cmd /c $(Enable-ADFSPerfcounters $Scenario Disable) |Out-Null 1344 | cmd /c $(Enable-ADFSPerfcounters $Scenario Delete) |Out-Null 1345 | Pop-Location 1346 | } 1347 | else { Write-Host "Performance Monitoring was not sampled due to selected scenario" -ForegroundColor DarkCyan } 1348 | } 1349 | 1350 | Function EnableNetworkTrace { 1351 | if ($TraceEnabled -and $NetTraceEnabled) { 1352 | Write-host "Starting Network Trace" -ForegroundColor DarkCyan 1353 | Push-Location $TraceDir 1354 | #workaround for trace driver initialization failure on certain intel platforms 1355 | $ns = 'netsh trace start capture=yes report=disabled maxsize=1 tracefile=.\%COMPUTERNAME%-network.etl overwrite=yes' 1356 | cmd /c $ns |Out-Null 1357 | cmd /c $DisableNetworkTracer |Out-Null 1358 | #workaround ends 1359 | cmd /c $EnableNetworkTracer |Out-Null 1360 | Pop-Location 1361 | } 1362 | } 1363 | 1364 | Function DisableNetworkTrace { 1365 | if ($TraceEnabled -and $NetTraceEnabled) { 1366 | Write-host "Stopping Network Trace. It may take some time for the data to be flushed to disk. Please be patient`n" -ForegroundColor Yellow 1367 | cmd /c $DisableNetworkTracer |Out-Null 1368 | } 1369 | } 1370 | 1371 | Function EnableLDAPTrace { 1372 | if(!$IsProxy) { 1373 | if ($TraceEnabled -and $LdapTraceEnabled) { 1374 | Write-host "Starting LDAP Trace" -ForegroundColor DarkCyan 1375 | #enable per ldap tracing for: powershell/ise; wsmprovhost and the service itself 1376 | New-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\powershell_ise.exe' -Force | Out-Null 1377 | New-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\powershell.exe' -Force | Out-Null 1378 | New-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\Microsoft.IdentityServer.ServiceHost.exe' -Force | Out-Null 1379 | New-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\wsmprovhost.exe' -Force | Out-Null 1380 | 1381 | Push-Location $TraceDir 1382 | ForEach ($log in $ldapetlOn) { 1383 | cmd.exe /c $log |Out-Null 1384 | } 1385 | Pop-Location 1386 | } 1387 | } 1388 | } 1389 | 1390 | Function DisableLDAPTrace { 1391 | if(!$IsProxy) { 1392 | if($TraceEnabled -and $LdapTraceEnabled) { 1393 | Write-Host "Stopping LDAP Tracing" -ForegroundColor DarkCyan 1394 | Push-Location $TraceDir 1395 | 1396 | ForEach ($log in $ldapetlOff) { 1397 | cmd.exe /c $log |Out-Null 1398 | } 1399 | Pop-Location 1400 | Remove-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\powershell_ise.exe' -Force | Out-Null 1401 | Remove-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\powershell.exe' -Force | Out-Null 1402 | Remove-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\Microsoft.IdentityServer.ServiceHost.exe' -Force | Out-Null 1403 | Remove-Item 'HKLM:\System\CurrentControlSet\Services\ldap\Tracing\wsmprovhost.exe' -Force | Out-Null 1404 | } 1405 | else { Write-host "LDAP Tracing was not enabled" -ForegroundColor DarkCyan } 1406 | } 1407 | } 1408 | 1409 | Function EnableWAPTrace { 1410 | if($IsProxy) { 1411 | if ($TraceEnabled -and $WAPTraceEnabled) { 1412 | Write-host "Starting WAP Tracing" -ForegroundColor DarkCyan 1413 | Push-Location $TraceDir 1414 | ForEach ($log in $WAPTraceOn) { 1415 | cmd.exe /c $log |Out-Null 1416 | } 1417 | 1418 | Pop-Location 1419 | } 1420 | } 1421 | } 1422 | 1423 | Function DisableWAPTrace { 1424 | if($IsProxy) { 1425 | if($TraceEnabled -and $WAPTraceEnabled) { 1426 | Write-Host "Stopping WAP Tracing" -ForegroundColor DarkCyan 1427 | Push-Location $TraceDir 1428 | 1429 | ForEach ($log in $WapTraceOff) { 1430 | cmd.exe /c $log |Out-Null 1431 | } 1432 | 1433 | Pop-Location 1434 | } 1435 | else { Write-host "WAP Tracing was not enabled" -ForegroundColor DarkCyan } 1436 | } 1437 | } 1438 | 1439 | function Test-KRBEncTypePolicy { 1440 | # Specify the registry key path and the value name 1441 | $keyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters\" 1442 | $valueName = "SupportedEncryptionTypes" 1443 | #by default all Enctypes are enabled .This may change in future we assume defaults unless a policy is configured 1444 | $EncType = 31 1445 | 1446 | $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath) 1447 | 1448 | #if policy key exists try to get the value 1449 | if (($key.Name)) { 1450 | if ($key.GetValueNames() -icontains $valueName) { 1451 | try { 1452 | [int]$EncType= $key.GetValue($valueName); 1453 | #close key handle when done 1454 | $key.Close() 1455 | } 1456 | catch { 1457 | #regvalue is not defined only thing we want to do is to close key handle 1458 | $key.Close() 1459 | } 1460 | } 1461 | } 1462 | 1463 | #do we still need to look into the classic path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters ? 1464 | #if yes we will loop it in here. 1465 | 1466 | #desktop GPO allows to configure ETypes only and FutureFlags. if the value is greater than 0x1f in GPO we must expect that Futureflags had been set 1467 | #remove the futureflag from enumeration 1468 | 1469 | if ($EncType -gt 31) { 1470 | $EncType = $EncType - 2147483616 1471 | } 1472 | #watch out if there is someone configuring the regkeys manually instead via GPO, it might be they use wrong or negative values 1473 | # finally convert the value to a meaningful string and return 1474 | return ([KrbEnum]::EnumerateKrb($EncType)) 1475 | } 1476 | 1477 | Function Test-ADFSComputerNameEqFarmName { 1478 | param( 1479 | [Parameter(Mandatory=$true)] 1480 | [string]$farmName 1481 | ) 1482 | 1483 | $errortmpl=@" 1484 | Error: The host computer name '{0}' is identical to the configured ADFS Farmname '{1}'. 1485 | This configuration is unsupported and is known to cause the following issues: 1486 | - windows integrated authentication will fail since the kerberos SPN cannot be registered on the service account without causing conflicts 1487 | - failure to perform Remote Management (WinRM) 1488 | - cause WID synchronization issues 1489 | - preventing the setup/use of additional farmnodes 1490 | "@ 1491 | try 1492 | { 1493 | $netprop = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() 1494 | $computerName = [string]::Format("{0}.{1}",$netprop.HostName,$netprop.Domainname) 1495 | 1496 | if ($computerName -eq $farmName) 1497 | { 1498 | $testResult = [string]::Format($errortmpl,$computerName.ToUpper(),$farmName.ToUpper()) 1499 | return $testResult 1500 | } 1501 | 1502 | $testResult = "Test passed" 1503 | return $testResult 1504 | } 1505 | catch [Exception] 1506 | { 1507 | return [string]::format("Error: Failed to verify the computer name and ADFS Farmname are not overlapping. Error {0}", $_.Exception.Message) 1508 | } 1509 | } 1510 | 1511 | Function Test-ADFSFarmnameIsNotCNAME { 1512 | param( 1513 | [Parameter(Mandatory=$true)] 1514 | [string]$farmName 1515 | ) 1516 | try { 1517 | $resolutionResult = [System.Net.Dns]::GetHostEntry($farmname) 1518 | $resolvedHostName = $resolutionResult.HostName 1519 | 1520 | if ($resolvedHostName -ne $farmname) { 1521 | $testResult = [String]::format("Warning: The ADFS Farm Name '{0}' is resolved as host '{1}'. This might break windows integrated authentication scenarios.`n",$farmname,$resolvedHostName) 1522 | return $testResult 1523 | } 1524 | 1525 | $testResult = "Test passed" 1526 | return $testResult 1527 | } catch [System.Net.Sockets.SocketException] { 1528 | return [string]::format("Error: Could not resolve the farm name {0} with exception '{1}'",$farmname, $_.Exception.Message) 1529 | } 1530 | } 1531 | 1532 | function Get-ADFSFarmNameFromSSLBinding { 1533 | <# 1534 | .SYNOPSIS 1535 | Retrieves the ADFS farm name from SSL certificate bindings. 1536 | 1537 | .DESCRIPTION 1538 | This function examines SSL certificate bindings to identify the ADFS farm hostname. 1539 | It filters out localhost, enterprise registration, and certificate authentication endpoints 1540 | to find the primary ADFS service hostname. 1541 | 1542 | .OUTPUTS 1543 | String - Returns the first valid ADFS farm hostname found, or $null if none found. 1544 | #> 1545 | 1546 | try { 1547 | # Get all SSL certificate bindings first 1548 | $sslCertificates = Get-AdfsSslCertificate 1549 | # Find the first valid hostname 1550 | foreach ($cert in $sslCertificates) { 1551 | if (($cert.PortNumber -eq 443) -and 1552 | ($cert.AppId -eq '5d89a20c-beab-4389-9447-324788eb944a') -and 1553 | ($cert.HostName -inotlike 'localhost') -and 1554 | ($cert.HostName -inotlike 'enterpriseregistration*') -and 1555 | ($cert.HostName -inotlike 'certauth*')) { 1556 | 1557 | # Return the first valid hostname immediately 1558 | return $cert.HostName 1559 | } 1560 | } 1561 | # If no valid hostname found, return $null 1562 | return $null 1563 | } 1564 | catch { 1565 | Write-Warning "Failed to retrieve ADFS SSL certificate bindings: $($_.Exception.Message)" 1566 | return $null 1567 | } 1568 | } 1569 | 1570 | function Get-ServiceAccountDetails { 1571 | #initialize object to store the result: gsad is the accronym of the function name ( g = get, sa = service account, d = details ) 1572 | $gsad = New-Object -TypeName PSObject 1573 | 1574 | #only execute if we are not on proxy/wap 1575 | if (!$IsProxy) { 1576 | #get currently config service account if this fails 1577 | try { 1578 | $svc = new-object System.ServiceProcess.ServiceController('adfssrv') 1579 | $SVCACC = [ServiceConfigHelper]::GetServiceAccount($svc.Name) 1580 | } catch { 1581 | $gsad | Add-Member -MemberType NoteProperty -Name "ADFS Service Account" -Value "Error: Failed to retrieve ADFS Service Account from Service Controll Manager. The AD FS Role may not be installed. Skipping Service Account checks." 1582 | return $gsad 1583 | } 1584 | 1585 | if (!$SVCACC) { 1586 | #this would be unexpected as the service account is mandatory for a service to run unless someone deleted the service account from service config/registry 1587 | $gsad | Add-Member -MemberType NoteProperty -Name "ADFS Service Account" -Value "Error: No ADFS Service Account configured. This is unexpected and could mean the service account was removed from the service configuration in Windows(Registry)." 1588 | return $gsad 1589 | } else { 1590 | $gsad | Add-Member -MemberType NoteProperty -Name "ADFS Service Account" -Value $SVCACC 1591 | } 1592 | 1593 | #detect name format UPN vs Legacy 1594 | if ($SVCACC.contains('@')) { 1595 | $filter ="(userprincipalname="+$SVCACC+")" 1596 | $domain = $SVCACC.Split('@')[1] 1597 | } 1598 | 1599 | if ($SVCACC.contains('\')) { 1600 | $filter ="(samaccountname="+$SVCACC.Split('\')[1]+")" 1601 | $domain = $SVCACC.Split('\')[0] 1602 | } 1603 | 1604 | $conn= (New-Object System.DirectoryServices.DirectoryEntry("LDAP://$domain/RootDSE")).dnshostname 1605 | [string]$att = "*" 1606 | 1607 | #Performing LDAP Lookup of ADFS Service Account 1608 | $re= LDAPQuery -filter $filter -att $att -conn $conn 1609 | #1st test if its a GMSA 1610 | $gmsa =$false 1611 | 1612 | if(($re.GetType().Name -eq 'SearchResponse') -and ($re.Entries.Count -eq 1)) { 1613 | $gmsa = [Bool]($re.Entries.Attributes.objectclass.GetValues('string') -eq 'msDS-GroupManagedServiceAccount') 1614 | $gsad | Add-Member -MemberType NoteProperty -Name "IsManagedServiceAccount" -Value $gmsa 1615 | 1616 | if($gmsa -eq $true) { 1617 | $adl = new-object System.DirectoryServices.ActiveDirectorySecurity 1618 | $adl.SetSecurityDescriptorBinaryForm($re.Entries[0].Attributes.'msds-groupmsamembership'[0]) 1619 | $gsad | Add-Member -MemberType NoteProperty -Name "GMSA allowed Hosts" -value ($adl.AccessToString) 1620 | } 1621 | #try reading the SPN configuration from the service account 1622 | try { 1623 | $gsad | Add-Member -MemberType NoteProperty -Name "OnAccountRegisteredSPN" -Value ($re.Entries.Attributes.serviceprincipalname.GetValues('string')) 1624 | } catch { 1625 | #we failed to read the SPN value and must assume there is no SPN configured for this service account 1626 | if ($_.FullyQualifiedErrorID -eq 'InvokeMethodOnNull' ) { 1627 | $gsad | Add-Member -MemberType NoteProperty -Name "OnAccountRegisteredSPN" -Value "ERROR: No SPNs are configured for this Service Account." 1628 | } 1629 | } 1630 | 1631 | #whilst we are at it try and get the kerberos encryption type value if there is one configured 1632 | Try { 1633 | $EncType= [int]::Parse($re.Entries[0].Attributes.'msds-supportedencryptiontypes'.GetValues('string')) 1634 | } Catch { 1635 | #if we dont find a configured value we must assume its not set so lets use -1 explicitly 1636 | $EncType=-1 1637 | } 1638 | 1639 | } else { 1640 | if ($re.Response.ResultCode -eq "NoSuchObject") { 1641 | $gsad | Add-Member -MemberType NoteProperty -Name "ServiceAccount query failed" -value "Service Account not found. Ldap Error:`r`n$($re.Response.ErrorMessage)" 1642 | } else { 1643 | $gsad | Add-Member -MemberType NoteProperty -Name "ServiceAccount query failed" -value "Unable to resolve the Service Account. Use AD tools like 'setspn' or 'dsa.msc' to verify the Account exists in AD." 1644 | } 1645 | # if we failed to find the account use -2 so we later dont call enum but log appropropriate message 1646 | $EncType=-2 1647 | } 1648 | 1649 | #refined the Dupe SPN check to not only rely on get-ADFSProperties alone for building the SPN query...this may not work in all cases 1650 | #we now go by the order: http hostname binding -> ADFS Properties -> lastly directly from database 1651 | $farmname = Get-ADFSFarmNameFromSSLBinding 1652 | 1653 | #hostname may still be empty so we may have failed to find the bindings. 1654 | #let assume ADFS service is running and we can query adfsproperties from powershell 1655 | if ($null -eq $farmname ) { 1656 | try { 1657 | $farmname = (get-adfsproperties).hostname 1658 | } catch { } 1659 | } 1660 | 1661 | # if still no hostname last attempt to get the farmname from DB 1662 | # this is best effort here and limited to WID. The user may not be a DBA or have access to the SQL server 1663 | if ($null -eq $farmname ) { 1664 | try { 1665 | $dbconfig = Test-IsWID 1666 | if ($dbconfig.IsWID -and ($dbconfig.IsWIDStarted -ne [System.ServiceProcess.ServiceControllerStatus]::Running )) { 1667 | $errMsg = "Error: WID (Windows Internal Database) service is not running." 1668 | throw [System.Exception]::new($errMsg) 1669 | } 1670 | if ($null -ne $dbconfig.ConfigurationDatabaseConnectionString) { 1671 | $farmname = (get-servicesettingsfromdb -DBConnectionString $dbconfig.ConfigurationDatabaseConnectionString ).ServiceSettingsData.SecurityTokenService.Host.Name 1672 | } 1673 | } catch {} 1674 | } 1675 | 1676 | #if we have a hostname lets attempt to perform a check for duplicate SPNs 1677 | #first check create the connection object. Use GlobalCatalog as we may have a dupe in a child domain of the forest 1678 | if (!($null -eq $farmname )) { 1679 | $gconn= (New-Object System.DirectoryServices.DirectoryEntry("GC://$domain/RootDSE")).dnshostname 1680 | $filter= [string]::format("(serviceprincipalname=*/{0})", $farmname ) 1681 | [string]$att = "*" 1682 | } 1683 | 1684 | #if we dont have a hostname we didnt cannot create the ldap connection and filter so we dont need to run the query after all 1685 | if (!($null -eq $gconn)) { 1686 | 1687 | $re= LDAPQuery -filter $filter -att $att -conn $gconn 1688 | 1689 | if ($re.GetType().Name -eq 'SearchResponse' -And ($re.entries.count -ge 1)) { 1690 | $obj= $re.Entries | ForEach-Object { 1691 | [String]::Format("`r`nAccount: {0}`r`nSPNs : {1}`n", 1692 | $_.distinguishedName , 1693 | ($_.Attributes.'serviceprincipalname'.GetValues('string') -join " ; ") ) 1694 | } 1695 | $gsad | Add-Member -MemberType NoteProperty -Name "Duplicate SPN" -Value $obj 1696 | } else { 1697 | $gsad | Add-Member -MemberType NoteProperty -Name "Duplicate SPN" -Value "Warning: Duplicate SPN check failed.`r`nThe query may have timed-out or may have returned no results.`r`nYou can use 'setspn.exe -f -q */$($farmname)' to query for duplicate SPN's in the forest.`r`n." 1698 | } 1699 | } 1700 | 1701 | #test for DNS Cname since it can break kerberos auth 1702 | if (!($null -eq $farmname )) { 1703 | $adfscnamecheck = Test-ADFSFarmnameIsNotCNAME -farmName $farmname 1704 | if ($adfscnamecheck -ne "Test passed") { 1705 | $gsad | Add-Member -MemberType NoteProperty -Name "DNS-Alias Check" -Value $adfscnamecheck 1706 | } else { 1707 | $gsad | Add-Member -MemberType NoteProperty -Name "DNS-Alias Check" -Value ( [String]::Format("Success: The ADFS Farmname '{0}' resolves correctly without CNAME indirection.", $farmname) ) 1708 | } 1709 | } else { 1710 | $gsad | Add-Member -MemberType NoteProperty -Name "DNS-Alias Check" -Value "Test skipped. Could not retrieve ADFS farmname from configuration. The service may not be running or is not yet configured" 1711 | } 1712 | 1713 | #computername must not be identical to farmname else breaks kerberos auth and farm management 1714 | if (!($null -eq $farmname )) { 1715 | $adfseqhostres = Test-ADFSComputerNameEqFarmName -farmName $farmname 1716 | if ($adfseqhostres -ne "Test passed") { 1717 | $gsad | Add-Member -MemberType NoteProperty -Name "Farmname-Computername Check" -Value $adfseqhostres 1718 | } else { 1719 | $gsad | Add-Member -MemberType NoteProperty -Name "Farmname-Computername Check" -Value "Success: ADFS Farmname and Computername are different." 1720 | } 1721 | } else { 1722 | $gsad | Add-Member -MemberType NoteProperty -Name "Farmname-Computername Check" -Value "Test skipped. Could not retrieve ADFS farmname from configuration. The service may not be running or is not yet configured" 1723 | } 1724 | 1725 | #Finally validate that Kerberos Etype Config is sound and we have a matching config between OS and Service Account 1726 | #some message strings 1727 | $RC4NotSetMsg="The Service Account is not configured for AES support. Service tickets will be RC4 encrypted! 1728 | `r`nWe recommend configuring the ADFS Service Account for AES Support.`r`nIn Active Directory configure the attribute 'msds-supportedencryptiontypes' for the ADFS ServiceAccount with a value of:`r`n24(decimal) => AES only `n or `n28(decimal) => AES & RC4" 1729 | 1730 | $RC4NoPolicysupMsg="The ADFS service account is not configured properly. Local policy/registry for KerberosEncryptionTypes disabled RC4 support,`r`nbut the service account has not been configured for AES support. 1731 | `r`nThis configuration can lead to authentication failures and other erroneous behavior and MUST be corrected. 1732 | `r`nWe recommend configuring the ADFS Service Account for AES Support.`r`nIn Active Directory configure the attribute 'msds-supportedencryptiontypes' for the ADFS ServiceAccount with a value of:`r`n24(decimal) => AES only `n or `n28(decimal) => AES & RC4" 1733 | 1734 | #get KrbConfig from OS Policy 1735 | $HostKrbCfg = Test-KRBEncTypePolicy 1736 | 1737 | #theoretically this cannot be null unless the module failed 1738 | if (!($null -eq $HostKrbCfg)) { 1739 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEtype from OS (Policy)" -Value $($HostKrbCfg -join " | ") 1740 | } 1741 | 1742 | #check etypes from ServiceAccountQuery if not set or explicitly 0 default to RC4 1743 | #if we failed to find service account previously..skip etype config evaluation 1744 | 1745 | switch ($EncType) { 1746 | 0 { $SvcKrbCfg = "RC4_HMAC"; 1747 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEtype from ServiceAccount" -Value "explicitly set to 0. Defaulting to RC4_HMAC" 1748 | } 1749 | -1 { $SvcKrbCfg = "RC4_HMAC"; 1750 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEType from ServiceAccount" -Value "not configured. Defaulting to RC4_HMAC" 1751 | } 1752 | -2 { #we failed to find service account previously..nothing to evaluate here 1753 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEType from ServiceAccount" -Value "Failed to enumerate ServiceAccount" 1754 | } 1755 | default { $SvcKrbCfg = [KrbEnum]::EnumerateKrb($EncType) 1756 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEType from ServiceAccount" -Value $($SvcKrbCfg -join " | ") 1757 | } 1758 | } 1759 | 1760 | if (!($null -eq $HostKrbCfg) -and !($null -eq $SvcKrbCfg)) { 1761 | $commonItems = [System.Linq.Enumerable]::Intersect( 1762 | [System.Collections.Generic.List[object]]@($SvcKrbCfg), 1763 | [System.Collections.Generic.List[object]]@($HostKrbCfg) 1764 | ) 1765 | } 1766 | 1767 | #we have intersection and AES is listed --> AES is used 1768 | if ($commonItems.Count -gt 0 -and $commonItems -like "*AES*") { 1769 | $gsad | Add-Member -MemberType NoteProperty -Name "Kerberos EncryptionType expected" -Value "AES" 1770 | 1771 | #we have intersection but not AES but RC4 or maybe even weaker ..log warning and recommend AES 1772 | } elseif ( $commonItems.Count -gt 0 -and ($commonItems -notlike "*AES*") ) { 1773 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEType expected" -Value "RC4" 1774 | $gsad | Add-Member -MemberType NoteProperty -Name "Warning" -Value $RC4NotSetMsg 1775 | 1776 | #no intersection at all. This does not look good and auth will break . log error 1777 | } elseif ( $null -eq $commonItems.Count -and ($SvcKrbCfg -like "*RC4*") -and ($HostKrbCfg -notlike "*RC4*") ) { 1778 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEType expected" -Value "RC4" 1779 | $gsad | Add-Member -MemberType NoteProperty -Name "Error" -Value $RC4NoPolicysupMsg 1780 | 1781 | #we should only get here if we have no Service Account queried (notFound/NotExisting) 1782 | } else { 1783 | $gsad | Add-Member -MemberType NoteProperty -Name "KrbEType expected" -Value "Cannot predict KrbEType usage. A previous query failed" 1784 | 1785 | } 1786 | } 1787 | 1788 | Return $gsad 1789 | } 1790 | 1791 | Function GetADFSConfig { 1792 | 1793 | Push-Location $TraceDir 1794 | if ($IsProxy) { # ADFS proxy 2012 1795 | if ($WinVer -eq [Version]"6.2.9200" ) { 1796 | Get-AdfsProxyProperties | format-list * | Out-file "Get-AdfsProxyProperties.txt" 1797 | } 1798 | else { # ADFS 2012 R2 or ADFS 2016 or 2019 1799 | Get-WebApplicationProxyApplication | format-list * | Out-file "Get-WebApplicationProxyApplication.txt" 1800 | Get-WebApplicationProxyAvailableADFSRelyingParty | format-list * | Out-file "Get-WebApplicationProxyAvailableADFSRelyingParty.txt" 1801 | Get-WebApplicationProxyConfiguration | format-list * | Out-file "Get-WebApplicationProxyConfiguration.txt" 1802 | Get-WebApplicationProxyHealth | format-list * | Out-file "Get-WebApplicationProxyHealth.txt" 1803 | Get-WebApplicationProxySslCertificate | format-list * | Out-file "Get-WebApplicationProxySslCertificate.txt" 1804 | copy-item -path "$env:windir\ADFS\Config\Microsoft.IdentityServer.ProxyService.exe.config" -Destination $TraceDir 1805 | } 1806 | } 1807 | else { 1808 | # Common ADFS commands to all version 1809 | if ((Get-AdfsSyncProperties).Role -eq 'PrimaryComputer') { 1810 | Get-AdfsAttributeStore | format-list * | Out-file "Get-AdfsAttributeStore.txt" 1811 | Get-AdfsCertificate | format-list * | Out-file "Get-AdfsCertificate.txt" 1812 | Get-AdfsClaimDescription | format-list * | Out-file "Get-AdfsClaimDescription.txt" 1813 | Get-AdfsClaimsProviderTrust | format-list * | Out-file "Get-AdfsClaimsProviderTrust.txt" 1814 | Get-AdfsEndpoint | format-list * | Out-file "Get-AdfsEndpoint.txt" 1815 | Get-AdfsProperties | format-list * | Out-file "Get-AdfsProperties.txt" 1816 | Test-WiaSupportedUseragents | format-list * | Out-file "Get-ADFSproperties.txt" -Append 1817 | Get-AdfsRelyingPartyTrust | format-list * | Out-file "Get-AdfsRelyingPartyTrust.txt" 1818 | } 1819 | 1820 | Get-AdfsSyncProperties | format-list * | Out-file "Get-AdfsSyncProperties.txt" 1821 | Get-AdfsSslCertificate | format-list * | Out-file "Get-AdfsSslCertificate.txt" 1822 | Get-ServiceAccountDetails | format-list * | Out-file "Get-ServiceAccountDetails.txt" 1823 | 1824 | if ($WinVer -ge [Version]"10.0.14393") {# ADFS commands specific to ADFS 2016,2019,2022 1825 | if((Get-AdfsSyncProperties).Role -eq 'PrimaryComputer') { 1826 | Get-AdfsAccessControlPolicy | format-list * | Out-file "Get-AdfsAccessControlPolicy.txt" 1827 | Get-AdfsApplicationGroup | format-list * | Out-file "Get-AdfsApplicationGroup.txt" 1828 | Get-AdfsApplicationPermission | format-list * | Out-file "Get-AdfsApplicationPermission.txt" 1829 | Get-AdfsCertificateAuthority | format-list * | Out-file "Get-AdfsCertificateAuthority.txt" 1830 | Get-AdfsClaimsProviderTrustsGroup | format-list * | Out-file "Get-AdfsClaimsProviderTrustsGroup.txt" 1831 | Get-AdfsFarmInformation | format-list * | Out-file "Get-AdfsFarmInformation.txt" 1832 | Get-AdfsLocalClaimsProviderTrust | format-list * | Out-file "Get-AdfsLocalClaimsProviderTrust.txt" 1833 | Get-AdfsNativeClientApplication | format-list * | Out-file "Get-AdfsNativeClientApplication.txt" 1834 | Get-AdfsRegistrationHosts | format-list * | Out-file "Get-AdfsRegistrationHosts.txt" 1835 | Get-AdfsRelyingPartyTrustsGroup | format-list * | Out-file "Get-AdfsRelyingPartyTrustsGroup.txt" 1836 | Get-AdfsScopeDescription | format-list * | Out-file "Get-AdfsScopeDescription.txt" 1837 | Get-AdfsServerApplication | format-list * | Out-file "Get-AdfsServerApplication.txt" 1838 | Get-AdfsTrustedFederationPartner | format-list * | Out-file "Get-AdfsTrustedFederationPartner.txt" 1839 | Get-AdfsWebApiApplication | format-list * | Out-file "Get-AdfsWebApiApplication.txt" 1840 | Get-AdfsAdditionalAuthenticationRule | format-list * | Out-file "Get-AdfsAdditionalAuthenticationRule.txt" 1841 | Get-AdfsAuthenticationProvider | format-list * | Out-file "Get-AdfsAuthenticationProvider.txt" 1842 | Get-AdfsAuthenticationProviderWebContent | format-list * | Out-file "Get-AdfsAuthenticationProviderWebContent.txt" 1843 | Get-AdfsClient | format-list * | Out-file "Get-AdfsClient.txt" 1844 | Get-AdfsGlobalAuthenticationPolicy | format-list * | Out-file "Get-AdfsGlobalAuthenticationPolicy.txt" 1845 | Get-AdfsGlobalWebContent | format-list * | Out-file "Get-AdfsGlobalWebContent.txt" 1846 | Get-AdfsNonClaimsAwareRelyingPartyTrust | format-list * | Out-file "Get-AdfsNonClaimsAwareRelyingPartyTrust.txt" 1847 | Get-AdfsRelyingPartyWebContent | format-list * | Out-file "Get-AdfsRelyingPartyWebContent.txt" 1848 | Get-AdfsWebApplicationProxyRelyingPartyTrust | format-list * | Out-file "Get-AdfsWebApplicationProxyRelyingPartyTrust.txt" 1849 | Get-AdfsWebConfig | format-list * | Out-file "Get-AdfsWebConfig.txt" 1850 | Get-AdfsWebTheme | format-list * | Out-file "Get-AdfsWebTheme.txt" 1851 | Get-AdfsRelyingPartyWebTheme | format-list * | Out-file "Get-AdfsRelyingPartyWebTheme.txt" 1852 | } 1853 | copy-item -path "$env:windir\ADFS\Microsoft.IdentityServer.ServiceHost.Exe.Config" -Destination $TraceDir 1854 | Get-ADFSAzureMfaAdapterconfig |format-list | Out-file "Get-ADFSAzureMfaAdapterconfig.txt" 1855 | 1856 | ##comming soon: WHFB Cert Trust Informations 1857 | 1858 | if ($WinVer -ge [Version]"10.0.17763") { #ADFS command specific to ADFS 2019+ 1859 | if((Get-AdfsSyncProperties).Role -eq 'PrimaryComputer') { 1860 | Get-AdfsDirectoryProperties | format-list * | Out-file "Get-AdfsDirectoryProperties.txt" 1861 | } 1862 | } 1863 | 1864 | } 1865 | 1866 | if ($WinVer -eq [Version]"6.3.9600") { # ADFS commands specific to ADFS 2012 R2/consolidate this in next release 1867 | Get-AdfsAdditionalAuthenticationRule | format-list * | Out-file "Get-AdfsAdditionalAuthenticationRule.txt" 1868 | Get-AdfsAuthenticationProvider | format-list * | Out-file "Get-AdfsAuthenticationProvider.txt" 1869 | Get-AdfsAuthenticationProviderWebContent | format-list * | Out-file "Get-AdfsAuthenticationProviderWebContent.txt" 1870 | Get-AdfsClient | format-list * | Out-file "Get-AdfsClient.txt" 1871 | Get-AdfsGlobalAuthenticationPolicy | format-list * | Out-file "Get-AdfsGlobalAuthenticationPolicy.txt" 1872 | Get-AdfsGlobalWebContent | format-list * | Out-file "Get-AdfsGlobalWebContent.txt" 1873 | Get-AdfsNonClaimsAwareRelyingPartyTrust | format-list * | Out-file "Get-AdfsNonClaimsAwareRelyingPartyTrust.txt" 1874 | Get-AdfsRelyingPartyWebContent | format-list * | Out-file "Get-AdfsRelyingPartyWebContent.txt" 1875 | Get-AdfsWebApplicationProxyRelyingPartyTrust | format-list * | Out-file "Get-AdfsWebApplicationProxyRelyingPartyTrust.txt" 1876 | Get-AdfsWebConfig | format-list * | Out-file "Get-AdfsWebConfig.txt" 1877 | Get-AdfsWebTheme | format-list * | Out-file "Get-AdfsWebTheme.txt" 1878 | copy-item -path "$env:windir\ADFS\Microsoft.IdentityServer.ServiceHost.Exe.Config" -Destination $TraceDir 1879 | } 1880 | elseif ($WinVer -eq [Version]"6.2.9200") { # No specific cmdlets for this version 1881 | } 1882 | } 1883 | Pop-Location 1884 | } 1885 | 1886 | Function EndOfCollection { 1887 | $date = get-date -Format yyyy-dd-MM_hh-mm 1888 | $computername = (Get-Childitem env:computername).value 1889 | $zip = $computername + "_ADFS_traces_"+$date 1890 | $datafile = "$(Join-Path -Path $path -ChildPath $zip).zip" 1891 | Stop-Transcript |Out-Null 1892 | Write-host "Creating Archive File" -ForegroundColor Green 1893 | 1894 | [System.IO.Compression.ZipFile]::CreateFromDirectory($TraceDir, $datafile) 1895 | 1896 | Write-host "Archive File created in $datafile" -ForegroundColor Green 1897 | 1898 | # Cleanup the Temporary Folder (if error retain the temp files) 1899 | if(Test-Path -Path $Path) { 1900 | Write-host "Removing Temporary Files" -ForegroundColor Green 1901 | Remove-Item -Path $TraceDir -Force -Recurse | Out-Null 1902 | } 1903 | else { 1904 | Write-host "The Archive could not be created. Keeping Temporary Folder $TraceDir" -ForegroundColor Yellow 1905 | New-Item -ItemType directory -Path $Path -Force | Out-Null 1906 | } 1907 | } 1908 | 1909 | Function GetDRSConfig { 1910 | if ((-Not $IsProxy) -And ($WinVer -gt [Version]"6.2.9200")) { 1911 | Push-Location $TraceDir 1912 | Get-AdfsDeviceRegistrationUpnSuffix | format-list * | Out-file "Get-AdfsDeviceRegistrationUpnSuffix.txt" 1913 | Try { $drs= Get-AdfsDeviceRegistration; $drs| Out-file "Get-AdfsDeviceRegistration.txt" } Catch { $_.Exception.Message | Out-file "Get-AdfsDeviceRegistration.txt" } 1914 | 1915 | $dse = (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+(Get-WmiObject -Class Win32_ComputerSystem).Domain+"/RootDSE")) 1916 | $conn= $dse.dnsHostName 1917 | $basednq = "CN=DeviceRegistrationService,CN=Device Registration Services,CN=Device Registration Configuration,CN=Services," +$dse.configurationNamingContext 1918 | $filter= "(objectClass=*)" 1919 | $re= LDAPQuery -filter $filter -att $att -conn $conn -basedn $basednq 1920 | if($re.GetType().Name -eq 'SearchResponse') { 1921 | $DScloudissuerpubliccert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 1922 | $DSissuerpubliccert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 1923 | try{$DScloudissuerpubliccert.Import($re.Entries.Attributes.'msds-cloudissuerpubliccertificates'.GetValues('byte[]')[0])}catch{} 1924 | try{$DSissuerpubliccert.Import($re.Entries.Attributes.'msds-issuerpubliccertificates'.GetValues('byte[]')[0]) }catch{} 1925 | 1926 | "DRS Cloud Issuer Certificate`nThumbprint:"+ $DScloudissuerpubliccert.Thumbprint + "`nIssuer:" +$DScloudissuerpubliccert.Issuer |Out-File Get-AdfsDeviceRegistration.txt -Append 1927 | "`nDRS Onprem Issuer Certificate`nThumbprint:"+ $DSissuerpubliccert.Thumbprint + "`nIssuer:" +$DSissuerpubliccert.Issuer |Out-File Get-AdfsDeviceRegistration.txt -Append 1928 | } 1929 | else { "DRS Service Object search failed: "+$re.Message |Out-File Get-AdfsDeviceRegistration.txt -Append } 1930 | 1931 | pop-location 1932 | } 1933 | } 1934 | 1935 | function netfxversion { 1936 | $fx=[PSCustomObject]@{}; 1937 | $fx| Add-Member -MemberType NoteProperty -Name 'Release' -Value ((Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full").Release) 1938 | foreach ($fxver in ($fxversions.GetEnumerator() | Sort-Object -Property Value )) { 1939 | if (($fx.Release -eq $fxver.Value) -ne 0) { 1940 | $fx | Add-Member -MemberType NoteProperty -Name 'Version' -Value ($fxver.Key.ToString()) 1941 | } 1942 | } 1943 | 1944 | if ($fx.Release -ilt [int]394802) { 1945 | $fx | Add-Member -MemberType NoteProperty -Name 'Lifecyclestate' -Value 'no longer supported. Please Update to at minimum .Net 4.6.2' 1946 | } 1947 | else { $fx | Add-Member -MemberType NoteProperty -Name 'Lifecyclestate' -Value 'supported' } 1948 | return $fx 1949 | } 1950 | 1951 | function VerifyNetFX { 1952 | $nfx = [PSCustomObject]@{} 1953 | $cSP = [Net.ServicePointManager]::SecurityProtocol 1954 | $SUSC= switch ((get-itemproperty -PATH "HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319").SchUseStrongCrypto) 1955 | { $null {"not configured"} 0 {" explicitly disabled by registry value (0)"} 1 {"explicitly enabled by registry"} } 1956 | $SDTV= switch ((get-itemproperty -PATH "HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319").SystemDefaultTlsVersions) 1957 | { $null {"not configured"} 0 {" explicitly disabled by registry value (0)"} 1 {"explicitly enabled by registry"} } 1958 | 1959 | $fxr = netfxversion 1960 | 1961 | $nfx | Add-Member -MemberType NoteProperty -Name '.Net-Release' -Value ([String]::Format([CultureInfo]::InvariantCulture, "{0} {1} is {2}.", $fxr.Release,$fxr.Version,$fxr.Lifecyclestate)) 1962 | if (($cSP -split ', ' ) -contains 'TLS12' -or ($cSP -split ', ' ) -contains 'SystemDefault') { 1963 | $nfx | Add-Member -MemberType NoteProperty -Name 'ServicePoint' -Value ("SSL/TLS Protocols available: " + $cSP) 1964 | $nfx | Add-Member -MemberType NoteProperty -Name 'Information' -Value ("The Script detected that StrongCrypto is enabled`neither by default (2019 or higher) or by Registry") 1965 | $nfx | Add-Member -MemberType NoteProperty -Name 'SchUseStrongCrypto' -Value $SUSC 1966 | $nfx | Add-Member -MemberType NoteProperty -Name 'SystemDefaultTlsVersions' -Value $SDTV 1967 | } 1968 | else { 1969 | $nfx | Add-Member -MemberType NoteProperty -Name 'ServicePoint' -Value ("SSL/TLS Protocols available: " + $cSP) 1970 | $nfx | Add-Member -MemberType NoteProperty -Name 'Critical' -Value ("Current Configuration implies TLS1.2 is NOT enabled for .Net Framework`n") 1971 | $nfx | Add-Member -MemberType NoteProperty -Name 'SchUseStrongCrypto' -Value $SUSC 1972 | $nfx | Add-Member -MemberType NoteProperty -Name 'SystemDefaultTlsVersions' -Value $SDTV 1973 | } 1974 | return $nfx 1975 | } 1976 | 1977 | function Get-NetframeworkInstalledUpdates { 1978 | $updates = New-Object -ComObject "Microsoft.Update.Session" 1979 | $searcher = $updates.CreateUpdateSearcher() 1980 | $historyCount = $searcher.GetTotalHistoryCount() 1981 | $updateHistory = $searcher.QueryHistory(0,$historyCount ) 1982 | 1983 | # This will return a list of all updates installed over the existence of this system including Defender, Malicious Software Removal Tool etc.. 1984 | # We only care about Framework Cumulative Updates here (the last 5x) that got successfully installed 1985 | try { 1986 | $updatelist = ($updateHistory | Where-Object { ($_.Title.Contains('Cumulative Update for .NET Framework')) -and $_.ResultCode -eq 2})[0..4] | 1987 | Select-Object -Property @{Name='Installation time'; Expression={$_.Date}}, 1988 | @{Name='Title (KB)'; Expression={$_.Title}}, 1989 | @{Name='KB Number'; Expression={ 1990 | [regex]::replace($_.Title, '.*\(KB(\d+)\).*', 'KB$1') 1991 | } }, 1992 | @{Name='Support Url'; Expression={ 1993 | [regex]::replace($_.Title, '.*\(KB(\d+)\).*', 'https://support.microsoft.com/help/$1') 1994 | } }| 1995 | Sort-Object -Property 'Title (KB)' -Unique | 1996 | Sort-Object -Property 'Installation time' -Descending 1997 | } catch {} 1998 | 1999 | if ($null -eq $updatelist) { 2000 | $htmlTable = "

Windows Update history returned no results.
.NET Framework Updates may not have been installed to date.

" 2001 | 2002 | } else { 2003 | $htmlTable = $updatelist | convertto-html -Property 'Installation time','KB Number', 'Title (KB)', 'Support Url' -Fragment 2004 | } 2005 | return $htmlTable 2006 | } 2007 | 2008 | function Get-InstalledWindowsUpdates { 2009 | #we use get-hotfix to get the classic output of installed updates it will not return optional updates though 2010 | $htmlTable = get-hotfix | 2011 | Sort-Object -Descending InstalledOn | 2012 | select-object @{Name='Support Url'; Expression={$_.Caption}}, 2013 | @{Name='Update type'; Expression={$_.Description}}, 2014 | @{Name='KB Number'; Expression={$_.HotFixId}}, 2015 | @{Name='Installation time'; Expression={$_.InstalledOn}}, 2016 | InstalledBy | 2017 | convertto-html -Property 'Installation time','Update type','KB Number',InstalledBy,'Support Url' -Fragment 2018 | 2019 | return $htmlTable 2020 | } 2021 | 2022 | function Get-WindowsUpdateHTMLReport { 2023 | $htmlTemplate = @" 2024 | 2025 | 2026 | 2072 | 2073 | 2074 |

Hotfix Information for: $env:COMPUTERNAME

2075 | $(Get-InstalledWindowsUpdates) 2076 |

.Net Framework Cumulative Updates - History (last 5)

2077 | $(Get-NetframeworkInstalledUpdates) 2078 | 2079 | 2080 | "@ 2081 | 2082 | return $htmlTemplate 2083 | } 2084 | #endregion 2085 | ########################################################################## 2086 | #region Execution 2087 | 2088 | if (IsAdminAccount){ 2089 | Write-host "Script is executed as Administrator. Resuming execution" -ForegroundColor Green 2090 | 2091 | if ([string]::IsNullOrEmpty($Path)) { 2092 | $RunProp = RunDialog 2093 | $Path = $RunProp.Path.ToString() 2094 | $TraceEnabled = $RunProp.TraceEnabled 2095 | $NetTraceEnabled = $RunProp.NetTraceEnabled 2096 | $ConfigOnly = $RunProp.ConfigOnly 2097 | $PerfCounter = $RunProp.PerfCounter 2098 | $LdapTraceEnabled= $RunProp.LdapTraceEnabled 2099 | $WAPTraceEnabled= $RunProp.WAPTraceEnabled 2100 | } 2101 | elseif (![string]::IsNullOrEmpty($Path)) { 2102 | if($Tracing.IsPresent -eq $false){ $TraceEnabled=$false;$NetTraceEnabled=$false;$PerfCounter=$false;$LdapTraceEnabled=$false;$ConfigOnly=$true;$WAPTraceEnabled=$false } 2103 | else { 2104 | $TraceEnabled=$true; 2105 | $ConfigOnly=$false; 2106 | $LdapTraceEnabled=$false 2107 | $WAPTraceEnabled=$false 2108 | $PerfCounter=$false 2109 | if($NetworkTracing.IsPresent -eq $true){ $NetTraceEnabled=$true } else { $NetTraceEnabled=$false } 2110 | if($PerfTracing.IsPresent -eq $true) { $PerfCounter=$true } 2111 | if(($LDAPTracing.IsPresent -eq $true) -and (!$IsProxy)) { $LdapTraceEnabled=$true } 2112 | if(($WAPTracing.IsPresent -eq $true) -and ($IsProxy)) { $WAPTraceEnabled=$true } 2113 | } 2114 | } 2115 | 2116 | if(Test-Path -Path $Path) { Write-host "Your folder: $Path already exists. Starting Data Collection..." -ForegroundColor DarkCyan } 2117 | else { 2118 | Write-host "Your Logfolder: $Path does not exist. Creating Folder" -ForegroundColor DarkCyan 2119 | New-Item -ItemType directory -Path $Path -Force | Out-Null 2120 | } 2121 | $FEL=$Global:FormatEnumerationLimit ##secure current EnumLimit.Script should revert to this value at the end of execution 2122 | $Global:FormatEnumerationLimit=-1 2123 | 2124 | $TraceDir = $Path +"\temporary" 2125 | # Save execution output to file 2126 | Write-host "Creating Temporary Folder in $path" -ForegroundColor DarkCyan 2127 | New-Item -ItemType directory -Path $TraceDir -Force | Out-Null 2128 | if($PSVersionTable.PSVersion -le [Version]'4.0') { Start-Transcript -Path "$TraceDir\transscript_output.txt" -Append |out-null} else { Start-Transcript -Path "$TraceDir\transscript_output.txt" -Append -IncludeInvocationHeader |out-null} 2129 | Write-Host "Debug logs will be saved in: " $Path -ForegroundColor DarkCyan 2130 | Write-Host "Options selected: TracingEnabled:"$TraceEnabled "NetworkTrace:" $NetTraceEnabled " ConfigOnly:" $ConfigOnly " PerfCounter:" $PerfCounter " LDAPTrace:" $LdapTraceEnabled "WAPTrace:" $WAPTraceEnabled -ForegroundColor DarkCyan 2131 | Write-Progress -Activity "Preparation" -Status 'Setup Data Directory' -percentcomplete 5 2132 | 2133 | if ($TraceEnabled) { 2134 | $MessageTitle = "Initialization completed`n" 2135 | $MessageIse = "Data Collection is ready to start.`nPrepare other computers to start collecting data.`n`nWhen ready, Click OK to start the collection...`n" 2136 | $MessageC = "`nData Collection is ready to start.`nPrepare other computers to start collecting data.`n`nWhen ready, press CTRL+Y to start the collection...`n" 2137 | Pause $MessageIse $MessageTitle $MessageC 2138 | } 2139 | 2140 | Write-Host "Tracing is starting. Current UTC time: $([DateTime]::UtcNow)" -ForegroundColor Cyan 2141 | Write-Host "Timezone: $([System.TimeZoneInfo]::Local.StandardName)" -ForegroundColor DarkCyan 2142 | 2143 | Write-Progress -Activity "Gathering Configuration Data" -Status 'Getting ADFS Configuration' -percentcomplete 7 2144 | GetADFSConfig 2145 | GetDRSConfig 2146 | Clear-DnsClientCache 2147 | 2148 | 2149 | Write-Progress -Activity "Enable Logging" -Status 'Eventlogs' -percentcomplete 15 2150 | $starttime = (get-date) 2151 | 2152 | Write-host "Configuring Event Logging" -ForegroundColor DarkCyan 2153 | if ($IsProxy) { EnableDebugEvents $WAPDebugEvents } 2154 | else { EnableDebugEvents $ADFSDebugEvents } 2155 | 2156 | Write-Progress -Activity "Enable Logging" -Status 'Netlogon Debug Logging' -percentcomplete 30 2157 | EnableNetlogonDebug 2158 | 2159 | Write-Progress -Activity "Enable Logging" -Status 'Additional ETL Logging' -percentcomplete 40 2160 | LogManStart 2161 | EnableNetworkTrace 2162 | EnablePerfCounter 2163 | EnableLDAPTrace 2164 | EnableWAPTrace 2165 | 2166 | if($TraceEnabled) { 2167 | Write-Progress -Activity "Ready for Repro" -Status 'Waiting for Repro' -percentcomplete 50 2168 | $MessageTitle = "Data Collection Running" 2169 | $MessageIse = "Data Collection is currently running`nProceed reproducing the problem now or`n`nPress OK to stop the collection...`n" 2170 | $MessageC = "Data Collection is currently running`nProceed reproducing the problem now or `n`nPress CTRL+Y to stop the collection...`n" 2171 | Pause $MessageIse $MessageTitle $MessageC 2172 | } 2173 | 2174 | Write-Progress -Activity "Collecting" -Status 'Stop Event logging' -percentcomplete 55 2175 | if ($IsProxy) { DisableDebugEvents $WAPDebugEvents } 2176 | else { DisableDebugEvents $ADFSDebugEvents } 2177 | 2178 | Write-Progress -Activity "Collecting" -Status 'Stop additional logs' -percentcomplete 65 2179 | LogManStop 2180 | DisableNetworkTrace 2181 | DisablePerfCounter 2182 | DisableNetlogonDebug 2183 | DisableLDAPTrace 2184 | DisableWAPTrace 2185 | 2186 | Write-Progress -Activity "Collecting" -Status 'Getting otherlogs' -percentcomplete 70 2187 | GatherTheRest 2188 | 2189 | Write-Host "Tracing has completed. Current UTC time: $([DateTime]::UtcNow)" -ForegroundColor Cyan 2190 | Write-Progress -Activity "Collecting" -Status 'Exporting Eventlogs' -percentcomplete 85 2191 | [int]$endtimeinmsec= (New-TimeSpan -start $starttime -end (get-date).AddMinutes(5)).TotalMilliseconds 2192 | 2193 | if ($IsProxy) { ExportEventLogs $WAPExportEvents $endtimeinmsec } 2194 | else { ExportEventLogs $ADFSExportEvents $endtimeinmsec } 2195 | $Global:FormatEnumerationLimit=$FEL 2196 | Write-Progress -Activity "Saving" -Status 'Compressing Files - This may take some moments to complete' -percentcomplete 95 2197 | Write-host "Almost done. We are compressing all Files. Please wait" -ForegroundColor Green 2198 | EndOfCollection 2199 | 2200 | } 2201 | else { 2202 | Write-Host "You do not have Administrator rights!`nPlease re-run this script as an Administrator!" -ForegroundColor Red 2203 | Break 2204 | } 2205 | #endregion 2206 | --------------------------------------------------------------------------------