QCecuring - Enterprise Security Solutions

NDES Configuration & Troubleshooting: Complete Guide for SCEP Enrollment

PKI 11 May, 2026 · 08 Mins read

Configure Microsoft NDES (Network Device Enrollment Service) for SCEP certificate enrollment. Covers IIS setup, certificate templates, registration authority, challenge passwords, and fixes for every common NDES error.


NDES (Network Device Enrollment Service) is Microsoft’s implementation of SCEP — the protocol that lets routers, switches, firewalls, MDM-managed devices, and IoT endpoints request certificates from your AD CS infrastructure without human intervention. When it works, it’s invisible. When it breaks, you get a flood of “HTTP 403” errors and devices falling off the network.

This guide covers the full NDES lifecycle: installation, configuration, template setup, IIS hardening, and — most importantly — fixing the errors that will inevitably appear in production.


How NDES Works

NDES sits between your network devices and your Certificate Authority. Devices speak SCEP (Simple Certificate Enrollment Protocol) over HTTP/HTTPS, and NDES translates those requests into certificate enrollment operations against AD CS.

Sequence diagram showing interaction flow between components

Key components:

ComponentRole
SCEP endpointIIS application (/certsrv/mscep/mscep.dll) handling HTTP requests
Registration Authority (RA)Two certificates (encryption + signing) that NDES uses to process requests
Challenge passwordOne-time password that authorizes a single enrollment request
Certificate templateDefines what kind of certificate gets issued (key usage, validity, etc.)
Service accountDomain account running the NDES application pool

Prerequisites

Before installing NDES, verify:

RequirementDetails
AD CS Issuing CAEnterprise CA (not standalone) already operational
Dedicated serverNDES should NOT run on the CA server itself
Windows Server2016, 2019, or 2022 (2022 recommended)
IISWill be installed automatically with the NDES role
Service accountDomain user account with specific permissions
Certificate templatesPublished to AD and available for enrollment
Network accessDevices must reach NDES on port 80/443

Service Account Setup

Create a dedicated domain account for NDES:

# Create NDES service account
New-ADUser -Name "svc-ndes" `
  -SamAccountName "svc-ndes" `
  -UserPrincipalName "svc-ndes@contoso.com" `
  -Path "OU=Service Accounts,DC=contoso,DC=com" `
  -AccountPassword (ConvertTo-SecureString "P@ssw0rd!Complex" -AsPlainText -Force) `
  -Enabled $true `
  -PasswordNeverExpires $true `
  -CannotChangePassword $true

# Add to IIS_IUSRS group on the NDES server (run on NDES server)
Add-LocalGroupMember -Group "IIS_IUSRS" -Member "CONTOSO\svc-ndes"

Required permissions for the service account:

  1. Request Certificates permission on the CA
  2. Read and Enroll on the certificate template
  3. Local logon right on the NDES server
  4. Member of IIS_IUSRS local group on NDES server

Set the CA permission:

# On the CA server — grant Request Certificates permission
certutil -setreg ca\InterfaceFlags +IF_ENFORCEENCRYPTICERTREQUEST
# Then in certsrv.msc → Right-click CA → Properties → Security
# Add svc-ndes → Grant "Request Certificates"

Installation

Install the NDES Role

# Install NDES role with management tools
Install-WindowsFeature -Name ADCS-Device-Enrollment -IncludeManagementTools

# Install IIS management tools (useful for troubleshooting)
Install-WindowsFeature -Name Web-Mgmt-Console, Web-Mgmt-Tools

Configure NDES Post-Installation

# Configure NDES with the service account
Install-AdcsNetworkDeviceEnrollmentService `
  -ServiceAccountName "CONTOSO\svc-ndes" `
  -ServiceAccountPassword (ConvertTo-SecureString "P@ssw0rd!Complex" -AsPlainText -Force) `
  -CAConfig "CA-SERVER\Contoso-Issuing-CA" `
  -RAName "NDES-RA" `
  -RACountry "US" `
  -RACompany "Contoso" `
  -SigningProviderName "Microsoft Strong Cryptographic Provider" `
  -SigningKeyLength 2048 `
  -EncryptionProviderName "Microsoft Strong Cryptographic Provider" `
  -EncryptionKeyLength 2048

After installation, verify the SCEP endpoint responds:

# Test NDES endpoint
Invoke-WebRequest -Uri "http://ndes-server.contoso.com/certsrv/mscep/mscep.dll" -Method GET
# Should return HTTP 200 with SCEP capabilities

Certificate Template Configuration

NDES uses a certificate template to determine what certificates it issues. The default template is “IPSec (Offline request)” — which is almost never what you want.

Create a Custom NDES Template

  1. Open Certificate Templates Console (certtmpl.msc)
  2. Duplicate the Computer or Web Server template
  3. Configure:
TabSettingValue
GeneralTemplate nameNDES-Device-Certificate
GeneralValidity1 year (or per policy)
GeneralRenewal period6 weeks
Request HandlingPurposeSignature and encryption
Request HandlingAllow private key exportNo (unless required)
Subject NameSupply in requestYes (SCEP devices provide their own subject)
SecurityNDES service accountRead + Enroll
CryptographyKey size2048 minimum
CryptographyProviderMicrosoft RSA SChannel
ExtensionsApplication PoliciesClient Authentication + Server Authentication

Publish the Template

# Publish template to the CA
Add-CATemplate -Name "NDES-Device-Certificate" -Force

# Verify it's published
Get-CATemplate | Where-Object { $_.Name -eq "NDES-Device-Certificate" }

Configure NDES to Use Your Template

Edit the registry on the NDES server:

# Set the certificate template NDES uses
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" `
  -Name "EncryptionTemplate" -Value "NDES-Device-Certificate"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" `
  -Name "GeneralPurposeTemplate" -Value "NDES-Device-Certificate"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" `
  -Name "SignatureTemplate" -Value "NDES-Device-Certificate"

