Scheduling renewal?

We have CertifyTheWeb installed on a Windows 2019 server which handles our Remote Desktop Gateway… all of our users got booted off the server this morning and after investigating, it appears to be because the Certificate renewed at 9:30am. Is there a way to schedule this to happen during off hours somehow? I’m not finding the option, or a scheduled task that seems to coincide with this renewal.

Yes, if you are using v5.x onwards, you can set the Deployment Task (under Tasks in the latest version) to Trigger: Manual - this will show you the command you can run (from the C:\ProgramFiles\CertifyTheWeb working directory) as a scheduled task.

You can schedule that as a windows scheduled task for maintenance windows or just run it manually (which you can also do using the Play button in the UI). It will just apply the latest cert so it doesn’t matter when the cert actually last renewed.

By default deployment tasks happen when the cert renewal happens and these are performed by the background service, not a windows scheduled task.

Thank you. The “certify deploy” command is the one I put in scheduled tasks then? The more I think about this, if i run it as a scheduled task… it wont know the variables its getting from CTW, right?

This just ran automatically and error’d out but I’m not sure why… nothing has changed since last time:

> "2020-09-23 09:53:37.511 -07:00 [INF] Requesting Certificate via Certificate Authority
> 2020-09-23 09:53:39.428 -07:00 [INF] Completed Certificate Request.
> 2020-09-23 09:53:39.478 -07:00 [INF] Performing Automated Certificate Binding
> 2020-09-23 09:53:40.890 -07:00 [INF] Completed certificate request and automated bindings update (IIS)
> 2020-09-23 09:53:41.933 -07:00 [INF] Request completed
> 2020-09-23 09:53:41.938 -07:00 [INF] Performing Post-Request (Deployment) Tasks..
> 2020-09-23 09:53:41.971 -07:00 [INF] Task [[Post-Request Script]] :: Task is enabled and primary request was successful.
> 2020-09-23 09:53:41.982 -07:00 [INF] Executing command via PowerShell
> 2020-09-23 09:56:28.579 -07:00 [ERR] Invoke-Command: Object reference not set to an instance of an object.
> At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\RemoteDesktop\Certificate.psm1:389 char:5
> +     Invoke-Command -Session $M3PSession -ArgumentList @($params)`
> +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> Invoke-Command: Object reference not set to an instance of an object.
> At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\RemoteDesktop\Certificate.psm1:389 char:5
> +     Invoke-Command -Session $M3PSession -ArgumentList @($params)`
> +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> 2020-09-23 09:56:28.580 -07:00 [INF] [Post-Request Script] :: Invoke-Command: Object reference not set to an instance of an object.
> At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\RemoteDesktop\Certificate.psm1:389 char:5
> +     Invoke-Command -Session $M3PSession -ArgumentList @($params)`
> +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> Invoke-Command: Object reference not set to an instance of an object.
> At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\RemoteDesktop\Certificate.psm1:389 char:5
> +     Invoke-Command -Session $M3PSession -ArgumentList @($params)`
> +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> 2020-09-23 09:56:28.580 -07:00 [INF] Deployment Tasks did not complete successfully."

Powershell script is:
param($result)

$pfxpath = $result.ManagedItem.CertificatePath

Import-Module RemoteDesktop

Set-RDCertificate -Role RDPublishing -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDWebAcces -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDGateway -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDRedirector -ImportPath $pfxpath -Force

is there a way to schedule the cert renewal itself so it doesn’t happen in the middle of the day? Then I can just leave the Trigger set to Auto for the post successful script in the middle of the night.

Hi, you can’t scheduled actual cert renewal because that’s something that needs to be allowed to fail and then be repeated (for instance if the Let’s Encrypt API temporarily stops working) so that renewal/recovery is automatic, but you can set the Task to Manual and use your own windows scheduled task to trigger it, or run it manually during maintenance windows.

You add the Task under Tasks in Certify The Web and set it to manual, this then gives you a command line command you can optionally run, this in turn will perform the normal certify task execution just like if you hit the Play button in the UK.

Thanks, but will my PowerShell script to import the certificates into RDS still work if its ran via task scheduler vs automatically from “within” CTW?

$pfxpath = $result.ManagedItem.CertificatePath
Import-Module RemoteDesktop
Set-RDCertificate -Role RDPublishing -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDWebAcces -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDGateway -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDRedirector -ImportPath $pfxpath -Force

