Share

Monitoring SSL Certificate Expiry Using VCF Operations Orchestrator

by Mayank Goyal · 24 Jun 2026

SSL certificate expiration is one of those issues that nobody notices until an application suddenly becomes inaccessible. A recent customer requirement was to proactively monitor SSL certificate expiry across multiple HTTPS endpoints and generate a report showing certificate validity and remaining days before expiration.

The customer wanted to know whether there was an out-of-the-box capability within VCF Operations to monitor external SSL certificates or whether a custom solution would be required.

After some investigation, I found that VCF Operations Orchestrator already provides most of the required functionality through the built-in ImportCAFromUrlAction. This action can retrieve and validate certificates directly from HTTPS endpoints without requiring external PowerShell scripts, Python scripts, or third-party tooling.

Solution Overview

The workflow performs the following operations:

  1. Accept a list of HTTPS URLs.
  2. Retrieve the certificate chain from each endpoint.
  3. Extract certificate metadata.
  4. Calculate the number of days remaining before expiration.
  5. Generate a Markdown report summarizing the results.

The final output can be:

  • Displayed in workflow logs
  • Stored as a Resource Element
  • Sent via email
  • Attached to ServiceNow or Jira tickets
  • Used as input for additional automation workflows

Retrieving the Certificate

The core functionality relies on the built-in certificate import action.

var ld = Config.getKeystores().getImportCAFromUrlAction(); 

var model = ld.getModel(); 
model.value = url; 

var validationResult = ld.validateCertificates(); 
var chainInfo = ld.getCertificateChainInfo();

The action connects to the remote HTTPS endpoint, retrieves the certificate chain, and performs validation checks.

Validating HTTPS URLs

To avoid unnecessary failures, the workflow validates URLs before attempting certificate retrieval.