# Restart IIS to apply
iisreset /restart

All three registry values (EncryptionTemplate, GeneralPurposeTemplate, SignatureTemplate) must be set. If any one points to a non-existent or unpublished template, NDES silently fails.


Challenge Password Configuration

NDES uses one-time challenge passwords to authorize enrollment requests. By default, each password is valid for 60 minutes and can be used once.

Password Behavior Options

# Registry path for NDES password settings
$regPath = "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP"

# Option 1: Enforce passwords (default — most secure)
Set-ItemProperty -Path $regPath -Name "EnforcePassword" -Value 1

# Option 2: Disable password requirement (for MDM/Intune scenarios)
# WARNING: Any device can enroll without authorization
Set-ItemProperty -Path $regPath -Name "EnforcePassword" -Value 0

# Set password cache duration (minutes) — default 60
Set-ItemProperty -Path $regPath -Name "PasswordMax" -Value 60

# Set max number of cached passwords — default 5
Set-ItemProperty -Path $regPath -Name "PasswordCacheMax" -Value 5

# Restart after changes
iisreset /restart

Retrieving Challenge Passwords

Administrators retrieve passwords from the NDES web page:

http://ndes-server.contoso.com/certsrv/mscep_admin/

For automated enrollment (Intune, JAMF), the MDM platform retrieves challenge passwords via the NDES admin endpoint using a service account with appropriate permissions.

Flowchart showing top-down process flow


IIS Configuration and Hardening

Enable HTTPS on the NDES Endpoint

NDES installs with HTTP by default. For production, bind an SSL certificate:

# Request a web server certificate for the NDES server
# (Use your internal CA or a template with Server Authentication EKU)

# Bind SSL to the Default Web Site
New-WebBinding -Name "Default Web Site" -Protocol https -Port 443

# Assign the certificate (get thumbprint first)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*ndes*" }
$binding = Get-WebBinding -Name "Default Web Site" -Protocol https
$binding.AddSslCertificate($cert.Thumbprint, "My")

Request Size Limits

SCEP requests can be large (especially with long certificate chains). Increase IIS limits:

# Increase max request size for SCEP (default 30MB is usually fine)
Set-WebConfigurationProperty -Filter "/system.webServer/security/requestFiltering/requestLimits" `
  -PSPath "IIS:\Sites\Default Web Site\certsrv\mscep" `
  -Name "maxAllowedContentLength" -Value 52428800

# Increase URL length limit
Set-WebConfigurationProperty -Filter "/system.webServer/security/requestFiltering/requestLimits" `
  -PSPath "IIS:\Sites\Default Web Site\certsrv\mscep" `
  -Name "maxUrl" -Value 65534

Application Pool Settings

# Set the NDES app pool to use the service account
Set-ItemProperty "IIS:\AppPools\SCEP" -Name processModel.identityType -Value SpecificUser
Set-ItemProperty "IIS:\AppPools\SCEP" -Name processModel.userName -Value "CONTOSO\svc-ndes"
Set-ItemProperty "IIS:\AppPools\SCEP" -Name processModel.password -Value "P@ssw0rd!Complex"

# Enable 32-bit applications (required for NDES on 64-bit)
Set-ItemProperty "IIS:\AppPools\SCEP" -Name enable32BitAppOnWin64 -Value $true

# Restart the app pool
Restart-WebAppPool -Name "SCEP"

Troubleshooting NDES Errors

HTTP 403 Forbidden

The most common NDES error. Multiple causes:

Cause 1: Service account lacks permissions

# Verify the service account has Request Certificates on the CA
certutil -getreg ca\Security
# Look for the NDES service account with "Request Certificates" permission

# Fix: Add permission via certsrv.msc → CA Properties → Security

Cause 2: Certificate template not available

# Check which templates are published on the CA
certutil -CATemplates

# Verify the template name matches registry exactly (case-sensitive!)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" | Select-Object *Template*

Cause 3: RA certificates expired or missing

# Check RA certificates in the NDES server's Local Machine store
Get-ChildItem Cert:\LocalMachine\My | Where-Object {
  $_.Subject -like "*CEP Encryption*" -or $_.Subject -like "*Exchange Enrollment*"
} | Select-Object Subject, NotAfter, Thumbprint

# If expired, re-run NDES configuration or manually renew

Cause 4: Challenge password expired or already used

# Check NDES event log for password-related errors
Get-WinEvent -LogName "Application" -FilterXPath "*[System[Provider[@Name='NetworkDeviceEnrollmentService']]]" |
  Select-Object -First 20 TimeCreated, Message

HTTP 500 Internal Server Error

Cause 1: MSCEP DLL registration issue

# Re-register the SCEP ISAPI DLL
regsvr32 /s "C:\Windows\System32\certsrv\mscep\mscep.dll"
iisreset /restart

Cause 2: Cryptographic provider mismatch

# Check which provider NDES is configured to use
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" |
  Select-Object SigningProviderName, EncryptionProviderName

# If using a provider that's not available, reconfigure:
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography\MSCEP" `
  -Name "SigningProviderName" -Value "Microsoft Strong Cryptographic Provider"

Cause 3: IIS app pool crash

# Check if the SCEP app pool is running
Get-WebAppPoolState -Name "SCEP"

# Check event log for app pool crashes
Get-WinEvent -LogName "System" -FilterXPath "*[System[Provider[@Name='WAS']]]" |
  Where-Object { $_.Message -like "*SCEP*" } | Select-Object -First 5

”The Network Device Enrollment Service cannot process the request”

Cause: CA connectivity issue

# Test connectivity to the CA
certutil -ping -config "CA-SERVER\Contoso-Issuing-CA"

# Check RPC connectivity
Test-NetConnection -ComputerName CA-SERVER -Port 135

# Verify the CA is running
certutil -isvalid

”The certificate template is not supported by this CA"

# List templates the CA supports
certutil -CATemplates | findstr /i "NDES"

# If missing, publish the template
Add-CATemplate -Name "NDES-Device-Certificate" -Force

# Verify template version compatibility
# V2/V3 templates require Enterprise CA (not Standalone)
certutil -template "NDES-Device-Certificate" | findstr "Schema Version"

"The enrollment policy server cannot be reached”

