Skip to content

Commit

Permalink
Merge pull request #802 from d365collaborative/development
Browse files Browse the repository at this point in the history
Get-D365LcsEnvironmentRsatCertificate
  • Loading branch information
Splaxi authored Jan 23, 2024
2 parents e8c0cca + 0df947a commit 305ac90
Show file tree
Hide file tree
Showing 9 changed files with 810 additions and 4 deletions.
178 changes: 178 additions & 0 deletions d365fo.tools/bin/d365fo.tools-index.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions d365fo.tools/d365fo.tools.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
'Get-D365LcsDeploymentStatus',
'Get-D365LcsEnvironmentHistory',
'Get-D365LcsEnvironmentMetadata',
'Get-D365LcsEnvironmentRsatCertificate',

'Get-D365MaintenanceMode',
'Get-D365Model',
Expand Down
159 changes: 159 additions & 0 deletions d365fo.tools/functions/get-d365lcsenvironmentrsatcertificate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@

<#
.SYNOPSIS
Get LCS environment meta data from within a project
.DESCRIPTION
Get all meta data details for environments from within a LCS project
It supports listing all environments, but also supports single / specific environments by searching based on EnvironmentId or EnvironmentName
.PARAMETER ProjectId
The project id for the Dynamics 365 for Finance & Operations project inside LCS
Default value can be configured using Set-D365LcsApiConfig
.PARAMETER BearerToken
The token you want to use when working against the LCS api
Default value can be configured using Set-D365LcsApiConfig
.PARAMETER EnvironmentId
Id of the environment that you want to be working against
.PARAMETER OutputPath
Path to where you want the certificate files to be saved
The default value is: "c:\temp\d365fo.tools\RsatCert\"
.PARAMETER LcsApiUri
URI / URL to the LCS API you want to use
The value depends on where your LCS project is located. There are multiple valid URI's / URL's
Valid options:
"https://lcsapi.lcs.dynamics.com"
"https://lcsapi.eu.lcs.dynamics.com"
"https://lcsapi.fr.lcs.dynamics.com"
"https://lcsapi.sa.lcs.dynamics.com"
"https://lcsapi.uae.lcs.dynamics.com"
"https://lcsapi.ch.lcs.dynamics.com"
"https://lcsapi.no.lcs.dynamics.com"
"https://lcsapi.lcs.dynamics.cn"
"https://lcsapi.gov.lcs.microsoftdynamics.us"
Default value can be configured using Set-D365LcsApiConfig
.PARAMETER FailOnErrorMessage
Instruct the cmdlet to write logging information to the console, if there is an error message in the response from the LCS endpoint
Used in combination with either Enable-D365Exception cmdlet, or the -EnableException directly on this cmdlet, it will throw an exception and break/stop execution of the script
This allows you to implement custom retry / error handling logic
.PARAMETER RetryTimeout
The retry timeout, before the cmdlet should quit retrying based on the 429 status code
Needs to be provided in the timspan notation:
"hh:mm:ss"
hh is the number of hours, numerical notation only
mm is the number of minutes
ss is the numbers of seconds
Each section of the timeout has to valid, e.g.
hh can maximum be 23
mm can maximum be 59
ss can maximum be 59
Not setting this parameter will result in the cmdlet to try for ever to handle the 429 push back from the endpoint
.PARAMETER EnableException
This parameters disables user-friendly warnings and enables the throwing of exceptions
This is less user friendly, but allows catching exceptions in calling scripts
.EXAMPLE
PS C:\> Get-D365LcsEnvironmentRsatCertificate -ProjectId "123456789" -EnvironmentId "13cc7700-c13b-4ea3-81cd-2d26fa72ec5e"
This will download the active rsat certificate file for the environment from the LCS project.
The LCS project is identified by the ProjectId 123456789, which can be obtained in the LCS portal.
The environment is identified by the EnvironmentId "13cc7700-c13b-4ea3-81cd-2d26fa72ec5e", which can be obtained in the LCS portal.
A result set example:
Path : c:\temp\d365fo.tools\RsatCert\RSATCertificate_ABC-UAT_20240101-012030
CerFile : C:\temp\d365fo.tools\RsatCert\RSATCertificate_ABC-UAT_20240101-012030\RSATCertificate_ABC-UAT_20240101-012030.cer
PfxFile : C:\temp\d365fo.tools\RsatCert\RSATCertificate_ABC-UAT_20240101-012030\RSATCertificate_ABC-UAT_20240101-012030.pfx
FileName : RSATCertificate_ABC-UAT_20240101-012030.zip
Password : 9zbPiLMTk676mkq5FvqQ
.NOTES
Author: Mötz Jensen (@Splaxi)
#>
function Get-D365LcsEnvironmentRsatCertificate {
[CmdletBinding(DefaultParameterSetName = 'Default')]
[OutputType('PSCustomObject')]
param(
[int] $ProjectId = $Script:LcsApiProjectId,

[Alias('Token')]
[string] $BearerToken = $Script:LcsApiBearerToken,

[Parameter(Mandatory = $true)]
[string] $EnvironmentId,

[string] $OutputPath = $(Join-Path $Script:DefaultTempPath "RsatCert"),

[string] $LcsApiUri = $Script:LcsApiLcsApiUri,

[switch] $FailOnErrorMessage,

[Timespan] $RetryTimeout = "00:00:00",

[switch] $EnableException
)

process {
Invoke-TimeSignal -Start

if (-not (Test-PathExists -Path $OutputPath -Type Container -Create)) { return }

if (-not ($BearerToken.StartsWith("Bearer "))) {
$BearerToken = "Bearer $BearerToken"
}

$parms = @{}
$parms.ProjectId = $ProjectId
$parms.BearerToken = $BearerToken
$parms.LcsApiUri = $LcsApiUri
$parms.RetryTimeout = $RetryTimeout
$parms.EnableException = $EnableException
$parms.EnvironmentId = $EnvironmentId

$resCertDetails = Get-LcsEnvironmentRsatCertificate @parms

if (Test-PSFFunctionInterrupt) { return }

if ($FailOnErrorMessage -and $deploymentStatus.ErrorMessage) {
$messageString = "The request against LCS succeeded, but the response was an error message for the operation: <c='em'>$($deploymentStatus.ErrorMessage)</c>."
$errorMessagePayload = "`r`n$($deploymentStatus | ConvertTo-Json)"
Write-PSFMessage -Level Host -Message $messageString -Exception $([System.Exception]::new($($errorMessagePayload))) -Target $deploymentStatus
Stop-PSFFunction -Message "Stopping because of errors." -Exception $([System.Exception]::new($($errorMessagePayload))) -Target $deploymentStatus
}

$outFile = Join-Path -Path $OutputPath -ChildPath $resCertDetails.Data.Filename
Set-Content -Path $outFile -Value $([System.Convert]::FromBase64String($resCertDetails.Data.CertificateZipEncoded)) -Encoding Byte

$outExtract = Join-Path -Path $OutputPath -ChildPath $([System.IO.Path]::GetFileNameWithoutExtension($outFile))
Expand-Archive -Path $outFile -DestinationPath $outExtract -Force

Invoke-TimeSignal -End

[PSCustomObject][ordered]@{
Path = $outExtract
CerFile = Get-Item -Path "$outExtract\*.cer" | Select-Object -First 1 -ExpandProperty FullName
PfxFile = Get-Item -Path "$outExtract\*.pfx" | Select-Object -First 1 -ExpandProperty FullName
FileName = $resCertDetails.Data.Filename
Password = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($resCertDetails.Data.CertificateSecretEncoded))
}
}
}
2 changes: 1 addition & 1 deletion d365fo.tools/functions/new-d365entraintegration.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ function New-D365EntraIntegration {
Stop-PSFFunction -Message "Stopping because the certificate thumbprint could not be retrieved"
return
}
$certificateObject = Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq $certificateThumbprint
$certificateObject = Get-ChildItem $certificateStoreLocation | Where-Object Thumbprint -eq $certificateThumbprint
if (-not $certificateObject) {
Write-PSFMessage -Level Host -Message "Unable to get the certificate object."
Stop-PSFFunction -Message "Stopping because the certificate object could not be retrieved"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
It supports listing all environments, but also supports single / specific environments by searching based on EnvironmentId or EnvironmentName
.PARAMETER ProjectId
The project id for the Dynamics 365 for Finance & Operations project inside LCS
Expand All @@ -29,7 +28,6 @@
Either you want to utilize the EnvironmentName parameter or you can utilize the EnvironmentId parameter, only one of them is valid in a request
.PARAMETER Page
Page number that you want to request from the LCS API
Expand Down
125 changes: 125 additions & 0 deletions d365fo.tools/internal/functions/get-lcsenvironmentrsatcertificate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@

<#
.SYNOPSIS
Get LCS environment rsat certificate from within a project
.DESCRIPTION
Download and persist the active rsat certificate from environments from within a LCS project
.PARAMETER ProjectId
The project id for the Dynamics 365 for Finance & Operations project inside LCS
.PARAMETER BearerToken
The token you want to use when working against the LCS api
.PARAMETER EnvironmentId
The unique id of the environment that you want to work against
The Id can be located inside the LCS portal
.PARAMETER LcsApiUri
URI / URL to the LCS API you want to use
The value depends on where your LCS project is located. There are multiple valid URI's / URL's
Valid options:
"https://lcsapi.lcs.dynamics.com"
"https://lcsapi.eu.lcs.dynamics.com"
"https://lcsapi.fr.lcs.dynamics.com"
"https://lcsapi.sa.lcs.dynamics.com"
"https://lcsapi.uae.lcs.dynamics.com"
"https://lcsapi.ch.lcs.dynamics.com"
"https://lcsapi.no.lcs.dynamics.com"
"https://lcsapi.lcs.dynamics.cn"
"https://lcsapi.gov.lcs.microsoftdynamics.us"
.PARAMETER RetryTimeout
The retry timeout, before the cmdlet should quit retrying based on the 429 status code
Needs to be provided in the timspan notation:
"hh:mm:ss"
hh is the number of hours, numerical notation only
mm is the number of minutes
ss is the numbers of seconds
Each section of the timeout has to valid, e.g.
hh can maximum be 23
mm can maximum be 59
ss can maximum be 59
Not setting this parameter will result in the cmdlet to try for ever to handle the 429 push back from the endpoint
.PARAMETER EnableException
This parameters disables user-friendly warnings and enables the throwing of exceptions
This is less user friendly, but allows catching exceptions in calling scripts
.EXAMPLE
PS C:\> Get-LcsEnvironmentRsatCertificate -ProjectId 123456789 -Token "Bearer JldjfafLJdfjlfsalfd..." -EnvironmentId "13cc7700-c13b-4ea3-81cd-2d26fa72ec5e" -LcsApiUri "https://lcsapi.lcs.dynamics.com"
This will get the raw rsat details for the environment from the LCS API.
The ProjectId "123456789" is the desired project.
The Token "Bearer JldjfafLJdfjlfsalfd..." is the authentication to be used.
The EnvironmentId "13cc7700-c13b-4ea3-81cd-2d26fa72ec5e" is the specific environment that we want the rsat certificate details from.
The http request will be going to the LcsApiUri "https://lcsapi.lcs.dynamics.com" (NON-EUROPE).
.NOTES
Author: Mötz Jensen (@Splaxi)
#>
function Get-LcsEnvironmentRsatCertificate {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
# [CmdletBinding()]
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
[Parameter(Mandatory = $true)]
[int] $ProjectId,

[Alias('Token')]
[Parameter(Mandatory = $true)]
[string] $BearerToken,

[Parameter(Mandatory = $true)]
[string] $EnvironmentId,

[Parameter(Mandatory = $true)]
[string] $LcsApiUri,

[Timespan] $RetryTimeout = "00:00:00",

[switch] $EnableException
)

begin {
Invoke-TimeSignal -Start

$headers = @{
"Authorization" = "$BearerToken"
}

$parms = @{}
$parms.Method = "GET"
$parms.Uri = "$LcsApiUri/environmentinfo/v1/rsatdownload/project/$($ProjectId)/environment/$EnvironmentId"
$parms.Headers = $headers
$parms.RetryTimeout = $RetryTimeout
}

process {
try {
Write-PSFMessage -Level Verbose -Message "Invoke LCS request."
Invoke-RequestHandler @parms
}
catch [System.Net.WebException] {
Write-PSFMessage -Level Host -Message "Error status code <c='em'>$($_.exception.response.statuscode)</c> in request for getting the environment rsat certificate in LCS. <c='em'>$($_.exception.response.StatusDescription)</c>." -Exception $PSItem.Exception
Stop-PSFFunction -Message "Stopping because of errors" -StepsUpward 1
return
}
catch {
Write-PSFMessage -Level Host -Message "Something went wrong while working against the LCS API." -Exception $PSItem.Exception
Stop-PSFFunction -Message "Stopping because of errors" -StepsUpward 1
return
}

Invoke-TimeSignal -End
}
}
2 changes: 1 addition & 1 deletion d365fo.tools/internal/scripts/variables.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ if ($environment.Infrastructure.HostName -like "*cloud.onebox.dynamics.com*") {
$Script:EnvironmentType = [EnvironmentType]::LocalHostedTier1
$Script:CanUseTrustedConnection = $true
}
elseif ($environment.Infrastructure.HostName -like "*cloudax.*dynamics.com*") {
elseif ($environment.Infrastructure.HostName -match "(cloudax|axcloud).*dynamics.com") {
$Script:EnvironmentType = [EnvironmentType]::AzureHostedTier1
$Script:CanUseTrustedConnection = $true
}
Expand Down
Loading

0 comments on commit 305ac90

Please sign in to comment.