If you just setup a windows scheduled task and point it to your powershell script then no, it won’t work because there is nothing to pass it the $result object which contains all the magic values for things like cert file path and thumbprint etc. To get that approach to work you’d need either another script task in Certify to write out a ‘proper’ version of your script populated with the variable values or you could also write out the $result object as JSON to a file, then read that into your own script later but that’s a bit more complicated.

A certify script to write out the results object to a file (e.g. C:\temp\result.json) would be:

param($result)

$result | ConvertTo-Json -depth 100 | Out-File "C:\temp\result.json"

A script to then read the results and work with them (like in your own windows scheduled task) would be:


$result = Get-Content -Raw -Path "C:\temp\result.json" | ConvertFrom-Json

# use the result object values, e.g.:
$pfxpath = $result.ManagedItem.CertificatePath

Import-Module RemoteDesktop

Set-RDCertificate -Role RDPublishing -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDWebAcces -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDGateway -ImportPath $pfxpath -Force
Set-RDCertificate -Role RDRedirector -ImportPath $pfxpath -Force

The ‘proper’ way (i.e. the way it’s designed) is to call the certify deployment task from the command line, i.e. certify deploy "<managed cert id>:" "<deployment task id>" then certify calls your script, but there are pros and cons to both approaches.

I understand what you’re saying, and thanks for writing out that script, might be the way we end up doing it. As you mention, the ‘proper’ way (unless I’m misunderstanding) wont work because it’ll be missing the $result value that the renewal process spits out. So you agree that making a powershell script that writes to JSON via an automatic post deployment task? Then use the scheduled task to do the second part during off hours so users dont get booted off during the certificate import process. Unless we were able to schedule the entire certificate renewal process, that is…

Just to clarify, the proper way calls certify itself as a command line (which in turn calls the background service), which in turn invokes the deployment task as if it had run automatically. It’s just a way for you to control when that certify task is triggered and is designed to allow for maintenance window scenarios where you don’t want a service to risk dropping/restarting until a scheduled time.

The $result value will indeed be passed in, as if the renewal had just happened and you were running the certify task automatically.

Apologize for being so dense with understanding this. lol

Previously, you said “If you just setup a windows scheduled task and point it to your powershell script then no, it won’t work because there is nothing to pass it the $result object which contains all the magic values for things like cert file path and thumbprint etc.”

So are you saying if I change the post deployment task (the PowerShell script to import the certificates into RDS) to “manual”… which gives me the command “certify deploy XXXX XXXXX”… if I make that a scheduled task via Windows… the PowerShell script that gets ran will utilize the $result value that Certify passes, etc?

Yep. I’m saying that if you make a windows scheduled task and point it to the certify command then the Task script will run as normal and will receive the $result parameter object.

The best test to see how it all works is to just create a certify powershell task that writes something simple to a file, then try scheduling a windows task for that to check it works.

Hi there,

Just jumping in on this, is there a way in the script make it check if you need to deploy the cert in the script when you’re running it from the scheduled task/ manual run.

As in I don’t need to set the RD certificate if the certificate hasn’t renewed. The schedtask runs each night so i don’t really want it to try and replace the same cert over and over again.

Thanks!

Hi, currently there isn’t anything built in to the manual task execution that works that way but you could script it. i.e. if you compare the thumbprint of the latest cert vs what’s currently applied. Personally I’d just run the scheduled task less frequently, e.g. once a month - the old cert will be valid as long as it’s still in the store and by default we don’t clean up old ones until they’re expired.

Other alternatives include creating a simple control file as a deployment task (e.g. renewed.txt) whose presence indicates that there is work to do, then you check for that file, run your task, and then delete the file. Once you’re scripting and scheduling tasks etc the options are pretty much unlimited.

What we’re doing is: We raise the builtin certificate auto renewal days to 35. Then we make a scheduled task run every 4 weeks that does “certify renew –force-renew-all

And just keep the deployment tasks on the “on success”. This way we have control over when the scheduled task runs, but if it fails we’ll fall back to the certify run as soon as possible method.

Not perfect, but good enough!

CertRenew.xml · GitHub ← Here’s the scheduled task exported for you.

I’m not sure I understand - why wouldn’t you just use the manual /deferred task option and run that in a scheduled task? That way your cert renews when it needs to (so it’s always a recent cert) but it doesn’t get applied until you tell it to - which is the intended design of that feature.

To clarify --force-renew-all should be used with caution, it’s ok if you have one cert but it you have many it could result in rate limits against the LE API and is only really for use in extraordinary circumstances.