-1

I wrote a Powershell-Script which downloads a pfx-Container via SCP to a Windows 2019 Server. After that it will check if the currently installed server certificate matches the downloaded one or not. If it does not match it will replace it with the downloaded one. This is to replace an outdated Letcencrypt certificate with a new one.

#Retrieving SecureString Instance of PFX-Password
$pw = Get-Content "C:\Users\user\Desktop\example_password.txt" | ConvertTo-SecureString -Key (1..16) 

$getPwResult = $LastExitCode
if ($getPwResult -eq 0)
{
  Write-Host "Successfully got Secure Password!"
}
else
{
  Write-Host "Error retrieving password"
  Write-Host $getPwResult
  exit $getPwResult
}



$oldThumbprint = Get-ChildItem  -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -Match "server.example.org"} | Select Name -ExpandProperty Thumbprint 

$getoldThumbprintResult = $LastExitCode
if ($getoldThumbprintResult -eq 0)
{
 
  Write-Host "Successfully got old thumbprint! $oldThumbprint"
}
else
{
  Write-Host "Error retrieving old thumbprint"
  exit $getoldThumbprintResult
}

#Downloading current file via WinSCP
& "C:\Program Files (x86)\WinSCP\WinSCP.com" `
  /log="C:\Users\user\Desktop\WinSCP.log" /ini=nul `
  /command `
    "open sftp://username@server/" `
    "get -neweronly /path/to/server.pfx C:\Users\user\Desktop\" `
    "exit"

$winscpResult = $LastExitCode
if ($winscpResult -eq 0)
{
  Write-Host "Successfully downloaded file!"
}
else
{
  Write-Host "Error while downloading file"
  exit $winscpResult
}

$newThumbprint = (Get-PfxData -Password $pw -FilePath C:\Users\user\Desktop\server.pfx).EndEntityCertificates.Thumbprint
$getnewThumbprintResult = $LastExitCode
if ($getnewThumbprintResult -eq 0)
{
  Write-Host "Successfully got new thumbprint! $newThumbprint"
}
else
{
  Write-Host "Error retrieving new thumbprint"
  exit $getnewThumbprintResult
}


if ($oldThumbprint -ne $newThumbprint) {
  #Executing the import of new PFX
  Import-PfxCertificate -FilePath "C:\Users\user\Desktop\server.pfx" -Password $pw -CertStoreLocation Cert:\LocalMachine\My
  $installCertExitCode = $LastExitCode

  if ($getnewThumbprintResult -eq 0) {
    Write-Host "Installed new certificate"
  } else {
    Write-Host "An error occured while installing new certificate."
    exit $installCertExitCode
  }

} else {
    Write-Host "File has not been changed!"
}

All in all this works fine but when I wanted to create a scheudule for execution strange things happened. First of all the sript won't be executed via Windows scheudler regardless of what I tried...

After closer inspection I recognized that sometimes most commands fail when I execute the Powershell-Script. I always happens at a freshly opened Powershell-Terminal at the first execution.

So the folling happens (in the same PS-Terminal):

#First attempt:

PS C:\Users\user\Desktop> C:\Users\user\Desktop\install_cert.ps1
Error retrieving password


#Second attempt:

PS C:\Users\user\Desktop> C:\Users\user\Desktop\install_cert.ps1
Successfully got Secure Password!
Successfully got old thumbprint! 
[...]
Successfully downloaded file!
Successfully got new thumbprint! 
Installed new certificate.

I wrote a small batch file with the sole purpose of reproduce the behaeveour of the scheuduler task:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile -ExecutionPolicy UnRestricted -File C:\Users\user\Desktop\install_cert.ps1

When I execute it the script fails every time.

PS C:\Users\user\Desktop> C:\Users\user\Desktop\install_cert.ps1
Error retrieving password

So I guess that this is the problem which has to be solved.

I also removed the first exit-statement which results in a error while executing 'Get-ChildItem -Path Cert:\LocalMachine\My [...]'.

There is no output from the commands which makes debugging hard for me.

Does anybody know what is happening here and how to fix it?

Thanks in advance!

Edit:

The WinSCP part works fine. The error occures in the statements before:

#Retrieving SecureString Instance of PFX-Password
$pw = Get-Content "C:\Users\user\Desktop\example_password.txt" | ConvertTo-SecureString -Key (1..16) 

$getPwResult = $LastExitCode
if ($getPwResult -eq 0)
{
  Write-Host "Successfully got Secure Password!"
}
else
{
  Write-Host "Error retrieving password"
  Write-Host $getPwResult
  exit $getPwResult
}



$oldThumbprint = Get-ChildItem  -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -Match "server.example.org"} | Select Name -ExpandProperty Thumbprint 

$getoldThumbprintResult = $LastExitCode
if ($getoldThumbprintResult -eq 0)
{
 
  Write-Host "Successfully got old thumbprint! $oldThumbprint"
}
else
{
  Write-Host "Error retrieving old thumbprint"
  exit $getoldThumbprintResult
}

As suggested I tried to replace the WinSCP-Code and it did not fix the problem.

