Skip to content

Commit 8911947

Browse files
authored
Get VDC certificate status, export vc report (#381)
1 parent 9f11192 commit 8911947

File tree

10 files changed

+377
-37
lines changed

10 files changed

+377
-37
lines changed

RELEASE.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
- Honor the default certificate creation timeout builtin to CMSH. Remove the default of 60 seconds from `New-VdcCertificate -TimeoutSec`. There is a chance if you've been relying on the 60 seconds default and haven't set it in product, this could break your script. If this is the case, add `-TimeoutSec 60` to your script. To set the default timeout, see https://docs.venafi.com/Docs/currentSDK/TopNav/Content/SDK/WebSDK/r-SDK-Certificates-API-settings.php. Closes [#371](https://github.com/Venafi/VenafiPS/issues/371).
2-
- Add tab completion to all CMSH functions with parameters 'CertificateAuthorityPath', 'CredentialPath', 'CertificatePath', 'ApplicationPath', 'EnginePath', 'CertificateLinkPath', and 'NewPath'.
3-
- Fix incorrect token expiration timezone when using `New-VcToken`
4-
- Add `New-VenafiSession -VcAccessToken` if the access token is obtained outside VenafiPS
5-
- Fix `Set-VcCertificate -Tag` when the tag includes both a name and value
6-
- Add support to `Get-VcTag -Tag` for getting by name and value
7-
- Fix for parallel processing across CMSH environments, eg. `Export-VdcCertificate -VenafiSession $sess1 | Import-VdcCertificate -VenafiSession $sess2`
1+
- Add `Get-VdcCertificate -IncludeStatus` to include `Status` and `StatusText` properties in the response. These correspond to the Certificate Status as seen in the WebAdmin Certificate -> Summary tab.
2+
- Add `Export-VcReport` to export a Certificate Manager SaaS custom report either to a file or pipeline as a pscustomobject.
3+
- Final round of VenafiSession improvements. All advanced use cases, eg. pipe from one environment to another, are now working across ps5/ps7 and parallel processing or not.
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# PowerShell implementation of X509StatusHelper.GetStatus() logic
2+
3+
function Get-VdcCertificateStatus {
4+
<#
5+
.SYNOPSIS
6+
Calculate the Status field for a certificate, similar to what's shown on the Certificate Summary tab.
7+
8+
.DESCRIPTION
9+
This function replicates the logic from X509StatusHelper.GetStatus() to determine the certificate status.
10+
It checks InError, Status attribute, workflow tickets, revocation status, disabled state,
11+
consumer errors, and certificate expiration.
12+
13+
The current use for this is from Get-VdcCertificate -IncludeStatus
14+
15+
.PARAMETER Certificate
16+
Output from certificates/{guid} api call
17+
18+
.PARAMETER VenafiSession
19+
Authentication for the function.
20+
The value defaults to the script session object $VenafiSession created by New-VenafiSession.
21+
22+
#>
23+
24+
[CmdletBinding()]
25+
param(
26+
[Parameter(Mandatory, ValueFromPipeline)]
27+
[object]$Certificate,
28+
29+
[Parameter()]
30+
[ValidateNotNullOrEmpty()]
31+
[psobject] $VenafiSession = (Get-VenafiSession)
32+
33+
)
34+
35+
process {
36+
# Initialize status
37+
$statusSummary = 'Ok' # Ok, Warning, Error
38+
$statusText = ''
39+
40+
$attribs = Get-VdcAttribute -Path $Certificate.DN -Attribute 'Disabled', 'Ticket DN' -VenafiSession $VenafiSession
41+
$certAttributes = @{
42+
'In Error' = $Certificate.ProcessingDetails.InError
43+
'Status' = $Certificate.ProcessingDetails.Status
44+
'Disabled' = $attribs.Disabled -eq 1
45+
'Ticket DN' = $attribs.'Ticket DN'
46+
}
47+
48+
# Check InError attribute
49+
$inError = $certAttributes.'In Error'
50+
if ($inError) {
51+
$statusSummary = 'Error'
52+
}
53+
54+
# Check Status attribute
55+
$statusAttr = $certAttributes.'Status'
56+
if ($statusAttr) {
57+
$statusText = $statusAttr
58+
if (-not $inError) {
59+
$statusSummary = 'Warning'
60+
}
61+
}
62+
else {
63+
if (-not $inError) {
64+
$statusText = 'OK'
65+
$statusSummary = 'Ok'
66+
}
67+
}
68+
69+
# Check for pending workflow (Ticket DN)
70+
$ticketDN = $certAttributes.'Ticket DN'
71+
if ($ticketDN) {
72+
$statusSummary = 'Warning'
73+
$statusText = 'Pending workflow resolution'
74+
}
75+
76+
$stage = $certAttributes.'Stage'
77+
# Check revocation status from CertificateDetails.RevocationStatus
78+
# Known values: Requested, Confirmed, Complete, DiscoveredRevoked, Failed, Pending
79+
if (-not $ticketDN -and -not $stage -and $Certificate.CertificateDetails.RevocationStatus) {
80+
81+
$statusSummary = 'Warning'
82+
if ($Certificate.CertificateDetails.RevocationDate) {
83+
$revocationDate = ([DateTime]$Certificate.CertificateDetails.RevocationDate).ToLocalTime().ToString()
84+
}
85+
86+
switch ($Certificate.CertificateDetails.RevocationStatus) {
87+
'Complete' {
88+
$statusText = if ($statusText) { '{0}; External Revocation Reported By CA' -f $statusText } else { 'External Revocation Reported By CA' }
89+
if ($Certificate.CertificateDetails.RevocationDate) {
90+
$statusText += " On: $revocationDate"
91+
}
92+
}
93+
'Confirmed' {
94+
if ($Certificate.CertificateDetails.RevocationDate) {
95+
$statusText = 'Revocation Submitted On {0} But Not Confirmed By CA' -f $revocationDate
96+
}
97+
else {
98+
$statusText = 'Revocation Submitted But Not Confirmed By CA'
99+
}
100+
}
101+
'DiscoveredRevoked' {
102+
$statusText = if ($statusText) { '{0}; External Revocation Reported By CA' -f $statusText } else { 'External Revocation Reported By CA' }
103+
if ($Certificate.CertificateDetails.RevocationDate) {
104+
$statusText += " On: $revocationDate"
105+
}
106+
}
107+
'Pending' {
108+
$statusText = if ($statusText) { '{0}; Revocation Pending' -f $statusText } else { 'Revocation Pending' }
109+
}
110+
'Failed' {
111+
$statusSummary = 'Error'
112+
# to get the underlying error involves a bit more digging in SecretStore. TODO possibly.
113+
$statusText = if ($statusText) { '{0}; Revocation Failed' -f $statusText } else { 'Revocation Failed' }
114+
}
115+
}
116+
117+
# check for user who initiated the revocation
118+
if ( $Certificate.CertificateDetails.RevocationInitiatedBy ) {
119+
$identity = Get-VdcIdentity -ID $Certificate.CertificateDetails.RevocationInitiatedBy
120+
$initBy = if ( $identity.FullName ) {
121+
$identity.FullName
122+
}
123+
elseif ($identity.Name ) {
124+
$identity.Name
125+
}
126+
else {
127+
$Certificate.CertificateDetails.RevocationInitiatedBy
128+
}
129+
130+
$statusText += '; Initiated By {0}' -f $initBy
131+
}
132+
}
133+
134+
# Check Disabled attribute
135+
if ($certAttributes.'Disabled') {
136+
if ($statusText) {
137+
$statusText += ' (Processing disabled)'
138+
}
139+
else {
140+
$statusText = 'Processing disabled'
141+
}
142+
$statusSummary = 'Warning'
143+
}
144+
145+
# Check consumer (application) errors
146+
if ($statusSummary -eq 'Ok' -and $Certificate.Consumers) {
147+
$consumerErrors = 0
148+
$consumerWarnings = 0
149+
$consumerDisabled = 0
150+
$consumerOk = 0
151+
152+
$allAttribs = $Certificate.Consumers | Get-VdcAttribute -Attribute @('In Error', 'Status', 'Disabled') -VenafiSession $VenafiSession
153+
foreach ($consumerAttrs in $allAttribs) {
154+
try {
155+
156+
if ($consumerAttrs.'Disabled' -eq 1) {
157+
$consumerDisabled++
158+
$statusSummary = 'Warning'
159+
continue
160+
}
161+
162+
if ($consumerAttrs.'In Error' -eq 1) {
163+
$consumerErrors++
164+
$statusSummary = 'Error'
165+
}
166+
elseif ($consumerAttrs.'Status') {
167+
$consumerWarnings++
168+
$statusSummary = 'Warning'
169+
}
170+
else {
171+
$consumerOk++
172+
}
173+
}
174+
catch {
175+
# Consumer may not be accessible
176+
Write-Verbose "Could not read consumer: $consumerPath"
177+
}
178+
}
179+
180+
if ($statusSummary -ne 'Ok') {
181+
$statusText = "Certificate Ok; Application errors: $consumerErrors, caution: $consumerWarnings, disabled: $consumerDisabled, Ok: $consumerOk"
182+
}
183+
}
184+
185+
# Check certificate expiration
186+
if ($statusSummary -eq 'Ok' -and $Certificate.CertificateDetails.ValidTo) {
187+
$validTo = [DateTime]$Certificate.CertificateDetails.ValidTo
188+
if ($validTo -lt (Get-Date)) {
189+
$statusSummary = 'Error'
190+
$statusText = 'Certificate expired'
191+
}
192+
}
193+
194+
# Return results
195+
@{
196+
Status = $statusSummary
197+
StatusText = $statusText
198+
}
199+
}
200+
}

VenafiPS/Private/Invoke-VcGraphQL.ps1

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function Invoke-VcGraphQL {
2828

2929
[Parameter()]
3030
[ValidateNotNullOrEmpty()]
31-
[psobject] $VenafiSession
31+
[psobject] $VenafiSession = (Get-VenafiSession)
3232
)
3333

3434
$params = @{
@@ -39,8 +39,6 @@ function Invoke-VcGraphQL {
3939
ErrorAction = 'Stop'
4040
}
4141

42-
$VenafiSession = Get-VenafiSession
43-
4442
$Server = $VenafiSession.Server
4543
$auth = $VenafiSession.Key.GetNetworkCredential().password
4644
$SkipCertificateCheck = $VenafiSession.SkipCertificateCheck

VenafiPS/Private/Invoke-VenafiParallel.ps1

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ function Invoke-VenafiParallel {
4545
In your ScriptBlock:
4646
- Use either $PSItem or $_ to reference the current input object
4747
- Remember, hashtables are reference types so be sure to clone if 'using' from parent
48+
- all function calls which call the api require '-VenafiSession $using:VenafiSession' to be provided
4849
4950
#>
5051

@@ -88,11 +89,9 @@ function Invoke-VenafiParallel {
8889

8990
# throttle to 1 = no parallel
9091
if ( -not $goParallel ) {
91-
# remove $using: from vars, not threaded and not supported
92-
$scriptBlockWithoutUsing = [ScriptBlock]::Create(($ScriptBlock.ToString() -ireplace [regex]::Escape('$using:'), '$'))
9392

9493
# Add progress bar for non-parallel execution if progress is enabled
95-
if ( $ProgressPreference -eq 'Continue' -and $InputObject.Count -gt 25 ) {
94+
if ( $ProgressPreference -eq 'Continue' -and $InputObject.Count -gt 10 ) {
9695
$totalCount = $InputObject.Count
9796
$currentCount = 0
9897

@@ -102,31 +101,32 @@ function Invoke-VenafiParallel {
102101
$progressInterval = [Math]::Max(1, [Math]::Floor($totalCount / 100)) # Update every 1% or minimum every item
103102
if ($totalCount -lt 20) { $progressInterval = 1 } # Always show progress for small sets
104103

105-
$InputObject | ForEach-Object -Process {
104+
$progressSb = {
106105
$currentCount++
107106

108107
# Only update progress at intervals or for the last item
109108
if (($currentCount % $progressInterval -eq 0) -or ($currentCount -eq $totalCount)) {
110109
$percent = [int](($currentCount / $totalCount) * 100)
111110
Write-Progress -Activity $ProgressTitle -Status ("{0}% complete ({1}/{2})" -f $percent, $currentCount, $totalCount) -PercentComplete $percent
112111
}
113-
114-
& $scriptBlockWithoutUsing
115112
}
116113

114+
# remove $using: from vars, not threaded and not supported
115+
$sb = ([ScriptBlock]::Create($progressSb.ToString() + ($ScriptBlock.ToString() -ireplace [regex]::Escape('$using:'), '$')))
116+
$InputObject | ForEach-Object -Process $sb
117+
117118
Write-Progress -Completed -Activity $ProgressTitle
118119
}
119120
else {
120121
# No progress bar needed
121-
$InputObject | ForEach-Object -Process $scriptBlockWithoutUsing
122+
# remove $using: from vars which is only available in the parallel context
123+
$InputObject | ForEach-Object -Process ([ScriptBlock]::Create(($ScriptBlock.ToString() -ireplace [regex]::Escape('$using:'), '$')))
122124
}
123125
return
124126
}
125127

126128
# parallel processing from here down
127129

128-
# $VenafiSession = Get-VenafiSession
129-
130130
$starterSb = {
131131

132132
# need to import module until https://github.com/PowerShell/PowerShell/issues/12240 is complete
@@ -202,4 +202,3 @@ function Invoke-VenafiParallel {
202202
}
203203

204204
}
205-

0 commit comments

Comments
 (0)