Venafi Issue – Certs Immediately Revoked as Superseded When Using Stand-Alone Microsoft AD CS CA

Background:

Environment

  • Dev environment, Venafi 25.3.0.2740
  • Microsoft ADCS stand-alone CA
  • Enrollment method: DCOM
  • CA object uses a local account on the ADCS server
  • No custom workflows
  • No customizations
  • No consumers/app installation tied to the cert object
  • Simple certificate object created for testing

Problem

When a certificate is requested from Venafi against the stand-alone Microsoft CA, ADCS successfully issues the certificate, but the certificate is immediately revoked with revocation reason:

  • Superseded

This is happening to the same certificate that was just issued, not a prior cert.

Expected behavior

Venafi should submit the CSR, obtain the issued certificate, and leave the newly issued certificate valid.

Actual behavior

Venafi submits the CSR, ADCS issues the certificate successfully, and then the same certificate is immediately revoked as Superseded.

Evidence gathered

1. ADCS database confirms issued cert is the same cert being revoked

Example request:

  • Request ID: 41
  • Requester Name: HOSTNAME\venafi
  • Common Name: 20260331-withrevoke.example.com
  • Serial Number: 55000000299749d000d299f5ae000100000029
  • Disposition: Revoked
  • Disposition Message: Revoked by HOSTNAME\venafi
  • Revocation Reason: 0x4 — Superseded

This proves Venafi is revoking the cert it just obtained.

2. ADCS request contents are valid

For the same request, ADCS shows the CSR and issued certificate are normal and match expectations.

Request attributes

  • CertificateTemplate: WebServer
  • ccm: venafihost.servers.example.com

CSR / issued cert contents

  • Subject: CN=20260331-withrevoke.example.com, O=”Uniti Group, Inc”, L=Little Rock, S=Arkansas, C=US
  • SAN: DNS Name=20260331-withrevoke.example.com
  • RSA 2048 key
  • Certificate issued successfully before revoke

This suggests the CA is not returning malformed or obviously incorrect cert content.

3. Security event log confirms immediate issue then revoke

After enabling Certification Services auditing, Security log shows this sequence:

Event 4886

  • Certificate Services received the request

Event 4887

  • Certificate Services approved the request and issued the certificate
  • Requester: HOSTNAME\venafi
  • DCOM/RPC authentication path used
  • Template shown as WebServer

Event 4870

  • Certificate Services revoked the certificate
  • Same serial number as the issued certificate
  • Reason: 4 (Superseded)

This happens effectively immediately.

4. Pattern is repeatable

Querying the CA database for requests from HOSTNAME\venafi shows a repeated pattern where most requests are immediately revoked with:

  • Disposition: Revoked
  • Revocation Reason: Superseded
  • Disposition Message: Revoked by HOSTNAME\venafi

The exceptions were tests where revoke capability had been intentionally removed from the Venafi CA account.

5. Permission test changed behavior but did not fix root cause

When Issue and Manage Certificates was removed from the Venafi CA account, the request no longer completed the revoke path and instead failed earlier with:

  • PostCSR failed with error: CCertAdmin::SetCertificateExtension: Access is denied. 0x80070005

This indicates Venafi is performing CA administrative operations after CSR submission, and revocation happens later in that same general post-issuance path.

6. Procmon on the Venafi host shows VPlatform.exe using both CertRequest and CertAdmin

Procmon on CWWAPP1989D captured VPlatform.exe doing the following:

Cert enrollment path

VPlatform.exe queries and activates:

  • HKCR\CLSID\{98AFF3F0-5524-11D0-8812-00A0C903B83C}
  • CertRequest Class
  • C:\Windows\System32\certcli.dll

CA admin path

VPlatform.exe then queries and activates:

  • HKCR\CLSID\{37EABAF0-7FB6-11D0-8817-00A0C903B83C}
  • CertAdmin Class
  • %systemroot%\system32\certadm.dll

DCOM/RPC communication

Procmon also shows:

  • endpoint mapper (135) traffic via svchost.exe
  • VPlatform.exe connecting to the CA host on dynamic RPC port 50014

This strongly suggests:

  • VPlatform.exe first issues via CertRequest
  • then immediately performs CA admin operations via CertAdmin

Given the ADCS security logs, that admin path appears to be what revokes the newly issued cert.

Additional observations

Stand-alone CA

This is a stand-alone Microsoft CA, not enterprise template-based ADCS.

No special Venafi workflow/customization

This is a dev system with:

  • no custom workflows
  • no special consumers
  • no installation/application integration
  • minimal test object

That makes this look less like an environmental customization problem and more like:

  • default Venafi behavior in this integration path, or
  • a product defect in the stand-alone Microsoft CA DCOM path

Failed auth events also observed

We saw Security log 4625 failures from CWWAPP1989D for WINDSTREAM\g9897431.

From the Security log:

  • 11:53:34 — 4886 request received
  • 11:53:36 — 4887 certificate issued
  • 11:53:36 — 4870 certificate revoked
  • 11:53:36 — multiple 4625 failures for DOMAIN\venafisystemuser
  • 11:53:37 — another 4625