izphi78
  • 1
  • 1
  • If you open this in the ISE or VSCode, you'll see that there are several syntax errors, and I am really surprised that this is working at all. Specifically, everything from [#Downloading current file via WinSCP], which is not properly quoted. Though using the graveyard accent (backtick) can be used as a continuation character (many frowned on this though), proper quoting of strings when running external commands/executables is a must-have. – postanote Dec 22 '20 at 01:23
  • Using executables in Powershell is documented here: [• PowerShell: Running Executables](https://social.technet.microsoft.com/wiki/contents/articles/7703.powershell-running-executables.aspx) Try using this implementation instead. [WinSCP.NET assembly from PowerShell](https://winscp.net/eng/docs/library_powershell). – postanote Dec 22 '20 at 01:26
  • Yes, my bad. When I stripped the credentials I accidentally left one excess quote... I removed it. I did the developement in ISE. I will definetely have a look at the stated documentation but do you really think that the issue of not beeing executed at first run could be due to wrong a handled executable? I mean it works when I let it run at least twice. – izphi78 Dec 22 '20 at 02:15
  • I've had this happen in the past and that was my catch22. If in the ISE, remember it loads on a line by line read, and so, when the first run happens, it just pops stuff on the stack for a run, then second time thru, any variable population, etc., are set, which were not set in the first run. One way to discover such things is via stack traces, or initially using [Trace-Command](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/trace-command?view=powershell-7.1). – postanote Dec 22 '20 at 03:30

2 Answers2

1

Refactor your WinSCP command to this (that is if you choose not to use the WinSCP .NET implementation):

#Downloading current file via WinSCP
& "C:\Program Files (x86)\WinSCP\WinSCP.com" `
    '/log="C:\Users\user\Desktop\WinSCP.log"' `
    '/ini=nul' `
    /command `
    "open sftp://username@server/" `
    "get -neweronly /path/to/server.pfx C:\Users\user\Desktop\" `
    "exit"

Or this...

$AllArgs = @(
    '/log="C:\Users\user\Desktop\WinSCP.log"',
    '/ini=nul',
    '/command',
    'open sftp://username@server/',
    'get -neweronly /path/to/server.pfx C:\Users\user\Desktop\',
    'exit'
)
 & '$WinScpCmd' $AllArgs

and see if that works on the first run use case. Again, as per my comment above, use Trace-Command to watch what is happening.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
postanote
  • 15,138
  • 2
  • 14
  • 25
0

Okay I got the solution:

I just copied the $LastExitCode from a script found on the internet and thought it was like $? in linux. Unfortunately it it happens to be that this exact same command is suitable here too.

So if i replace it the script works fine:

#Retrieving SecureString Instance of PFX-Password
$pw = Get-Content "C:\Users\user\Desktop\example_password.txt" | ConvertTo-SecureString -Key (1..16) 

$getPwResult = $?
if ($getPwResult)
{
  Write-Host "Successfully got Secure Password!"
}
else
{
  Write-Host "Error retrieving password"
  Write-Host $getPwResult
  exit $getPwResult
}



$oldThumbprint = Get-ChildItem  -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -Match "server.example.org"} | Select Name -ExpandProperty Thumbprint 

$getoldThumbprintResult = $?
if ($getoldThumbprintResult)
{
 
  Write-Host "Successfully got old thumbprint! $oldThumbprint"
}
else
{
  Write-Host "Error retrieving old thumbprint"
  exit $getoldThumbprintResult
}

#Downloading current file via WinSCP
& "C:\Program Files (x86)\WinSCP\WinSCP.com" `
  /log="C:\Users\user\Desktop\WinSCP.log" /ini=nul `
  /command `
    "open sftp://username@server/" `
    "get -neweronly /path/to/server.pfx C:\Users\user\Desktop\" `
    "exit"

$winscpResult = $?
if ($winscpResult)
{
  Write-Host "Successfully downloaded file!"
}
else
{
  Write-Host "Error while downloading file"
  exit $winscpResult
}

$newThumbprint = (Get-PfxData -Password $pw -FilePath C:\Users\user\Desktop\server.pfx).EndEntityCertificates.Thumbprint
$getnewThumbprintResult = $?
if ($getnewThumbprintResult)
{
  Write-Host "Successfully got new thumbprint! $newThumbprint"
}
else
{
  Write-Host "Error retrieving new thumbprint"
  exit $getnewThumbprintResult
}


if ($oldThumbprint -ne $newThumbprint) {
  #Executing the import of new PFX
  Import-PfxCertificate -FilePath "C:\Users\user\Desktop\server.pfx" -Password $pw -CertStoreLocation Cert:\LocalMachine\My
  $installCertExitCode = $?

  if ($installCertExitCode) {
    Write-Host "Installed new certificate"
  } else {
    Write-Host "An error occured while installing new certificate."
    exit $installCertExitCode
  }

} else {
    Write-Host "File has not been changed!"
}

This would somehow explain why the script would run in the second attempt because this variable was set like postanote stated in the comment above.

Thank you for your help!

izphi78
  • 1
  • 1