-
-
Notifications
You must be signed in to change notification settings - Fork 797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exchange script adjustments #702
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Just giving my thoughts.
[Parameter(Position=1,Mandatory=$true)] | ||
[string]$ExchangeServices | ||
[string]$ExchangeServices, # comma-separated list of exchange services to import for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should make this a ValidateSet() with the possible values 'IMAP', 'POP', 'UM', 'IIS', 'SMTP', 'Federation', 'UMCallRouter'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that might be a bit out of scope as it would require updating with specific knowledge on Exchange. Keep in mind this script should never be run interactively.
[string]$ExchangeServices | ||
[string]$ExchangeServices, # comma-separated list of exchange services to import for | ||
[Parameter(Position=2,Mandatory=$false)] | ||
[bool]$LeaveOldExchangeCerts = 1, # 0 for false, 1 for true. If set to 1, will skip removal of old exchange certs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use $true
and $false
instead of 0 and 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$true
and $false
are a pain to pass through from cmd. passing 0 and 1 are dead simple.
Since it's a bool, powershell will interpret all those the same if you really want to.
## Documentation referenced from https://technet.microsoft.com/en-us/library/aa997231(v=exchg.160).aspx | ||
|
||
# Should work with exchange 2007 and higher | ||
Get-PSSnapin -registered | ? {$_.Name -match "Microsoft.Exchange.Management.PowerShell" -and ($_.Name -match "Admin" -or $_.Name -match "E2010" -or $_.Name -match "SnapIn")} | Add-PSSnapin -ErrorAction SilentlyContinue | ||
Get-PSSnapin -registered | Where-Object {$_.Name -match "Microsoft.Exchange.Management.PowerShell" -and ($_.Name -match "Admin" -or $_.Name -match "E2010" -or $_.Name -match "SnapIn")} | Add-PSSnapin -ErrorAction SilentlyContinue | ||
|
||
#$OldThumbprint = (Get-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint).CurrentValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this comment.
## Documentation referenced from https://technet.microsoft.com/en-us/library/aa997231(v=exchg.160).aspx | ||
|
||
# Should work with exchange 2007 and higher | ||
Get-PSSnapin -registered | ? {$_.Name -match "Microsoft.Exchange.Management.PowerShell" -and ($_.Name -match "Admin" -or $_.Name -match "E2010" -or $_.Name -match "SnapIn")} | Add-PSSnapin -ErrorAction SilentlyContinue | ||
Get-PSSnapin -registered | Where-Object {$_.Name -match "Microsoft.Exchange.Management.PowerShell" -and ($_.Name -match "Admin" -or $_.Name -match "E2010" -or $_.Name -match "SnapIn")} | Add-PSSnapin -ErrorAction SilentlyContinue | ||
|
||
#$OldThumbprint = (Get-Item -Path RDS:\GatewayServer\SSLCertificate\Thumbprint).CurrentValue | ||
$CertInStore = Get-ChildItem -Path Cert:\LocalMachine -Recurse | Where-Object {$_.thumbprint -eq $NewCertThumbprint} | Sort-Object -Descending | Select-Object -f 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does Sort-Object -Descending
by default sort on the correct date field, so that the newest are on top?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly not sure why i kept using sort, since I'm not specifying the field to sort on and, more importantly, because we are filtering by thumbprint so we will never get certs from different days.
@@ -1,56 +1,95 @@ | |||
param( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add comment-based help with a .SYNOPSIS
, and an .EXAMPLE
in the comment-based help how to call this using Let's encrypt? If there are more than one option to call we should add more than one .EXAMPLE
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little torn on whether to put it there or just in the wiki. I've never done that so I can see how difficult it is. probably cleaner than the way i have remarks currently
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
|
||
$cert = $SourceStore.Certificates | Where-Object {$_.thumbprint -eq $CertInStore.Thumbprint} | ||
# Now try to find it again... | ||
$CertInStore = Get-ChildItem -Path Cert:\LocalMachine -Recurse | Where-Object {$_.thumbprint -eq $NewCertThumbprint} | Sort-Object -Descending | Select-Object -f 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not need to do this, Import-ExchangeCertificate
returns the certificate if you add $CertInStore = Import-ExchangeCertificate @importExchangeCertificateParameters -ErrorAction Stop
or throws an error (stops) if it can't find the certificate.
If you keep this row, the same comment about -Descending
as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems cleaner to do this so we are sure to get the right type of object. I don't have an exchange install to know if the passthrough from import-exchangecertificate
returns the same type of object as get-childitem
. I like to play it safe in that regard.
# Make sure variable is defined | ||
$null = Get-ChildItem $CertInStore.PSPath -ErrorAction Stop | ||
# Cert must exist in the personal store of machine to bind to exchange services | ||
if($CertInStore.PSPath -notlike "*LocalMachine\My\*"){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Are we using this code path as a fail safe if the Import-ExchangeCertificate
did not add the certificate to the LocalMachine\My
store? Is there a scenario when Import-ExchangeCertificate
does not import the certificate to the LocalMachine\My
store?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, keep in mind the script's original intent was not to be used with centralssl (the fqdn and storepath aren't required parameters)
|
||
if(-not $LeaveOldExchangeCerts){ | ||
"Old Exchange certificates being cleaned up" | ||
Get-ExchangeCertificate -DomainName $CertInStore.Subject.split("=")[1] | Where-Object -FilterScript { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$CertInStore.Subject.split("=")[1]
Could we use $TargetHost
instead of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TargetHost isn't guaranteed to be passed. According to everything I'm reading in these issue threads, the primary CN of the cert should be the same as TargetHost
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Didn't thought about that. Looks good to me.
|
||
}catch{ | ||
"Cert thumbprint was not set successfully" | ||
"Error: $($Error[0])" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Error: $($Error[0])"
Could we use throw $_
instead, to throw a real error? Or after this row maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be a question for @WouterTinus . I don't see the difference really. Either way it'll be logged.
Disclaimer: I'm not a proper developer, so I'm not really well-versed error handling. Outputting them always works fine enough for my needs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Your way works as well if outputting is the only requirement and not stopping execution telling the user something failed with red error text. :)
Looks good to me.
Updated per recommendations from @johlju |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome job adding comment based help! Added a few more suggestive comments around the code.
@@ -1,56 +1,95 @@ | |||
param( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
$CertInStore = Get-ChildItem -Path Cert:\LocalMachine -Recurse | Where-Object {$_.thumbprint -eq $NewCertThumbprint} | Select-Object -f 1 | ||
try{ | ||
if($CertInStore.PSPath -notlike "*LocalMachine\My\*"){ | ||
"Cert thumbprint not found in the cert store... which means we should load it. This means TargetHost and StorePath must be specified" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it is not certain that $TargetHost
and $StorePath
is passed, then if they are not, we should not go down this code path since Import-ExchangeCertificate
will fail.
So we should add something like
if ($TargetHost -and $StorePath)
{
...
$null = Import-ExchangeCertificate @importExchangeCertificateParameters -ErrorAction Stop
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it'll fail into the catch block. I see no functional difference since both would stop the script from continuing.
Get-PSSnapin -registered | ? {$_.Name -match "Microsoft.Exchange.Management.PowerShell" -and ($_.Name -match "Admin" -or $_.Name -match "E2010" -or $_.Name -match "SnapIn")} | Add-PSSnapin -ErrorAction SilentlyContinue | ||
Get-PSSnapin -registered | Where-Object {$_.Name -match "Microsoft.Exchange.Management.PowerShell" -and ($_.Name -match "Admin" -or $_.Name -match "E2010" -or $_.Name -match "SnapIn")} | Add-PSSnapin -ErrorAction SilentlyContinue | ||
|
||
$CertInStore = Get-ChildItem -Path Cert:\LocalMachine -Recurse | Where-Object {$_.thumbprint -eq $NewCertThumbprint} | Select-Object -f 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We expect the certificate to me in LocalMachine\My. Why don't we just look for it there?
$CertInStore = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.thumbprint -eq $NewCertThumbprint}
try
{
if (-not $CertInStore)
{
...
$null = Import-ExchangeCertificate @importExchangeCertificateParameters -ErrorAction Stop
...
}
}
|
||
$cert = $SourceStore.Certificates | Where-Object {$_.thumbprint -eq $CertInStore.Thumbprint} | ||
# Make sure variable is defined | ||
$null = Get-ChildItem $CertInStore.PSPath -ErrorAction Stop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do the change above, and below, then this row is not needed.
|
||
# Cert must exist in the personal store of machine to bind to exchange services | ||
if($CertInStore.PSPath -notlike "*LocalMachine\My\*"){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use the same check as above here.
try
{
if (-not $CertInStore)
{
...
$SourceStoreScope = 'LocalMachine'
$SourceStoreName = 'My'
...
}
}
PrivateKeyExportable = $true | ||
} | ||
|
||
$null = Import-ExchangeCertificate @importExchangeCertificateParameters -ErrorAction Stop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could just do $CertInStore = Import-ExchangeCertificate ...
here and skip the row below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to the docs, there are no outputs of this cmdlet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the method of getting the cert after the import is a bit of a check to make sure $NewCertThumbprint
is set properly.
|
||
if(-not $LeaveOldExchangeCerts){ | ||
"Old Exchange certificates being cleaned up" | ||
Get-ExchangeCertificate -DomainName $CertInStore.Subject.split("=")[1] | Where-Object -FilterScript { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Didn't thought about that. Looks good to me.
|
||
}catch{ | ||
"Cert thumbprint was not set successfully" | ||
"Error: $($Error[0])" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Your way works as well if outputting is the only requirement and not stopping execution telling the user something failed with red error text. :)
Looks good to me.
@LBegnaud - How are you feeling about this? My gut says that the script is much better already than the once that's currently being distributed in our releases. Would fixing the parameter type issue at a later date cause problems for people upgrading? I guess what I mean to say is don't let perfect get in the way of good :). |
I have reviewed the existing script and the proposed script and I'm confident that what they are both trying to do is going to work fine, but it does seem like the new changes to the proposed script might need a little touching up before merging. |
I honestly had forgotten about this. I think that it's fine in its current state. I'm going to make a quick fix to the parameter to be an int that gets passed a 0 or 1 as I don't have time to mess with it too much right now. I'll push a commit then you're good to merge. |
K I think it's ready. Thanks for the reminder |
No problem, thanks for contributing! |
Supports the ability to import cert from filesystem, add option to remove old exchange certificates, and cleans up logic
Might be best to commit and squash.