Here on the heels of my last two blog posts about the Smo.ManagedComputer class, I wanted to wrap up with a function I put together to help with managing startup parameters. It is the result of the work I did to figure out how to move the master db along with the inspiration I got from Shawn Melton’s(@wsmelton) blog post on the matter.
Looking back at the previous blog post, changing the the startup parameters through the SMO is pretty easy with the ManagedComputer class. In some ways, it is too easy. As Shawn calls out, you could easily overwrite the full string and remove the startup locations for your master database (and breaking your instance). This is where tool building can be such an aid, because by wrapping the change code in a function, we can build some safety mechanisms to protect us (or others) from doing harm when trying to make this sort of change. The function I wrote is not terribly long, but I’ll spare you the whole thing by letting you view it on GitHub. We’ll use our time better by going over how I constructed it while focusing on some of my tool building principles.
The first is trying to build around multi-instance execution. You will note that my parameter block uses a string array called Instances ($Instances):
function Set-SQLStartupParameters{ [cmdletbinding(SupportsShouldProcess=$true)] param([string[]] $Instance ,[string[]] $StartupParameters )
This array will drive a foreach loop inside the function, allowing me to apply the same block of code to each instance name. There are a lot of situations when I am applying changes across multiple SQL instances and I want to keep the call simple.
The second parameter is also a string array, which is a collection of the startup parameters I want to apply. While the property in the SMO is a semi-colon delimited string and will ultimately be set that way, I find that using a string array makes the collection of parameters much more manageable and readable. It is important that any tool you create is not a struggle to use.
Next up, as we walk through the function, you will see some Write-Verbose statements:
Write-Verbose "Old Parameters for $i :" Write-Verbose $wmisvc.StartupParameters
It is easy to write a script that can execute a bunch of things, but when it starts throwing out red error text you could have a debugging nightmare. By adding these statements, I can add logging information to the output so I can see things like the old startup parameters and the string that will be added to the service for the new parameters.
The final item to call out is the meat of the function. As it has been emphasized, altering these startup parameters can be very dangerous and could possibly break the instance if we leave out the -d, -e, or -l parameters or set them improperly. I wrote this function to capture the existing values of these parameters and, if they are not being changed, keep them.
$oldparams = $wmisvc.StartupParameters -split ';' $newparams = @() foreach($param in $StartupParameters){ if($param.Substring(0,2) -match '-d|-e|-l'){ $SystemPaths = $true $newparams += $param $oldparams = $oldparams | Where-Object {$_.Substring(0,2) -ne $param.Substring(0,2)} } else{ $newparams += $param } } $newparams += $oldparams | Where-Object {$_.Substring(0,2) -match '-d|-e|-l'}
While not the most graceful approach, the logic is as follows. Start with the old parameter block and check each new parameter. If any of the sensitive parameters are specified, remove it from the old parameter set and use the new one. Once we have gone through the new ones, pull any remaining sensitive parameters from the old set and insert them into the new. This way we should always keep a value for the sensitive parameters.
The non-sensitive parameters are a different manner. They will not be retained, but instead overwritten. This means if you want to retain an existing trace flag, you will need to include it in your new parameter set when you call the function.
Now, what this does NOT do is insure these sensitive parameters are valid. This is up to the user to make sure that the files and paths are valid. I added a warning to the function that if these values are changed, the user needs to validate them, but the rest is on the person executing the call. This function will also not restart the service to apply the changes. Because we need to be sensitive about when our SQL Server services restart, I wanted to leave that in the control of the user.
Let’s look at some examples and see how this function works. If we want to add the trace flag to suppress successful backup messages, it’s just a simple line of code:
Set-SQLStartupParameters -Instance PICARD -StartupParameters '-T3226' -Verbose -WhatIf
By specifying the -Verbose switch, all the included verbose messages I built into the function will display. Using -Whatif then gives us a chance to see what is going to happen before we actually apply it. This becomes useful both for debugging and checking that what we are doing is really what we want to do.
If we remove the -WhatIf, the change will actually be applied:
Set-SQLStartupParameters -Instance PICARD -StartupParameters '-T3226' -Verbose
This gives us all the same information, but now actually applies the change. The function is written to warn us that, while the startup parameters have been changed, we still need to restart the instance.
What about moving our master database? Let’s use last week’s example and relocate the master database as part of a set:
#Set the params as a string array $params = @('-dC:\DBFiles\MSSQLSERVER\master.mdf', '-eC:\DBFiles\MSSQLSERVER\ERRORLOG', '-lC:\MSSQLSERVER\mastlog.ldf') Set-SQLStartupParameters -Instance PICARD -StartupParameters $params -Verbose
Because of how the function is built, it displays the additional warning that we have changed the sensitive parameters. While the function can not save us from everything, it will try and provide as much information as it can to help.
I have tried to share with you some of the things I think about when building my own functions. However, I also want to call out the importance of getting other opinions. While I wrote the code, I got a LOT of help from Shawn Melton in the way of code review. He was kind enough to look over my function and suggest a few things (like the -WhatIf flag). Building code is a team effort and having a supportive community is a great way to help you create your own tools.