Since time resolution in the log is seconds, it is possible Venafi is requesting the cert under the configured credential (HOSTNAME\venafi), attempting to do something else under DOMAIN\venafisystemuser, getting an auth failure, and then revoking the certificate under the configured credential (DOMAIN\venafisystemuser). I would be surprised if this is the case because “superseded” is a very specific revocation reason. I would expect something like a generic “Unspecified” or “Cessation of Operation” to be used.

Summary conclusion

Current evidence indicates that:

  • Venafi successfully enrolls the certificate from the stand-alone Microsoft CA using DCOM / CertRequest
  • VPlatform.exe then immediately invokes the Microsoft CA admin COM interface (CertAdmin)
  • the newly issued certificate is then revoked by the Venafi CA account with reason Superseded

At this point, this appears to be:

  • Venafi-driven post-issuance behavior
  • not spontaneous ADCS behavior
  • and likely either:
    1. expected-but-unwanted default behavior in this integration mode, or
    2. a product defect in the stand-alone Microsoft CA DCOM workflow

Resolution

The issue was resolved by changing the policy module settings to set the cert request to pending instead of automatically issue. While I expected this to leave the cert in a pending state and require manual intervention (or a batch job to bulk approve whatever is pending), the cert was immediately issued.

 

Buzzards on the Roof

The buzzards used to hang out in this old, dead tree across the street; and, after the rain, they would spread their wings and dry out. The tree fell a while ago; but, now that our roof is stable … we’ve got buzzards airing out their wings on the roof.

Creating and Using an AD CS Custom Policy Module

This information relates to the custom CA policy module implementation at https://github.com/ljr55555/CustomEkuPolicy

Values Used in this Implementation

The following GUID values are used within the code

LIBID CustomEkuPolicyModule 71e54225-3720-45d5-a181-1ce854b74c58

CLSID CustomEkuPolicyModule bea77360-4ed0-469c-a888-7b5cac3b8776

Project GUID 98d72278-b7ab-4f6b-817c-55dac0796675

Documentation is written while adding the custom module to a CA named UG-IssuingCA-02

Code Overview

CustomEkuPolicy.dll is a custom AD CS policy module implemented as a native ATL COM DLL. It is designed to work as a wrapper around Microsoft’s default AD CS policy module rather than replacing that behavior entirely.

Purpose

The DLL adds a controlled enhancement to certificate issuance:

  • preserve normal Microsoft CA policy behavior
  • preserve compatibility with Venafi/MS CA workflows
  • inject Server Authentication and Client Authentication EKUs only when appropriate

High-level behavior

When the CA processes a certificate request, the module does the following:

  1. Loads and delegates to the Microsoft default policy module:
    • CertificateAuthority_MicrosoftDefault.Policy
  2. Lets the default policy module evaluate the request normally:
    • issuance
    • pending/deny decisions
    • standard AD CS behavior
  3. If the default policy module decides to issue immediately:
    • inspect the request/certificate context
    • determine whether the certificate is eligible for EKU injection
  4. Inject the following EKUs only when all conditions are met:
    • Server Authentication: 1.3.6.1.5.5.7.3.1
    • Client Authentication: 1.3.6.1.5.5.7.3.2

Injection conditions

EKU injection occurs only if:

  • the default Microsoft policy module returned issue now
  • the certificate is not a CA certificate
  • the certificate’s template/type name is allowed
  • the certificate does not already contain an EKU extension

If any of those conditions are not met, the request is left unchanged.

Why the wrapper design is used

Earlier testing showed that completely replacing Microsoft’s default policy logic caused incompatibility with Venafi.
To avoid that, this module wraps the default policy module and preserves its normal behavior first, then applies the EKU enhancement afterward.

This design is what allows:

  • successful issuance through Venafi
  • retention of normal CA behavior
  • selective EKU injection

Template/type filtering

The module supports a registry-driven allow-list of certificate template/type names.

Registry path:

HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CAName>\PolicyModules\CustomEkuPolicy.Module

 

Registry value:

AllowedTemplateNames

Type:

REG_MULTI_SZ

If the registry value is missing or empty, the module defaults to allowing only:

WebServer

This prevents the module from broadly injecting TLS EKUs into unrelated certificate types such as code-signing or other special-purpose leaf certificates.

Important implementation details

  • Implemented as ICertPolicy2
  • Built as an in-process COM DLL loaded by certsrv.exe
  • Uses ATL for COM plumbing
  • Uses CryptoAPI to ASN.1-encode the EKU extension
  • Supports binary extension values returned by AD CS as either:
    • VT_BSTR
    • VT_ARRAY | VT_UI1

Failure behavior

If EKU injection fails:

  • the module logs the failure
  • the default Microsoft issuance decision is preserved

This means the module is effectively fail-open with respect to EKU injection, to avoid breaking certificate issuance workflows.

Result

In normal operation, certificates of the allowed type that do not already contain EKUs are issued with:

  • Server Authentication
  • Client Authentication

Other certificate types are left unchanged.

Function Reference Table

