Octopus.Script exported 2018-06-07 by sbrickey belongs to ‘XML’ category.
Updates the web/app config files’ WIF TrustedIssuer thumbprints based on a realtime metadata request.
Changes are made to the following section: /configuration/system.identityModel/identityConfiguration/issuerNameRegistry/trustedIssuers
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
web config file path
FilePath =
null
Trusted Issuer Name
TrustedIssuerName = https://adfs/adfs/services/trust
Metadata Uri
MetadataUri = https://adfs/FederationMetadata/2007-06/FederationMetadata.xml
null
Script body
Steps based on this template will execute the following PowerShell script.
$FilePath = "#{FilePath}"
$TrustedIssuerName = "#{TrustedIssuerName}"
$MetadataUri = "#{MetadataUri}"
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq")
# because Octo calls powershell steps in a stupid manor...
$charGT = [System.Text.Encoding]::ASCII.GetString( @(62) )
function Get-Thumbprints($MetadataUri) {
$MetadataTxt = Invoke-WebRequest -Uri $MetadataUri
$MetadataXml = [xml]($MetadataTxt.Content)
$outval = @()
# new certs
$MetadataXml.EntityDescriptor.IDPSSODescriptor.KeyDescriptor | ? { $_.use -eq "signing" } | % {
$Cert_Bytes = [System.Convert]::FromBase64String( $_.KeyInfo.X509Data.X509Certificate )
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2( , $Cert_Bytes ) # powershell is stupid about arrays
Write-Host "Found certificate for [$($_.use)] : [$($cert.NotBefore.ToString("yyyyMMdd")) - $($cert.NotAfter.ToString("yyyyMMdd"))] : Thumbprint [$($Cert.Thumbprint)] for Subject [$($Cert.Subject)]"
$outval += $Cert.Thumbprint
}
return $outval
}
function Get-TextIndex([string]$text, [int]$LineNumber = 0, [int]$LinePosition = 0) {
# Ported from : https://github.com/futurist/src-location/blob/master/index.js function locationToIndex
# NOTE: diff from source to address bug. Test-GetTextIndex validates the changes.
$strLF = [char]10 # \n
$strCR = [char]13 # \r
$idx = -1 # text index
$lc = 1 # Line Count
for($i = 0; $lc -lt $LineNumber -and $i -lt $text.Length; $i++) {
$idx++
$c = $text[$i] # cur char
if ($c -eq $strLF -or $c -eq $strCR) {
$lc++
if ($c -eq $strCR -and $text[$i + 1] -eq $strLF) { # DOS CRLF
$i++
$idx++
}
}
}
return $idx + $LinePosition
}
function Replace-TrustedIssuerThumbprints($FilePath, $TrustedIssuerName, $Thumbprints) {
# Load the file twice - once as text for manipulation, once as XML for xpath and positions
$fileText = [System.IO.File]::ReadAllText($FilePath)
$fileXml = [System.Xml.Linq.XDocument]::Load($FilePath, [System.Xml.Linq.LoadOptions]::SetLineInfo -bor [System.Xml.Linq.LoadOptions]::PreserveWhitespace )
$IdpsXml = $fileXml.Descendants("configuration")[0].Descendants("system.identityModel")[0].Descendants("identityConfiguration")[0].Descendants("issuerNameRegistry")[0].Descendants("trustedIssuers")[0].Descendants("add")
# Figure out which elements to manipulate... First delete from the bottom up, then replace the top-most element
$IdpMatches = $IdpsXml | ? { $_.Attribute("name").Value -eq $TrustedIssuerName } | Sort-Object -Property LineNumber, LinePosition -Descending
$IdpsToDelete = $IdpMatches | Select-Object -First ($IdpMatches.Count - 1)
$IdpsToReplace = $IdpMatches | Select-Object -Last 1
# Delete from the bottom up, so that the LineNumber/LinePosition remain valid during the manipulation
foreach ($IdP in $IdpsToDelete) {
Write-Host ( "DEL [{0}:{1}] {2}" -f $IdP.LineNumber, $IdP.LinePosition, $IdP.ToString() )
$fileIdxOpen = Get-TextIndex -text $fileText -LineNumber $IdP.LineNumber -LinePosition ( $IdP.LinePosition - 1 )
$fileIdxClose = $fileText.IndexOf($charGT, $fileIdxOpen) + 1 # add one to include the closing >
$fileSubstr = $fileText.Substring($fileIdxOpen, $fileIdxClose - $fileIdxOpen)
Write-Host ( " [$fileIdxOpen .. $fileIdxClose] : $fileSubstr" )
$fileIdxPrior = $fileText.LastIndexOf($charGT, $fileIdxOpen) + 1
$fileText = $fileText.Remove($fileIdxPrior, $fileIdxClose - $fileIdxPrior)
}
# Replace the top-most element with each thumbprint
foreach ($IdP in $IdpsToReplace) {
Write-Host ( "FIX [{0}:{1}] {2}" -f $IdP.LineNumber, $IdP.LinePosition, $IdP.ToString() )
$fileIdxOpen = Get-TextIndex -text $fileText -LineNumber $IdP.LineNumber -LinePosition ( $IdP.LinePosition - 1 )
$fileIdxClose = $fileText.IndexOf($charGT, $fileIdxOpen) + 1 # add one to include the closing >
$fileSubstr = $fileText.Substring($fileIdxOpen, $fileIdxClose - $fileIdxOpen)
Write-Host ( " [$fileIdxOpen .. $fileIdxClose] : $fileSubstr" )
$fileIdxPrior = $fileText.LastIndexOf($charGT, $fileIdxOpen) + 1
$ElementDelim = $fileText.Substring($fileIdxPrior, $fileIdxOpen - $fileIdxPrior)
Write-Host ( " -[{0} .. {1}]" -f $fileIdxPrior, $fileIdxClose )
$fileText = $fileText.Remove($fileIdxPrior, $fileIdxClose - $fileIdxPrior)
foreach ($Thumbprint in $Thumbprints) {
$newAttribs = [System.Xml.Linq.XAttribute[]]@(
( New-Object System.Xml.Linq.XAttribute("thumbprint", $Thumbprint ) ),
( New-Object System.Xml.Linq.XAttribute("name" , $TrustedIssuerName) )
)
$newValue = ( New-Object System.Xml.Linq.XElement("add", $newAttribs) ).ToString()
$fileText = $fileText.Insert($fileIdxPrior, $ElementDelim + $newValue)
}
}
return $fileText
}
$ThumbPrints = Get-Thumbprints -MetadataUri $MetadataUri
$fileContent = Replace-TrustedIssuerThumbprints -FilePath $FilePath -TrustedIssuerName $TrustedIssuerName -Thumbprints $ThumbPrints
[System.IO.File]::WriteAllText($FilePath, $fileContent)
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "77331575-0628-455d-b484-cfd4703e2081",
"Name": "Load WIF Issuer Thumbprint(s)",
"Description": "Updates the web/app config files' WIF TrustedIssuer thumbprints based on a realtime metadata request.\n\nChanges are made to the following section:\n/configuration/system.identityModel/identityConfiguration/issuerNameRegistry/trustedIssuers",
"Version": 5,
"ExportedAt": "2018-06-07T19:59:41.925Z",
"ActionType": "Octopus.Script",
"Author": "sbrickey",
"Parameters": [
{
"Id": "9fa8d0c0-53a3-4d38-b183-bab04037869e",
"Name": "FilePath",
"Label": "web config file path",
"HelpText": null,
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "e97dcceb-fbf1-41f2-9829-e32c6322fe58",
"Name": "TrustedIssuerName",
"Label": "Trusted Issuer Name",
"HelpText": "",
"DefaultValue": "https://adfs/adfs/services/trust",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "9670671a-5d7e-4232-9d45-5fc47d005167",
"Name": "MetadataUri",
"Label": "Metadata Uri",
"HelpText": null,
"DefaultValue": "https://adfs/FederationMetadata/2007-06/FederationMetadata.xml",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
}
],
"Properties": {
"Octopus.Action.Script.Syntax": "PowerShell",
"Octopus.Action.Script.ScriptSource": "Inline",
"Octopus.Action.RunOnServer": "false",
"Octopus.Action.Script.ScriptBody": "$FilePath = \"#{FilePath}\"\n$TrustedIssuerName = \"#{TrustedIssuerName}\"\n$MetadataUri = \"#{MetadataUri}\"\n\n\n[void][System.Reflection.Assembly]::LoadWithPartialName(\"System.Xml.Linq\")\n\n# because Octo calls powershell steps in a stupid manor...\n$charGT = [System.Text.Encoding]::ASCII.GetString( @(62) )\n\nfunction Get-Thumbprints($MetadataUri) {\n $MetadataTxt = Invoke-WebRequest -Uri $MetadataUri\n $MetadataXml = [xml]($MetadataTxt.Content)\n \n $outval = @()\n # new certs\n \n $MetadataXml.EntityDescriptor.IDPSSODescriptor.KeyDescriptor | ? { $_.use -eq \"signing\" } | % {\n $Cert_Bytes = [System.Convert]::FromBase64String( $_.KeyInfo.X509Data.X509Certificate )\n $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2( , $Cert_Bytes ) # powershell is stupid about arrays\n Write-Host \"Found certificate for [$($_.use)] : [$($cert.NotBefore.ToString(\"yyyyMMdd\")) - $($cert.NotAfter.ToString(\"yyyyMMdd\"))] : Thumbprint [$($Cert.Thumbprint)] for Subject [$($Cert.Subject)]\"\n $outval += $Cert.Thumbprint\n }\n return $outval\n}\n\nfunction Get-TextIndex([string]$text, [int]$LineNumber = 0, [int]$LinePosition = 0) {\n # Ported from : https://github.com/futurist/src-location/blob/master/index.js function locationToIndex\n # NOTE: diff from source to address bug. Test-GetTextIndex validates the changes.\n $strLF = [char]10 # \\n\n $strCR = [char]13 # \\r\n $idx = -1 # text index\n $lc = 1 # Line Count\n for($i = 0; $lc -lt $LineNumber -and $i -lt $text.Length; $i++) {\n $idx++\n $c = $text[$i] # cur char\n if ($c -eq $strLF -or $c -eq $strCR) {\n $lc++\n if ($c -eq $strCR -and $text[$i + 1] -eq $strLF) { # DOS CRLF\n $i++\n $idx++\n }\n }\n }\n return $idx + $LinePosition\n}\n\nfunction Replace-TrustedIssuerThumbprints($FilePath, $TrustedIssuerName, $Thumbprints) {\n # Load the file twice - once as text for manipulation, once as XML for xpath and positions\n $fileText = [System.IO.File]::ReadAllText($FilePath)\n $fileXml = [System.Xml.Linq.XDocument]::Load($FilePath, [System.Xml.Linq.LoadOptions]::SetLineInfo -bor [System.Xml.Linq.LoadOptions]::PreserveWhitespace )\n $IdpsXml = $fileXml.Descendants(\"configuration\")[0].Descendants(\"system.identityModel\")[0].Descendants(\"identityConfiguration\")[0].Descendants(\"issuerNameRegistry\")[0].Descendants(\"trustedIssuers\")[0].Descendants(\"add\")\n\n # Figure out which elements to manipulate... First delete from the bottom up, then replace the top-most element\n $IdpMatches = $IdpsXml | ? { $_.Attribute(\"name\").Value -eq $TrustedIssuerName } | Sort-Object -Property LineNumber, LinePosition -Descending\n $IdpsToDelete = $IdpMatches | Select-Object -First ($IdpMatches.Count - 1)\n $IdpsToReplace = $IdpMatches | Select-Object -Last 1\n\n # Delete from the bottom up, so that the LineNumber/LinePosition remain valid during the manipulation\n foreach ($IdP in $IdpsToDelete) {\n Write-Host ( \"DEL [{0}:{1}] {2}\" -f $IdP.LineNumber, $IdP.LinePosition, $IdP.ToString() )\n\n $fileIdxOpen = Get-TextIndex -text $fileText -LineNumber $IdP.LineNumber -LinePosition ( $IdP.LinePosition - 1 )\n $fileIdxClose = $fileText.IndexOf($charGT, $fileIdxOpen) + 1 # add one to include the closing >\n $fileSubstr = $fileText.Substring($fileIdxOpen, $fileIdxClose - $fileIdxOpen)\n Write-Host ( \" [$fileIdxOpen .. $fileIdxClose] : $fileSubstr\" )\n\n $fileIdxPrior = $fileText.LastIndexOf($charGT, $fileIdxOpen) + 1\n $fileText = $fileText.Remove($fileIdxPrior, $fileIdxClose - $fileIdxPrior)\n }\n # Replace the top-most element with each thumbprint\n foreach ($IdP in $IdpsToReplace) {\n Write-Host ( \"FIX [{0}:{1}] {2}\" -f $IdP.LineNumber, $IdP.LinePosition, $IdP.ToString() )\n\n $fileIdxOpen = Get-TextIndex -text $fileText -LineNumber $IdP.LineNumber -LinePosition ( $IdP.LinePosition - 1 )\n $fileIdxClose = $fileText.IndexOf($charGT, $fileIdxOpen) + 1 # add one to include the closing >\n $fileSubstr = $fileText.Substring($fileIdxOpen, $fileIdxClose - $fileIdxOpen)\n Write-Host ( \" [$fileIdxOpen .. $fileIdxClose] : $fileSubstr\" )\n\n $fileIdxPrior = $fileText.LastIndexOf($charGT, $fileIdxOpen) + 1\n $ElementDelim = $fileText.Substring($fileIdxPrior, $fileIdxOpen - $fileIdxPrior)\n Write-Host ( \" -[{0} .. {1}]\" -f $fileIdxPrior, $fileIdxClose )\n $fileText = $fileText.Remove($fileIdxPrior, $fileIdxClose - $fileIdxPrior)\n foreach ($Thumbprint in $Thumbprints) {\n $newAttribs = [System.Xml.Linq.XAttribute[]]@(\n ( New-Object System.Xml.Linq.XAttribute(\"thumbprint\", $Thumbprint ) ),\n ( New-Object System.Xml.Linq.XAttribute(\"name\" , $TrustedIssuerName) )\n )\n $newValue = ( New-Object System.Xml.Linq.XElement(\"add\", $newAttribs) ).ToString()\n $fileText = $fileText.Insert($fileIdxPrior, $ElementDelim + $newValue)\n }\n }\n return $fileText\n}\n\n\n$ThumbPrints = Get-Thumbprints -MetadataUri $MetadataUri\n$fileContent = Replace-TrustedIssuerThumbprints -FilePath $FilePath -TrustedIssuerName $TrustedIssuerName -Thumbprints $ThumbPrints\n[System.IO.File]::WriteAllText($FilePath, $fileContent)\n",
"Octopus.Action.Script.ScriptFileName": null,
"Octopus.Action.Package.FeedId": null,
"Octopus.Action.Package.PackageId": null
},
"Category": "XML",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/Load-WIF-Issuer-Thumbprints.json",
"Website": "/step-templates/77331575-0628-455d-b484-cfd4703e2081",
"Logo": "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",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Thursday, June 7, 2018