For EZCA to connect to your ADCS CAs it is required for a domain joined machine to run the EZCA certificate agent web service. This web service receives authenticated requests from EZCA and then requests the certificate on behalf of EZCA. In this page we will set up the Agent, from registering the gMSA to getting the Agent operational.
In a domain joined PC open an Administrator PowerShell window and run the following command:
Install-WindowsFeature -name Web-Server -IncludeManagementTools
Once IIS is installed, download web-deploy from the official Microsoft site and open the installer.
To have a fully password-less infrastructure we recommend having a gMSA (Group Managed Service Accounts) as the domain identity for the EZCA agent. First the domain admin has to create the gMSA by running the following commands (where ezcagMSA is the name of the gMSA. agent.yourdmoain is the DNS name of your agent, and ezcaAgents is the domain group that the agent is part of):
You can add all the agents to the same gMSA by adding them to the group that has access to the password.
Add-KdsRootKey -EffectiveImmediately
#wait 10 hours (this is made by Microsoft to ensure replication, for test environment you can bypass with Add-KdsRootKey –EffectiveTime ((get-date).addhours(-10))
#Ref https://social.technet.microsoft.com/Forums/ie/en-US/82617035-254f-4078-baa2-7b46abb9bb71/newadserviceaccount-key-does-not-exist?forum=winserver8gen
New-ADServiceAccount -Name ezcagMSA -DNSHostName agent.yourdmoain -KerberosEncryptionType AES256 -PrincipalsAllowedToRetrieveManagedPassword ezcaAgents
In each agent we have to register the gMSA, to do this we first have to install the AD-Domain-Services PowerShell Modules and restarting the computer by running:
Get-WindowsFeature AD-Domain-Services
Install-WindowsFeature AD-DOMAIN-SERVICES
Restart-Computer
After the computer is restarted we add the gMSA and test, that is working by running these two commands:
$gmsaName = 'ezcagMSA'
Install-AdServiceAccount $gmsaName
Test-AdServiceAccount $gmsaName
Next we have to create the site in IIS, to do this run the following script replacing the variables with your gMSA name and certificate subject name:
You can download the signed version of this script from here
#usage .\InstallIISSite.ps1 -iisAppPoolName "YOURPOOLNAME" -iisAppName = "SITENAME" -directoryPath "PATHTOSITEFILES" -gMSAName 'YOURDOMAIN\ezcagMSA$' -certificateSubject 'YOUR SSL CERTIFICATE SUBJECT' -appInsights "YOUR APP Insights connection string" -testTemplate "ADCS Test Template"
param ($iisAppPoolName = "ezcaPool", $iisAppName = "ezca", $directoryPath = "C:\ezca",
$gMSAName, $certificateSubject, $appInsights, $testTemplate = "EZCATestTemplate")
Import-Module WebAdministration
if($null -eq $gMSAName -or $gMSAName -eq '')
{
Write-Host "Error: No gMSA name provided. Please provide a valid gMSA name Format: domain\gMSA$" -ForegroundColor Red
exit 1
}
if($null -eq $certificateSubject -or $certificateSubject -eq '')
{
Write-Host "Error: No certificate subject provided. Please provide a valid certificate subject name" -ForegroundColor Red
exit 1
}
$directoryPath = $directoryPath.TrimEnd('\')
$originalPath = Get-Location
#navigate to the app pools root
cd IIS:\AppPools\
#check if the app pool exists
if (!(Test-Path $iisAppPoolName -pathType container))
{
#create the app pool
$appPool = New-Item $iisAppPoolName
$appPool.processModel.userName = $gMSAName
$appPool | Set-ItemProperty -Name "managedRuntimeVersion" -Value ""
Set-ItemProperty IIS:\AppPools\$iisAppPoolName -name processModel.identityType -Value SpecificUser
Set-ItemProperty IIS:\AppPools\$iisAppPoolName -name processModel.userName -Value $gMSAName
}
#navigate to the root of the site
cd IIS:\Sites\
#check if the site exists
if (Test-Path $iisAppName -pathType container)
{
return
}
$cert = (Get-ChildItem cert:\LocalMachine\My | where-object { $_.Subject -like "*$certificateSubject*" } | Select-Object -First 1).Thumbprint
if($null -eq $cert -or $cert -eq '')
{
Write-Host "Error: No certificate found for the provided certificate subject. Please provide a valid certificate subject name, and make sure your certificate is in the the following cert store: Cert:\LocalMachine\My" -ForegroundColor Red
exit 1
}
#create the site
Start-IISCommitDelay
$TestSite =New-IISSite -Name $iisAppName -PhysicalPath $directoryPath -BindingInformation "*:443:" -CertificateThumbPrint $cert -CertStoreLocation "Cert:\LocalMachine\My" -Protocol https -Passthru
$TestSite.Applications["/"].ApplicationPoolName =$iisAppPoolName
Stop-IISCommitDelay
# Download Site
cd $originalPath
Invoke-WebRequest -Uri https://download.keytos.io/Downloads/WindowsAgentWeb/EZCAAgent.zip -OutFile ezcaAgent.zip
Expand-Archive -LiteralPath ezcaAgent.zip -DestinationPath $directoryPath
$webAppSettings = Get-Content -Raw -Path "$($directoryPath)\appsettings.json"
$webAppSettings = $webAppSettings.Replace('$SUBJECTNAME$', $gMSAName.Split('\')).Replace("EZCATestTemplate",$testTemplate).Replace('$GMSA$', $gMSAName.Replace("\","\\")).Replace('$APPINSIGHTS_CONNECTION_STRING$',$appInsights)
$webAppSettings | Out-File -FilePath "$($directoryPath)\appsettings.json" -Force
Remove-Item -Path ezcaAgent.zip
Once the site is set up, the last step before testing it is creating the gMSA’s agent certificate. This certificate is used to sign each request for the CA to issue the certificate to the agent on behalf of the user/computer requesting the certificate. To do this, an automated task periodically rotates certificates to mitigate the risk of certificate expiration related outages.
This Certificate will have very similar permissions to a PKI administrator. We strongly recommend having the private key of this certificate protected by an HSM. This example will use the native crypto provider to ensure compatibility with most set ups.
$gmsaName = 'DOMAIN\GMSANAME$'
$caName = "CA.DNS\CA NAME"
$pathToBinary = 'c:\ezcaTools\AgentCertificateRequester.exe'
$templateName = "YOURTEMPLATENAME"
$cryptoProvider = "Microsoft Enhanced Cryptographic Provider v1.0"
$appInsightsKey = "" #(Optional) Enter App Insights Connection String to receive alerts on errors during runs
## Do Not Change Below This Line
$gmsaShort = $gmsaName.Split('\')[1]
$logsPath = $pathToBinary.Replace('AgentCertificateRequester.exe', 'logs.txt')
# Construct the arguments for the scheduled task action
$arguments = "$($pathToBinary) -ca $($caName) --userName $($gmsaShort) -t $($templateName) -c $($cryptoProvider)"
if (-not [string]::IsNullOrEmpty($appInsightsKey)) {
$arguments += " -ai $($appInsightsKey)"
}
$arguments += " | Out-File -FilePath $($logsPath)"
$action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument $arguments
$trigger = New-ScheduledTaskTrigger -At 11:00 -Weekly -WeeksInterval 3 -DaysOfWeek Monday
$principal = New-ScheduledTaskPrincipal -UserID $gmsaName -LogonType Password
Register-ScheduledTask ezCACertRotation -Action $action -Trigger $trigger -Principal $principal
If there are some issues creating the certificate, you can download PSExec and run the following commands to run the AgentCertificateRequester.exe as the gMSA:
.\PsExec.exe -i -u YOURDOMAIN\ezcagMSA$ -p ~ powershell.exe
$gmsaName = 'DOMAIN\GMSANAME$'
$caName = "CA.DNS\CA NAME"
$pathToBinary = 'c:\ezcaTools\AgentCertificateRequester.exe'
$templateName = "YOURTEMPLATENAME"
$cryptoProvider = "Microsoft Enhanced Cryptographic Provider v1.0"
$appInsightsKey = "" #(Optional) Enter App Insights Connection String to receive alerts on errors during runs
## Do Not Change Below This Line
$gmsaShort = $gmsaName.Split('\')[1]
c:\ezcaTools\AgentCertificateRequester.exe --ca $($caName) --userName $($gmsaShort) -t $($templateName) -c $($cryptoProvider)
Get-ChildItem cert:\CurrentUser\My | where-object { $_.Subject -like "*$($gmsaShort)*" }
After this your agent is ready to receive requests from EZCMS and request the certificates on behalf of the users.
To verify that the agent is working correctly you can navigate to https://agent.yourdomain/api/health/settings and you should see the following result:
{ "Success": true, "Message": "Health" }
You can also validate that the agent can issue test certificates by calling the following endpoint:
https://localhost/api/Health/CAConnection?AgentURL=https%3A%2F%2Fmyagent.com&TemplateName=EZCA&CAName=YOURCAFQDN%5CYOURCANAME&AuthCert=asdf
where YOURCAFQDN is the FQDN of your CA and YOURCANAME is the name of your CA. If the agent is working correctly you should see the following result:
{ "Success": true, "Message": "Test certificate was issued successfully" }
Security is paramount when dealing with PKI and critical services. To ensure a robust defense in depth, we have simplified our agent’s networking needs. Inbound traffic can be restricted to only allow port 443 from our IP addresses. You can find the specific IP addresses for your instance by visiting the /status
page of your EZCA instance (e.g., https://portal.ezca.io/status
).
For outbound traffic, only access to Entra ID authentication endpoints is required. For government environments and EZCMS, we offer the option of certificate-based authentication, which eliminates the need for outbound access to Microsoft, requiring only outbound access to the CRL endpoint and or OCSP endpoint.