Function Location Called from Calls Purpose
DllMain dllmain.cpp Windows loader _AtlModule.DllMain Standard DLL entry point for process/thread attach and detach.
DllCanUnloadNow dllmain.cpp COM runtime _AtlModule.DllCanUnloadNow Standard COM export used to determine whether the DLL can be unloaded.
DllGetClassObject dllmain.cpp COM runtime _AtlModule.DllGetClassObject Standard COM export that returns the class factory for CCustomEkuPolicyModule.
DllRegisterServer dllmain.cpp COM registration tools / COM runtime if invoked _AtlModule.DllRegisterServer Standard COM registration export. Manual registry registration is used operationally instead.
DllUnregisterServer dllmain.cpp COM registration tools / COM runtime if invoked _AtlModule.DllUnregisterServer Standard COM unregistration export.
GetTypeInfoCount CustomEkuPolicyModule.cpp COM/IDispatch clients if they query dispatch metadata none Minimal IDispatch stub implementation. Returns no type information.
GetTypeInfo CustomEkuPolicyModule.cpp COM/IDispatch clients none Minimal IDispatch stub implementation. Not implemented for this module.
GetIDsOfNames CustomEkuPolicyModule.cpp COM/IDispatch clients none Minimal IDispatch stub implementation. Not used by AD CS in this design.
Invoke CustomEkuPolicyModule.cpp COM/IDispatch clients none Minimal IDispatch stub implementation. Not used by AD CS in this design.
Initialize CustomEkuPolicyModule.cpp AD CS when the policy module is initialized EnsureDefaultPolicyLoaded, LoadConfiguration, m_spDefaultPolicy->Initialize Stores the CA config string, loads registry-based configuration, loads the Microsoft default policy module, and delegates initialization to the default module.
VerifyRequest CustomEkuPolicyModule.cpp AD CS for each certificate request EnsureDefaultPolicyLoaded, m_spDefaultPolicy->VerifyRequest, TryInjectDefaultEku, LogHr Main policy processing function. Delegates request handling to the Microsoft default policy module and, if the request is immediately issued, attempts EKU injection.
GetDescription CustomEkuPolicyModule.cpp AD CS / administrative interfaces none Returns a human-readable description of the policy module.
ShutDown CustomEkuPolicyModule.cpp AD CS during service shutdown / module unload m_spDefaultPolicy->ShutDown Delegates shutdown to the default policy module.
GetManageModule CustomEkuPolicyModule.cpp AD CS / administrative interfaces m_spDefaultPolicy2->GetManageModule Pass-through to the default policy module’s ICertPolicy2::GetManageModule() when available.
EnsureDefaultPolicyLoaded CustomEkuPolicyModule.cpp Initialize, VerifyRequest CLSIDFromProgID, CoCreateInstance, QueryInterface Loads the Microsoft default policy module (CertificateAuthority_MicrosoftDefault.Policy) and caches interface pointers.
LoadConfiguration CustomEkuPolicyModule.cpp Initialize LoadAllowedTemplateNamesFromRegistry Loads custom registry-driven configuration for the wrapper module.
LoadAllowedTemplateNamesFromRegistry CustomEkuPolicyModule.cpp LoadConfiguration RegOpenKeyExW, RegQueryValueExW, RegCloseKey, LogInfo Reads AllowedTemplateNames from the CA policy module registry key and populates the internal allow-list. Falls back to WebServer if not configured.
TryInjectDefaultEku CustomEkuPolicyModule.cpp VerifyRequest CoCreateInstance, SetContext, IsCaRequest, GetTemplateName, IsTemplateAllowed, IsExtensionPresent, BuildDefaultEkuEncoded, SetExtensionBytes, LogInfo, LogHr Performs the conditional EKU injection logic after default policy approval.
GetExtensionBytes CustomEkuPolicyModule.cpp IsExtensionPresent, IsCaRequest, GetTemplateName ICertServerPolicy::GetCertificateExtension, ICertServerPolicy::GetCertificateExtensionFlags, VariantClear Reads a certificate/request extension value from ICertServerPolicy as binary.
SetExtensionBytes CustomEkuPolicyModule.cpp TryInjectDefaultEku SysAllocStringByteLen, ICertServerPolicy::SetCertificateExtension, VariantClear Writes a binary certificate/request extension value back through ICertServerPolicy. Used to inject EKU.
IsExtensionPresent CustomEkuPolicyModule.cpp TryInjectDefaultEku GetExtensionBytes, VariantClear Checks whether a specific extension OID already exists in the current request/certificate context.
BuildDefaultEkuEncoded CustomEkuPolicyModule.cpp TryInjectDefaultEku CryptEncodeObjectEx Builds and ASN.1-encodes the default EKU extension containing Server Auth and Client Auth OIDs.
IsCaRequest CustomEkuPolicyModule.cpp TryInjectDefaultEku GetExtensionBytes, VariantBinaryToBytes, CryptDecodeObjectEx, VariantClear, LocalFree Reads and decodes Basic Constraints to determine whether the request is for a CA certificate.
VariantBinaryToBytes CustomEkuPolicyModule.cpp IsCaRequest, GetTemplateName SysStringByteLen, SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayAccessData, SafeArrayUnaccessData Normalizes binary values returned from AD CS (VT_BSTR or `VT_ARRAY
GetTemplateName CustomEkuPolicyModule.cpp TryInjectDefaultEku GetExtensionBytes, VariantBinaryToBytes, CryptDecodeObjectEx, VariantClear, LocalFree Reads and decodes the certificate template/type name extension (1.3.6.1.4.1.311.20.2).
IsTemplateAllowed CustomEkuPolicyModule.cpp TryInjectDefaultEku EqualsIgnoreCase Checks whether the decoded template/type name matches one of the allowed names from configuration.
EqualsIgnoreCase CustomEkuPolicyModule.cpp IsTemplateAllowed towlower Case-insensitive string comparison helper for template/type matching.
LogEventWord CustomEkuPolicyModule.cpp LogInfo, LogHr RegisterEventSourceW, ReportEventW, DeregisterEventSource Low-level Windows Event Log writer for the CustomEkuPolicy source.
LogInfo CustomEkuPolicyModule.cpp Multiple internal functions LogEventWord Convenience wrapper for informational event logging.
LogHr CustomEkuPolicyModule.cpp Multiple internal functions StringCchPrintfW, LogEventWord Convenience wrapper for logging HRESULT failures to the Windows Event Log.

Code Flow

This section describes how the main code paths in CustomEkuPolicy.dll work.

1. DLL load and COM registration

The DLL is an ATL COM in-process server.

Key file:

  • dllmain.cpp

Key responsibilities:

  • exports standard COM entry points:
    • DllMain
    • DllCanUnloadNow
    • DllGetClassObject
    • DllRegisterServer
    • DllUnregisterServer
  • exposes the COM class:
    • CCustomEkuPolicyModule

The class is registered in the ATL object map with:

OBJECT_ENTRY_AUTO(CLSID_CustomEkuPolicyModule, CCustomEkuPolicyModule)

This is what allows AD CS to instantiate the policy module through COM.

2. AD CS loads the policy module

When Certificate Services starts, AD CS reads the configured active policy module from the registry and creates the COM object.

Main class:

  • CCustomEkuPolicyModule

Implemented interface:

  • ICertPolicy2

This class is the entry point for all CA policy decisions handled by the DLL.

3. Initialize()

Function:

  • CCustomEkuPolicyModule::Initialize(BSTR strConfig)

Purpose:

  • store the CA configuration string
  • load configuration from registry
  • load the Microsoft default policy module
  • delegate initialization to the default module

Flow:

  1. Save strConfig
  2. Call EnsureDefaultPolicyLoaded()
  3. Call LoadConfiguration()
  4. Call the default module’s Initialize()

This ensures the wrapper is fully initialized before the CA begins handling requests.

4. EnsureDefaultPolicyLoaded()

Function:

  • EnsureDefaultPolicyLoaded()

Purpose:

  • create an instance of the Microsoft default policy module

How it works:

  1. Resolve the ProgID:

CertificateAuthority_MicrosoftDefault.Policy

  1. Convert it to a CLSID using CLSIDFromProgID
  2. Create the COM object with CoCreateInstance
  3. Store:
    • ICertPolicy
    • optionally ICertPolicy2

Why this matters:

  • This is the core wrapper behavior
  • Instead of replacing Microsoft policy behavior, the module delegates to it first

5. LoadConfiguration()

Function:

  • LoadConfiguration()

Purpose:

  • load custom module behavior from registry

Currently this loads:

  • AllowedTemplateNames

Supporting function:

  • LoadAllowedTemplateNamesFromRegistry()

Flow:

  1. Read the CA name from strConfig
  2. Build registry path:

HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CAName>\PolicyModules\CustomEkuPolicy.Module

  1. Read AllowedTemplateNames as REG_MULTI_SZ
  2. Store values in:
    • m_allowedTemplateNames

Fallback behavior:

  • if the value is missing or unusable, default to:
    • WebServer

This controls which certificate types are eligible for EKU injection.

6. VerifyRequest()

Function:

  • CCustomEkuPolicyModule::VerifyRequest(…)

This is the most important function in the module.

Purpose:

  • let Microsoft default policy evaluate the request
  • preserve its result
  • attempt EKU injection only after successful default issuance

Flow:

  1. Call EnsureDefaultPolicyLoaded()
  2. Call the default module’s:

m_spDefaultPolicy->VerifyRequest(…)

  1. If the default module fails:
    • return the failure unchanged
  2. If the default module returns a disposition other than:

VR_INSTANT_OK

then exit without modification

  1. If the request is being issued immediately:
    • call TryInjectDefaultEku(Context)
  2. If EKU injection fails:
    • log the failure
    • preserve the default issue decision
  3. Return success

7. TryInjectDefaultEku()

Function:

  • TryInjectDefaultEku(LONG Context)

Purpose:

  • inspect the certificate request/certificate context
  • determine whether EKU injection should happen
  • add the EKU extension if appropriate

Flow:

  1. Create ICertServerPolicy
  2. Call SetContext(Context)
  3. Determine whether the request is a CA certificate:
    • IsCaRequest()
  4. If CA cert:
    • skip
  5. Read certificate type/template name:
    • GetTemplateName()
  6. Check whether template/type is allowed:
    • IsTemplateAllowed()
  7. If not allowed:
    • skip
  8. Check whether EKU already exists:
    • IsExtensionPresent(“2.5.29.37”)
  9. If EKU already exists:
    • skip
  10. Build the encoded EKU extension:
  • BuildDefaultEkuEncoded()
  1. Write the extension:
  • SetExtensionBytes(“2.5.29.37”, …)

Only if all of those conditions succeed does the module inject the EKU.

8. IsCaRequest()

Function:

  • IsCaRequest(ICertServerPolicy* pServer, bool& isCa)

Purpose:

  • prevent EKU injection on CA certificates

How it works:

  1. Read Basic Constraints extension:
    • OID 2.5.29.19
  2. Decode it with CryptoAPI
  3. Check fCA
  4. Set isCa = true if CA certificate

This protects subordinate CA and CA certificate requests from receiving inappropriate TLS EKUs.

9. GetTemplateName()

Function:

  • GetTemplateName(ICertServerPolicy* pServer, std::wstring& templateName)

Purpose:

  • identify the certificate type/template name

How it works:

  1. Read extension:
    • 1.3.6.1.4.1.311.20.2
  2. Convert returned binary value into bytes
  3. Decode the string value
  4. Return the template/type name

Example expected values:

  • WebServer

This is the main filter used to decide whether EKU injection is allowed.

10. IsTemplateAllowed()

Function:

  • IsTemplateAllowed(const std::wstring& templateName) const

Purpose:

  • compare the certificate type name against the configured allow-list

How it works:

  • case-insensitive comparison against m_allowedTemplateNames

If the template/type is not in the allow-list, the request is left unchanged.

11. IsExtensionPresent()

Function:

  • IsExtensionPresent(ICertServerPolicy* pServer, LPCWSTR wszOid, bool& present)

Purpose:

  • determine whether a specific extension already exists

Used for:

  • checking whether 2.5.29.37 Enhanced Key Usage is already present

If it is already present, the module does not modify it.

This preserves explicitly requested EKUs.

12. BuildDefaultEkuEncoded()

Function:

  • BuildDefaultEkuEncoded(BYTE** ppbEncoded, DWORD* pcbEncoded)

Purpose:

  • ASN.1-encode the default EKU list

EKUs encoded:

  • Server Authentication
    • 1.3.6.1.5.5.7.3.1
  • Client Authentication
    • 1.3.6.1.5.5.7.3.2

How it works:

  1. Build CERT_ENHKEY_USAGE
  2. Encode it using:
    • CryptEncodeObjectEx
  3. Return the encoded blob

This produces the value written into extension 2.5.29.37.

13. SetExtensionBytes()

Function:

  • SetExtensionBytes(ICertServerPolicy* pServer, LPCWSTR wszOid, const BYTE* pbData, DWORD cbData, LONG extFlags)

Purpose:

  • write a binary certificate extension back into the current request/certificate context

How it works:

  1. Convert the encoded bytes into a binary BSTR
  2. Wrap in a VARIANT
  3. Call:
    • ICertServerPolicy::SetCertificateExtension(…)

This is the actual point where EKU injection occurs.

14. VariantBinaryToBytes()

Function:

  • VariantBinaryToBytes(const VARIANT& var, std::vector<BYTE>& bytes)

Purpose:

  • normalize AD CS binary extension values into a byte vector

Why it exists:

  • AD CS may return binary property values as either:
    • VT_BSTR
    • VT_ARRAY | VT_UI1

This helper makes downstream decoding logic reliable and avoids earlier failures caused by assuming only one format.

15. Logging

Functions:

  • LogEventWord()
  • LogInfo()
  • LogHr()

Purpose:

  • write operational/debug events to the Windows Application log

Used for:

  • initialization failures
  • EKU injection failures
  • important decision points

This was added mainly to make runtime behavior observable and troubleshoot CA/AD CS integration.

16. Failure model

The wrapper preserves Microsoft default behavior wherever possible.

Important principle:

  • default policy decision comes first
  • EKU injection is best effort
  • if EKU injection fails, issuance is not blocked unless the default policy blocked it

This makes the module effectively fail-open for EKU injection, while still preserving standard CA behavior.

Summary of end-to-end request flow

In simplified form, the code path is:

  1. AD CS loads CustomEkuPolicy.dll
  2. Initialize() loads the Microsoft default policy module and configuration
  3. On each request, VerifyRequest() calls the Microsoft default policy module
  4. If Microsoft policy chooses immediate issuance:
    • inspect request
    • check CA/not-CA
    • check template/type allow-list
    • check existing EKU
    • inject Server Auth + Client Auth EKU if eligible
  5. Return the Microsoft default issuance result

Build

Install Build Tools for Visual Studio 2022 with the Desktop development with C++ workload, including the MSVC v143 toolset, Windows SDK, and ATL support.

Build the project from the x64 Native Tools Command Prompt for VS 2022 using msbuild CustomEkuPolicy.vcxproj /p:Configuration=Release /p:Platform=x64

The equivalent of make clean is

msbuild CustomEkuPolicy.vcxproj /t:Clean /p:Configuration=Release /p:Platform=x64

Installation

Copy the file into a location on the CA server. I am using a path under c:\program files\EKUPolicy\CustomEkuPolicy

Create a REG file to import the COM registration

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{BEA77360-4ED0-469C-A888-7B5CAC3B8776}]

@=”CustomEkuPolicyModule Class”

“ProgID”=”CustomEkuPolicy.Module”

“VersionIndependentProgID”=”CustomEkuPolicy.Module”

[HKEY_CLASSES_ROOT\CLSID\{BEA77360-4ED0-469C-A888-7B5CAC3B8776}\InprocServer32]

@=”C:\\Program Files\\EKUPolicy\\CustomEkuPolicy\\CustomEkuPolicy.dll”

“ThreadingModel”=”Both”

[HKEY_CLASSES_ROOT\CustomEkuPolicy.Module]

@=”CustomEkuPolicyModule Class”

“CLSID”=”{BEA77360-4ED0-469C-A888-7B5CAC3B8776}”

[HKEY_CLASSES_ROOT\CustomEkuPolicy.Module\CLSID]

@=”{BEA77360-4ED0-469C-A888-7B5CAC3B8776}”

In an elevated command prompt, import the reg file and verify the values are present

Back up the current registry settings

Add key for custom module

reg add “HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CAName>\PolicyModules\CustomEkuPolicy.Module” /f

Add WebServer to the registry key for the custom policy module unless you want to rely on the DLL defaults

reg add “HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CANAME>\PolicyModules\CustomEkuPolicy.Module” /v AllowedTemplateNames /t REG_MULTI_SZ /d “WebServer\0” /f

And activate the custom module:

Restart the certificate server service

 

Blender API: List All Items in a Collection

Instead of iterating through all objects, you can iterate through the items in a specific collection:

import bpy

# Name of the collection to inspect
collection_name = "TestCollection"

collection = bpy.data.collections.get(collection_name)

if collection is None:
    print(f"Collection '{collection_name}' not found.")
else:
    print(f"Objects in collection '{collection_name}':")
    for obj in collection.objects:
        print(f"- {obj.name}")

Printing to the console:

For this sample workspace that contains a torus and sphere with the default names

Blender API: Finding The Orange Dot

A quick script to get each object and the location of the “orange dot” … the origin of the object

# Get location of orange dot for each object in Blender
import bpy

scene = bpy.context.scene
us = scene.unit_settings

unit_system = getattr(us, "system", "NONE")  # 'NONE', 'METRIC', 'IMPERIAL'

meters_per_bu = us.scale_length if unit_system != 'NONE' else 1.0
mm_per_bu = meters_per_bu * 1000.0

for obj in bpy.data.objects:
    if obj.type != 'MESH':
        continue

    origin_world = obj.matrix_world.translation          # in BU
    origin_world_mm = origin_world * mm_per_bu           # in mm

    print(f"Object: {obj.name}")
    print(f"  origin_world (BU): {origin_world.x:.6f}, {origin_world.y:.6f}, {origin_world.z:.6f}")
    print(f"  origin_world (mm): {origin_world_mm.x:.3f}, {origin_world_mm.y:.3f}, {origin_world_mm.z:.3f}")
    print("-" * 30)

Blender API: Bending a 2D Rectangle

Another attempt to create a t-post bracket using a script. This creates a 2D rectangle, bends it, and then solidifies it into a 3d object.

import bpy
import bmesh
import math
from mathutils import Vector, Matrix

# -----------------------------
# Reset / clear scene
# -----------------------------
for obj in list(bpy.data.objects):
    bpy.data.objects.remove(obj, do_unlink=True)

# -----------------------------
# Scene units: mm (1 BU = 1 mm)
# -----------------------------
scene = bpy.context.scene
scene.unit_settings.system = 'METRIC'
scene.unit_settings.scale_length = 0.001

INCH_TO_MM = 25.4
def inch(x):  # returns mm (Blender units)
    return x * INCH_TO_MM

# -----------------------------
# Parameters
# -----------------------------
size_x_in = 3.0
size_y_in = 7.0
thickness_in = 0.25  # SOLIDIFY thickness

fold1_offset_in = 0.5   # from MIN-Y end
fold2_offset_in = 2.0   # from MIN-Y end

fold1_rad = math.radians(-80.0)
fold2_rad = math.radians(80.0)

subdivide_cuts = 60
EPS_Y = 1e-5  # mm tolerance for "on the fold line"

# -----------------------------
# Create flat sheet (plane)
# -----------------------------
bpy.ops.mesh.primitive_plane_add(size=1.0, location=(0.0, 0.0, 0.0))
obj = bpy.context.active_object
obj.name = "Bracket"
obj.dimensions = (inch(size_x_in), inch(size_y_in), 0.0)
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

# Subdivide for clean fold lines
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.subdivide(number_cuts=subdivide_cuts)
bpy.ops.object.mode_set(mode='OBJECT')

# Compute fold Y positions
half_y = inch(size_y_in) / 2.0
min_y = -half_y
y_fold1 = min_y + inch(fold1_offset_in)
y_fold2 = min_y + inch(fold2_offset_in)

# Add both fold lines
bm = bmesh.new()
bm.from_mesh(obj.data)

for y_fold in (y_fold1, y_fold2):
    geom = bm.verts[:] + bm.edges[:] + bm.faces[:]
    bmesh.ops.bisect_plane(
        bm,
        geom=geom,
        plane_co=Vector((0.0, y_fold, 0.0)),
        plane_no=Vector((0.0, 1.0, 0.0)),
        clear_inner=False,
        clear_outer=False
    )

bm.normal_update()
bm.to_mesh(obj.data)
bm.free()

# -----------------------------
# Re-open bmesh, store ORIGINAL Y per vertex
# -----------------------------
bm = bmesh.new()
bm.from_mesh(obj.data)
bm.verts.ensure_lookup_table()

orig_y_layer = bm.verts.layers.float.new("orig_y")
for v in bm.verts:
    v[orig_y_layer] = v.co.y

# ============================================================
# FOLD 1
# ============================================================
hinge_verts_1 = [v for v in bm.verts if abs(v[orig_y_layer] - y_fold1) < EPS_Y]
if not hinge_verts_1:
    raise RuntimeError("No hinge vertices found for fold 1. Increase subdivide_cuts or EPS_Y.")

hinge_point_1 = Vector((0.0, 0.0, 0.0))
for v in hinge_verts_1:
    hinge_point_1 += v.co
hinge_point_1 /= len(hinge_verts_1)

verts_to_rotate_1 = [v for v in bm.verts if v[orig_y_layer] > (y_fold1 + EPS_Y)]
rot1 = Matrix.Rotation(fold1_rad, 4, 'X')
bmesh.ops.rotate(bm, verts=verts_to_rotate_1, cent=hinge_point_1, matrix=rot1)

# ============================================================
# FOLD 2
# ============================================================
hinge_verts_2 = [v for v in bm.verts if abs(v[orig_y_layer] - y_fold2) < EPS_Y]
if not hinge_verts_2:
    raise RuntimeError("No hinge vertices found for fold 2. Increase subdivide_cuts or EPS_Y.")

hinge_point_2 = Vector((0.0, 0.0, 0.0))
for v in hinge_verts_2:
    hinge_point_2 += v.co
hinge_point_2 /= len(hinge_verts_2)

verts_to_rotate_2 = [v for v in bm.verts if v[orig_y_layer] > (y_fold2 + EPS_Y)]
rot2 = Matrix.Rotation(fold2_rad, 4, 'X')
bmesh.ops.rotate(bm, verts=verts_to_rotate_2, cent=hinge_point_2, matrix=rot2)

# Write back mesh
bm.normal_update()
bm.to_mesh(obj.data)
bm.free()

# -----------------------------
# Solidify AFTER folding
# -----------------------------
solid = obj.modifiers.new(name="Solidify_0p5in", type='SOLIDIFY')
solid.thickness = inch(thickness_in)  # 0.5"
solid.offset = 0.0                    # centered thickness (equal on both sides)
solid.use_even_offset = True
solid.use_rim = True

# Optional: keep object active
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj

Blender API: Playing with Cylinders

This script was mostly made to play around with rotation on cylinders.

import bpy
import math

# Delete all existing objects
for obj in list(bpy.data.objects):
    bpy.data.objects.remove(obj, do_unlink=True)

for i in range(4):
    bpy.ops.mesh.primitive_cylinder_add(
        radius=0.5,
        depth=10.0,
        location=(0, 0.0, 0.0),
        rotation=((i * 5.5), 0.0, 0.0)
    )

    cyl = bpy.context.active_object
    cyl.name = f"DemoCylinderX{i}"

for i in range(4):
    bpy.ops.mesh.primitive_cylinder_add(
        radius=0.5,
        depth=10.0,
        location=(0, 0.0, 0.0),
        rotation=(0.0, (i * 5.5), 0.0)
    )

    cyl = bpy.context.active_object
    cyl.name = f"DemoCylinderY{i}"

# cyl.rotation_euler = (15.0,13.0,12.0)

# Or single-axis rotation
# Rotate 45 degrees about X axis
#cyl.rotation_euler[0] = math.radians(45.0)


Querying the RPC Endpoint Mapper

A lot of RPC services start out on a standard port (TCP port 135) and then move over to a dynamically allocated port. Fortunately, there’s a way to ask the RPC endpoint mapper what services are available and what port(s) have been assigned to that service. It uses the portqry command:

C:\PortQryV2>portqry -n host2043.servers.example.com -e 135 -p tcp -v

Note: the -v option only displays extra data in local mode

Querying target system called:

 host2043.servers.example.com

Attempting to resolve name to IP address...


Name resolved to 10.237.73.103

querying...

TCP port 135 (epmap service): LISTENING

Using ephemeral source port
Querying Endpoint Mapper Database...
Server's response:

UUID: 04eeb297-cbf4-466b-8a2a-bfd6a2f10bba EFSK RPC Interface
ncacn_np:host2043.servers.example.com[\\pipe\\efsrpc]

UUID: 367abb81-9844-35f1-ad32-98f038001003
ncacn_ip_tcp:host2043.servers.example.com[50007]

UUID: 91ae6020-9e3c-11cf-8d7c-00aa00c091be
ncacn_np:host2043.servers.example.com[\\pipe\\cert]

UUID: 91ae6020-9e3c-11cf-8d7c-00aa00c091be
ncacn_ip_tcp:host2043.servers.example.com[50006]

UUID: 29770a8f-829b-4158-90a2-78cd488501f7
ncacn_np:host2043.servers.example.com[\\pipe\\SessEnvPublicRpc]

UUID: 29770a8f-829b-4158-90a2-78cd488501f7
ncacn_ip_tcp:host2043.servers.example.com[50004]

UUID: 7f1343fe-50a9-4927-a778-0c5859517bac DfsDs service
ncacn_np:host2043.servers.example.com[\\PIPE\\wkssvc]

UUID: f6beaff7-1e19-4fbb-9f8f-b89e2018337c Windows Event Log
ncacn_np:host2043.servers.example.com[\\pipe\\eventlog]

UUID: f6beaff7-1e19-4fbb-9f8f-b89e2018337c Windows Event Log
ncacn_ip_tcp:host2043.servers.example.com[50002]

UUID: 1ff70682-0a51-30e8-076d-740be8cee98b
ncacn_np:host2043.servers.example.com[\\PIPE\\atsvc]

UUID: 378e52b0-c0a9-11cf-822d-00aa0051e40f
ncacn_np:host2043.servers.example.com[\\PIPE\\atsvc]

UUID: 33d84484-3626-47ee-8c6f-e7e98b113be1
ncacn_np:host2043.servers.example.com[\\PIPE\\atsvc]

UUID: 86d35949-83c9-4044-b424-db363231fd0c
ncacn_np:host2043.servers.example.com[\\PIPE\\atsvc]

UUID: 86d35949-83c9-4044-b424-db363231fd0c
ncacn_ip_tcp:host2043.servers.example.com[50003]

UUID: 3a9ef155-691d-4449-8d05-09ad57031823
ncacn_np:host2043.servers.example.com[\\PIPE\\atsvc]

UUID: 3a9ef155-691d-4449-8d05-09ad57031823
ncacn_ip_tcp:host2043.servers.example.com[50003]

UUID: c9ac6db5-82b7-4e55-ae8a-e464ed7b4277 Impl friendly name
ncacn_hvsocket:host2043.servers.example.com[F58797F6-C9F3-4D63-9BD4-E52AC020E586]

UUID: 76f226c3-ec14-4325-8a99-6a46348418af
ncacn_np:host2043.servers.example.com[\\PIPE\\InitShutdown]

UUID: d95afe70-a6d5-4259-822e-2c84da1ddb0d
ncacn_np:host2043.servers.example.com[\\PIPE\\InitShutdown]

UUID: d95afe70-a6d5-4259-822e-2c84da1ddb0d
ncacn_ip_tcp:host2043.servers.example.com[50001]

UUID: 12345778-1234-abcd-ef00-0123456789ac
ncacn_np:host2043.servers.example.com[\\pipe\\lsass]

UUID: 12345778-1234-abcd-ef00-0123456789ac
ncacn_ip_tcp:host2043.servers.example.com[50000]

UUID: 0b1c2170-5732-4e0e-8cd3-d9b16f3b84d7 RemoteAccessCheck
ncacn_np:host2043.servers.example.com[\\pipe\\lsass]

UUID: 0b1c2170-5732-4e0e-8cd3-d9b16f3b84d7 RemoteAccessCheck
ncacn_ip_tcp:host2043.servers.example.com[50000]

UUID: 0b1c2170-5732-4e0e-8cd3-d9b16f3b84d7 RemoteAccessCheck
ncacn_ip_tcp:host2043.servers.example.com[50005]

UUID: 0b1c2170-5732-4e0e-8cd3-d9b16f3b84d7 RemoteAccessCheck
ncacn_np:host2043.servers.example.com[\\pipe\\lsass]

UUID: 0b1c2170-5732-4e0e-8cd3-d9b16f3b84d7 RemoteAccessCheck
ncacn_ip_tcp:host2043.servers.example.com[50000]

UUID: 0b1c2170-5732-4e0e-8cd3-d9b16f3b84d7 RemoteAccessCheck
ncacn_ip_tcp:host2043.servers.example.com[50005]

UUID: b25a52bf-e5dd-4f4a-aea6-8ca7272a0e86 KeyIso
ncacn_np:host2043.servers.example.com[\\pipe\\lsass]

UUID: b25a52bf-e5dd-4f4a-aea6-8ca7272a0e86 KeyIso
ncacn_ip_tcp:host2043.servers.example.com[50000]

UUID: b25a52bf-e5dd-4f4a-aea6-8ca7272a0e86 KeyIso
ncacn_ip_tcp:host2043.servers.example.com[50005]

UUID: 8fb74744-b2ff-4c00-be0d-9ef9a191fe1b Ngc Pop Key Service
ncacn_np:host2043.servers.example.com[\\pipe\\lsass]

UUID: 8fb74744-b2ff-4c00-be0d-9ef9a191fe1b Ngc Pop Key Service
ncacn_ip_tcp:host2043.servers.example.com[50000]

UUID: 8fb74744-b2ff-4c00-be0d-9ef9a191fe1b Ngc Pop Key Service
ncacn_ip_tcp:host2043.servers.example.com[50005]

UUID: 51a227ae-825b-41f2-b4a9-1ac9557a1018 Ngc Pop Key Service
ncacn_np:host2043.servers.example.com[\\pipe\\lsass]

UUID: 51a227ae-825b-41f2-b4a9-1ac9557a1018 Ngc Pop Key Service
ncacn_ip_tcp:host2043.servers.example.com[50000]

UUID: 51a227ae-825b-41f2-b4a9-1ac9557a1018 Ngc Pop Key Service
ncacn_ip_tcp:host2043.servers.example.com[50005]

UUID: df1941c5-fe89-4e79-bf10-463657acf44d EFS RPC Interface
ncacn_np:host2043.servers.example.com[\\pipe\\efsrpc]

Total endpoints found: 38



==== End of RPC Endpoint Mapper query response ====