Getting Expiration Dates for all hosted websites

ChatGPT has become my new programming partner. I tried many variations of .net code to read host bindings from IIS and then run a WHOIS service to get expiration dates, including C# code. Nothing worked and I got very tired of the process. To be clear, it takes knowledge of programming and architecture to get CHATGPT to create the code base you want.

So I ran APPCMD from a command prompt and found that the easiest way to get the bindings. Then I prompted CHATGPT to create various Powershell functions to read the root domain and get the registrar and expiration date for each domain. I then instructed CHATGPT to write code to create a CSV file sorted by expiration date and write out the domain, expiration date, and name of the Registrar.  D:\sites.xml  and D:\domain_expiration.csv are hard coded in the powershell script. The powershell script also uses the whois service which took several prompts to get CHATGPT to tell me how to install it.

Start with this appcmd

appcmd list sites /xml >d:\sites.xml

Many of us don’t use PowerShell very often and often forget how to run it. I use the developer version of powershell for visual studio 2022.

I ran these commands directly with just a cut and paste. You first need to install the whois utility using the Chocolatey package manager.

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString(''))

choco install whois

Here’s the powershell script to run. To run this script I copied the script below into a file called ExpirationDates.ps1. To run the file from PowerShell


The output again is D:\domain_expiration.csv

Here’s the complete script.

function Get-RootDomain {
    param (
        [Parameter(Mandatory = $true)]

    if ([string]::IsNullOrWhiteSpace($Domain)) {
        return ""

    $rootDomain = $Domain -replace "^.*?(([^.]+)\.[^.]+)$", '$1'

    return $rootDomain

function Get-DomainExpirationDate {
    param (
        [Parameter(Mandatory = $true)]

    $whoisResponse = whois $Domain 2>$null
#Write-Host $whoisResponse
    $expiryPatterns = @(
        "(?m)Registry Expiry Date:(.+)",
        "(?m)Registrar Registration Expiration Date:(.+)",
        "(?m)Expiration Date:(.+)",
        "(?m)Renewal Date:(.+)"

    $expirationDate = $null
    foreach ($pattern in $expiryPatterns) {
        $match = [regex]::Match($whoisResponse, $pattern)
        if ($match.Success) {
            $expirationString = $match.Groups[1].Value.Trim()

            # Extract the date portion from the expiration string
            $datePattern = "\d{4}-\d{2}-\d{2}"
            $dateMatch = [regex]::Match($expirationString, $datePattern)
            if ($dateMatch.Success) {
                $expirationDate = $dateMatch.Value


#$registrationPattern = "(?m)Registrar:(.+)"
#    $registrationMatch = [regex]::Match($whoisResponse, $registrationPattern)
#    if ($registrationMatch.Success) {
#        $registrationCompany = $registrationMatch.Groups[1].Value.Trim()
#    }

            $registrationCompanyPattern = "(?m)Registrar:\s*([\s\S]*?Registrar)"
            $registrationCompanyMatch = [regex]::Match($whoisResponse, $registrationCompanyPattern)
            $registrationCompany = ""
            if ($registrationCompanyMatch.Success) {
                $registrationCompany = $registrationCompanyMatch.Groups[1].Value.Trim()

    $result = [PSCustomObject]@{
        Domain = $Domain
        ExpirationDate = $expirationDate
        RegistrationCompany = $registrationCompany

    return $result

    #return $expirationDate

$xmlPath = "D:\sites.xml"
$outputPath = "D:\domain_expiration.csv"

# Read XML from the file
$xmlContent = Get-Content $xmlPath -Raw

# Load XML content
$xml = $xmlContent

# Get the SITE nodes
$sites = $xml.SelectNodes("//SITE")

# Create an array to store the domain names
$domainArray = @()

# Loop through each SITE node
foreach ($site in $sites) {
    $bindings = $site.GetAttribute("bindings")

    # Extract the domain names from the bindings attribute
    $domainNames = $bindings -split ","
    foreach ($domainName in $domainNames) {
        $domain = ($domainName -split ":")[2].Trim()
        #$domain = ($domain -split "\.")[1..($domain -split "\.").Length] -join "."
 	#$domain = $domain -replace ".*?\.", ""
 #$domain = $domain -replace ".*?(\w+\.\w+)$", '$1'
#$domain = $domain -replace ".*?((?:[\w-]+\.)+[\w-]+)$", '$1'

     if (![string]::IsNullOrWhiteSpace($domain)) {
	$rootDomain = Get-RootDomain -Domain $domain
        if (![string]::IsNullOrWhiteSpace($rootDomain) -and $domainArray -notcontains $rootDomain) {
            Write-Host $domain + ":" + $rootDomain
            $domainArray += $rootDomain


$domainArray = $domainArray | Select-Object -Unique
# Create an array to store the domain name and expiration date
$domainExpirationArray = @()

# Loop through each domain and retrieve the expiration date
foreach ($domain in $domainArray) {
    $expirationDate = Get-DomainExpirationDate -Domain $domain
    $domainExpirationArray += [PSCustomObject]@{
        Domain = $domain
        ExpirationDate = $expirationDate.ExpirationDate

$domainExpirationArray = $domainExpirationArray | Sort-Object -Property ExpirationDate, Domain, RegistrationCompany

# Export the domain name and expiration date to a CSV file
$domainExpirationArray | Export-Csv -Path $outputPath -NoTypeInformation

Write-Host "CSV file generated: $outputPath"

Leave a Reply

Your email address will not be published. Required fields are marked *