My last post had a lot of information about the new .NET PowerShell notebooks including installation instructions.
.NET Notebooks are Jupyter Notebooks that use .NET core to enable C#, F# and PowerShell kernels.
Use Cases
One of the main benefits that I see for Jupyter Notebooks for Ops folk is that the results of the query are saved with the notebook. This makes them fantastic for Incident resolution.
If you have an incident at 3am and you know that you will need that information in the wash up meeting the next day instead of copying and pasting results into a OneNote document or a text file, you can simply run the queries in a notebook and save it.
In the meeting, you can simply open the notebook and the results will be available for everyone to see.
Even better, if you have a template notebook for those scenarios and you can then compare them to previous occurrences.
Using Pester
Using Pester to validate that an environment is as you expect it is a good resource for incident resolution, potentially enabling you to quickly establish an area to concentrate on for the issue. However, if you try to run Pester in a .NET Notebook you will receive an error
Describe:
Line |
3 | Describe "Checking Problem ...... by $($ENV:USERDOMAIN) $($ENV:UserName)" {
| ^ The 'Describe' command was found in the module 'Pester', but the module could not be loaded. For more information, run 'Import-Module Pester'.
Fixing it
When you try to Import-Module Pester you get the following error
Get-Command: C:\Users\mrrob\Documents\PowerShell\Modules\Pester\4.9.0\Pester.psm1
Line |
94 | $script:SafeCommands['Get-CimInstance'] = Get-Command -Name Get-CimInstance -Module CimCmdlets @safeCommandLookupParameters
| ^ The term 'Get-CimInstance' is not recognized as the name of a
| cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
| verify that the path is correct and try again.
Import-Module: The module to process 'Pester.psm1', listed in field 'ModuleToProcess/RootModule' of module manifest 'C:\Users\mrrob\Documents\PowerShell\Modules\Pester\4.9.0\Pester.psd1' was not processed because no valid module was found in any module directory.
Thats odd, why is it failing there? Dongbo Wang from the PowerShell team explains in the issue that I raised
Yes, it was the CimCmdlets module from the system32 module path that got imported (via the WinCompat feature added in PS7). This is because currently the PS kernel don’t ship all the built-in modules along with it … The built-in modules are not published anywhere and are platform specific, it’s hard for an application that host powershell to ship them along. We have the issue PowerShell/PowerShell#11783 to track this work.
The way to resolve this is to Import the CimCmdlets Module from your local PowerShell 7 installation until the issue is resolved
Notebooks – A brilliant way of sharing what you did and the results that you got enabling others to follow along. You can do this with this Notebook. Download it and open it in your Jupyter Lab and you will be able to run it and see all of the errors and the fix on your machine.
I enjoying maintaining open source GitHub repositories such as dbachecks and ADSNotebook. I absolutely love it when people add more functionality to them.
To collaborate with a repository in GitHub you need to follow these steps
Fork the repository into your own GitHub
Clone the repository to your local machine
Create a new branch for your changes
Make some changes and commit them with useful messages
Push the changes to your repository
Create a Pull Request from your repository back to the original one
You will need to have git.exe available which you can download and install from https://git-scm.com/downloads if required
Fork the repository into your own GitHub
A fork is a copy of the original repository. This allows you to make changes without affecting the original project. It does not get updated when the original project gets updated (We will talk about that in the next post) This enables you to code a new feature or a bug fix, test it locally and make sure it is working.
Let’s take dbachecks as our example. Start by going to the project in GiHub. In this case the URL is https://github.com/sqlcollaborative/dbachecks You will see a Fork button at the top right of the page
When you click the button the repository is copied into your own GitHub account
Forking the repository has created a remote repository stored on the GitHub servers. Now that the repository has been forked you need to clone it to your local machine to create a local repository so that you can start coding your amazing fix. When you have finished you can then sync it back to your remote repository ready for a Pull Request back to the original repository.
In your browser, at your remote repository that you just created (https://github.com/YOURGITHUBUSERNAME/NameOfRepository if you have closed the page) click on Clone or Download and then the icon to the right to copy the url
You can clone your repository in VS Code or Azure Data Studio by clicking F1 or CTRL + SHIFT + P in Windows or Linux and ⇧⌘P or F1 on a Mac
then start typing clone until you see Git:Clone and press enter or click
Paste in the URL that you just copied and click enter. A dialog will open asking you to select a folder. This is the parent directory where your local repository will be created. The clone will create a directory for your repository so you do not need to. I suggest that you use a folder called GitHub or something similar to place all of the repositories that you are going to clone and create.
When it has finished it will ask you if you wish to open the repository
if you click Open it will close anything that you have already got opened and open the folder. If you click Add to Workspace it will add the folder to the workspace and leave everything you already had open as it was and surprisingly clicking Open in New Window will open the folder in a new instance of Visual Studio Code or Azure Data Studio!
and you will also be able to see the local repository files on your computer
You can clone the repository at the command line if you wish by navigating to your local GitHub directory and running git clone TheURLYouCopied
Now your local repository has been created, it’s time to do your magic coding.
Create a new branch for your changes
It is a good idea to create a branch for your amazing new feature This enables you to work on coding for that feature in isolation. It has the added advantage that if you mess it right royally up, you can just delete that branch and start again with a new one!
To create a branch in VS Code or Azure Data Studio you can click on the branch name at the bottom left.
Or open the Command Palette and type Branch until you see Git: Create Branch
You will be prompted for a branch name
I like to choose a name that relates to the code that I am writing like configurable_engine or removeerroringexample You can see the name of the branch in the bottom left so that you always know which branch you are working on.
The icon shows that the branch is only local and hasn’t been pushed (published) to the remote repository yet
Make some changes and commit them with useful messages
Now you can start writing your code for your awesome new feature, bug fix or maybe just documentation improvement. Keep your commits small and give them useful commit messages that explain why you have made the change as the diff tooling will be able to show what change you have made
Write your code or change the documentation, save the file and in Visual Studio Code or Azure Data Studio you will see that the source control icon has a number on it
Clicking on the icon will show the files that have changes ready
You can write your commit message in the box and click CTRL + ENTER to commit your changes with a message
If you want to do this at the command line, you can use git status to see which files have changes
You will need to git add .or git add .\pathtofile to stage your changes ready for committing and then git commit -m 'Commit Message' to commit them
Notice that I did exactly what I just said not to do! A better commit message would have been So that people can find the guide to forking and creating a PR
Push the changes to your repository
You only have the changes that you have made in your local repository on your computer. Now you need to push those changes to Github your remote repository. You can click on the publish icon
You will get a pop-up asking you if you wish to stage your changes. I click Yes and never Always so that I can use this prompt as a sanity check that I am doing the right thing
At the command line you can push the branch, if you do that, you will have to tell git where the branch needs to go. If you just type git push it will helpfully tell you
fatal: The current branch AwesomeNewFeature has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin AwesomeNewFeature
So you will need to use that command
You can see in the bottom left that the icon has changed
and if you read the output of the git push command you will see what the next step is also.
Create a Pull Request from your repository back to the original one
You can CTRL click the link in the git push output if you have pushed from the command line or if you visit either you repository or the original repository in your browser you will see that there is a Compare and Pull Request button
You click that and let GitHub do its magic
and it will create a Pull Request for you ready for you to fill in the required information, ask for reviewers and other options. Once you have done that you can click Create pull request and wait for the project maintainer to review it and (hopefully) accept it into their project
If you make more changes to the code in the same branch in your local repository and push them, they will automatically be added to this Pull Request whilst it is open. You can do this if the maintainer or reviewer asks for changes.
Shane has asked for a change
So I can go to my local repository in Azure Data Studio and make the requested change and save the file. If I look in the source control in Azure Data Studio I can again see there is a change waiting to be committed and if I click on the name of the file I can open the diff tool to see what the change was
Once I am happy with my change I can commit it again in the same way as before either in the editor or at the command line. The icon at the bottom will change to show that I have one commit in my local repository waiting to be pushed
To do the same thing at the command line I can type git status and see the same thing.
I can then push my change to my remote repository either in the GUI or by using git push
and it will automatically be added to the Pull Request as you can see
Now that the required changes for the review have been made, the review has been approved by Shane and the pull request is now ready to be merged. (You can also see that dbachecks runs some checks against the code when a Pull Request is made)
Many, many thanks to Shane b | t who helped with the writing of this post even whilst on a “no tech” holiday.
Go Ahead – Contribute to an Open Source Project
Hopefully you can now see how easy it is to create a fork of a GitHub repository, clone it to your own machine and contribute. There are many open source projects that you can contribute to.
You can use this process to contribute to the Microsoft Docs for example by clicking on the edit button on any page.
You can contribute other open source projects like
Until recently, this had worked successfully. In the last few weeks I have been receiving errors
Exception : Microsoft.PowerShell.Commands.WriteErrorException: Failed to generate the compressed file for module 'C:\Program Files\dotnet\dotnet.exe failed to pack: error
C:\Program Files\dotnet\sdk\3.0.100\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(198,5): error :
2 Index was outside the bounds of the array.
[C:\Users\VssAdministrator\AppData\Local\Temp\cbc14ba6-5832-46fd-be89-04bb552a83ac\Temp.csproj]
'.
At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\2.2.1\PSModule.psm1:10944 char:17
20 Publish-PSArtifactUtility @PublishPSArtifactUtility_Param ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Write-Error], WriteErrorException
2019-11-25T22:44:46.8459493Z + FullyQualifiedErrorId : FailedToCreateCompressedModule,Publish-PSArtifactUtility
This was very frustrating as it was stopping the continuous delivery to the PowerShell Gallery. It was even more confusing as I was successfully deploying the ADSNotebook module to the gallery using the same method as you can see here.
I asked the wonderful folk in the PowerShell Slack channel – Through the magic of automation, you can also interact with them via the powershellhelp channel in the SQL Server Slack as well but there were no answers that could assist.
So I had to go searching for an answer. PowerShellGet uses nuget for package management. I found that if I downloaded an earlier version and placed it in my user profile (in the right location) I could publish the module.
I found this out by removing the nuget.exe from anywhere useful on the machine and trying to publish the module. The error message says
NuGet.exe upgrade is required to continue
This version of PowerShellGet requires minimum version '4.1.0' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in
'C:\ProgramData\Microsoft\Windows\PowerShell\PowerShellGet\' or 'C:\Users\BeardyMcBeardFace\AppData\Local\Microsoft\Windows\PowerShell\PowerShellGet\', or under
one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see
https://aka.ms/installing-powershellget . Do you want PowerShellGet to upgrade to the latest version of NuGet.exe now?
If I said yes then I got the latest version and the error continued.
However, on my laptop I can go to the nuget downloads page and download an earlier version and place it in one of those paths then I could publish the module.
Can I Automate it?
I would rather not have to deploy manually though, and as I use hosted agents my access to the operating system is limited so I wondered if I could place the nuget.exe in the user profile and it would get used or if it would look for the the latest one. Turns out it uses the one in the user profile 🙂
So now I have this code as a step in my Azure DevOps Release pipeline before calling Publish-Module and we have automated the releases again.
and now deployments to the PowerShell Gallery are just triggered by the build and the pipeline is green again 🙂
You can do it with Azure Data Studio as well. It’s exactly the same steps!
The blog post could end here but read on for some screen shots 😉
Follow the previous post for details of setting up a new GitHub account
Create a repository in Github
Open the folder in Azure Data Studio with CTRL K CTRL O (Or File –> Open Folder)
Click on the Source Control icon or CTRL + SHIFT + G and then Initialize Repository
Choose the folder
Write a commit message
Say yes to the prompt. Press CTRL + ‘ to open the terminal
Navigate to the scripts folder. (I have a PSDrive set up to my Git folder)
Set-Location GIT:\ADS-Scripts\
and copy the code from the GitHub page after “…or push an existing repository from the command line”
and run it
and there are your scripts in GitHub
Make some changes to a script and it will go muddy brown
and then write a commit message. If you click on the file name in the scource control tab then you can see the changes that have been made, that are not currently tracked
Commit the change with CTRL + ENTER and then click the roundy-roundy icon (seriously anyone know its name ?) click yes on the prompt and your changes are in GitHub as well 🙂
Realistically, you can use the previous post to do this with Azure Data Studio as it is built on top of Visual Studio Code but I thought it was worth showing the steps in Azure Data Studio.
Allen wanted to add his scripts folder to source control but didn’t have a how to do it handy. So I thought I would write one. Hopefully this will enable someone new to GitHub and to source control get a folder of scripts under source control
GitHub account
If you do not have a GitHub account go to https://github.com and create a new account
There is a funky are you a human challenge
Then you can choose your subscription
Then answer some questions (Note – you probably want to choose different answers to the what are you interested in question! I’d suggest something technical)
You need to do the email verification
Next is a very important step – Please do not skip this. You should set up 2 factor authentication. Yes even if “It’s just for me there is nothing special here”
Click your user icon top right and then settings
Then click set up two factor authentication
and either set up with an app or via SMS (I suggest the app is better)
OK – Now you have your GitHub account set up. It should have taken you less time than reading this far.
Add a Scripts Folder to GitHub
OK, Now to add a folder of scripts to a repository. Here is my folder of scripts. They can be any type of files. I would recommend copy the folder to a specific Git folder.
Open VS Code – If you don’t have VS Code, download it from https://code.visualstudio.com/ From the welcome window choose open folder
and open your scripts folder
In VS Code click the Source Control button
and up at the top you will see a little icon – initialise repository
Click that and choose your folder
Which will then show all of the changes to the repository (adding all the new files)
Now we need to add a commit message for our changes. I generally try to write commit messages that are the reason why the change has been made as the what has been changed is made easy to see in VS Code (as well as other source control GUI tools)
Click the tick or press CTRL + ENTER and this box will pop up
I never click Always, I click yes, so that I can check if I am committing the correct files. Now we have created a local repository for our scripts folder. Our next step is to publish it to GitHub
Create a New Repository in GitHub
In Github we need to create a remote repository. Click on the New Button. Give your repository a name and decide if you want it to be Public (available for anyone to search and find) or Private (only available to people you explicitly provide access to).
This will give you a page that looks like this
Copy the code after …or push an existing repository from the command line
# make sure prompt is at right place
Set-Location C:\Git\MyScriptsFolder
# Then paste the code
git remote add origin https://github.com/SQLDBAWithABeard-Test/TheBeardsFunkyScriptFolder.git
git push -u origin master
and paste it into PowerShell in VS Code. Make sure that your prompt is at the root of your scripts folder.
Fill in your username and password and your 2FA
Then you will see a page like this
and if you refresh your GitHub page you will see
Congratulations, your code is source controlled 🙂
Making Changes
Now you can make a change to a file
Commit your change
Hit the roundy-roundy icon (anyone know its proper name ?)
Press OK and your commit will be pushed to Github 🙂
Azure Data Studio is a cross-platform database tool for data professionals using the Microsoft family of on-premises and cloud data platforms on Windows, MacOS, and Linux.
Recently Vicky Harp tweeted
We're getting very close to release of SQL Notebooks in @AzureDataStudio! You can give the feature an early spin today with the insider build. pic.twitter.com/SEZp7ZdxCp
See the change log for additional details of what’s in this release.
Once you have installed you can connect to an instance, right click and choose New Notebook or you can use File – New Notebook
Incidentally, I use the docker-compose file here to create the containers and I map C:\MSSQL\BACKUP\KEEP on my local machine (where my backups are) to /var/opt/mssql/backups on the containers on lines 10 and 17 of the docker-compose so change as required . If you want to follow along then put the ValidationResults.bak in the folder on your local machine. The Create-Ag.ps1 shows the code and creates an AG with dbatools. But I digress!
Install Notebook Dependencies
Once you click New Notebook you will get a prompt to install the dependencies.
It will show its output
and take a few minutes to run
It took all but 11 minutes on my machine
#
Create a Notebook
OK, so now that we have the dependencies installed we can create a notebook. I decided to use the ValidationResults database that I use for my dbachecks demos and describe here. I need to restore it from my local folder that I have mapped as a volume to my container. Of course, I use dbatools for this 🙂
I had already got a connection saved to the instance in Azure Data Studio, you may need to create a new one using the new connection icon at the top left and filling in the details. The password is in the code above.
Now I can start with my notebook. I am faced with this
I click on text and provide an intro
Once I had written that and clicked out, I couldn’t see what to do straight away!
Then I saw the code and text buttons at the top 🙂 Right, lets get on with it 🙂 I hit the code button and paste in the T-SQL to reset the dates in the database to simulate dbachecks having been run this morning.
There’s a run cell button on the right and when I press it
Cool 🙂
If the SQL query has results then they are shown as well
This is fun and I can see plenty of uses for it. Go and have a play with SQL notebooks 🙂
Source Control
I used CTRL K, CTRL O to open a folder and saved my notebook in my local Presentations folder which is source controlled. When I opened the explorer CTRL + SHIFT + E I can see that the folder and the file are colour coded green and have a U next to them marking them as Untracked. I can also see that the source control icon has a 1 for the number of files with changes and in the bottom left that I am in the master branch.
If I click on the source control icon (or CTRL + SHIFT + G) I can see the files with the changes and can enter a commit message
I then press CTRL + ENTER to commit my change and get this pop-up
As I only have one file and it has all the changes for this commit I click yes. If I had changed more than one file and only wanted to commit a single one at a time I would hover my mouse over the file and click the + to stage my change.
If I make a further change to the notebook and save it, I can see that the source control provider recognises the change but this time the folder the file is in and the file are colour coded brown with an M to show that they have been modified.
Unlike Visual Studio Code, when you then click on the source control icon and click on the change it does not show the differences in the notebook although this works with SQL files.
When I have made all my changes and committed them with good commit messages
I can see that there are 3 local changes ready to be pushed to by remote repository (GitHub in this case) and 0 remote commits in this branch by looking at the bottom left
I can click on the “roundy roundy” icon (I dont know its proper name 😊) and synchronise my changes. This comes with a pop-up
Personally I never press OK, Don’t Show Again because I like the double check and to think “Is this really what I want to do right now”. Once I press OK my changes will be synched with the remote repository. Explaining this means that you can find the notebook I have used in my Presentations GitHub Repository which means that you can run the Notebook too using the docker-compose file here and the instructions further up in the post.
It reminded me that I do something very similar to test dbachecks code changes. I thought this might make a good blog post. I will talk through how I do this locally as I merge a PR from another great friend Cláudio Silva who has added agent job history checks.
GitHub PR VS Code Extension
I use the GitHub Pull Requests extension for VS Code to work with pull requests for dbachecks. This enables me to see all of the information about the Pull Request, merge it, review it, comment on it all from VS Code
I can also see which files have been changed and which changes have been made
Once I am ready to test the pull request I perform a checkout using the extension
This will update all of the files in my local repository with all of the changes in this pull request
You can see at the bottom left that the branch changes from development to the name of the PR.
Running The Unit Tests
The first thing that I do is to run the Unit Tests for the module. These will test that the code is following all of the guidelines that we require and that the tests are formatted in the correct way for the Power Bi to parse. I have blogged about this here and here and we use this Pester in our CI process in Azure DevOps which I described here.
I navigate to the root of the dbachecks repository on my local machine and run
Thank you Cláudio, the code has passed the tests 😉
Running Some Integration Tests
The difference between Unit tests and Integration tests in a nutshell is that the Unit tests are testing that the code is doing what is expected without any other external influences whilst the Integration tests are checking that the code is doing what is expected when running on an actual environment. In this scenario we know that the code is doing what is expected but we want to check what it does when it runs against a SQL Server and even when it runs against multiple SQL Servers of different versions.
Multiple Versions of SQL Server
As I have described before my friend and former colleague Andrew Pruski b | t has many resources for running SQL in containers. This means that I can quickly and easily create fresh uncontaminated instances of SQL 2012, 2014, 2016 and 2017 really quickly.
I can create 4 instances of different versions of SQL in (a tad over) 1 minute. How about you?
Imagine how long it would take to run the installers for 4 versions of SQL and the pain you would have trying to uninstall them and make sure everything is ‘clean’. Even images that have been sysprep’d won’t be done in 1 minute.
Docker Compose Up ?
So what is this magic command that has enabled me to do this? docker compose uses a YAML file to define multi-container applications. This means that with a file called docker-compose.yml like thish
and 4 SQL containers are available to you. You can interact with them via SSMS if you wish with localhost comma PORTNUMBER. The port numbers in the above file are 15586, 15587,15588 and 15589
Now it must be noted, as I describe here that first I pulled the images to my laptop. The first time you run docker compose will take significantly longer if you haven’t pulled the images already (pulling the images will take quite a while depending on your broadband speed)
Credential
The next thing is to save a credential to make it easier to automate. I use the method described by my PowerShell friend Jaap Brasser here. I run this code
Now I can start to run my Integration tests. First reset the dbachecks configuration and set some configuration values
# run the checks against these instances
$null = Set-DbcConfig -Name app.sqlinstance $containers
# We are using SQL authentication
$null = Set-DbcConfig -Name policy.connection.authscheme -Value SQL
# sometimes its a bit slower than the default value
$null = Set-DbcConfig -Name policy.network.latencymaxms -Value 100 # because the containers run a bit slow!
Then I will run the dbachecks connectivity checks and save the results to a variable without showing any output
I can then use Pester to check that dbachecks has worked as expected by testing if the failedcount property returned is 0.
Describe "Testing the checks are running as expected" -Tag Integration {
Context "Connectivity Checks" {
It "All Tests should pass" {
$ConnectivityTests.FailedCount | Should -Be 0 -Because "We expect all of the checks to run and pass with default settings"
}
}
}
What is the Unit Test for this PR?
Next I think about what we need to be testing for the this PR. The Unit tests will help us.
Choose some Integration Tests
This check is checking the Agent job history settings and the unit tests are
It “Passes Check Correctly with Maximum History Rows disabled (-1)”
It “Fails Check Correctly with Maximum History Rows disabled (-1) but configured value is 1000”
It “Passes Check Correctly with Maximum History Rows being 10000”
It “Fails Check Correctly with Maximum History Rows being less than 10000”
It “Passes Check Correctly with Maximum History Rows per job being 100”
It “Fails Check Correctly with Maximum History Rows per job being less than 100”
So we will check the same things on real actual SQL Servers. First though we need to start the SQL Server Agent as it is not started by default. We can do this as follows
Unfortunately, the agent service wont start in the SQL 2014 container so I cant run agent integration tests for that container but it’s better than no integration tests.
This is What We Will Test
So we want to test if the check will pass with default settings. In general, dbachecks will pass for default instance, agent or database settings values by default.
We also want the check to fail if the configured value for dbachecks is set to default but the value has been set on the instance.
We want the check to pass if the configured value for the dbachecks configuration is set and the instance (agent, database) setting matches it.
If You Are Doing Something More Than Once ……
Let’s automate that. We are going to be repeatedly running those three tests for each setting that we are running integration tests for. I have created 3 functions for this again checking that FailedCount or Passed Count is 0 depending on the test.
function Invoke-DefaultCheck {
It "All Checks should pass with default for $Check" {
$Tests = get-variable "$($Check)default" -ValueOnly
$Tests.FailedCount | Should -Be 0 -Because "We expect all of the checks to run and pass with default setting (Yes we may set some values before but you get my drift)"
}
}
function Invoke-ConfigCheck {
It "All Checks should fail when config changed for $Check" {
$Tests = get-variable "$($Check)configchanged" -ValueOnly
$Tests.PassedCount | Should -Be 0 -Because "We expect all of the checks to run and fail when we have changed the config values"
}
}
function Invoke-ValueCheck {
It "All Checks should pass when setting changed for $Check" {
$Tests = get-variable "$($Check)valuechanged" -ValueOnly
$Tests.FailedCount | Should -Be 0 -Because "We expect all of the checks to run and pass when we have changed the settings to match the config values"
}
}
Now I can use those functions inside a loop in my Integration Pester Test
And then we will check that all of the checks are passing and failing as expected
Invoke-Pester .\DockerTests.ps1
Integration Test For Error Log Counts
There is another integration test there for the error logs count. This works in the same way. Here is the code
#region error Log Count - PR 583
# default test
$errorlogscountdefault = Invoke-DbcCheck -SqlCredential $cred -Check ErrorLogCount -Show None -PassThru
# set a value and then it will fail
$null = Set-DbcConfig -Name policy.errorlog.logcount -Value 10
$errorlogscountconfigchanged = Invoke-DbcCheck -SqlCredential $cred -Check ErrorLogCount -Show None -PassThru
# set the value and then it will pass
$null = Set-DbaErrorLogConfig -SqlInstance $containers -SqlCredential $cred -LogCount 10
$errorlogscountvaluechanged = Invoke-DbcCheck -SqlCredential $cred -Check ErrorLogCount -Show None -PassThru
#endregion
Merge the Changes
So with all the tests passing I can merge the PR into the development branch and Azure DevOps will start a build. Ultimately, I would like to add the integration to the build as well following André‘s blog post but for now I used the GitHub Pull Request extension to merge the pull request into development which started a build and then merged that into master which signed the code and deployed it to the PowerShell gallery as you can see here and the result is
Michael Karpenko wrote a comment pointing out that I had not supported named instances, which was correct as it had not been written for that. Thank you Michael 🙂 I have updated the code to deal with named instances.
Confusing results
I also realised as we started testing the code that if you had run the code once and then ran it again against a different availability group the tool does not clear out the data folder that it uses so you can get confusing results.
In the image below I had looked at the default instance and then a MIRROR named instance. As you can see the results json on the left shows the default instance SQLClusterAG while the one on the right shows both the SQLClusterAG and the MirrrAG instance results.
This is not so useful if you don’t notice this at first with the expanded json!! Now you may in this situation want to see the combined results from all of the availability groups on one cluster. You could gather all of the data from each instance and then add it to the data folder easily enough.
By cleaning out the data folder before running the utility the results are as expected.
Archive the data for historical analysis
One of the production DBAs pointed out that having gathered the information, it would be useful to hold it for better analysis of repeated issues. I have added an archiving step so that when the tools runs, if there is already data in the data gathering folder, it will copy that to an archive folder and name it with the date and time that the cluster log was created as this is a good estimation of when the analysis was performed. If an archive folder location is not provided it will create an archive folder in the data folder. This is not an ideal solution though, as the utility will copy all of the files and folders from there to its own location so it is better to define an archive folder in the parameters.
Get-Eventlog is sloooooooooooow
I was running the tools and noticed it sat running the system event log task for a long long time. I ran some tests using a variation of the dbatools prompt.
This will show in the prompt how long it took to run the previous statement .
In the image above (which you can click to get a larger version as with all images on this blog) you can see that it took 18ms to set the date variable, FOUR MINUTES and FORTY THREE seconds to get the system log in the last 2 days using Get-EventLog and 29.1 seconds using Get-WinEvent and a FilterHashtable.
Getting the function
This function requires PowerShell version 5 and the dbatools module.
30/11/2018 – Function has been updated to deal with named instances.
Last week the Tiger Team released their Availability Group Failover Detection Utility which will provide root cause analysis on Cluster Logs, SQL Error Logs, and the Availability groups extended events logs. There is a blog post here and the tool can be downloaded from the Tiger Team GitHub Repository
A Bit of Faffing*
It states on the readme for the Tiger Team GitHub Repository.
Repository for Tiger team for “as-is” solutions and tools/scripts that the team publishes.
The important words are “as-is” sometimes these tools need a bit of faffing some looking after!
There is a pre-requisite and sometimes a little “fixing” that you need to do to get it to run correctly.
First, install the “Microsoft Visual C++ Redistributable for Visual Studio 2017” from here. On the download page, scroll down to the “Other Tools and Frameworks” section to download the redistributable (x64 version).
Then when you run FailoverDetection.exe you may get strong name validation errors like.
Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly ‘Microsoft.Sq1Server.XEvent.Linq, Version=15.0.0.0, Culture=neutral, PublicKeyToken=89845dcd808cc91’ or one of it s dependencies. Strong name validation failed. (Exception from HRESULT; 0x8013141A) – – – >.Security.SecurityException: Strong name validation failed. (Exception from HRESULT: 0x8e13141A)
—End of inner exception stack trace —
at FailoverDetector. XeventParser.LoadXevent(String xelFi1eName, String serverName)
Then you will need to run the sn.exe tool which is in the zip file. Use this syntax.
.\sn.exe -Vr PATHTODLLFile
I had to do it for two DLLs.
NOTE – If you get an error like this when running sn.exe (or any executable) from PowerShell it means that you have missed the .\ (dot whack) in front of the executable name.
* Faffing – Doing something that is a bit awkward See Link .
Logs required for the Tool
To run the Failover Detection Utility you need to gather the following information from each replica and place it in the specified data folder.
SQL error logs
Always On Availability Groups Extended Event Logs
System Health Extended Event Logs
System log
Windows cluster log
Once you have gathered all of that data then you need to alter the configuration file for the executable.
{
"Data Source Path": "Path to Data File",
"Health Level": 3,
"Instances": [
"Replica1",
"Replica2",
"Replica3"
]
}
Running The Tool
Once you have done that you can then run the Failover Detection Utility. You can double click the exe,
or you can run it from the command line.
In both cases it won’t exit so when you see the Saving Results to JSON file, you can press enter (sometimes twice!).
The results can be seen in the JSON file which will be stored in a Results directory in the directory that the the FailoverDetection.exe exists.
You can also use some switches with the FailoverDetection utility.
–Analyze – When “–Analyze” is specified as a parameter, the utility will load configuration file without copying log data. It assumes the log files have already been copied over. It does everything as default mode except copying log data. This option is useful if you already have the data in the local tool execution subdirectories and want to rerun the analysis.
–-Show -The utility after analyzing log data will display the results in the command console. Additionally, the results will be persisted to a JSON file in the results folder.
They look like this
Again, you need to press enter for the details to come through. The results are still saved to the Results folder as json as well so you won’t lose them.
When You Are Doing Something More Than Once ….
Automate it 🙂
When I saw the data that needed to be gathered for this tool, I quickly turned to PowerShell to enable me to easily gather the information. That has turned into a function which will
Download and extract the zip file from the Tiger Team GitHub repository
Identify all of the replicas for an Availability Group and dynamically create the configuration JSON file
Gather all of the required log files and place them in a specified data folder
Run the FailoverDetection.exe with any of the switches
Includes -Verbose, -Confirm, -Whatif switches so that you can easily see what is happening, be prompted to confirm before actions or see what would happen if you ran the function
You still need to press enter at the end though 🙁
and you will still need to install the “Microsoft Visual C++ Redistributable for Visual Studio 2017” and runt he strong names tool if needed
This function requires PowerShell version 5, the failovercluster module and and the dbatools module.
So you can see that if we run it without the -WhatIf switch it will
Create some directories
Download the zip file from the repo
Extract the zip file
Copy the required logs from each of the replicas to the data folder
Create the JSON configuration file
Run the executable
NOTE : – I have limited the gathering of the system event log to the last 2 days to limit the amount of time spent dealing with a large system log. I gather all of the SQL Error logs in the Error log path as that works for the first scenario I wrote this for, your mileage may vary.
So if we want to run the command we can remove the -WhatIf switch.
It can take a little while to run depending on the number of replicas, size of logs etc but once it has started running you can do other things.
It will require being run as an account with permissions to all of the folders specified and Windows and SQL permissions on all of the replicas in the Availability Group.
As you can see below it has gathered all of the results and placed them in the data folder.
The results can be found in the results folder.
If I have already run the tool, I can use the Analyze switch to save gathering the data again. I also use the AlreadyDownloaded switch as I do not need to download the zip file again.
You will then need to press enter to get the next lot of results.
Why Not Add This To dbatools?
I haven’t added this to dbatools (yet) because I wrote it in this way for a particular need and dbatools requires support for PowerShell V3 . I have, however created an issue added to this issue in the dbatools GitHub Repository (as this is how you to start the process of adding things to dbatools) so hopefully we can get it in there soon as well – in which case I will come back and update this post.
I love showing dbachecks to people. It’s really cool seeing how people will use it and listening to their experiences. I was showing it to a production DBA a month or so ago and he said
How Do I Know Which Checks There Are?
OK you just need to run
Get-DbcCheck
and it will show you
It will show you the group, the type (does it need a computer name or an instance name), The description, the unique tag for running just that check and all the tags that will run that check
OK he said, you talked about configurations
How Do I Know Which Configurations There Are?
So to do that you just need to run
Get-DbcConfig
and it will show you
You can see the name, the current value and the description
Ah thats cool he said so
How Do I Know Which Configuration Is For Which Check?
Well, you just…. , you know…… AHHHHHHH
Ping – light bulb moment!
It’s always really useful to give something you have built to people who have never seen it before and then listen to what they say. Their new eyes and different experiences or expectations will give you lots of insight
None of the amazing contributors to dbachecks had thought of this scenario so I decided to fix this. First I asked for an issue to be raised in GitHub because an issue can be an improvement or a suggestion not just a bug.
Then I fixed it so that it would do what was required. Thank you Nick for this feedback and for helping to improve dbachecks
I improved Get-DbcCheck so that now it shows the configuration item related to each check
It is easier to see (and sort or search) if you use Out-GridView
Get-DbcCheck | Out-GridView
So now you can see which configuration can be set for each check!