Created
March 7, 2019 15:20
-
-
Save JohnLBevan/3cd5ec5e826033c1f9283f1b31dc2a82 to your computer and use it in GitHub Desktop.
Code to pull values from Dynamics 365 for Finance and Operations (aka DFO365 / unified operations) Web.Config file and decrypt values where required, to find relevant credential information used by the OneBox install.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[string[]]$Assemblies = @( | |
'C:\AOSService\webroot\bin\Microsoft.Dynamics.AX.Framework.EncryptionEngine.dll' | |
) | |
[string]$CSSource = @" | |
using System; | |
using Microsoft.Dynamics.Ax.Xpp.Security; | |
namespace n201903071243 //use an odd namespace so I don't have to reload powershell each time I want to tweak this code; just tweak the NS | |
{ | |
public class Dfo365CertificateThumbprintProvider: Microsoft.Dynamics.Ax.Xpp.Security.ICertificateThumbprintProvider | |
{ | |
public string EncryptionThumbprint {get;private set;} | |
public string SigningThumbprint {get;private set;} | |
public Dfo365CertificateThumbprintProvider(string encryptionThumbprint, string signingThumbprint) | |
{ | |
EncryptionThumbprint = encryptionThumbprint; | |
SigningThumbprint = signingThumbprint; | |
} | |
} | |
public class Dfo365EncryptionExceptionHandler: Microsoft.Dynamics.Ax.Xpp.Security.IEncryptionExceptionHandler | |
{ | |
private Action<Exception> exceptionHandler; | |
public Dfo365EncryptionExceptionHandler() | |
{ | |
exceptionHandler = WriteToConsoleThenThrow; | |
} | |
public Dfo365EncryptionExceptionHandler(Action<Exception> exceptionHandler) | |
{ | |
this.exceptionHandler = exceptionHandler; | |
} | |
public void HandleException(Exception exception) | |
{ | |
if (exceptionHandler != null) | |
{ | |
exceptionHandler(exception); | |
} | |
} | |
public static void WriteToConsoleThenThrow(Exception exception) | |
{ | |
Console.WriteLine(exception.ToString()); | |
throw exception; | |
} | |
} | |
} | |
"@ | |
Add-Type -ReferencedAssemblies $Assemblies -TypeDefinition $CSSource -Language 'CSharp' | |
Add-Type -Path 'C:\AOSService\webroot\bin\Microsoft.Dynamics.AX.Framework.EncryptionEngine.dll' | |
function Decrypt-Dfo365EncryptedString { | |
[CmdletBinding(DefaultParameterSetName = 'ByEncryptionEngine')] | |
Param ( | |
[Parameter(ParameterSetName = 'ByEncryptionEngine', Mandatory = $true)] | |
[Microsoft.Dynamics.Ax.Xpp.Security.EncryptionEngine]$EncryptionEngine = $null | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
, | |
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | |
[string]$EncryptedString | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
if ($PSCmdlet.ParameterSetName -ne 'ByEncryptionEngine') { | |
$EncryptionEngine = New-Dfo365EncryptionEngine -WebConfig $WebConfig | |
} | |
[string]$purpose = 'PurposeName' | |
} | |
Process { | |
[byte[]]$cipher = [Convert]::FromBase64String($EncryptedString) | |
$encryptionEngine.Decrypt($cipher, $purpose) | |
} | |
} | |
function Get-Dfo365ConfigSetting { | |
[CmdletBinding(DefaultParameterSetName = 'ByPath')] | |
Param ( | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
, | |
[Parameter(Mandatory = $true, ValueFromPipeLine = $true)] | |
[string]$PropertyName | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
} | |
Process { | |
[string]$xpath = ("/configuration/appSettings/add[@key='{0}']/@value" -f $PropertyName) #I've not bothered adding escaping logic as key names unlikely to contain apostrophies; but may be worth adding at some point? | |
$WebConfig.SelectSingleNode($xpath) | Select-Object -ExpandProperty 'value' | |
} | |
} | |
function Get-Dfo365EncryptedConfigSetting { | |
[CmdletBinding(DefaultParameterSetName = 'ByPath')] | |
Param ( | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
, | |
[Parameter(Mandatory = $true, ValueFromPipeLine = $true)] | |
[string]$PropertyName | |
, | |
[Parameter(Mandatory = $false)] | |
[Microsoft.Dynamics.Ax.Xpp.Security.EncryptionEngine]$EncryptionEngine = $null | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
if ($EncryptionEngine -eq $null) { | |
$EncryptionEngine = New-Dfo365EncryptionEngine -WebConfig $WebConfig | |
} | |
} | |
Process { | |
$encryptedValue = Get-Dfo365ConfigSetting -WebConfig $WebConfig -PropertyName $PropertyName | |
Decrypt-Dfo365EncryptedString -EncryptionEngine $EncryptionEngine -EncryptedString $encryptedValue | |
} | |
} | |
function New-Dfo365EncryptionEngine { | |
[CmdletBinding(DefaultParameterSetName = 'AllObjects')] | |
Param( | |
[Parameter(ParameterSetName = 'AllObjects', Mandatory = $true)] | |
[Parameter(ParameterSetName = 'ByPath', Mandatory = $false)] | |
[Parameter(ParameterSetName = 'ByXml', Mandatory = $false)] | |
[Microsoft.Dynamics.Ax.Xpp.Security.ICertificateThumbprintProvider]$certificateThumbprintProvider = $null | |
, | |
[Parameter(ParameterSetName = 'AllObjects', Mandatory = $true)] | |
[Parameter(ParameterSetName = 'ByPath', Mandatory = $false)] | |
[Parameter(ParameterSetName = 'ByXml', Mandatory = $false)] | |
[System.Collections.Generic.IDictionary[[string], [string]]]$certificateHandlerSettings = $null | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
, | |
[Parameter(Mandatory = $false)] | |
[Microsoft.Dynamics.Ax.Xpp.Security.IEncryptionExceptionHandler]$encryptionExceptionHandler = $null #gets defaulted in the Begin block if left as null | |
, | |
[Parameter(Mandatory = $false)] | |
[Microsoft.Dynamics.Ax.Xpp.Security.ICertificateThumbprintProvider]$legacyCertificateThumbprintProvider = $null | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
if ($PSCmdlet.ParameterSetName -ne 'AllObjects') { | |
if ($certificateThumbprintProvider -eq $null) {$certificateThumbprintProvider = New-CertificateThumbprintProvider -WebConfig $WebConfig} | |
if ($certificateHandlerSettings -eq $null) {$certificateHandlerSettings = New-CertificateHandlerSettings -WebConfig $WebConfig} | |
#legacyCertificateThumbprintProvider doesn't seem to be used; though has values in the web.config keys... leave as null/provided for now | |
#if ($legacyCertificateThumbprintProvider -eq $null) {$legacyCertificateThumbprintProvider = New-CertificateThumbprintProvider -WebConfig $WebConfig -EncryptionThumbprintKey 'DataAccess.DataEncryptionCertificateThumbprintLegacy' -SigningThumbprintKey 'DataAccess.DataSigningCertificateThumbprintLegacy'} | |
} | |
if ($encryptionExceptionHandler -eq $null) {$encryptionExceptionHandler = New-Dfo365EncryptionExceptionHandler} | |
} | |
Process { | |
(New-Object -TypeName 'Microsoft.Dynamics.Ax.Xpp.Security.EncryptionEngine' -ArgumentList $certificateThumbprintProvider, $encryptionExceptionHandler, $certificateHandlerSettings, $legacyCertificateThumbprintProvider) | |
} | |
} | |
function New-Dfo365EncryptionExceptionHandler { | |
[CmdletBinding()] | |
Param () | |
Process { | |
(New-Object -TypeName 'n201903071243.Dfo365EncryptionExceptionHandler') | |
} | |
} | |
function New-CertificateThumbprintProvider { | |
[CmdletBinding(DefaultParameterSetName = 'ByPath')] | |
Param ( | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
, | |
#make these parameters in case we want to reuse this for fetching the legacy thumprints too (i.e. to reuse for legacyCertificateThumbprintProvider) | |
[Parameter()] | |
[string]$EncryptionThumbprintKey = 'DataAccess.DataEncryptionCertificateThumbprint' | |
, | |
[Parameter()] | |
[string]$SigningThumbprintKey = 'DataAccess.DataSigningCertificateThumbprint' | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
} | |
Process { | |
$encr = Get-Dfo365ConfigSetting -WebConfig $WebConfig -PropertyName $EncryptionThumbprintKey | |
$sign = Get-Dfo365ConfigSetting -WebConfig $WebConfig -PropertyName $SigningThumbprintKey | |
(New-Object -TypeName 'n201903071243.Dfo365CertificateThumbprintProvider' -ArgumentList $encr, $sign) | |
} | |
} | |
function New-CertificateHandlerSettings { | |
[CmdletBinding(DefaultParameterSetName = 'ByPath')] | |
Param ( | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
} | |
Process { | |
[System.Collections.Generic.IDictionary[[string], [string]]]$result = New-Object -TypeName 'System.Collections.Generic.Dictionary[[string], [string]]' | |
[PSObject[]]$keyValuePairs = $WebConfig.SelectNodes("/configuration/appSettings/add[starts-with(@key,'CertificateHandler')]") | Select-Object @('key', 'value') | |
foreach ($kvp in $keyValuePairs) { | |
$result.Add($kvp.key, $kvp.value) | |
} | |
$result | |
} | |
} | |
function Get-Dfo365CredentialData { | |
[CmdletBinding(DefaultParameterSetName = 'ByPath')] | |
Param ( | |
[Parameter(Mandatory = $false, ParameterSetName = 'ByPath')] | |
[string]$PathToWebConfig = 'C:\AOSService\webroot\web.config' | |
, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByXml')] | |
[xml]$WebConfig | |
) | |
Begin { | |
if ($PSCmdlet.ParameterSetName -eq 'ByPath') { | |
$WebConfig = [xml](Get-Content -Path $PathToWebConfig | Out-String) | |
} | |
[Microsoft.Dynamics.Ax.Xpp.Security.EncryptionEngine]$encryptionEngine = New-Dfo365EncryptionEngine -WebConfig $WebConfig | |
} | |
Process { | |
[PSObject[]]$settings = @( | |
@{Key='AzureStorage.StorageConnectionString';Encrypted=$true} | |
,@{Key='DataAccess.Database';Encrypted=$false} | |
,@{Key='DataAccess.DbServer';Encrypted=$false} | |
,@{Key='DataAccess.SqlPwd';Encrypted=$true} | |
,@{Key='DataAccess.SqlUser';Encrypted=$false} | |
,@{Key='Provisioning.AdminPrincipalName';Encrypted=$false} | |
,@{Key='BiReporting.DW';Encrypted=$false} | |
,@{Key='BiReporting.DWServer';Encrypted=$false} | |
,@{Key='BiReporting.DWUser';Encrypted=$false} | |
,@{Key='BiReporting.DWPwd';Encrypted=$true} | |
,@{Key='BiReporting.DWRuntimeUser';Encrypted=$false} | |
,@{Key='BiReporting.DWRuntimePwd';Encrypted=$true} | |
,@{Key='DataAccess.AxAdminSqlUser';Encrypted=$false} | |
,@{Key='DataAccess.AxAdminSqlPwd';Encrypted=$true} | |
) | ForEach-Object {(New-Object -TypeName 'PSObject' -Property $_)} | |
$settings | ForEach-Object { | |
$setting = $_.Key | |
$value = if ($_.Encrypted -eq $true) { | |
Get-Dfo365EncryptedConfigSetting -WebConfig $WebConfig -PropertyName $setting -EncryptionEngine $encryptionEngine | |
} else { | |
Get-Dfo365ConfigSetting -WebConfig $WebConfig -PropertyName $setting | |
} | |
(New-Object -TypeName 'PSObject' -Property @{Key=$setting;Value=$value}) | |
} | |
} | |
} | |
Clear-Host | |
Get-Dfo365CredentialData | Format-Table -AutoSize | |
<# Sample output | |
Key Value | |
--- ----- | |
AzureStorage.StorageConnectionString UseDevelopmentStorage=true | |
DataAccess.Database AxDB | |
DataAccess.DbServer localhost | |
DataAccess.SqlPwd AOSWebSite@123 | |
DataAccess.SqlUser axdbadmin | |
Provisioning.AdminPrincipalName myusername@mycompany.com | |
BiReporting.DW AxDW | |
BiReporting.DWServer localhost | |
BiReporting.DWUser axdwadmin | |
BiReporting.DWPwd AOSWebSite@123 | |
BiReporting.DWRuntimeUser axdwruntimeuser | |
BiReporting.DWRuntimePwd AOSWebSite@123 | |
DataAccess.AxAdminSqlUser axdbadmin | |
DataAccess.AxAdminSqlPwd AOSWebSite@123 | |
#> | |
<# NB: At some point investigate whether we can use this bit of the config to sync OneBox data back to LCS | |
<!-- Begin: LCS Config Section --> | |
<add key="LCS.APIEndPoint" value="https://lcsapi.lcs.tie.dynamics.com" /> | |
<add key="LCS.GettingStartedLibrary" value="" /> | |
<add key="LCS.LcsClientCertificateThumbprint" value="" /> | |
<add key="LCS.GERConfigurationImport" value="" /> | |
<add key="LCS.EnvironmentId" value="" /> | |
<add key="LCS.BpmAuthClient" value="SysBpmCertClient" /> | |
<add key="LCS.ProjectId" value="" /> | |
<!-- End: LCS Config Section--> | |
#> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment