Export and import certificate templates with PowerShell

Hello S-1-1-0, Crypto Guy is on a failboat board again.

Sometimes it is useful to export a certificate template to a file for future use. For example:

  • for custom certificate template backup;
  • if you have multiple AD forests, you can transfer configured certificate templates between forests;
  • share custom certificate template with partners;
  • share certificate template when you are making a online learning session;
  • etc..

Till Windows Server 2008 R2 release there was no supported way to export (or serialize) certificate template and move it out of band between two forests. With Windows Server 2008 R2 there was the only publically described way to transfer templates between two forests: AD CS: Cross-forest Certificate Enrollment with Windows Server 2008 R2. This whitepaper includes a PKISync.ps1 script (the script was written by a man who first time faced PowerShell, he-he) which copies certificate templates along other AD data between two forests. The downside of this approach is that it requires a two-way trust between forests and performs data transfer online.

In addition, Windows Server 2008 R2 introduces Enrollment Web Services. These services implement two communication protocols: MS-XCEP and MS-WSTEP.

Certificate template export

MS-XCEP is used to transfer policy information from policy (CEP) server to client. Policy information contains the following data:

  • Information about Certificate Enrollment Server (CES) endpoints and CA servers. Clients will communicate with CES by using MS-WSTEP protocol.
  • Information about available certificate templates with all settings.

Since, these protocols implement simple (comparing with low-level DCOM communications) XML over HTTPS, there is a way to reuse them manually. What you need in order to write a code that reads certificate templates from Active Directory and convert them to a XCEP-compatible XML format:

  • MS-CRTD – Certificate Template Structure
  • MS-XCEP - X.509 Certificate Enrollment Policy Protocol

MS-CRTD contains information about certificate template structure, field meanings, dependencies and other useful information. From MS-XCEP you will find information about XML structure and XML Schema. Also, a sample XML response will be very helpful. I used these documents and got the following script:

Note: there is no built-in support for certificate templates neither in .NET or PowerShell, therefore the script relies on a PKI.Core.dll which contains a set of underlying APIs for PowerShell PKI Module and exposes a set of classes to work with certificate templates in PowerShell. If you have module installed, just import it to a current PS session. Alternatively, you can download this DLL from PSPKI project home page (in the Downloads section, select PSPKI sources) and use Add-Type cmdlet to load it to current PS session.

#####################################################################
# Export-CertificateTemplate.ps1
# Version 1.0
#
# Exports certificate templates to a serialized format.
#
# Vadims Podans (c) 2013
# http://en-us.sysadmins.lv/
#####################################################################
#requires -Version 2.0

function Export-CertificateTemplate {
<#
.Synopsis
    Exports certificate templates to a serialized format.
.Description
    Exports certificate templates to a serialized format. Exported templates can be distributed
    and imported in another forest.
.Parameter Template
    A collection of certificate templates to export. A collection can be retrieved by running
    Get-CertificateTemplate that is a part of PSPKI module: https://pspki.codeplex.com
.Parameter Path
    Specifies the path to export.
.Example
    $Templates = Get-CertificateTemplate -Name SmartCardV2, WebServerV3
    PS C:\> Export-CertificateTemplate $templates c:\temp\templates.dat
#>
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [PKI.CertificateTemplates.CertificateTemplate[]]$Template,
        [Parameter(Mandatory = $true)]
        [IO.FileInfo]$Path
    )
    if ($Template.Count -lt 1) {throw "At least one template must be specified in the 'Template' parameter."}
    $ErrorActionPreference = "Stop"

#region enums
    $HashAlgorithmGroup = 1
    $EncryptionAlgorithmGroup = 2
    $PublicKeyIdGroup = 3
    $SigningAlgorithmIdGroup = 4
    $RDNIdGroup = 5
    $ExtensionAttributeGroup = 6
    $EKUGroup = 7
    $CertificatePolicyGroup = 8
    $EnrollmentObjectGroup = 9
#endregion