# Verify NDES URL is accessible from the device's network
Test-NetConnection -ComputerName ndes-server.contoso.com -Port 443

# Check if the SCEP endpoint responds
Invoke-WebRequest "https://ndes-server.contoso.com/certsrv/mscep/mscep.dll?operation=GetCACaps" `
  -UseBasicParsing

# Expected response: plain text with capabilities like:
# POSTPKIOperation
# SHA-512
# SHA-256
# AES
# SCEPStandard
# Renewal

RA Certificate Renewal Failures

The NDES Registration Authority certificates expire (typically every 2 years). When they do, all enrollment stops.

# Check RA certificate expiry
Get-ChildItem Cert:\LocalMachine\My | Where-Object {
  $_.EnhancedKeyUsageList.FriendlyName -contains "Certificate Request Agent"
} | Format-Table Subject, NotAfter, Thumbprint

# Manual RA renewal: remove old RA certs and reconfigure NDES
# Step 1: Note the old thumbprints
# Step 2: Remove from store
# Step 3: Re-run Install-AdcsNetworkDeviceEnrollmentService with -Force

Monitoring NDES Health

Event Log Monitoring

# NDES logs to Application log under source "NetworkDeviceEnrollmentService"
# Key Event IDs:
#   1 = Service started
#   2 = Certificate issued successfully
#   4 = Request denied (check details for reason)
#   6 = Challenge password validation failed
#  16 = RA certificate problem

# Create a scheduled task to alert on errors
$query = @"
<QueryList>
  <Query Id="0" Path="Application">
    <Select Path="Application">
      *[System[Provider[@Name='NetworkDeviceEnrollmentService'] and (Level=2 or Level=3)]]
    </Select>
  </Query>
</QueryList>
"@

Get-WinEvent -FilterXml $query | Select-Object TimeCreated, Id, Message

Health Check Script

# ndes-health-check.ps1 — Run as scheduled task every 15 minutes

$ndesUrl = "https://ndes-server.contoso.com/certsrv/mscep/mscep.dll?operation=GetCACaps"
$alertEmail = "pki-team@contoso.com"

try {
    $response = Invoke-WebRequest -Uri $ndesUrl -UseBasicParsing -TimeoutSec 10
    if ($response.StatusCode -ne 200) {
        throw "NDES returned HTTP $($response.StatusCode)"
    }
    if ($response.Content -notmatch "POSTPKIOperation") {
        throw "NDES response missing expected capabilities"
    }
    Write-Output "NDES healthy: $(Get-Date)"
}
catch {
    $body = "NDES health check failed at $(Get-Date): $($_.Exception.Message)"
    Send-MailMessage -To $alertEmail -From "monitoring@contoso.com" `
      -Subject "ALERT: NDES Unhealthy" -Body $body -SmtpServer "smtp.contoso.com"
}

