Closed FH-Inway closed 2 years ago
The same issue occurs when Disable-D365MaintenanceMode is executed without administrator privileges.
I also tried cmdlet Get-D365User thinking that the issue might impact all cmdlets that use a SQL script. However, that cmdlet was able to execute without administrator privileges. I think it might be related to how the parameters for the Get-SqlCommand
call are created.
I'll do some testing tonight 👍
The 128 char limit / exception is normally tied back to the capabilities to descrypt the web.config file.
Why one cmdlet is capable of doing just that, while another isn't - seems odd. As promised, I'll dive into the details tonight.
Okay - things are a bit complicated.
I have been testing on a CHE - my VPN isn't working, so I can't test against an onebox. But my memory should serve me right on this one:
It all boils down to what privileges Microsoft has enforced on the different components / areas / layers on either the CHE build or the Onebox build.
Getting the webconfig file is easy - but decrypting it is the hard part. While running in elevated (RunAs Administrator), our powershell session is capable of opening the certificate store and using the needed certificate to decrypt the web.config on the fly and obtain the sql password.
This explains the Exception setting "ConnectionString": "The value's length for key 'password' exceeds it's limit of '128'
- generally speaking.
What you found, is in fact a bug: Get-D365User (Works) Line 80: We test whether or not we can use a Trusted Connection (AKA - Windows Authentication, current user signed in) Line 86: We pass the object (true in this case) directly to the command builder and get a SQL connection object that will use Trusted Connection = True (AKA - NO SQL USER OR PASSWORD) https://github.com/d365collaborative/d365fo.tools/blob/91be942e9ed19e2c84133c4058a78aa950f9e14f/d365fo.tools/functions/get-d365user.ps1#L80-L86
Enable-D365MaintenanceMode
Line 117: We test whether or not we can use a Trusted Connection (AKA - Windows Authentication, current user signed in)
Line 132: We pass the object (true in this case) into a SUB function, Invoke-D365SqlScript
...
https://github.com/d365collaborative/d365fo.tools/blob/91be942e9ed19e2c84133c4058a78aa950f9e14f/d365fo.tools/functions/enable-d365maintenancemode.ps1#L117-L132
Invoke-D365SqlScript
Remember line 119-125 from Enable-D365MaintenanceMode
??
Line 82: We test whether or not we can use a Trusted Connection (We already did test it), but because we supplied SqlUser and SqlPwd, the second Test-TrustedConnection
disables the Trusted Connection.
https://github.com/d365collaborative/d365fo.tools/blob/91be942e9ed19e2c84133c4058a78aa950f9e14f/d365fo.tools/functions/invoke-d365sqlscript.ps1#L63-L82
I believe the fix is to move the first elseif (2nd logical testing) down one and move the second elseif (3rd logical testing) up and that way always test for TrustedConnection first before going for SqlUser / SqlPwd. https://github.com/d365collaborative/d365fo.tools/blob/91be942e9ed19e2c84133c4058a78aa950f9e14f/d365fo.tools/internal/functions/test-trustedconnection.ps1#L28-L43
But changing this could be breaking change, as this logic has been around for so long and people most like are used to the way it works now. On the other side, looking at the current cmdlets that double jump like this are:
Finally - Do we as a community gain anything while supporting non-Administrator / non-Elevated? All CHE gives us access to the Administrator. All PowerShell sessions started by that account, auto-elevates. Onebox is a different breed entirely. Here people needs to learn how to configure the PowerShell session to always elevate (which will prompt them) - but when that is first in place, things are easier for everyone and will behave the same way.
Nice analysis and I like how the links to line numbers in a file are displayed.
I guess the question on supporting non-Administrator should at least be put into a discussion to get more eyes on it. I created #596 for that.
For cmdlets that currently fail in non-Administrator execution, I suggest we add a warning that informs the user that the cmdlet can only be executed with admin privileges and prevent the error message.
For other cmdlets, maybe add a similar warning but without preventing the execution?
Does PowerShell have a hook when a module is loaded/imported, so we could check then for admin privilege?
So PowerShell as a concept doesn't have what we are looking for - but we implemented our own solution.
$Script:IsAdminRuntime
https://github.com/d365collaborative/d365fo.tools/blob/c34e0fd70311f7b368c618f0a54c29a917428d53/d365fo.tools/functions/set-d365sdpcleanup.ps1#L35-L39
It is based on the assumption, that an user in CHE or onebox, will be part of the built-in Administrators User Group on the Windows Installation. It was perfect back in the days - and I expect it to still be working as intended.
Looks good, maybe extract it into an external function so there is a consistent behavior across all cmdlets? Probably with a parameter that determines if the calling function should be stopped.
So something like https://github.com/FH-Inway/d365fo.tools/blob/%23590-CheckForAdminRuntime/d365fo.tools/internal/functions/test-isadminruntime.ps1#L27-L42
And then used like https://github.com/FH-Inway/d365fo.tools/blob/%23590-CheckForAdminRuntime/d365fo.tools/functions/enable-d365maintenancemode.ps1#L104-L108
User experience would look like this:
PS C:\windows\system32> Enable-D365MaintenanceMode
[19:33:53][Test-IsAdminRuntime] It seems that you ran this cmdlet non-elevated. To run this cmdlet, it needs to be called from an elevated console. Please exit the current console and start a new one with "Run As Administrator"
[19:33:53][Test-IsAdminRuntime] Elevated permissions needed. Please start an elevated session and run the cmdlet again.
[19:33:53][Enable-D365MaintenanceMode] Maintenance mode can only be enabled in an elevated session.
WARNING: [19:33:53][Enable-D365MaintenanceMode] Stopping because of running non-elevated.
What do you think?
As an aside, while writing this, I went down a bit of a rabbit hole: I first tried to do this with Stop-PSFFunction
and along the way came across #265, which discusses how exception should be handled by the module. I also came across #173 Test-PSFFunctionInterrupt doesn't catch Stop-PSFFunction, which is why I decided to not use it.
I did read all - but will just comment on the rabbit hole for now:
We actually solved that together with Fred.
Note the -StepsUpward 1
parameter ? This marks the callstack, so that the parent caller function can rely on Test-PSFFunctionInterrupt
.
This is in place for all cmdlets that use the Test-PathExists
.
So your idea is solid, you just need this small trick and then you're good to go 🤘
Got it. Could you take another look at the two changes?
Coming with 0.6.70 in 20 minutes
When running the Enable-D365MaintenanceMode cmdlet from a PowerShell session without administrator privileges, an error is shown and the maintenance mode is not enabled.
Repro steps
Enable-D365MaintenanceMode
Result Error message
See Gist d365fo.tools Enable-D365MaintenanceMode error for the full error message.
Expected result Maintenance mode is enabled without error message, just like when running the cmdlet as administrator. Or if that is not possible, a warning message should inform the user that enabling the maintenance mode requires administrator privileges.