Category: Windows

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=”Example Company, Inc”, L=Temple Terrace, S=Florida,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 HOSTNAME for DOMAIN\USER

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.

 

Verifying WinRM access to CAPI

Powershell script that verifies WinRM access and lists certs from CAPI store.

# ============================================================================
# User-configurable variables
# ============================================================================

$TargetHost = ‘hostname.example.com’
$TargetLocalComputerName = ‘HOSTNAME
$Username = ‘localuserid’
$Password = ‘localuserpassword’
$Port = 5985

Set-StrictMode -Version Latest
$ErrorActionPreference = ‘Stop’

# Remote certificate store to inspect.
# Common values:
# Cert:\LocalMachine\My
# Cert:\LocalMachine\WebHosting
# Cert:\LocalMachine\Root
# Cert:\LocalMachine\CA
$RemoteCertStorePath = ‘Cert:\LocalMachine\My’

# Optional subject filter. Leave blank to return everything in the store.
$CertificateSubjectFilter = ”

# ============================================================================
# Build local SAM credential
# ============================================================================

# Unqualified – fails
# $QualifiedUsername = $Username
# .\ – works
# $QualifiedUsername = ‘{0}\{1}’ -f ‘.’, $Username
# With hostname – works
$QualifiedUsername = ‘{0}\{1}’ -f $TargetLocalComputerName, $Username

$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
$Credential = [System.Management.Automation.PSCredential]::new($QualifiedUsername, $SecurePassword)

# ============================================================================
# Check TrustedHosts on the client
# ============================================================================

$trustedHostsValue = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
$trustedHostEntries = @()

if (-not [string]::IsNullOrWhiteSpace($trustedHostsValue)) {
$trustedHostEntries = $trustedHostsValue -split ‘\s*,\s*’ | Where-Object {
-not [string]::IsNullOrWhiteSpace($_)
}
}

$trustedHostMatch = $false
foreach ($entry in $trustedHostEntries) {
if ($TargetHost -like $entry -or $TargetLocalComputerName -like $entry) {
$trustedHostMatch = $true
break
}
}

Write-Host ”
Write-Host ‘=== Client Context ===’ -ForegroundColor Cyan
[pscustomobject]@{
RunningAs = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
TargetHost = $TargetHost
Port = $Port
CredentialUser = $Credential.UserName
TrustedHostsValue = $trustedHostsValue
TrustedHostMatched = $trustedHostMatch
RemoteCertStorePath = $RemoteCertStorePath
CertificateSubjectFilter = $CertificateSubjectFilter
} | Format-List

if (-not $trustedHostMatch) {
Write-Warning “TrustedHosts does not appear to include $TargetHost or $TargetLocalComputerName. HTTP/5985 with a local account will usually fail until that is fixed.”
}

# ============================================================================
# Raw TCP check
# ============================================================================

Write-Host ”
Write-Host ‘=== TCP Connectivity Check ===’ -ForegroundColor Cyan