if (!url || !url.match(/^https:\/\//i)) {

    report += "| " + url +
        " | N/A | N/A | N/A | ⚠ Invalid URL (HTTPS Required) |\n";

    continue;
}

This ensures that HTTP, FTP, malformed URLs, or accidental user input errors are handled gracefully.

Extracting Certificate Details

The certificate chain information contains details such as Common Name and certificate validity dates.

Example output:

Validity : [From : May 25, 2026 To : Aug 17, 2026]
Common Name (6 characters min) : *.google.com

The workflow extracts the Common Name using a regular expression.

var cnMatch = chainInfo.match(
    /Common Name.*?:\s*(.*)/
);

if (cnMatch && cnMatch.length > 1) {
    commonName = cnMatch[1].trim();
}

Calculating Remaining Certificate Lifetime

The certificate expiration date is extracted from the validity section and converted into a JavaScript Date object.

var validityMatch = chainInfo.match(
    /Validity\s*:\s*\[From\s*:\s*(.*?)\s*To\s*:\s*(.*?)\]/
);

if (validityMatch && validityMatch.length > 2) {

    validTo = validityMatch[2].trim();

    var expiryDate = new Date(validTo);
    var today = new Date();

    daysRemaining = Math.floor(
        (expiryDate.getTime() - today.getTime()) /
        (1000 * 60 * 60 * 24)
    );
}

This provides the number of days remaining before the certificate expires.

Determining Certificate Health

Certificates are categorized into three simple states.

if (daysRemaining < 0) {

    status = "❌ Expired";

} else if (daysRemaining < 30) {

    status = "⚠ Expiring Soon";

} else {

    status = "✅ Healthy";
}

These thresholds can easily be adjusted to align with organizational policies.

Generating the Markdown Report

Instead of returning JSON, the workflow generates a human-readable Markdown report.

report += "| URL | Common Name | Expiry Date | Days Remaining | Status |\n";
report += "|------|------|------|------|------|\n";

report += "| "
    + url + " | "
    + commonName + " | "
    + validTo + " | "
    + daysRemaining + " | "
    + status + " |\n";

Sample output:

| URL | Common Name | Expiry Date | Days Remaining | Status |
|------|------|------|------|------|
| https://www.google.com | www.google.com | Aug 17, 2026 | 53 | ✅ Healthy |
| https://www.github.com | github.com | Aug 2, 2026 | 38 | ✅ Healthy |
| https://www.vmware.com | www.vmware.com | Sep 18, 2026 | 85 | ✅ Healthy |

The report also includes a summary section showing healthy, expiring, expired, and invalid endpoints.

Example Output

## Summary

| Metric | Count |
|---------|---------|
| Healthy | 7 |
| Expiring Soon (<30 Days) | 0 |
| Expired | 0 |
| Errors / Invalid URLs | 3 |
| Total URLs | 10 |

Complete Code – vRO JavaScript Action

Just copy this code and put it into an action and click SAVE and then RUN.

//
// SSL Certificate Report Generator
//

var urls = [
    "https://www.google.com",
    "https://www.github.com",
    "https://www.vmware.com",
    "https://www.broadcom.com",
    "https://www.microsoft.com",
    "https://www.amazon.com",
    "https://www.cloudflare.com",

    // Test invalid entries
    "http://www.google.com",
    "ftp://test.com",
    "not-a-url"
];

var report = "";
var results = [];

var healthy = 0;
var warning = 0;
var expired = 0;
var errors = 0;

report += "# SSL Certificate Report\n\n";
report += "Generated: " + new Date().toISOString() + "\n\n";

report += "| URL | Common Name | Expiry Date | Days Remaining | Status |\n";
report += "|------|------|------|------|------|\n";

for each (var url in urls) {

    try {

        //
        // Validate URL
        //
        if (!url || !url.match(/^https:\/\//i)) {

            report += "| " + url +
                " | N/A | N/A | N/A | ⚠ Invalid URL (HTTPS Required) |\n";

            errors++;
            continue;
        }

        //
        // Retrieve certificate
        //
        var ld = Config.getKeystores().getImportCAFromUrlAction();

        var model = ld.getModel();
        model.value = url;

        var validationResult = ld.validateCertificates();

        var chainInfo = ld.getCertificateChainInfo();

        //
        // Extract CN
        //
        var commonName = "Unknown";

        var cnMatch = chainInfo.match(/Common Name.*?:\s*(.*)/);

        if (cnMatch && cnMatch.length > 1) {
            commonName = cnMatch[1].trim();
        }

        //
        // Extract validity
        //
        var validTo = "Unknown";
        var daysRemaining = "Unknown";

        var validityMatch = chainInfo.match(
            /Validity\s*:\s*\[From\s*:\s*(.*?)\s*To\s*:\s*(.*?)\]/
        );

        if (validityMatch && validityMatch.length > 2) {

            validTo = validityMatch[2].trim();

            var expiryDate = new Date(validTo);
            var today = new Date();

            daysRemaining = Math.floor(
                (expiryDate.getTime() - today.getTime()) /
                (1000 * 60 * 60 * 24)
            );
        }

        //
        // Determine status
        //
        var status = "";

        if (daysRemaining === "Unknown") {

            status = "⚠ Unable To Determine";
            errors++;

        } else if (daysRemaining < 0) {

            status = "❌ Expired";
            expired++;

        } else if (daysRemaining < 30) {

            status = "⚠ Expiring Soon";
            warning++;

        } else {

            status = "✅ Healthy";
            healthy++;
        }

        report += "| "
            + url + " | "
            + commonName + " | "
            + validTo + " | "
            + daysRemaining + " | "
            + status + " |\n";

    } catch (e) {

        report += "| "
            + url
            + " | N/A | N/A | N/A | ❌ Error: "
            + e.toString().replace(/\|/g, "")
            + " |\n";

        errors++;

        System.warn(
            "Failed to process URL [" +
            url +
            "] : " +
            e
        );
    }
}

//
// Summary
//
report += "\n";
report += "## Summary\n\n";

report += "| Metric | Count |\n";
report += "|---------|---------|\n";
report += "| Healthy | " + healthy + " |\n";
report += "| Expiring Soon (<30 Days) | " + warning + " |\n";
report += "| Expired | " + expired + " |\n";
report += "| Errors / Invalid URLs | " + errors + " |\n";
report += "| Total URLs | " + urls.length + " |\n";

System.log("\n\n" + report);

Output

2026-06-24 00:52:27.609 -07:00INFO(aaa.mayank/testAction) 

# SSL Certificate Report

Generated: 2026-06-24T07:52:24.128Z

| URL | Common Name | Expiry Date | Days Remaining | Status |
|------|------|------|------|------|
| https://www.google.com | www.google.com | Aug 17, 2026 | 53 | ✅ Healthy |
| https://www.github.com | github.com | Aug 2, 2026 | 38 | ✅ Healthy |
| https://www.vmware.com | www.vmware.com | Sep 18, 2026 | 85 | ✅ Healthy |
| https://www.broadcom.com | www.broadcom.com | Mar 20, 2027 | 268 | ✅ Healthy |
| https://www.microsoft.com | www.microsoft.com | Jan 17, 2027 | 206 | ✅ Healthy |
| https://www.amazon.com | www.amazon.com | Nov 18, 2026 | 146 | ✅ Healthy |
| https://www.cloudflare.com | www.cloudflare.com | Aug 5, 2026 | 41 | ✅ Healthy |
| http://www.google.com | N/A | N/A | N/A | ⚠ Invalid URL (HTTPS Required) |
| ftp://test.com | N/A | N/A | N/A | ⚠ Invalid URL (HTTPS Required) |
| not-a-url | N/A | N/A | N/A | ⚠ Invalid URL (HTTPS Required) |

## Summary

| Metric | Count |
|---------|---------|
| Healthy | 7 |
| Expiring Soon (<30 Days) | 0 |
| Expired | 0 |
| Errors / Invalid URLs | 3 |
| Total URLs | 10 |

Here is TypeScript version (thanks to Simon Sparks) for anyone using Build tools and want to use this capability.

Final Thoughts

Although VCF Operations does not provide an out-of-the-box dashboard specifically for monitoring arbitrary external SSL certificates, VCF Operations Orchestrator already includes the building blocks necessary to implement the solution.

By leveraging ImportCAFromUrlAction, organizations can quickly build certificate monitoring workflows without relying on external scripting languages or additional infrastructure. The generated Markdown reports can be stored as Resource Elements, emailed to administrators, or integrated into existing operational processes to provide proactive visibility into certificate expiration risks.


Discover more from Cloud Blogger

Subscribe to get the latest posts sent to your email.

You may also like