Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье описывается проблема, в которой функции службы федерации Active Directory (AD FS) (AD FS), такие как проверка подлинности устройств и обнаружение OAuth, не работают.
Симптомы
Вы можете испытать любой из следующих симптомов:
Зарегистрированные в AD FS конечные точки теряются периодически.
Идентификатор события 180 регистрируется каждые пять минут в журнале событий AD FS/Admin, как показано ниже.
Log Name: AD FS/Admin Source: AD FS Event ID: 180 Task Category: None Level: Error Keywords: AD FS Description: An error occurred while upgrading FarmBehaviorLevel 'Max' from Minor Version '0' to Minor Version '3'. Exception details: Object reference not set to an instance of an object.
Выполнение командлета
Get-AdfsGlobalAuthenticationPolicy
PowerShell показывает, что методы проверки подлинности клиента не заданы, как показано в нижней строке следующих выходных данных:Get-AdfsGlobalAuthenticationPolicy AdditionalAuthenticationProvider : {AzureMfaAuthentication} DeviceAuthenticationEnabled : True DeviceAuthenticationMethod : All TreatDomainJoinedDevicesAsCompliant : False PrimaryIntranetAuthenticationProvider : {FormsAuthentication, WindowsAuthentication, AzurePrimaryAuthentication, MicrosoftPassportAuthentication} PrimaryExtranetAuthenticationProvider : {FormsAuthentication, AzurePrimaryAuthentication, MicrosoftPassportAuthentication} WindowsIntegratedFallbackEnabled : True ClientAuthenticationMethods : None
Эта проблема возникает в ферме AD FS, к которым применяются следующие условия:
- Один или несколько серверов AD FS (WS2016) Windows Server 2016 (WS2016) были добавлены в установленную ферму серверов AD FS Windows Server 2012 R2 (WS2012R2) с 4041685 базы знаний (или более поздних ежемесячных или предварительных версий).
- Ферма серверов была обновлена до уровня поведения фермы WS2016.
- Обновление базы знаний 4088787 было установлено на ферме WS2016 AD FS.
Причина
Установка обновления 4088787 базы знаний 13 марта 2018 г. на первичном узле в ферме AD FS с 1 (WS2012R2) до 3 (WS2016) может привести к регрессии AD FS и переупорядочению строк в базе данных AD FS. Эта проблема оставляет базу данных в состоянии, в котором некоторые элементы данных имеют повторяющиеся вхождения. Это состояние приводит к сбоям запуска сервера AD FS и другим ошибкам.
Решение
Чтобы устранить эту проблему, выполните следующие действия.
Шаг 1. Проверка проблемы
Определите, можно ли достичь конечной точки openid-configuration, выполнив следующую команду в командной строке PowerShell:
Get-AdfsEndpoint -AddressPath "/adfs/.well-known/openid-configuration"
Если конечная точка не найдена, вы увидите следующие выходные данные, указывающие на наличие проблемы:
Get-AdfsEndpoint : PS0137: No Endpoint found with Address '/adfs/.well-known/openid-configuration'.
At line:1 char:1
+ Get-AdfsEndpoint -AddressPath "/adfs/.well-known/openid-configuration ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-AdfsEndpoint], ArgumentException
+ FullyQualifiedErrorId : PS0137,Microsoft.IdentityServer.Management.Commands.GetEndpointCommand
Шаг 2. Сохраните скрипт, найденный в конце этой статьи, как "KB4088787_Fix.ps1"
Команда AD FS разработала скрипт PowerShell, который можно запустить на сервере AD FS для исправления параметров конфигурации AD FS в базе данных, связанной с 4088787 базы данных.
Этот скрипт найден в конце этой статьи. Сохраните скрипт как "KB4088787_Fix.ps1" и скопируйте его на основной узел в ферме AD FS.
Шаг 3. Резервное копирование данных конфигурации AD FS
Перед запуском скрипта KB4088787_Fix.ps1 важно создать резервную копию конфигурации AD FS. Эта конфигурация AD FS хранится в внутренняя база данных Windows (WID) или в базе данных Microsoft SQL Server.
При использовании WID для хранения конфигурации AD FS можно использовать средство быстрого восстановления ADFS (доступно в Центре загрузки Майкрософт) для резервного копирования данных конфигурации. Если вы используете SQL Server для хранения базы данных конфигурации AD FS, перед запуском скрипта необходимо создать дополнительную резервную копию SQL Server. Состояние базы данных можно восстановить из одной из этих резервных копий, если это необходимо.
Шаг 4. Запуск скрипта KB4088787_Fix.ps1
В проводник щелкните правой кнопкой мыши файл KB4088787_Fix.ps1, который вы только что сохранили на основном узле в ферме AD FS, а затем выберите "Запустить с помощью PowerShell".
Скрипт сначала проверяет, повреждена ли текущая база данных сервера AD FS в описанной проблеме обновления. В этом случае скрипт найдет неисправные свойства и исправит их. Сценарий KB4088787_Fix.ps1 предложит подтвердить изменения базы данных, и при необходимости скрипт перезапустит AD FS.
Примечание.
При каждом запуске скрипта сохраняется копия XML-файла параметров службы. Данные сохраняются в рабочем каталоге в следующем формате имени:
serviceSettingsXml_<yyyy>-<MM>-<dd>-<hh>-<mm>-<ss>.xml
Например, если скрипт запущен 14 апреля 2021 г. в 10:14:53, он сохраняется следующим образом:
serviceSettingsXml_2021-04-14-10-14-53.xml
После завершения скрипта и перезапуска AD FS все ошибки проверки подлинности устройства и конечных точек должны быть исправлены.
Дополнительная информация
Если применить исправление скрипта и перезапустить систему не исправляет проблему, перейдите на веб-сайт служба поддержки Майкрософт.
Сценарий PowerShell: KB4088787_Fix.ps1
#
# Version 2.0.0
#
# Helper function - serializes any DataContract object to an XML string
function Get-DataContractSerializedString()
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true, HelpMessage="Any object serializable with the DataContractSerializer")]
[ValidateNotNull()]
$object
)
$serializer = New-Object System.Runtime.Serialization.DataContractSerializer($object.GetType())
$serializedData = $null
try
{
# No simple write to string option, so we have to write to a memory stream
# then read back the bytes...
$stream = New-Object System.IO.MemoryStream
$writer = New-Object System.Xml.XmlTextWriter($stream,[System.Text.Encoding]::UTF8)
$null = $serializer.WriteObject($writer, $object);
$null = $writer.Flush();
# Read back the text we wrote to the memory stream
$reader = New-Object System.IO.StreamReader($stream,[System.Text.Encoding]::UTF8)
$null = $stream.Seek(0, [System.IO.SeekOrigin]::Begin)
$serializedData = $reader.ReadToEnd()
}
finally
{
if ($reader -ne $null)
{
try
{
$reader.Dispose()
}
catch [System.ObjectDisposedException] { }
}
if ($writer -ne $null)
{
try
{
$writer.Dispose()
}
catch [System.ObjectDisposedException] { }
}
if ($stream -ne $null)
{
try
{
$stream.Dispose()
}
catch [System.ObjectDisposedException] { }
}
}
return $serializedData
}
function Write-XmlIndent
{
param
(
$xmlData
)
$strWriter = New-Object System.IO.StringWriter
$xmlWriter = New-Object System.XMl.XmlTextWriter $strWriter
# Default = None, change Formatting to Indented
$xmlWriter.Formatting = "indented"
# Gets or sets how many IndentChars to write for each level in
# the hierarchy when Formatting is set to Formatting.Indented
$xmlWriter.Indentation = 1
$xmlData.WriteContentTo($xmlWriter)
$xmlWriter.Flush()
$strWriter.Flush()
$strWriter.ToString()
}
function Get-ADFSXMLServiceSettings
{
param
(
$saveData
)
$doc = new-object Xml
$doc.Load("$env:windir\ADFS\Microsoft.IdentityServer.Servicehost.exe.config")
$connString = $doc.configuration.'microsoft.identityServer.service'.policystore.connectionString
$cli = new-object System.Data.SqlClient.SqlConnection
$cli.ConnectionString = $connString
$cli.Open()
try
{
$cmd = new-object System.Data.SqlClient.SqlCommand
$cmd.CommandText = "Select ServiceSettingsData from [IdentityServerPolicy].[ServiceSettings]"
$cmd.Connection = $cli
$configString = $cmd.ExecuteScalar()
$configXml = new-object XML
$configXml.LoadXml($configString)
if($saveData)
{
$script:originalPath = "original_serviceSettingsXml_$(get-date -f yyyy-MM-dd-hh-mm-ss).xml"
Write-XmlIndent($configXml) | Out-File $script:originalPath
Write-Host "Original XML saved to: $script:originalPath"
}
write-output $configXml
}
finally
{
$cli.CLose()
}
}
# Gets internal ADFS settings by extracting them Get-AdfsProperties
function Get-AdfsInternalSettings()
{
$settings = Get-AdfsProperties
$settingsType = $settings.GetType()
$propInfo = $settingsType.GetProperty("ServiceSettingsData", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
$internalSettings = $propInfo.GetValue($settings, $null)
return $internalSettings
}
function Set-AdfsInternalSettings()
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true, HelpMessage="Settings object fetched from Get-AdfsInternalSettings")]
[ValidateNotNull()]
$InternalSettings
)
$settingsData = Get-DataContractSerializedString -object $InternalSettings
$doc = new-object Xml
$doc.Load("$env:windir\ADFS\Microsoft.IdentityServer.Servicehost.exe.config")
$connString = $doc.configuration.'microsoft.identityServer.service'.policystore.connectionString
$cli = new-object System.Data.SqlClient.SqlConnection
$cli.ConnectionString = $connString
$cli.Open()
try
{
$cmd = new-object System.Data.SqlClient.SqlCommand
$cmd.CommandText = "update [IdentityServerPolicy].[ServiceSettings] SET ServiceSettingsData=@content,[ServiceSettingsVersion] = [ServiceSettingsVersion] + 1,[LastUpdateTime] = GETDATE()"
$cmd.Parameters.AddWithValue("@content", $settingsData) | out-null
$cmd.Connection = $cli
$rowsAffected = $cmd.ExecuteNonQuery()
# Update service state table for WID sync if required
if ($connString -match "##wid")
{
$cmd = new-object System.Data.SqlClient.SqlCommand
$cmd.CommandText = "UPDATE [IdentityServerPolicy].[ServiceStateSummary] SET [SerialNumber] = [SerialNumber] + 1,[LastUpdateTime] = GETDATE() WHERE ServiceObjectType='ServiceSettings'"
$cmd.Connection = $cli
$widRowsAffected = $cmd.ExecuteNonQuery()
}
}
finally
{
$cli.CLose()
}
}
function Create-EndpointConfiguration()
{
Param
(
[ValidateNotNull()]
$xmlData
)
# EndpointConfiguration
$enabled = Create-BoolFromString($xmlData.Enabled)
$proxy = Create-BoolFromString($xmlData.Proxy)
$canProxy = Create-BoolFromString($xmlData.CanProxy)
$endpointConfig = New-Object -TypeName Microsoft.IdentityServer.PolicyModel.Configuration.EndpointConfiguration -ArgumentList $xmlData.Address, $enabled , ([Microsoft.IdentityServer.PolicyModel.Configuration.EndpointMode] $xmlData.ModeValue), $proxy, $xmlData.Version, $canProxy
return $endpointConfig
}
function Create-ClientSecretSettings()
{
Param
(
[ValidateNotNull()]
$xmlData
)
# ClientSecretSettings
$clientSecretSettings = New-Object -TypeName Microsoft.IdentityServer.PolicyModel.Client.ClientSecretSettings
$clientSecretSettings.ClientSecretRolloverTimeMinutes = $xmlData.ClientSecretRolloverTimeMinutes
$clientSecretSettings.ClientSecretLockoutThreshold = $xmlData.ClientSecretLockoutThreshold;
$clientSecretSettings.SaltedHashAlgorithm = $xmlData.SaltedHashAlgorithm
$clientSecretSettings.SaltSize = $xmlData.SaltSize
$clientSecretSettings.SecretSize = $xmlData.SecretSize
return $clientSecretSettings
}
function Create-IdTokenIssuer()
{
Param
(
[ValidateNotNull()]
$xmlData
)
#IdTokenIssuer
$idTokenIssuerConfig = New-Object -TypeName Microsoft.IdentityServer.PolicyModel.Configuration.IdTokenIssuerConfiguration
$idTokenIssuerConfig.Address = $xmlData.Address
return $idTokenIssuerConfig
}
function Create-CertificateAuthorityConfiguration()
{
Param
(
[ValidateNotNull()]
$xmlData
)
# CertificateAuthorityConfiguration
$certAuthorityConfig = New-Object -TypeName Microsoft.IdentityServer.PolicyModel.Configuration.CertificateAuthorityConfiguration
$certAuthorityConfig.Mode.Value = [Microsoft.IdentityServer.PolicyModel.Configuration.CertificateAuthorityMode] $xmlData.ModeValue
$certAuthorityConfig.GenerationThresholdInMinutes = $xmlData.GenerationThresholdInMinutes
$certAuthorityConfig.PromotionThresholdInMinutes = $xmlData.PromotionThresholdInMinutes
$certAuthorityConfig.CertificateLifetimeInMinutes = $xmlData.CertificateLifetimeInMinutes
$certAuthorityConfig.RolloverIntervalInMinutes = $xmlData.RolloverIntervalInMinutes
$certAuthorityConfig.CriticalThresholdInMinutes = $xmlData.CriticalThresholdInMinutes
# Create Cert reference
if ($xmlData.PrimaryIssuerCertificate.IsEmpty -ne $true)
{
$certReference = New-Object -TypeName Microsoft.IdentityServer.PolicyModel.Configuration.CertificateReference
$certReference.IsChainIncluded = Create-BoolFromString($xmlData.PrimaryIssuerCertificate.IsChainIncluded)
$certReference.IsChainIncludedSpecified = Create-BoolFromString($xmlData.PrimaryIssuerCertificate.IsChainIncludedSpecified)
$certReference.FindValue = $xmlData.PrimaryIssuerCertificate.FindValue
$certReference.RawCertificate = $xmlData.PrimaryIssuerCertificate.RawCertificate
$certReference.EncryptedPfx = $xmlData.PrimaryIssuerCertificate.EncryptedPfx
$certReference.StoreName.Value = [System.Security.Cryptography.X509Certificates.StoreName] $xmlData.PrimaryIssuerCertificate.StoreNameValue
$certReference.StoreLocation.Value = [System.Security.Cryptography.X509Certificates.StoreLocation] $xmlData.PrimaryIssuerCertificate.StoreLocationValue
$certReference.X509FindType.Value = [System.Security.Cryptography.X509Certificates.X509FindType] $xmlData.PrimaryIssuerCertificate.X509FindTypeValue
$certAuthorityConfig.PrimaryIssuerCertificate = $certReference
}
if ($xmlData.QueuedIssuerCertificate.IsEmpty -ne $true){
# Create PromotionCert
$promotionCert = New-Object -TypeName Microsoft.IdentityServer.PolicyModel.Configuration.PromotionCertificate
$promotionCert.ObjectId = $xmlData.QueuedIssuerCertificate.ObjectId
$certAuthorityConfig.QueuedIssuerCertificate = $promotionCert
}
$certAuthorityConfig.CriticalThresholdInMinutes = $xmlData.CriticalThresholdInMinutes
if ($xmlData.CertificateAuthority.IsEmpty -ne $true){
$certAuthorityConfig.CertificateAuthority = $xmlData.CertificateAuthority
}
if ($xmlData.EnrollmentAgentCertificateTemplateName.IsEmpty -ne $true)
{
$certAuthorityConfig.EnrollmentAgentCertificateTemplateName = $xmlData.EnrollmentAgentCertificateTemplateName
}
if ($xmlData.LogonCertificateTemplateName.IsEmpty -ne $true)
{
$certAuthorityConfig.LogonCertificateTemplateName = $xmlData.LogonCertificateTemplateName
}
if ($xmlData.VPNCertificateTemplateName.IsEmpty -ne $true)
{
$certAuthorityConfig.VPNCertificateTemplateName = $xmlData.VPNCertificateTemplateName
}
if ($xmlData.WindowsHelloCertificateTemplateName.IsEmpty -ne $true)
{
$certAuthorityConfig.WindowsHelloCertificateTemplateName = $xmlData.WindowsHelloCertificateTemplateName
}
$certAuthorityConfig.AutoEnrollEnabled = Create-BoolFromString($xmlData.AutoEnrollEnabled)
$certAuthorityConfig.WindowsHelloCertificateProxyEnabled = Create-BoolFromString($xmlData.WindowsHelloCertificateProxyEnabled)
return $certAuthorityConfig
}
function Create-OAuthClientAuthenticationMethods()
{
Param
(
[ValidateNotNull()]
$xmlData
)
# OAuthClientAuthenticationMethods
return 15
}
function Create-BoolFromString()
{
Param
(
[ValidateNotNull()]
$xmlData
)
if ($xmlData -eq $null -or $xmlData.ToLower() -eq "false")
{
return 0
}else{
return 1
}
}
function Get-ObjectFromElement()
{
Param
(
[Parameter(Mandatory = $true, HelpMessage="Settings object fetched from Get-AdfsInternalSettings")]
[ValidateNotNull()]
$xmlData,
[Parameter(Mandatory = $true, HelpMessage="Settings object fetched from Get-AdfsInternalSettings")]
[ValidateNotNull()]
$elementName
)
$endpointConfigs = "DeviceRegistrationEndpoint", "OAuthJwksEndpoint", "OAuthDiscoveryEndpoint", "WebFingerEndpoint", "CertificateAuthorityCrlEndpoint", "UserInfoEndpoint"
# Microsoft.IdentityServer.PolicyModel.Configuration.EndpointConfiguration
$clientSecretSettings = "ClientSecretSettings"
# Microsoft.IdentityServer.PolicyModel.Client.ClientSecretSettings
$oauthClientAuthMethod = "OAuthClientAuthenticationMethods"
# Microsoft.IdentityServer.PolicyModel.Configuration.ClientAuthenticationMethod
$certAuthorityConfig = "CertificateAuthorityConfiguration"
# Microsoft.IdentityServer.PolicyModel.Configuration.CertificateAuthorityConfiguration
$idTokenIssuer = "IdTokenIssuer"
# Microsoft.IdentityServer.PolicyModel.Configuration.IdTokenIssuerConfiguration
$boolObjs = "EnableIdPInitiatedSignonPage", "IgnoreTokenBinding", "EnableOauthLogout"
$basicObjs = "DeviceUsageWindowInSeconds", "MaxLdapUserNameLength", "FarmBehaviorMinorVersion", "PromptLoginFederation", "PromptLoginFallbackAuthenticationType"
if ($endpointConfigs.Contains($elementName))
{
return Create-EndpointConfiguration($xmlData)
}elseif ($clientSecretSettings.Contains($elementName))
{
return Create-ClientSecretSettings($xmlData)
}elseif ($oauthClientAuthMethod.Contains($elementName))
{
return Create-OAuthClientAuthenticationMethods($xmlData)
}elseif ($certAuthorityConfig.Contains($elementName))
{
return Create-CertificateAuthorityConfiguration($xmlData)
}elseif ($idTokenIssuer.Contains($elementName))
{
return Create-IdTokenIssuer($xmlData)
}elseif ($boolObjs.Contains($elementName))
{
return Create-BoolFromString($xmlData)
}else{
return $xmlData
}
}
$role = (Get-AdfsSyncProperties).Role
if($role -ne "PrimaryComputer")
{
Write-Host "This script must execute on the primary node in the AD FS farm. Cannot continue."
exit
}
Add-Type -Path ('C:\\Windows\\ADFS\\Microsoft.IdentityServer.dll')
$script:originalPath = $null
# Get the XML of the Service Settings blob, and look for duplicate elements
$xmlData = Get-ADFSXMLServiceSettings($true)
$dataObj = Get-AdfsInternalSettings
if($dataObj.SecurityTokenService.DeviceRegistrationEndpoint.Length -ne 1)
{
if(($xmlData.ServiceSettingsData.SecurityTokenService | Select-Object "DeviceRegistrationEndpoint").DeviceRegistrationEndpoint.IsEmpty[0] -ne $True)
{
# In this case, the service settings object is showing an issue, but the XML data is not.
# This is possible in the case that the sequence of KB applications has occurred, but no Service Settings write oparation
# has occured.
# To handle this, we will prompt the user to allow us to throttle a Service Setting to force a write operation
# Do a no-op service settings action so that the duplicates are populated in the database (if issue exists)
$message = "Confirmation required"
$question = "To ensure data detection is possible, we need to perform an AD FS Properties operation. `nIf you continue, this script will add 1 minute to the value of 'ProxyTrustTokenLifetime', and then the value of 'ProxyTrustTokenLifetime' will be returned to its original setting. `nIf you wish to make a Set-AdfsProperties operation yourself, answer 'No', and perform the operation yourself. `n`nAre you sure you want to proceed?"
$choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$decision = $Host.UI.PromptForChoice($message, $question, $choices, 1)
if ($decision -eq 0) {
$prop = Get-AdfsProperties
$original = $prop.ProxyTrustTokenLifetime
Set-AdfsProperties -ProxyTrustTokenLifetime ($original + 1)
Set-AdfsProperties -ProxyTrustTokenLifetime $original
# Refresh the XML and data obj
$xmlData = Get-ADFSXMLServiceSettings($false)
$dataObj = Get-AdfsInternalSettings
}else{
Write-Host "We did not perform a service settings operation. Please make some service settings change by performing a Set-AdfsProperties command, and try the script again."
}
}
}
if(($xmlData.ServiceSettingsData.SecurityTokenService | Select-Object "DeviceRegistrationEndpoint").DeviceRegistrationEndpoint.IsEmpty[0] -eq $True)
{
$possibleDuplicateElements = "DeviceRegistrationEndpoint", "DeviceUsageWindowInSeconds", "OAuthClientAuthenticationMethods", "MaxLdapUserNameLength", "EnableIdPInitiatedSignonPage", "ClientSecretSettings", "OAuthDiscoveryEndpoint", "OAuthJwksEndpoint", "IdTokenIssuer", "CertificateAuthorityConfiguration", "WebFingerEndpoint", "CertificateAuthorityCrlEndpoint", "UserInfoEndpoint", "IgnoreTokenBinding", "FarmBehaviorMinorVersion", "EnableOauthLogout", "PromptLoginFederation", "PromptLoginFallbackAuthenticationType"
$existingDups = @()
foreach( $dup in $possibleDuplicateElements )
{
$object = $xmlData.ServiceSettingsData.SecurityTokenService | Select-Object $dup
if( $object.$dup.Count -gt 1 )
{
# We have an element with duplicate values
# Take the last one in the list
$newObj = $object.$dup[$object.$dup.Count - 1]
$savableObj = Get-ObjectFromElement -xmlData $newObj -elementName $dup
$existingDups += $dup
$dataObj.SecurityTokenService.$dup = $savableObj
}
}
# Write the modified Service Settings data object back to the database
if($existingDups.Count -gt 0)
{
$filename = "modified_serviceSettingsXml_$(get-date -f yyyy-MM-dd-hh-mm-ss).xml"
$modifiedData = Get-DataContractSerializedString -object $dataObj
Write-XmlIndent([xml]$modifiedData) | Out-File $filename
Write-Host "Modifed XML saved to: $filename"
$message = "Confirmation required"
$question = "We have located duplicate data in Service Settings, and need to make a modification to the configuration database. `nIf you continue, the values of some elements will be updated in your database. `nIf you wish to see the difference, you can compare the following two files from the working directory: `n`n$script:originalPath `n$filename `n`nAre you sure you want to proceed?"
$choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$decision = $Host.UI.PromptForChoice($message, $question, $choices, 1)
if ($decision -eq 0) {
Write-Host "Performing update to Service Settings"
Set-AdfsInternalSettings -InternalSettings $dataObj
Write-Host "Updated Service Settings`n`n"
Write-Host "The following elements in the service settings data were modified:`n"
foreach($d in $existingDups)
{
Write-Host $d
}
Write-Host "`nAll nodes in this farm should be restarted. This script will attempt to restart the current node, but no other nodes."
Write-Host "Please manually restart all nodes in your AD FS farm.`n`n"
$message = "Confirmation required"
$question = "An AD FS Service restart is required. Proceed with restart?"
$choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$decision2 = $Host.UI.PromptForChoice($message, $question, $choices, 1)
if($decision2 -eq 0){
Write-Host "Stopping AD FS"
net stop adfssrv
Write-Host "Starting AD FS"
net start adfssrv
}else{
Write-Host "You chose not to restart AD FS. Please manually restart AD FS to allow the changes to take effect."
}
} else {
Write-Host "Cancelling"
}
}else{
Write-Host "No Operations Needed"
}
}else
{
Write-Host "No issues detected. Terminating script."
}