$tcpClient = [System.Net.Sockets.TcpClient]::new()
try {
$asyncResult = $tcpClient.BeginConnect($TargetHost, $Port, $null, $null)
if (-not $asyncResult.AsyncWaitHandle.WaitOne(3000, $false)) {
throw “Timed out connecting to $TargetHost`:$Port”
}

$null = $tcpClient.EndConnect($asyncResult)
Write-Host “TCP connect to $TargetHost`:$Port succeeded.” -ForegroundColor Green
}
catch {
Write-Host “TCP connect to $TargetHost`:$Port failed: $($_.Exception.Message)” -ForegroundColor Red
throw
}
finally {
$tcpClient.Dispose()
}

# ============================================================================
# WinRM over HTTP/5985 test + remote machine-store certificate inventory
# ============================================================================

Write-Host ”
Write-Host ‘=== WinRM HTTP/5985 Test ===’ -ForegroundColor Cyan

$session = $null

try {
$session = New-PSSession `
-ComputerName $TargetHost `
-Port $Port `
-Authentication Negotiate `
-Credential $Credential `
-ErrorAction Stop

$remoteResult = Invoke-Command -Session $session -ErrorAction Stop -ArgumentList $RemoteCertStorePath, $CertificateSubjectFilter -ScriptBlock {
param(
[string]$StorePath,
[string]$SubjectFilter
)

$latfp = $null
try {
$latfp = Get-ItemPropertyValue `
-Path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System’ `
-Name ‘LocalAccountTokenFilterPolicy’ `
-ErrorAction Stop
}
catch {
$latfp = $null
}

$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [System.Security.Principal.WindowsPrincipal]::new($identity)
$isAdmin = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)

if (-not (Test-Path -Path $StorePath)) {
throw “Certificate store path not found: $StorePath”
}

$certs = Get-ChildItem -Path $StorePath -ErrorAction Stop

if (-not [string]::IsNullOrWhiteSpace($SubjectFilter)) {
$certs = $certs | Where-Object { $_.Subject -like “*$SubjectFilter*” }
}

$certInventory = foreach ($cert in $certs | Sort-Object NotAfter, Subject) {
$sanEntries = @()
$dnsNameList = $null

try {
$dnsNameList = $cert.DnsNameList
}
catch {
$dnsNameList = $null
}

if ($null -ne $dnsNameList) {
foreach ($dnsName in $dnsNameList) {
if ($null -ne $dnsName -and -not [string]::IsNullOrWhiteSpace($dnsName.Unicode)) {
$sanEntries += $dnsName.Unicode
}
}
}

$ekuEntries = @()
foreach ($eku in $cert.EnhancedKeyUsageList) {
if (-not [string]::IsNullOrWhiteSpace($eku.FriendlyName)) {
$ekuEntries += $eku.FriendlyName
}
elseif (-not [string]::IsNullOrWhiteSpace($eku.ObjectId)) {
$ekuEntries += $eku.ObjectId
}
}

[pscustomobject]@{
StorePath = $StorePath
Subject = $cert.Subject
Thumbprint = $cert.Thumbprint
NotBefore = $cert.NotBefore
NotAfter = $cert.NotAfter
HasPrivateKey = $cert.HasPrivateKey
Issuer = $cert.Issuer
SerialNumber = $cert.SerialNumber
SignatureAlgorithm = $cert.SignatureAlgorithm.FriendlyName
PublicKeyOid = $cert.PublicKey.Oid.FriendlyName
Archived = $cert.Archived
DnsNames = ($sanEntries -join ‘; ‘)
EnhancedKeyUsage = ($ekuEntries -join ‘; ‘)
PSParentPath = $cert.PSParentPath
}
}

[pscustomobject]@{
RemoteComputerName = $env:COMPUTERNAME
RemoteIdentity = $identity.Name
IsAdministrator = $isAdmin
LocalAccountTokenFilterPolicy = $latfp
WinRMServiceStatus = (Get-Service -Name WinRM).Status.ToString()
PSVersion = $PSVersionTable.PSVersion.ToString()
QueriedStorePath = $StorePath
CertificateCount = @($certInventory).Count
Certificates = @($certInventory)
}
}

Write-Host ‘WinRM HTTP/5985 test succeeded.’ -ForegroundColor Green

Write-Host ”
Write-Host ‘=== Remote Probe Data ===’ -ForegroundColor Cyan
[pscustomobject]@{
RemoteComputerName = $remoteResult.RemoteComputerName
RemoteIdentity = $remoteResult.RemoteIdentity
IsAdministrator = $remoteResult.IsAdministrator
LocalAccountTokenFilterPolicy = $remoteResult.LocalAccountTokenFilterPolicy
WinRMServiceStatus = $remoteResult.WinRMServiceStatus
PSVersion = $remoteResult.PSVersion
QueriedStorePath = $remoteResult.QueriedStorePath
CertificateCount = $remoteResult.CertificateCount
} | Format-List

Write-Host ”
Write-Host “=== Certificates in $($remoteResult.QueriedStorePath) ===” -ForegroundColor Cyan

if ($remoteResult.CertificateCount -eq 0) {
Write-Host ‘No certificates matched the requested store/filter.’ -ForegroundColor Yellow
}
else {
$remoteResult.Certificates |
Select-Object Subject, Thumbprint, NotAfter, HasPrivateKey, DnsNames, EnhancedKeyUsage |
Format-Table -Wrap -AutoSize
}

Write-Host ”
Write-Host ‘=== Computed Summary ===’ -ForegroundColor Cyan
[pscustomobject]@{
Http5985Success = $true
RemoteIdentity = $remoteResult.RemoteIdentity
IsAdministrator = $remoteResult.IsAdministrator
LocalAccountTokenFilterPolicy = $remoteResult.LocalAccountTokenFilterPolicy
WinRMServiceStatus = $remoteResult.WinRMServiceStatus
QueriedStorePath = $remoteResult.QueriedStorePath
CertificateCount = $remoteResult.CertificateCount
} | Format-List
}
catch {
Write-Host “WinRM HTTP/5985 test failed: $($_.Exception.Message)” -ForegroundColor Red

[pscustomobject]@{
Http5985Success = $false
ErrorMessage = $_.Exception.Message
HResult = (‘0x{0:X8}’ -f ($_.Exception.HResult -band 0xffffffff))
FullyQualifiedErrorId = $_.FullyQualifiedErrorId
} | Format-List
}
finally {
if ($null -ne $session) {
Remove-PSSession -Session $session -ErrorAction SilentlyContinue
}
}

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 ====




Chocolate Chip Cookies with Dark Cherries and Almond Flour

Anya made me birthday cookies! I wanted to save the recipe because they turned out really well.

 
Ingredients:
  • 1 3/4 cups all-purpose flour
  • 1/2 cup almond flour
  • 1 teaspoon baking soda
  • 1/2 teaspoon salt
  • 1 cup unsalted butter, softened
  • 1 cup maple syrup
  • 1/2 cup plain Greek yogurt
  • 2 large eggs
  • 2 cups semi-sweet chocolate chips
  • 1 cup frozen dark cherries, roughly chopped
Instructions:
  1. Preheat your oven to 375°F. Line baking sheets with parchment paper.
  2. In a medium bowl, whisk together the all-purpose flour, almond flour, baking soda, and salt. Set aside.
  3. In a large mixing bowl, beat the softened butter with the maple syrup until well mixed. The mixture will be looser than a typical creamed butter-sugar mixture due to the syrup.
  4. Mix in the Greek yogurt until smooth. Beat in the eggs one at a time, mixing well after each addition.
  5. Gradually add the dry ingredients to the wet ingredients, mixing until just combined. The dough will be a bit softer due to the syrup and yogurt.
  6. Gently fold in the chocolate chips and frozen dark cherries until evenly distributed throughout the dough.
  7. Drop rounded tablespoons of dough onto the prepared baking sheets, leaving about 2 inches between each cookie to allow for spreading.
  8. Bake the cookies in the preheated oven for 10-12 minutes, or until the edges are golden brown and the centers are set but still soft. If you made really big cookies, this may be more like 20 minutes!
  9. Allow the cookies to cool on the baking sheet for about 5 minutes before transferring them to a wire rack to cool completely.

Typing Unicode Characters

Found an interesting way to enter Unicode characters in Windows (beyond finding it in charmap and then copy/pasting the character!). There’s the technique where you hold alt, hit the plus on your numeric keypad, enter the hex code for the character, then release the alt key. Since a lot of laptops don’t have numeric keypads … this approach isn’t always feasible.

But there’s another way — once you’ve typed the hex code and your cursor is immediately after the code, press Alt-x …

The code magics itself into a Unicode character. You can even put your cursor immediately after a Unicode character, press Alt-x, and the character will turn back into the hex code.

Baked French Toast Casserole with Maple Syrup

Ingredients

  • 1 loaf French bread (~16 ounces)
  • 8 large eggs
  • 1.5 cups heavy whipping cream
  • 1.5 cups milk
  • 2 tablespoons maple syrup
  • 1 teaspoon vanilla extract
  • 1/4 teaspoon ground cinnamon
  • 1/4 teaspoon ground nutmeg
  • Dash salt

Directions

Slice bread into 20 slices, 1-inch each. Arrange slices in a generously buttered 9 by 13-inch baking dish in 2 rows, overlapping the slices. In a large bowl, combine the eggs, half-and-half, milk, sugar, vanilla, cinnamon, nutmeg and salt and beat with a rotary beater or whisk until blended but not too bubbly. Pour mixture over the bread slices, making sure all are covered evenly with the milk-egg mixture. Spoon some of the mixture in between the slices. Cover with foil and refrigerate overnight.

The next day, preheat oven to 350 degrees F.

Bake for 40 minutes, until puffed and lightly golden. Serve with maple syrup, fresh fruit, and fresh whipped cream.