Jul 9, 2013 5:29:48 AM
Migrating Relying Party Trusts

There are times when you might find yourself needing to migrate a relying party (RP) from one AD FS implementation to another. Unfortunately, at the moment there do not seem to be existing tools to do this. So, we offer the following rather quick and dirty approach. There might be better ways, but this gets the job done.

First, export all the relying party trusts that need migrating to XML files using the below export-rps.ps1 PowerShell script. The output from the script will be a file with a file name based on the relying party identifier. For example:

  • urn-federation-identifier.example.com
  • https---identifier.example.com

In the following PowerShell script, edit the value $filePathBase as appropriate for your environment.

export-rps.ps1

# Load the ADFS PowerShell snap-in
Add-PSSnapin Microsoft.Adfs.PowerShell

# The directory where the relying parties should be extracted
$filePathBase = "C:\extract-rp\"

$AdfsRelyingPartyTrusts = Get-AdfsRelyingPartyTrust
foreach ($AdfsRelyingPartyTrust in $AdfsRelyingPartyTrusts)
{
  # The identifier is actually an array of identifiers, we will just use the first one
  $rpIdentifier = $AdfsRelyingPartyTrust.Identifier[0]

  # We want a filename for this so we will try to make the identifier safe
  # Replace all of the following characters with a -
  #  : " / \ | ? *
  $fileNameSafeIdentifier = $rpIdentifier `
    -replace '', '-' `
    -replace ':', '-' `
    -replace '"', '-' `
    -replace '/', '-' `
    -replace '\\', '-' `
    -replace '\|', '-' `
    -replace '\?', '-' `
    -replace '\*', '-' 	

  # Create the filename of the XML file we will export
  $filePath = $filePathBase + $fileNameSafeIdentifier + '.xml'

  # Use Export-Clixml to export the object to an XML file
  $AdfsRelyingPartyTrust | Export-Clixml $filePath

}

After generating the export files, copy the XML files to the new AD FS server and import them one at a time as relying party trusts using the below import-an-rp.ps1 PowerShell script.

In the following PowerShell script, edit the value $rpIdentifier to specify which relying party you want to import and the value $filePathBase as appropriate for your environment.

import-an-rp.ps1

# Load the ADFS PowerShell snap-in
Add-PSSnapin Microsoft.Adfs.PowerShell

# location where the extracted XML files can be found
$filePathBase = "C:\extract-rp\"

# Identifier of the Relying Party (RP) we want to import
#$rpIdentifier = "urn:federation:identifier.example.com"
$rpIdentifier = "https://identifier.example.com"

# We want the name we created during extract for this so we will try to make the identifier safe
# Replace all of the following characters with a -
#  : " / \ | ? *
$directoryNameSafeIdentifier = $rpIdentifier `
  -replace '', '-' `
  -replace ':', '-' `
  -replace '"', '-' `
  -replace '/', '-' `
  -replace '\\', '-' `
  -replace '\|', '-' `
  -replace '\?', '-' `
  -replace '\*', '-' 	

$xmlFile =  $filePathBase + $directoryNameSafeIdentifier + ".xml"