# Check RA certificate expiry (warn 30 days before)
$raCerts = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Certificate Request Agent"
}
foreach ($cert in $raCerts) {
    $daysLeft = ($cert.NotAfter - (Get-Date)).Days
    if ($daysLeft -lt 30) {
        $body = "NDES RA certificate '$($cert.Subject)' expires in $daysLeft days"
        Send-MailMessage -To $alertEmail -From "monitoring@contoso.com" `
          -Subject "WARNING: NDES RA Certificate Expiring" -Body $body -SmtpServer "smtp.contoso.com"
    }
}

NDES with Microsoft Intune

The most common modern NDES deployment is as a backend for Intune SCEP profiles. The Intune Certificate Connector bridges Intune cloud to your on-premises NDES:

Flowchart showing top-down process flow

Intune-specific configuration:

  1. Install the Microsoft Intune Certificate Connector on the NDES server (or a separate server)
  2. Disable challenge password enforcement (EnforcePassword = 0) — Intune handles authorization
  3. Configure the SCEP profile in Intune with the NDES external URL
  4. Ensure the NDES URL is reachable from managed devices (often requires Azure AD App Proxy or reverse proxy)

Production Hardening

AreaRecommendation
HTTPSAlways use TLS on the NDES endpoint — SCEP sends challenge passwords in the clear over HTTP
Network isolationPlace NDES in a DMZ; only allow inbound 443 from device networks
Service accountMinimal permissions — only Request Certificates on the CA, Enroll on the template
Challenge passwordsKeep enforcement ON unless using MDM; set short expiry (15-30 min)
RA key protectionConsider storing RA keys in an HSM for high-security environments
Template restrictionsLimit SANs, key sizes, and validity periods in the template — don’t trust device-supplied values blindly
LoggingForward NDES events to your SIEM; alert on Event ID 4 (denied) and 16 (RA issues)
PatchingNDES runs on IIS — keep Windows and IIS patched; SCEP vulnerabilities have been found before

FAQ

Q: Can NDES run on the same server as the CA?

Microsoft supports it but strongly discourages it. NDES exposes an IIS endpoint to the network — putting it on your CA increases the CA’s attack surface. Always use a dedicated NDES server, especially if the endpoint is reachable from untrusted networks.

Q: How many devices can a single NDES server handle?

A single NDES server typically handles 500-1,000 concurrent enrollment requests. For larger deployments, deploy multiple NDES servers behind a load balancer. Each NDES server needs its own RA certificates and service account.

Q: Do I need NDES if I’m using Intune only for Windows devices?

For Windows 10/11 devices joined to Azure AD, you can use PKCS certificate profiles (which don’t require NDES) instead of SCEP profiles. NDES/SCEP is primarily needed for iOS, Android, macOS, and network devices that don’t support direct PKCS enrollment.

Q: How do I renew certificates issued through NDES?

SCEP supports renewal if the device’s existing certificate is still valid. The device re-enrolls using its current certificate for authentication instead of a challenge password. Configure the template’s renewal period (e.g., 6 weeks before expiry) and ensure devices have network access to NDES at renewal time.

Q: What’s the difference between NDES and CEP/CES?

NDES uses SCEP (for network devices and MDM). CEP/CES (Certificate Enrollment Policy/Service) uses WS-Trust and HTTPS for domain-joined Windows machines that can’t reach a CA directly. CEP/CES is for Windows clients behind firewalls; NDES is for non-Windows devices and network equipment.

Q: Why does NDES show “password cache is full”?

NDES caches a limited number of challenge passwords (default: 5). If admins generate passwords faster than devices use them, the cache fills up. Increase PasswordCacheMax in the registry, or reduce PasswordMax (expiry time) so old passwords expire faster.


Related Reading:

Automate SCEP Enrollment

Replace manual NDES management with automated certificate provisioning for network devices at scale.

Request Demo

Related Insights

Kubernetes

cert-manager Complete Setup Guide: Automated TLS Certificates in Kubernetes

Install and configure cert-manager for automated TLS certificate management in Kubernetes. Covers Issuers, ClusterIssuers, Let's Encrypt, Vault PKI, DNS-01 challenges, wildcard certs, and production troubleshooting.

By Shivam sharma

11 May, 2026 · 07 Mins read

KubernetesDevOpsPractical Guides

PKI

AD CS Complete Architecture Guide: Designing Enterprise Microsoft PKI

Design and deploy Microsoft Active Directory Certificate Services (AD CS) with proper hierarchy, role separation, template strategy, CRL distribution, and high availability. Covers 2-tier and 3-tier architectures for enterprise environments.

By Shivam sharma

11 May, 2026 · 09 Mins read

PKIWindows ServerEnterprise Security

Compliance

SOX Compliance & Cryptography: IT Controls Every Public Company Needs

The Sarbanes-Oxley Act requires IT controls that protect financial data integrity. Here's exactly which cryptographic controls SOX demands — encryption, key management, certificate governance, and audit evidence your auditors expect.

By Vedanti sharma

11 May, 2026 · 06 Mins read

ComplianceEnterprisePKI

Ready to Secure Your Enterprise?

Experience how our cryptographic solutions simplify, centralize, and automate identity management for your entire organization.

Stay ahead on cryptography & PKI

Get monthly insights on certificate management, post-quantum readiness, and enterprise security. No spam.

We respect your privacy. Unsubscribe anytime.