CiscoCertificates

Administration Guide
Automated Let's Encrypt certificate management for Cisco UC & Expressway
Version 1.0 — February 2026

Table of Contents

1. Overview
2. How It Works
    2.1 Cisco UC (CUCM / IM&P / CUC / CER)
    2.2 Cisco Expressway
3. Prerequisites
4. Installation
5. Configuration Editor (GUI)
    5.1 Getting Started
    5.2 ACME Settings
    5.3 DNS Provider
    5.4 UC Nodes
    5.5 Expressway Nodes
    5.6 Certificate Jobs
    5.7 Notifications
    5.8 General Settings
6. Configuration (Manual JSON)
    6.1 Automation Section
    6.2 Acme Section (Let's Encrypt)
    6.3 Deployment Section
    6.4 Notifications Section
    6.5 Secrets & Credentials
7. First-Run Walkthrough
8. Run Modes (Command-Line Switches)
9. Scheduling with Task Scheduler
10. State & Recovery
11. Troubleshooting
A. Exit Codes
B. Full Configuration Reference

1. Overview

CiscoCertificates is a Windows toolset that automates the entire lifecycle of TLS certificates for Cisco Unified Communications (UC) and Expressway systems. It includes two components:

The automation engine handles the complete certificate lifecycle:

  1. Issues trusted certificates from Let's Encrypt (free, automated, 90-day certs)
  2. Deploys them to Cisco CUCM, IM&P, Unity Connection, Emergency Responder, and Expressway
  3. Installs the Let's Encrypt CA trust chain on UC devices
  4. Restarts the affected services (e.g., Cisco Tomcat) so the new cert takes effect
  5. Notifies you via email when something succeeds or fails
  6. Renews automatically before expiration when run on a schedule

You configure it once (using the Config Editor or by editing appsettings.json), schedule it with Windows Task Scheduler, and it handles everything from that point on.

Why Automate?

TLS certificate lifetimes are shrinking industry-wide. The CA/Browser Forum has voted to reduce maximum certificate lifetimes to 47 days by 2029. Let's Encrypt already issues 90-day certificates. Managing these manually across multiple Cisco UC clusters — where each product (CUCM, IM&P, CUC, CER) requires its own certificate with a device-generated CSR — becomes an unsustainable operational burden. This tool eliminates that burden entirely.

2. How It Works

2.1 Cisco UC (CUCM / IM&P / CUC / CER)

Cisco UC devices do not allow importing externally-generated private keys through their API. Instead, the device generates its own private key and Certificate Signing Request (CSR). The automation follows this device-CSR pattern:

  1. The app connects to the publisher node and asks it to generate a CSR. With "CsrDistribution": "multi-server", the CSR includes all cluster node hostnames in the SAN.
  2. The app sends the CSR to Let's Encrypt using the ACME protocol. Let's Encrypt verifies you own the domain by checking a DNS TXT record (created automatically via your configured DNS provider API or a custom script).
  3. Let's Encrypt signs the CSR and returns a certificate chain (your cert + intermediate CA).
  4. The app uploads the Let's Encrypt intermediate and root CA as trusted certificates on the publisher (so the cluster trusts the new cert chain).
  5. The app uploads the signed certificate back to the publisher.
  6. The publisher replicates the certificate to all subscriber nodes automatically via Cisco's internal database replication.
  7. The app verifies that each subscriber received the cert by polling their API.
  8. The app restarts Cisco Tomcat on the publisher first, then each subscriber, so the new cert takes effect.
Why separate jobs per cluster? Each Cisco product cluster (CUCM, IM&P, CUC, CER) has its own certificate store and generates its own CSR. The API requires one cert per product cluster. This is a constraint of the Cisco platform, not a design choice.
IM&P (CUPS) SAN requirements: The CUPS Tomcat certificate typically needs to include CUCM server FQDNs in addition to the CUPS server hostnames, because IM&P services reference CUCM for user lookups and presence routing. Check your existing CUPS certificate's SAN list before configuring the job. If you omit the CUCM FQDNs, you may see certificate warnings in Jabber clients or IM&P service logs.

2.2 Cisco Expressway

Expressway uses a different approach — it accepts both the certificate and private key uploaded via SSH/SFTP:

  1. The app generates a private key and CSR locally.
  2. Let's Encrypt signs it (same DNS validation process as above).
  3. The app connects via SSH/SFTP, uploads the cert and key to temporary files on the Expressway.
  4. The app runs an install command via SSH to activate the certificate.
  5. The app deletes the temporary files and verifies the new cert via a TLS handshake.

3. Prerequisites

3.1 System Requirements

RequirementDetails
Operating SystemWindows Server 2016+ or Windows 10/11 (x64)
.NET Runtime.NET 8.0 Desktop Runtime (Windows x64). Download from https://dotnet.microsoft.com/download/dotnet/8.0. Not required if published as self-contained.
Network AccessOutbound HTTPS to Let's Encrypt (acme-v02.api.letsencrypt.org) and your DNS provider API
Cisco UC APIHTTPS access to each UC node on port 443 (Cisco Platform API v14+)
SSH AccessSSH on port 22 to UC nodes (for service restarts) and Expressway nodes (for cert deployment)

3.2 Cisco UC Requirements

3.3 DNS Requirements

Let's Encrypt uses DNS-01 challenge to verify domain ownership. You need one of the following:

3.4 Expressway Requirements (if applicable)

4. Installation

  1. Copy the application files
    Extract the release ZIP to a folder on the server. A typical install location is:
    C:\CiscoCertAutomation
    The folder should contain CiscoCertificates.Automation.exe, CiscoCertificates.ConfigEditor.exe, appsettings.json, and supporting files.
  2. Verify the automation engine
    Open a Command Prompt or PowerShell window and run:
    C:\CiscoCertAutomation\CiscoCertificates.Automation.exe --help
    You should see the usage text with available mode switches.
  3. Verify the Configuration Editor
    Double-click CiscoCertificates.ConfigEditor.exe. The application window should open with the Fluent UI sidebar showing navigation items for each configuration section.
  4. Edit the configuration
    Use the Config Editor (see Section 5) or manually edit appsettings.json (see Section 6) to configure the automation for your environment.
Tip: Keep the application folder outside of Program Files so that the state directory (which stores certificates and account keys) is writable without elevation issues.

5. Configuration Editor (GUI)

The CiscoCertificates.ConfigEditor is a Windows desktop application that provides a visual interface for editing appsettings.json. It eliminates the need to edit JSON directly and validates your configuration as you work.

5.1 Getting Started

Launch CiscoCertificates.ConfigEditor.exe. The main window has three areas:

The title bar shows the currently loaded file name and an unsaved-changes indicator. A status bar at the bottom displays operation feedback.

To create a new configuration:

  1. Click File → New (or Ctrl+N). This creates a blank configuration with default values.
  2. Work through each page in the sidebar, filling in your environment-specific settings.
  3. Click File → Save As (Ctrl+Shift+S) to save to the appsettings.json file in your automation folder.

To edit an existing configuration:

  1. Click File → Open (Ctrl+O) and navigate to your existing appsettings.json.
  2. Make your changes on any page.
  3. Click File → Save (Ctrl+S) to save in place.

5.2 ACME Settings Page

This page configures the ACME certificate authority connection. It has two sections:

Certificate Authority

ACME Timing

5.3 DNS Provider Page

Select your DNS provider from the dropdown at the top. The page dynamically shows configuration fields specific to the selected provider:

ProviderRequired Fields
CloudflareAPI Token, Zone ID (optional — auto-detected if not set)
DigitalOceanAPI Token
Route 53Access Key ID, Secret Access Key, Hosted Zone ID, Region
Azure DNSTenant ID, Client ID, Client Secret, Subscription ID, Resource Group, DNS Zone Name
Google Cloud DNSProject ID, Service Account JSON Key Path, Managed Zone
CommandCreate Command, Delete Command, Shell Executable, Shell Arguments Template
Tip: If using a DNS provider not listed here, select Command and write two PowerShell scripts — one to create a DNS TXT record and one to delete it. The automation calls them with {recordName} and {recordValue} placeholders.

5.4 UC Nodes Page

This page uses a split-panel layout. The left panel shows a list of all UC nodes with Add and Remove buttons. The right panel shows the details of the selected node.

Node Identity

Credentials

Security

Adding a cluster: Add the publisher first, then add each subscriber. Make sure all nodes in the same product share the same Cluster Group value. For example, all CUCM nodes should use cucm, all IM&P nodes should use cups, etc.

5.5 Expressway Nodes Page

Similar split-panel layout as UC Nodes. Configure each Expressway server:

Node Identity

Credentials

SSH Security

Command Templates

5.6 Certificate Jobs Page

This is the most detailed page. Each job represents one certificate to issue and deploy. The left panel shows the job list; the right panel shows the selected job's configuration.

Certificate Identity

Key & Algorithm

Device CSR

Deployment Targets

A data grid showing which nodes receive the certificate. Use the Add/Remove buttons to manage targets. Each target has:

For UC jobs: Add only the publisher as a target. Subscribers get the certificate via Cisco's internal replication — you don't need to (and shouldn't) upload to subscribers directly.

Trust Chain

EKU Requirements

5.7 Notifications Page

Configure email notifications. Settings cascade — additional fields appear as you enable features:

  1. Enable Email Notifications (master toggle)
  2. When enabled, SMTP server fields appear:
  3. Use Authentication toggle — when enabled, shows Username and Password fields
  4. Recipients:
  5. Send Success Summary — When disabled (default), emails are only sent on failure

5.8 General Settings Page

This page covers automation behavior settings organized into six cards:

CardSettings
AutomationDefault renew before days, max parallel deployments, continue on target failure
State ManagementEnable state tracking toggle, state directory path
Run LogRun logging enabled toggle, log directory path
HTTP ClientHTTP timeout (seconds), HTTP retry count
Replication VerificationMax polling attempts, poll interval (seconds)
Service RestartsEnable automatic restarts, fail-on-no-service toggle, SSH port, SSH timeouts, delay after restart, restart command template

6. Configuration (Manual JSON)

All configuration lives in appsettings.json. You can edit this file directly instead of using the Config Editor. The file has four main sections.

6.1 Automation Section

{
  "Automation": {
    "DefaultRenewBeforeDays": 30,
    "MaxParallelDeployments": 2,
    "ContinueOnTargetFailure": true,
    "StateDirectory": "state",
    "HttpTimeoutSeconds": 60,
    "HttpRetryCount": 4,
    "EnableStateTracking": true,
    "RestartAutomation": {
      "EnableUcAutomaticRestarts": true,
      "FailWhenRestartRequiredButNoServiceDetected": true,
      "UcSshPort": 22,
      "UcSshCommandTimeoutSeconds": 120,
      "UcSshConnectionTimeoutSeconds": 30,
      "DelayAfterRestartSeconds": 15,
      "UcRestartCommandTemplate": "utils service restart {service}"
    }
  }
}
SettingWhat It DoesChange?
DefaultRenewBeforeDaysHow many days before certificate expiry to trigger renewal. Let's Encrypt certs last 90 days, so 30 means it renews at day 60.Default is fine
ContinueOnTargetFailureIf one cluster fails, keep going with the others.Default is fine
StateDirectoryWhere state files and cached certificates are stored. Relative to the app folder.Default is fine
EnableUcAutomaticRestartsAutomatically restart Cisco Tomcat after deploying. If false, you'll need to restart services manually.Default is fine

6.2 Acme Section (Let's Encrypt)

{
  "Acme": {
    "ContactEmail": "admin@example.com",
    "AccountKeyPath": "acme-account-key.pem",
    "DnsProvider": "Cloudflare",
    "DnsPropagationWaitSeconds": 90,
    "AuthorizationPollSeconds": 15,
    "AuthorizationPollMaxAttempts": 20,
    "Cloudflare": {
      "ZoneId": "YOUR_ZONE_ID_HERE",
      "TtlSeconds": 120,
      "ApiToken": ""
    },
    "Command": {
      "PresentCommand": ".\\scripts\\dns-present.ps1 -RecordName {recordName} -RecordValue {recordValue}",
      "CleanupCommand": ".\\scripts\\dns-cleanup.ps1 -RecordName {recordName} -RecordValue {recordValue}",
      "ShellExecutable": "powershell",
      "ShellArgumentsTemplate": "-NoProfile -NonInteractive -Command '{command}'"
    }
  }
}

Settings you MUST change:

SettingWhat To Enter
ContactEmailYour real email address. Let's Encrypt uses this to warn you about expiring certs or policy changes.
DnsProviderSet to your DNS provider name: "Cloudflare", "Route53", "Azure", "GoogleCloud", "DigitalOcean", or "Command".
Cloudflare.ZoneId(Cloudflare only) Your Cloudflare Zone ID. Find it on the Overview page of your domain in the Cloudflare dashboard.
Cloudflare.ApiToken(Cloudflare only) A Cloudflare API Token with Zone.DNS.Edit permission.
For testing: Use Let's Encrypt's staging server first. Staging certs won't be trusted by browsers but have much higher rate limits. Set the ACME provider to "Let's Encrypt Staging" in the Config Editor, or add "DirectoryUrl": "https://acme-staging-v02.api.letsencrypt.org/directory" in JSON.

If using a custom DNS provider (not one of the built-in providers):

Set "DnsProvider": "Command" and write two PowerShell scripts:

6.3 Deployment Section

This is the largest section. It defines your Cisco nodes and the certificate jobs.

6.3.1 UC Nodes

List every Cisco UC server in your environment, even subscribers (they're needed for replication verification):

"UcNodes": [
  {
    "Name": "CUCM-PUB",
    "Host": "cucm-pub.example.com",
    "ProductType": "Cisco Unified CM Publisher",
    "ClusterRole": "publisher",
    "ClusterGroup": "cucm",
    "AllowUntrustedServerCertificate": true,
    "AllowUntrustedSshHostKey": true,
    "Username": "",
    "Password": ""
  },
  {
    "Name": "CUCM-SUB1",
    "Host": "cucm-sub1.example.com",
    "ProductType": "Cisco Unified CM Subscriber",
    "ClusterRole": "subscriber",
    "ClusterGroup": "cucm",
    "AllowUntrustedServerCertificate": true,
    "AllowUntrustedSshHostKey": true,
    "Username": "",
    "Password": ""
  }
]
FieldWhat It Does
NameA short label you choose (used in logs and reports).
HostThe FQDN or IP of the server. Must be reachable on HTTPS (443) and SSH (22).
ProductTypeDescriptive label (CUCM, IM&P, CUC, CER). Used in reports only.
ClusterRole"publisher" or "subscriber". Only publishers generate CSRs and receive cert uploads.
ClusterGroupGroups nodes that belong to the same product cluster (e.g., "cucm", "cups", "cuc", "cer"). Subscribers in the same group are polled for replication after the publisher gets its cert.
AllowUntrustedServerCertificateSet to true if the UC node currently has a self-signed or expired HTTPS cert (common before first run). You can set this to false after deploying valid certs.
AllowUntrustedSshHostKeySet to true to accept any SSH host key. For production, set to false and provide SshHostKeyFingerprint.
Username / PasswordUC admin credentials. See Section 6.5 for secure alternatives.

6.3.2 Expressway Nodes (if applicable)

"ExpresswayNodes": [
  {
    "Name": "EXP-E",
    "Host": "expressway.example.com",
    "SshPort": 22,
    "TlsPort": 443,
    "AllowUntrustedSshHostKey": true,
    "SshCommandTimeoutSeconds": 120,
    "InstallCommandTemplate": "...",
    "VerifyCommandTemplate": "...",
    "Username": "",
    "Password": ""
  }
]

6.3.3 Certificate Jobs

Each job represents one certificate to issue and deploy. You need one job per Cisco product cluster.

Example: CUCM Tomcat certificate

{
  "Name": "cucm-tomcat",
  "CommonName": "cucm-pub.example.com",
  "SubjectAlternativeNames": [
    "cucm-pub.example.com",
    "cucm-sub1.example.com",
    "cucm-sub2.example.com"
  ],
  "RenewBeforeDays": 30,
  "KeyAlgorithm": "RS256",
  "UseDeviceCsr": true,
  "CsrDistribution": "multi-server",
  "Targets": [
    {
      "Service": "tomcat",
      "Node": "CUCM-PUB",
      "Type": "uc",
      "RestartServices": [ "Cisco Tomcat" ]
    }
  ],
  "InstallChainAsTrust": true,
  "TrustServices": [ "tomcat" ],
  "TrustCertificateDescription": "Managed by CiscoCertificates.Automation"
}
FieldWhat It Does
NameA unique label for the job.
CommonNameThe primary hostname on the certificate (usually the publisher FQDN).
SubjectAlternativeNamesAll hostnames that should be on the cert. Include the publisher AND all subscribers.
KeyAlgorithm"RS256" (RSA 2048) is recommended for Cisco compatibility.
UseDeviceCsrtrue for all UC jobs. false for Expressway.
CsrDistribution"multi-server" tells the publisher to include all cluster hostnames in the CSR.
TargetsWhich node to deploy to. For UC jobs, list only the publisher. Subscribers get the cert via replication.
RestartServicesWhich Cisco services to restart after deployment.
InstallChainAsTrusttrue to upload the Let's Encrypt CA chain as trusted certs.
TrustServicesWhich trust stores to upload the CA chain to (e.g., ["tomcat"]).
How many jobs do I need?
IM&P (CUPS) SAN requirements: The CUPS Tomcat certificate typically needs CUCM server FQDNs as SANs in addition to the CUPS server hostnames. Check your existing CUPS certificate's SAN list for the complete set of required hostnames.

6.4 Notifications Section

{
  "Notifications": {
    "Enabled": true,
    "SendSuccessSummary": false,
    "SmtpHost": "smtp.example.com",
    "SmtpPort": 587,
    "FromAddress": "cert-automation@example.com",
    "ToAddresses": [ "voip-team@example.com" ],
    "UseStartTls": true,
    "UseAuthentication": false,
    "SubjectPrefix": "[CiscoCertAutomation]"
  }
}
SettingWhat It Does
EnabledSet to false to disable all email notifications.
SendSuccessSummaryWhen false, emails are only sent on failure. Set to true to get an email on every run.
SmtpHost / SmtpPortYour SMTP relay server.
UseAuthenticationSet to true if your SMTP server requires login, then provide Username and Password.

6.5 Secrets & Credentials

Every credential field supports two methods:

MethodExampleBest For
Direct in appsettings.json"Username": "admin"Quick setup, lab environments
Environment variable"UsernameEnvironmentVariable": "CISCO_UC_USER"Production — keeps secrets out of config files

The app checks in order: direct value → environment variable. The first non-empty value wins.

Security note: For production deployments, use environment variables or .NET user secrets. Do not commit real passwords to appsettings.json if the file is stored in source control.

7. First-Run Walkthrough

Follow these steps in order for your first deployment. Each step validates one piece of the puzzle before moving to the next.

1 Edit appsettings.json

Use the Config Editor (see Section 5) or a text editor to fill in your environment-specific values:

Set "AllowUntrustedServerCertificate": true on any UC nodes that currently have self-signed certs.

2 Run API Preflight

CiscoCertificates.Automation.exe --api-preflight

This validates:

Fix any errors before proceeding. Common issues: wrong hostname, firewall blocking port 443 or 22, incorrect credentials.

3 Run Status Report

CiscoCertificates.Automation.exe --status-report

This reads the current certificate from each target and reports:

This confirms the app can successfully read certificates from your devices. No changes are made.

4 Test with Let's Encrypt Staging

Switch the ACME provider to "Let's Encrypt Staging" in the Config Editor, or add the staging directory URL in JSON:

"DirectoryUrl": "https://acme-staging-v02.api.letsencrypt.org/directory"

Then run:

CiscoCertificates.Automation.exe --issue-only

This will:

Staging certificates are not trusted by browsers but are functionally identical. This step verifies your DNS automation works without risking Let's Encrypt production rate limits.

5 Test Full Deployment with Staging

If Step 4 succeeded, run the full production cycle (still using the staging server):

CiscoCertificates.Automation.exe --production

This performs the full deployment cycle including uploading the cert, installing trust chain, and restarting services. The cert won't be browser-trusted (it's from staging) but this validates the entire deployment pipeline end-to-end.

6 Switch to Production Let's Encrypt

Switch back to "Let's Encrypt Production" in the Config Editor (or remove the staging DirectoryUrl from JSON).

Run the full production cycle:

CiscoCertificates.Automation.exe --production

This issues real, browser-trusted certificates and deploys them. After completion:

7 Set Up Scheduled Runs

See Section 9 for Task Scheduler setup. The app handles renewals automatically — it only issues a new cert when the existing one is within RenewBeforeDays of expiration.

8. Run Modes (Command-Line Switches)

CiscoCertificates.Automation.exe [mode] [options]

8.1 Modes

Exactly one mode is required. Modes are mutually exclusive.

SwitchAliasesWhat It DoesMakes Changes?
--production--prodChecks each target's cert, and if it's within RenewBeforeDays of expiry, issues a new cert and deploys it. Skips targets that don't need renewal.Yes — issues certs, deploys, restarts services
--status-report--status, --report, --report-onlyProbes each target and reports the current certificate thumbprint, expiration date, and days remaining. Sends a summary email regardless of success/failure.No — read only
--issue-only--issue, --acme-onlyAlways issues new certificates from Let's Encrypt (even if current certs aren't expiring). Saves them locally but does NOT deploy to any Cisco device.Partial — issues certs, creates DNS records, but no Cisco changes
--api-preflight--preflight, --validate-apiTests API connectivity, authentication, and SSH access to all configured targets. No certificates are issued or deployed.No — read only
--cleanup-dns--cleanupRemoves stale _acme-challenge TXT records from the DNS provider. Useful if a previous run was interrupted before DNS cleanup.Partial — DNS only

You can also specify a mode using --mode <value> or --mode=<value> syntax (e.g., --mode production).

8.2 Options

These can be combined with any mode:

OptionAliasesWhat It Does
--dry-run--dryrunIssues from ACME and validates the certificate and trust stores, but skips deployment to devices. Can also be used standalone without a mode.
--restart--allow-restartAllows automatic UC service restarts (e.g., Cisco Tomcat) after certificate deployment. Without this flag, certificates are deployed but services are not restarted.
--name <job>--job <job>Runs only the specified job by name (e.g., --name cucm-tomcat). Without this, all configured jobs are processed.
--help-h, /?Displays help text and exits.

8.3 Config Overrides

Any additional argument in the format --Section:Key=Value is passed to the .NET configuration system. For example:

CiscoCertificates.Automation.exe --production --Logging:LogLevel:Default=Debug
CiscoCertificates.Automation.exe --production --Acme:DnsPropagationWaitSeconds=120

This overrides settings for a single run without changing appsettings.json.

9. Scheduling with Task Scheduler

9.1 Create a Scheduled Task

  1. Open Task Scheduler (taskschd.msc)
  2. Click Create Task (not "Create Basic Task")
  3. General tab:
  4. Triggers tab:
  5. Actions tab:
  6. Settings tab:
  7. Click OK and enter the service account credentials
How often? Daily is recommended. Let's Encrypt certs last 90 days, and RenewBeforeDays: 30 means the app starts renewing at day 60. Running daily gives you 30 days of automatic retries if something goes wrong.

9.2 Weekly Status Report (Optional)

Create a second scheduled task for a weekly status report:

This sends a summary email showing the current certificate status on all devices, giving you early warning if anything looks wrong.

10. State & Recovery

10.1 State Directory

When EnableStateTracking is true (the default), the app saves state in the state/ folder:

state/
  acme-account-key.pem          ← Your Let's Encrypt account key (auto-generated)
  jobs/
    cucm_tomcat/
      state.json                ← Thumbprint, expiry, per-target deployment status
      certificate-chain.pem     ← The last issued certificate chain
      private-key.pem.protected ← DPAPI-encrypted private key (Expressway only)
    cups_tomcat/
      ...
  runs/
    20260215-020000/            ← Artifacts from each run (timestamped)
      cucm_tomcat/
        certificate-chain.pem
        private-key.pem.protected
Do not delete acme-account-key.pem. This is your Let's Encrypt account key. If you delete it, the app will create a new account and your existing certificates can't be renewed through the old account. It's auto-generated on first run.

10.2 What State Tracking Does

10.3 Forcing a Fresh Issuance

If you need to force a completely new certificate (e.g., after adding a new SAN):

  1. Delete the job's state folder: state\jobs\cucm_tomcat\
  2. Run --production

Or use --issue-only to issue without deploying.

10.4 Private Key Security

Private keys stored on disk are encrypted using Windows DPAPI (DataProtectionScope.LocalMachine). This means:

11. Troubleshooting

11.1 Common Issues

SymptomLikely CauseFix
"Another instance is already running"A previous run crashed or is still runningCheck Task Manager for the process. A system-wide mutex prevents duplicate runs.
API preflight fails with 401Wrong username/password for UC nodeVerify credentials. Check if the account is locked out on the UC server.
API preflight fails with connection refusedFirewall blocking port 443 or wrong hostnameVerify network connectivity: Test-NetConnection server.example.com -Port 443
DNS challenge fails / ACME authorization timeoutTXT record not created or not propagatedRun nslookup -type=TXT _acme-challenge.yourdomain.com 8.8.8.8 to check if the record is visible. Increase DnsPropagationWaitSeconds if it's a timing issue.
SSH connection refused on UC nodeSSH not enabled on CUCM or firewallVerify SSH is enabled in CUCM Serviceability. Test with PuTTY or ssh admin@server.example.com.
Replication not verified after max attemptsCisco DB replication is slow or brokenCheck replication status in CUCM Admin. Increase ReplicationDelaySeconds or ReplicationVerifyMaxAttempts.
"Certificate was issued but no CERTIFICATE PEM blocks returned"Let's Encrypt returned an unexpected responseRetry. If persistent, check Let's Encrypt status page. Try the staging server.
Cert deployed but browser still shows old certCisco Tomcat wasn't restarted, or browser cacheVerify RestartServices includes "Cisco Tomcat". Clear browser cache or use an incognito window.
Email notification not receivedSMTP config incorrectCheck SMTP host, port, and authentication settings. Test with Send-MailMessage from PowerShell.

11.2 Enabling Debug Logging

For detailed output, run with debug logging:

CiscoCertificates.Automation.exe --production --Logging:LogLevel:Default=Debug

Or set it permanently in appsettings.json:

"Logging": {
  "LogLevel": {
    "Default": "Debug",
    "Microsoft": "Warning"
  }
}

11.3 Let's Encrypt Rate Limits

LimitValue
Certificates per Registered Domain50 per week
Duplicate Certificates5 per week (same exact set of hostnames)
Failed Authorizations5 per hour per account per hostname
New Orders300 per 3 hours

With state tracking enabled and normal daily runs, you'll never come close to these limits. The app only requests a new cert when the current one is near expiry.

Appendix A: Exit Codes

CodeMeaning
0Success — all jobs completed without errors
1Invalid command-line arguments
2One or more jobs failed
3Another instance is already running (mutex lock)
4All jobs succeeded but the notification email failed to send

You can check the exit code in Task Scheduler's "Last Run Result" column, or in PowerShell with $LASTEXITCODE after a manual run.

Appendix B: Full Configuration Reference

Automation

KeyTypeDefaultDescription
DefaultRenewBeforeDaysint30Days before cert expiry to trigger renewal
MaxParallelDeploymentsint2Max concurrent deployments across clusters
ContinueOnTargetFailurebooltrueContinue to next job/target if one fails
StateDirectorystring"state"Path for state files (relative to app folder)
HttpTimeoutSecondsint60HTTP request timeout for UC API calls
HttpRetryCountint4Number of retries on transient HTTP failures
EnableStateTrackingbooltruePersist state for cert reuse and deployment tracking
ReplicationVerifyMaxAttemptsint10Max polls when verifying subscriber replication
ReplicationVerifyPollSecondsint30Seconds between replication polls

Automation > RestartAutomation

KeyTypeDefaultDescription
EnableUcAutomaticRestartsbooltrueRestart Cisco services after cert deployment
FailWhenRestartRequiredButNoServiceDetectedbooltrueFail if UC says restart needed but no service name detected
UcSshPortint22SSH port for UC nodes
UcSshCommandTimeoutSecondsint120Timeout for SSH commands on UC
UcSshConnectionTimeoutSecondsint30SSH connection timeout
DelayAfterRestartSecondsint15Wait time after service restart before next action
UcRestartCommandTemplatestring"utils service restart {service}"SSH command template for restarting a service

Automation > RunLog

KeyTypeDefaultDescription
EnabledbooltrueEnable run-level artifact logging
Directorystring"runs"Directory for run artifacts

Acme

KeyTypeDefaultDescription
DirectoryUrlstring(LE production)ACME directory URL
ContactEmailstringEmail for Let's Encrypt account
AccountKeyPathstring"acme-account-key.pem"ACME account key file path
DnsProviderstring"Command""Cloudflare", "Route53", "Azure", "GoogleCloud", "DigitalOcean", or "Command"
DnsPropagationWaitSecondsint90Seconds to wait after creating DNS TXT record
AuthorizationPollSecondsint15Seconds between ACME authorization status checks
AuthorizationPollMaxAttemptsint20Max authorization polls before timeout

Deployment > UcNodes[]

KeyTypeDefaultDescription
NamestringUnique node label
HoststringFQDN or IP address
ProductTypestringDescriptive product type
ClusterRolestring"publisher""publisher" or "subscriber"
ClusterGroupstringnullGroups nodes into product clusters
AllowUntrustedServerCertificateboolfalseAccept untrusted HTTPS cert from UC API
AllowUntrustedSshHostKeyboolfalseAccept any SSH host key
SshHostKeyFingerprintstringnullExpected SSH host key fingerprint

Deployment > Jobs[]

KeyTypeDefaultDescription
NamestringUnique job label
CommonNamestringPrimary cert hostname (CN)
SubjectAlternativeNamesstring[][]All hostnames for the cert SAN
RenewBeforeDaysint?nullOverride DefaultRenewBeforeDays for this job
KeyAlgorithmstring"RS256"RS256, ES256, ES384, or ES512
UseDeviceCsrboolfalseGenerate CSR on the Cisco device (required for UC)
CsrDistributionstring"multi-server""multi-server" or "this-server"
ReplicationDelaySecondsint60Wait time after publisher deploy for DB replication
InstallChainAsTrustboolfalseUpload CA chain as trusted certs
TrustServicesstring[][]Trust stores to upload CA chain to
RequiredEnhancedKeyUsagesstring[][]EKU OIDs or names the cert must contain
FailIfRequiredEkuMissingbooltrueFail if issued cert lacks required EKUs

Deployment > Jobs[] > Targets[]

KeyTypeDefaultDescription
Typestring"uc" or "expressway"
NodestringMust match a UcNodes or ExpresswayNodes Name
Servicestring"tomcat"UC cert service (e.g., "tomcat", "CallManager")
DomainstringnullExpressway domain for cert install
RestartServicesstring[][]UC services to restart (e.g., ["Cisco Tomcat"])

Notifications

KeyTypeDefaultDescription
EnabledbooltrueEnable email notifications
SendSuccessSummaryboolfalseSend email on success (default: failures only)
SmtpHoststringSMTP server hostname
SmtpPortint587SMTP port
FromAddressstringSender email address
ToAddressesstring[][]Recipient email addresses
UseStartTlsbooltrueUse STARTTLS for SMTP
SkipTlsCertificateValidationboolfalseSkip TLS cert validation for SMTP
UseAuthenticationboolfalseAuthenticate with SMTP server
SubjectPrefixstring"[CiscoCertAutomation]"Email subject prefix