#region funcs
    function Get-OIDid ($OID,$group) {
        $found = $false
        :outer for ($i = 0; $i -lt $oids.Count; $i++) {
            if ($script:oids[$i].Value -eq $OID.Value) {
                $ID = ++$i
                $found = $true
                break outer
            }
        }
        if (!$found) {
            $script:oids += New-Object psobject -Property @{
                Value = $OID.Value;
                Group = $group;
                Name = $OID.FriendlyName;
            }
            $ID = $script:oids.Count
        }
        $ID
    }
    function Get-Seconds ($str) {
        [void]("$str" -match "(\d+)\s(\w+)")
        $period = $matches[1] -as [int]
        $units = $matches[2]
        switch ($units) {
            "hours" {$period * 3600}
            "days" {$period * 3600 * 24}
            "weeks" {$period * 3600 * 168}
            "months" {$period * 3600 * 720}
            "years" {$period * 3600 * 8760}
        }
    }
#endregion

    $SB = New-Object Text.StringBuilder
    [void]$SB.Append(
@"
<GetPoliciesResponse xmlns="http://schemas.microsoft.com/windows/pki/2009/01/enrollmentpolicy">
    <response>
        <policyID/>
        <policyFriendlyName/>
        <nextUpdateHours>8</nextUpdateHours>
        <policiesNotChanged a:nil="true" xmlns:a="http://www.w3.org/2001/XMLSchema-instance"/>
        <policies>
"@)
    $script:oids = @()
    foreach ($temp in $Template) {
         [void]$SB.Append("<policy>")
        $OID = New-Object Security.Cryptography.Oid $temp.OID.Value, $temp.DisplayName
        $tempID = Get-OIDid $OID $EnrollmentObjectGroup
        # validity/renewal
        $validity = Get-Seconds $temp.Settings.ValidityPeriod
        $renewal = Get-Seconds $temp.Settings.RenewalPeriod
        # key usages
        $KU = if ([int]$temp.Settings.Cryptography.CNGKeyUsage -eq 0) {
            '<keyUsageProperty xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            "<keyUsageProperty>$([int]$temp.Settings.CNGKeyUsage)</keyUsageProperty>"
        }
        # private key security
        $PKS = if ([string]::IsNullOrEmpty($temp.Settings.Cryptography.PrivateKeySecuritySDDL)) {
            '<permissions xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            "<permissions>$($temp.Settings.PrivateKeySecuritySDDL)</permissions>"
        }
        # public key algorithm
        $KeyAlgorithm = if ($temp.Settings.Cryptography.KeyAlgorithm.Value -eq "1.2.840.113549.1.1.1") {
            '<algorithmOIDReference xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            $kalgID = Get-OIDid $temp.Settings.Cryptography.KeyAlgorithm $PublicKeyIdGroup
            "<algorithmOIDReference>$kalgID</algorithmOIDReference>"
        }
        # superseded templates
        $superseded = if ($temp.Settings.SupersededTemplates -eq 0) {
            '<supersededPolicies xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'    
        } else {
            $str = "<supersededPolicies>"
            $temp.Settings.SupersededTemplates | ForEach-Object {$str += "<commonName>$_</commonName>"}
            $str + "</supersededPolicies>"
        }
        # list of CSPs
        $CSPs = if ($temp.Settings.Cryptography.CSPList.Count -eq 0) {
            '<cryptoProviders xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            $str = "<cryptoProviders>`n"
            $temp.Settings.Cryptography.CSPList | ForEach-Object {
                $str += "<provider>$_</provider>`n"
            }
            $str + "</cryptoProviders>"
        }
        # version
        [void]($temp.Version -match "(\d+)\.(\d+)")
        $major = $matches[1]
        $minor = $matches[2]
        # hash algorithm
        $hash = if ($temp.Settings.Cryptography.HashAlgorithm.Value -eq "1.3.14.3.2.26") {
            '<hashAlgorithmOIDReference xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            $hashID = Get-OIDid $temp.Settings.Cryptography.HashAlgorithm $HashAlgorithmGroup
            "<hashAlgorithmOIDReference>$hashID</hashAlgorithmOIDReference>"
        }
        # enrollment agent
        $RAR = if ($temp.Settings.RegistrationAuthority.SignatureCount -eq 0) {
            '<rARequirements xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            $str = @"
<rARequirements>
<rASignatures>$($temp.Settings.RegistrationAuthority.SignatureCount)</rASignatures>
"@
            if ([string]::IsNullOrEmpty($temp.Settings.RegistrationAuthority.ApplicationPolicy.Value)) {
                $str += '<rAEKUs xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
            } else {
                $raapID = Get-OIDid $temp.Settings.RegistrationAuthority.ApplicationPolicy $EKUGroup
                $str += @"
<rAEKUs>
    <oIDReference>$raapID</oIDReference>
</rAEKUs>
"@
            }
            if ($temp.Settings.RegistrationAuthority.CertificatePolicies.Count -eq 0) {
                $str += '<rAPolicies xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
            } else {
                $str += "                       <rAPolicies>"
                $temp.Settings.RegistrationAuthority.CertificatePolicies | ForEach-Object {
                    $raipID = Get-OIDid $_ $CertificatePolicyGroup
                    $str += "<oIDReference>$raipID</oIDReference>`n"
                }
                $str += "</rAPolicies>`n"
            }
            $str += "</rARequirements>`n"
            $str
        }
        # key archival
        $KAS = if (!$temp.Settings.KeyArchivalSettings.KeyArchival) {
            '<keyArchivalAttributes xmlns:a="http://www.w3.org/2001/XMLSchema-instance" a:nil="true"/>'
        } else {
            $kasID = Get-OIDid $temp.Settings.KeyArchivalSettings.EncryptionAlgorithm $EncryptionAlgorithmGroup
@"
<keyArchivalAttributes>
    <symmetricAlgorithmOIDReference>$kasID</symmetricAlgorithmOIDReference>
    <symmetricAlgorithmKeyLength>$($temp.Settings.KeyArchivalSettings.KeyLength)</symmetricAlgorithmKeyLength>
</keyArchivalAttributes>
"@
        }
        $sFlags = [Convert]::ToUInt32($("{0:x2}" -f [int]$temp.Settings.SubjectName),16)
        [void]$SB.Append(
@"
<policyOIDReference>$tempID</policyOIDReference>
<cAs>
    <cAReference>0</cAReference>
</cAs>
<attributes>
    <commonName>$($temp.Name)</commonName>
    <policySchema>$($temp.SchemaVersion)</policySchema>
    <certificateValidity>
        <validityPeriodSeconds>$validity</validityPeriodSeconds>
        <renewalPeriodSeconds>$renewal</renewalPeriodSeconds>
    </certificateValidity>
    <permission>
        <enroll>false</enroll>
        <autoEnroll>false</autoEnroll>
    </permission>
    <privateKeyAttributes>
        <minimalKeyLength>$($temp.Settings.Cryptography.MinimalKeyLength)</minimalKeyLength>
        <keySpec>$([int]$temp.Settings.Cryptography.KeySpec)</keySpec>
        $KU
        $PKS
        $KeyAlgorithm
        $CSPs
    </privateKeyAttributes>
    <revision>
        <majorRevision>$major</majorRevision>
        <minorRevision>$minor</minorRevision>
    </revision>
    $superseded
    <privateKeyFlags>$([int]$temp.Settings.Cryptography.PrivateKeyOptions)</privateKeyFlags>
    <subjectNameFlags>$sFlags</subjectNameFlags>
    <enrollmentFlags>$([int]$temp.Settings.EnrollmentOptions)</enrollmentFlags>
    <generalFlags>$([int]$temp.Settings.GeneralFlags)</generalFlags>
    $hash
    $rar
    $KAS
<extensions>
"@)
        foreach ($ext in $temp.Settings.Extensions) {
            $extID = Get-OIDid ($ext.Oid) $ExtensionAttributeGroup
            $critical = $ext.Critical.ToString().ToLower()
            $value = [Convert]::ToBase64String($ext.RawData)
            [void]$SB.Append("<extension><oIDReference>$extID</oIDReference><critical>$critical</critical><value>$value</value></extension>")
        }
        [void]$SB.Append("</extensions></attributes></policy>")
    }
    [void]$SB.Append("</policies></response>")
    [void]$SB.Append("<oIDs>")
    $n = 1
    $script:oids | ForEach-Object {
        [void]$SB.Append(@"
<oID>
    <value>$($_.Value)</value>
    <group>$($_.Group)</group>
    <oIDReferenceID>$n</oIDReferenceID>
    <defaultName>$($_.Name)</defaultName>
</oID>
"@)
        $n++
    }
    [void]$SB.Append("</oIDs></GetPoliciesResponse>")
    Set-Content -Path $Path -Value $SB.ToString() -Encoding Ascii
}

Although, the code is large, it is quite straightforward and tests certificate template properties, composes XML and saves it to a file. I didn’t bothered myself with XML formatting for brevity and it is not relevant in our case.

Certificate template import

Ok, we exported the template. How we can restore it or import it to another AD forest? Now we need to take a look at CertEnroll COM interfaces:

IX509CertificateTemplateWritable interface has a Initialize method that accepts a pointer to a IX509CertificateTemplate interface. However IX509CertificateTemplate do not contains any methods that could be used to instantiate a certificate template and implements the only read-only property that contains certificate template. Moreover, there is no appropriate public COM class for this interface. In other words, a way from nowhere to nowhere.

After careful research of related interfaces, I noticed that IX509EnrollmentPolicyServer interface (which has appropriate COM class) which implements GetTemplates method. This method returns a pointer (or pointers) to IX509CertificateTemplate interface. Luckily, there is a InitializeImport method that accepts an array of certificate templates. Since we have only XML file and the way how CryptoAPI COM interfaces works, I tried the most logical solution: read the file to a byte array and pass this array to a method and, BINGO, I got it working! I successfully initialized IX509EnrollmentPolicyServer interface object, retrieved a pointer to a IX509CertificateTemplate interface, instantiated IX509CertificateTemplateWritable interface, ??????, PROFIT!!!111oneone, hurrah!

To avoid long descriptions, I provide a small (comparing with Export-CertificateTemplate) function that illustrates all said above:

Note: in order to import certificate templates, you must run Windows 7 or Windows Server 2008 R2 at a minimum. Previous OS do not support these interfaces (as XCEP was first implemented only in mentioned OS versions)

#####################################################################
# Import-CertificateTemplate.ps1
# Version 1.0
#
# Imports and registers certificate templates in Active Directory from a file.
#
# Note: this function supports only Windows 7/Windows Server 2008 R2 and newer systems.
# Vadims Podans (c) 2013
# http://en-us.sysadmins.lv/
#####################################################################
#requires -Version 2.0

function Import-CertificateTemplate {
<#
.Synopsis
    Imports and registers certificate templates in Active Directory from a file.
.Description
    Imports certificate templates from a file that contains serialized templates. Use
    Export-CertificateTemplate command to export and serialize certificate templates.
    
    If certificate template is successfully imported, it is installed to Active Directory.
    The command must be run on a Windows 7/Windows Server 2008 R2 or newer OS. Windows
    Server 2003 and Windows Server 2008 are not supported.
    
    Note: the command generates new object identifier (OID) for the template. Existing
    OID reuse is not supported.
.Parameter Path
    Specifies the path to a file that contains exported certificate templates.
.Parameter ServerName
    Specifies the DNS name of the Active Directory server to which the changes will be applied.
    If this value is NULL, the changes will be applied to the default domain controller.
.Example
    Import-CertificateTemplate c:\temp\templates.dat
#>
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [IO.FileInfo]$Path,
        [Alias('DNSName','DC','DomainController','DomainControllerName','ComputerName')]
        [string]$ServerName
    )
    if (
        [Environment]::OSVersion.Version.Major -lt 6 -or
        [Environment]::OSVersion.Version.Major -eq 6 -and
        [Environment]::OSVersion.Version.Minor -lt 1
    ) {throw New-Object PlatformNotSupportedException}
    
    $bytes = [IO.File]::ReadAllBytes($Path)
    $pol = New-Object -ComObject X509Enrollment.CX509EnrollmentPolicyWebService
    $pol.InitializeImport($bytes)
    $templates = $pol.GetTemplates() | ForEach-Object {$_}
    $templates | ForEach-Object {
        $adwt = New-Object -ComObject X509Enrollment.CX509CertificateTemplateADWritable
        $adwt.Initialize($_)
        $adwt.Commit(1,$ServerName)
    }
}

The code do not implements any error handling, so errors may occur. To avoid errors and other unpredictable results, consider the following common restrictions:

  • Certificate template MUST NOT be version 1 (default unmanaged templates);
  • Certificate template MUST NOT be default certificate that are installed by default when you install first CA server in the forest;
  • Target forest MUST NOT contains a certificate template with common and/or display name that matches with names of the template being imported;
  • you MUST NOT modify generated XML file.

These scripts are great examples that demonstrate new techniques in advanced PKI/ADCS management with PowerShell. Please, test them in a test environment and add error handling before you will use them in a production environment.

HTH

Comments:

Chipeater
Chipeater 08.11.2013 22:17 (GMT+2)

Hi Vadims, I love this code and all the great PKI things you do. I wonder if it would be possible to update the code such that singular dat files are created for each certificate template listed - rather than just one blob. Using your example you'd then have SmartCardV2.dat and WebServerV3.dat, etc? This way gives more flexibility - I think to update / replace / etc. specific templates which is particularly helpful when going through development phases. FYI: I've previously always used LDIFDE to export and import certificate templates: Export: ldifde -m -v -d cn=%Template1%,%LDAP% -f %Template1%.ldf Import: ldifde -i -k -f %Template1%.ldf I doubt LDIFDE is a supported method but it certainly does work - tested through to 2012 R2. It also has the neat advantage that I can edit certain properties (such as issuance policy and CSP) directly in the ldf file. However, I do recognise that PowerShell is the future and want to get everything working that way. Anyway, if you were able to update the PowerShell code to make it do separate dat files great - if not, keep up the good work anyway. Cheers, Chipeater

Vadims Podans
Vadims Podans 08.11.2013 22:49 (GMT+2)

Few notes here: 1) LDIFDE is a working solution too, however (afaik) it export unnecessary attributes which may not be compatible when transferring certificate templates between forests. 2) you SHOULD NOT modify .ldf or .dat file directly, because there is a non-trivial logic on how different template settings are related between each other. 3) you definitely can export just single template: $Templates = Get-CertificateTemplate -Name SmartCardV2 Export-CertificateTemplate $templates c:\temp\SmartCardV2.dat you just pass a single template to "Template" parameter. or do this: Get-CertificateTemplate | foreach-object {Export-CertificateTemplate -Template $_ -Path c:\temp\($_.name).dat} in this case each template will be saved to a separate file. HTH.

Chipeater
Chipeater 09.11.2013 04:05 (GMT+2)

Hi Vadims, I agree that LDIFDE isn't the best solution - though FYI it's the -m switch on export which allows the resulting file to be "Forest-Free". The Get-CertificateTemplate | foreach-object {Export-CertificateTemplate -Template $_ -Path c:\temp\($_.name).dat} code is exactly what I was after - many thanks. I guess this seems obvious to you... but I'm tip toeing in PowerShell. Thanks again, Dave

Vadims Podans
Vadims Podans 09.11.2013 08:01 (GMT+2)

Ok, you win. LDIFDE looks like a another option. I'm not very familiar with this tool, so I ended up with PS version. Actually, this topic was a part of my MS-XCEP protocol learning. That is, you can use any tool you prefer. In the case if you want to dive to low-level details, this PS example would be helpful.

Ethan
Ethan 11.12.2013 06:01 (GMT+2)

I am trying to run these on Windows 2012 and I am not getting any output. Also when i run just the file name it doesn't output any help message as expected. I've installed the PSPKI and imported the module, get-CertificateTemplate works just fine. Any ideas what could be wrong here?

Vadims Podans
Vadims Podans 11.12.2013 20:28 (GMT+2)

This is because you didn't attached the function via dot-sourcing. In the console, you have to run" . path\scriptfile.ps1 and then run the function.

Stanislaw
Stanislaw 10.03.2014 04:01 (GMT+2)

Hi, In windows 2012, there is some issues with your scripts. I have described it in my blog post: http://wawszczak.pr0.pl/en/2014/03/09/eksportimport-szablon%c3%b3w-certyfikat%c3%b3w-z-ad-w-windows-2012/ Regards, Stanislaw Wawszczak

Vadims Podans
Vadims Podans 10.03.2014 15:18 (GMT+2)

yeah, it is the bug with with superseded templates. However, it is the only issue I see here. I don't understand your claims about function definition and CmdletBinding attribute. Can you elaborate?

Allen Hinkle
Allen Hinkle 15.10.2015 22:36 (GMT+2)

i am running under 2012.  new to all this power shell stuff.  But I created a copy of template.  ran the export, did indeed get an xml file that had stuff.  the import fails with:

 

Exception calling "InitializeImport" with "1" argument(s): "CX509EnrollmentPolicyWebService::InitializeImport: The input data was not in the expected format or did not

have the expected value. 0x803d0000 (-2143485952 WS_E_INVALID_FORMAT)"

At C:\temp\Import-CertificateTemplate.ps1:50 char:5

+ $pol.InitializeImport($bytes)

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException

+ FullyQualifiedErrorId : ComMethodTargetInvocation

Exception calling "GetTemplates" with "0" argument(s): "CX509EnrollmentPolicyWebService::GetTemplates: Uninitialized object 0x80040007 (-2147221497 OLE_E_BLANK)"

At C:\temp\Import-CertificateTemplate.ps1:51 char:5

+ $templates = $pol.GetTemplates() | ForEach-Object {$_}

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException

+ FullyQualifiedErrorId : ComMethodTargetInvocation

Exception calling "Initialize" with "1" argument(s): "Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"

At C:\temp\Import-CertificateTemplate.ps1:54 char:9

+ $adwt.Initialize($_)

+ ~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException

+ FullyQualifiedErrorId : ComMethodTargetInvocation

Exception calling "Commit" with "2" argument(s): "CertEnroll::CX509CertificateTemplateADWritable::Commit: Uninitialized object 0x80040007 (-2147221497 OLE_E_BLANK)"

At C:\temp\Import-CertificateTemplate.ps1:55 char:9

+ $adwt.Commit(1,$ServerName)

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException

+ FullyQualifiedErrorId : ComMethodTargetInvocation

Tim
Tim 13.11.2015 02:08 (GMT+2)

Vadims, I prefer using this over the LDIFDE method when importing to different forests because it's cleaner by not re-using the Template OID or having to edit the LDF file with forest specific RootDSE.

I did notice the Private Key SDDL was not exporting...looks like the ELSE statement in $PKS= section is missing "Cryptography". If i am not mistaken, "$temp.Settings.PrivateKeySecuritySDDL" should read  "$temp.Settings.Cryptography.PrivateKeySecuritySDDL". Please verify my thinking is valid. Thanks for all your work here. Regards, Tim.

Jeff
Jeff 19.10.2016 22:51 (GMT+2)

Tried running this code to export cert templates but nothing happens. Have installed the Public Key Infrastructure PowerShell module but do not see those even loading when i execute your code. Execution poiicy is RemoteSigned.

Only modules I see on my Cert server are:

 

    Directory: C:\Windows\system32\WindowsPowerShell\v1.0\Modules


ModuleType Name                                ExportedCommands
---------- ----                                ----------------
Manifest   ADRMS                               {Update-ADRMS, Uninstall-ADRMS, Install-ADRMS}
Manifest   AppLocker                           {Set-AppLockerPolicy, Get-AppLockerPolicy, Test-AppLockerPolicy, Get-...
Manifest   BestPractices                       {Get-BpaModel, Invoke-BpaModel, Get-BpaResult, Set-BpaResult}
Manifest   BitsTransfer                        {Add-BitsFile, Remove-BitsTransfer, Complete-BitsTransfer, Get-BitsTr...
Manifest   CimCmdlets                          {Get-CimAssociatedInstance, Get-CimClass, Get-CimInstance, Get-CimSes...
Script     ISE                                 {New-IseSnippet, Import-IseSnippet, Get-IseSnippet}
Manifest   Microsoft.PowerShell.Diagnostics    {Get-WinEvent, Get-Counter, Import-Counter, Export-Counter...}
Manifest   Microsoft.PowerShell.Host           {Start-Transcript, Stop-Transcript}
Manifest   Microsoft.PowerShell.Management     {Add-Content, Clear-Content, Clear-ItemProperty, Join-Path...}
Manifest   Microsoft.PowerShell.Security       {Get-Acl, Set-Acl, Get-PfxCertificate, Get-Credential...}
Manifest   Microsoft.PowerShell.Utility        {Format-List, Format-Custom, Format-Table, Format-Wide...}
Manifest   Microsoft.WSMan.Management          {Disable-WSManCredSSP, Enable-WSManCredSSP, Get-WSManCredSSP, Set-WSM...
Script     PSDiagnostics                       {Disable-PSTrace, Disable-PSWSManCombinedTrace, Disable-WSManTrace, E...
Binary     PSScheduledJob                      {New-JobTrigger, Add-JobTrigger, Remove-JobTrigger, Get-JobTrigger...}
Manifest   PSWorkflow                          {New-PSWorkflowExecutionOption, New-PSWorkflowSession, nwsn}
Manifest   PSWorkflowUtility                   Invoke-AsWorkflow
Manifest   ServerManager                       {Get-WindowsFeature, Add-WindowsFeature, Remove-WindowsFeature}
Manifest   TroubleshootingPack                 {Get-TroubleshootingPack, Invoke-TroubleshootingPack}
Manifest   WebAdministration                   {Start-WebCommitDelay, Stop-WebCommitDelay, Get-WebConfigurationLock,...

 

Please advise how to get this code to export our certificate templates

Vadims Podāns
Vadims Podāns 24.10.2016 22:34 (GMT+2)

Hi, these commands are not part of the PSPKI module, they are available as a standalone scripts. You can copy/paste the code from this article.

Someone
Someone 07.11.2016 19:25 (GMT+2)

just comment the line
function Export-CertificateTemplate {
 and the last curly brace

then the script works as expected.

 

Regards

A
A 06.02.2017 17:40 (GMT+2)

Hi Vadims,

Quick question, in a non XP/2003 environment, do you still need to copy the template over and/or execute PKISync ? I thought this was only a requirement when you had XP or 2003 based clients.

Regards,

Alex

Vadims Podāns
Vadims Podāns 06.02.2017 23:22 (GMT+2)

Template copy between forests (in the case of cross-forest enrollment) is still necessary if you are not using certificate enrollment web services which are supported starting with Windows 7.

Fred
Fred 27.06.2017 12:48 (GMT+2)

Hi Vadims,

not quite sure what's wrong in my lab - I've dot sourced the function, and can see it under dir function:\. No problem getting it to start. When checking the available template with Get-CertificateTemplate, I see a list of all available templates, 'FredLab OCSP Signing Response' amongst others. This is a v4 copy of the OCSP Signing Response Template. When selecting any copied Template (all copied Templates are v4), I receive the error, that 'a Template with that name' could not be found - even though I've copy/pasted it from the output of Get-CertificateTemplate. Any idea what might be going on there?

Thanks & Best Regards,

Fred

Captcha