if (!(Test-Path -path $xmlFile))
{
  "File not found" + $xmlFile
}
else
{
  $ADFSRelyingPartyTrust = Import-clixml $xmlFile
  $NewADFSRelyingPartyTrust = Add-ADFSRelyingPartyTrust -Identifier $rpIdentifier `
    -Name $ADFSRelyingPartyTrust.Name
  $rpIdentifierUri = $NewADFSRelyingPartyTrust.Identifier

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -AutoUpdateEnabled $ADFSRelyingPartyTrust.AutoUpdateEnabled

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -DelegationAuthorizationRules $ADFSRelyingPartyTrust.DelegationAuthorizationRules

  # note we need to do a ToString to not just get the enum number
  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -EncryptionCertificateRevocationCheck `
    $ADFSRelyingPartyTrust.EncryptionCertificateRevocationCheck.ToString()

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
-IssuanceAuthorizationRules $ADFSRelyingPartyTrust.IssuanceAuthorizationRules

  # note we need to do a ToString to not just get the enum number
  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -SigningCertificateRevocationCheck `
    $ADFSRelyingPartyTrust.SigningCertificateRevocationCheck.ToString()

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -WSFedEndpoint $ADFSRelyingPartyTrust.WSFedEndpoint

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -IssuanceTransformRules $ADFSRelyingPartyTrust.IssuanceTransformRules

  # Note ClaimAccepted vs ClaimsAccepted (plural)
  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -ClaimAccepted $ADFSRelyingPartyTrust.ClaimsAccepted

  ### NOTE this does not get imported
  #$ADFSRelyingPartyTrust.ConflictWithPublishedPolicy

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -EncryptClaims $ADFSRelyingPartyTrust.EncryptClaims

  ### NOTE this does not get imported
  #$ADFSRelyingPartyTrust.Enabled

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -EncryptionCertificate $ADFSRelyingPartyTrust.EncryptionCertificate

  # Identifier is actually an array but you can't add it when
  #   using Set-ADFSRelyingPartyTrust -TargetIdentifier
  #   so we use -TargetRelyingParty instead
  $targetADFSRelyingPartyTrust = Get-ADFSRelyingPartyTrust -Identifier $rpIdentifier
  Set-ADFSRelyingPartyTrust -TargetRelyingParty $targetADFSRelyingPartyTrust `
    -Identifier $ADFSRelyingPartyTrust.Identifier

  # SKIP we don't need to import these
  # $ADFSRelyingPartyTrust.LastMonitoredTime
  # $ADFSRelyingPartyTrust.LastPublishedPolicyCheckSuccessful
  # $ADFSRelyingPartyTrust.LastUpdateTime

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -MetadataUrl $ADFSRelyingPartyTrust.MetadataUrl

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -MonitoringEnabled $ADFSRelyingPartyTrust.MonitoringEnabled

  # Name is already done
  #Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
  #  -Name $ADFSRelyingPartyTrust.Name

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -NotBeforeSkew $ADFSRelyingPartyTrust.NotBeforeSkew

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -Notes "$ADFSRelyingPartyTrust.Notes"

  ### NOTE this does not get imported
  #$ADFSRelyingPartyTrust.OrganizationInfo

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -ImpersonationAuthorizationRules $ADFSRelyingPartyTrust.ImpersonationAuthorizationRules

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -ProtocolProfile $ADFSRelyingPartyTrust.ProtocolProfile

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -RequestSigningCertificate $ADFSRelyingPartyTrust.RequestSigningCertificate

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -EncryptedNameIdRequired $ADFSRelyingPartyTrust.EncryptedNameIdRequired

  # Note RequireSignedSamlRequests vs SignedSamlRequestsRequired,
  #Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
  #  -RequireSignedSamlRequests $ADFSRelyingPartyTrust.SignedSamlRequestsRequired
  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -SignedSamlRequestsRequired $ADFSRelyingPartyTrust.SignedSamlRequestsRequired  

  # Note SamlEndpoint vs SamlEndpoints (plural)
  # The object comes back as a
  #   [Deserialized.Microsoft.IdentityServer.PowerShell.Resources.SamlEndpoint]
  #   so we will reconstitute 

  # create a new empty array
  $newSamlEndPoints = @()
  foreach ($SamlEndpoint in $ADFSRelyingPartyTrust.SamlEndpoints)
  {
    # Is ResponseLocation defined?
    if ($SamlEndpoint.ResponseLocation)
    {
      # ResponseLocation is not null or empty
      $newSamlEndPoint = New-ADFSSamlEndpoint -Binding $SamlEndpoint.Binding `
        -Protocol $SamlEndpoint.Protocol `
        -Uri $SamlEndpoint.Location -Index $SamlEndpoint.Index `
        -IsDefault $SamlEndpoint.IsDefault
    }
    else
    {
      $newSamlEndPoint = New-ADFSSamlEndpoint -Binding $SamlEndpoint.Binding `
        -Protocol $SamlEndpoint.Protocol `
        -Uri $SamlEndpoint.Location -Index $SamlEndpoint.Index `
        -IsDefault $SamlEndpoint.IsDefault `
        -ResponseUri $SamlEndpoint.ResponseLocation
    }
    $newSamlEndPoints += $newSamlEndPoint
  }
  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -SamlEndpoint $newSamlEndPoints

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -SamlResponseSignature $ADFSRelyingPartyTrust.SamlResponseSignature

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -SignatureAlgorithm $ADFSRelyingPartyTrust.SignatureAlgorithm

  Set-ADFSRelyingPartyTrust -TargetIdentifier $rpIdentifier `
    -TokenLifetime $ADFSRelyingPartyTrust.TokenLifetime

}

# For comparison testing you can uncomment these lines
#   to export your new import as a ___.XML.new file
# $targetADFSRelyingPartyTrust = Get-ADFSRelyingPartyTrust -Identifier $rpIdentifier
# $filePath = $xmlFile + ".new"
# $AdfsRelyingPartyTrust | Export-Clixml $filePath

Many thanks to Doug McDorman for allowing us to post his script for general use.