{"id":81425,"date":"2018-10-29T13:56:17","date_gmt":"2018-10-29T13:56:17","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=81425"},"modified":"2021-08-24T13:39:18","modified_gmt":"2021-08-24T13:39:18","slug":"what-is-sqlpsx","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/what-is-sqlpsx\/","title":{"rendered":"What Is SQLPSX?"},"content":{"rendered":"<p>I was and still am a big fan of SQLPSX (SQL PowerShell Extensions). It\u00b4s is a complete PowerShell library for SQL Server written by Chad Miller and Mike Shepard, and I was one of the developers on some of the modules. It\u2019s composed of 13 modules with 163 advanced functions, two cmdlets, and seven scripts for working with ADO.NET, SMO (SQL Server Management Objects), SQL Server Agent, RMO (Replication Management Objects), SSIS (SQL Server Integration Services), SQL script files, PBM (Policy Based Management), Oracle, and MySQL. It uses the PowerShell ISE as a SQL and Oracle query tool. Also, optional backend databases and SQL Server Reporting Services 2008 reports are provided with the SQL Server and PBM modules. It works with any version of SQL Server, 2005 or later.<\/p>\n<p>The idea behind SQLPSX is \u201cGive a man a fish, and he eats for a day. Teach a man how to fish, he eats for a lifetime.\u201d There are plenty of great libraries that, for example, give the fish to you. If you want a fish baked instead of fried, you will be in trouble, because the function only returns fried or with a lot of parameters to expose this misleading flexibility. SQLPSX has the flexibility to do whatever you want because you choose the output. SQLPSX doesn\u2019t try to be an end-to-end solution for common problems. It is a toolbox you can use to assemble your own solutions.<\/p>\n<h2>Installing SQLPSX<\/h2>\n<p>Mike Shepard has made the SQLPSX available on GitHub, and you can just <a href=\"https:\/\/github.com\/MikeShepard\/SQLPSX\">download it<\/a> and add it to your profile. I like to put it in $PsHome, or all users all hosts: <em>c:\\windows\\system32\\windowspowershell\\v1.0\\Modules\\<\/em><\/p>\n<ol>\n<li>Unzip the file downloaded from GitHub into a temp directory.<\/li>\n<li>Open PowerShell and navigate to the Modules folder of the unzipped folder.\n<pre class=\"lang:ps theme:powershell-ise\">cd C:\\temp\\SQLPSX-master\\SQLPSX-master\\Modules <\/pre>\n<\/li>\n<li>Run this command to unblock the files.\n<pre class=\"lang:ps theme:powershell-ise\">get-childitem -Recurse | unblock-file<\/pre>\n<\/li>\n<li>Copy the contents of the Modules folder from temp into the Modules folder in the $PSHome path.<\/li>\n<\/ol>\n<div class=\"note\">\n<p><em>NOTE: Always be sure to fully review any PowerShell modules or scripts before executing them.<\/em><\/p>\n<\/div>\n<p>For a good understanding of the profiles you can have in PowerShell, review this article: <a href=\"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2012\/05\/21\/understanding-the-six-powershell-profiles\/\">Understanding the Six PowerShell Profiles<\/a>.<\/p>\n<h2>Playing with SQLPSX<\/h2>\n<p>To use the module, you must import it:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Import-Module sqlpsxserver <\/pre>\n<p>Then you will have access to all the features of this powerful library:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">get-command -Module sqlpsxserver <\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"935\" height=\"677\" class=\"wp-image-81426\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-146.png\" \/><\/p>\n<p>You can see from the list that there is a wealth of commands for managing SQL Server. To get the information about a SQL Server instance run:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlServer -sqlserver &lt;server\\instance&gt; <\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"981\" height=\"523\" class=\"wp-image-81427\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-147.png\" \/><\/p>\n<h2>Gathering Information from SQL Server<\/h2>\n<p>Several commands allow you to gather information from the instance:<\/p>\n<p>&nbsp;<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<h3>Command<\/h3>\n<\/td>\n<td>\n<h3>Description<\/h3>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-sqlprocess &lt;server\\instance&gt;  <\/pre>\n<\/td>\n<td>\n<p>Get the server process<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlVersion -sqlserver &lt;server\\instance&gt;  <\/pre>\n<\/td>\n<td>\n<p>Check the version<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlServer -sqlserver &lt;server\\instance&gt; | Select  -ExpandProperty  Databases <\/pre>\n<\/td>\n<td>\n<p>Return a list of the databases<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SysDatabases -sqlserver &lt;server\\instance&gt; <\/pre>\n<\/td>\n<td>\n<p>Return the system databases<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlServer -sqlserver &lt;server\\instance&gt; |\r\nSelect  -ExpandProperty  Databases | \r\nWhere-Object {$_.name -eq 'Master'}  |\r\nSelect * <\/pre>\n<\/td>\n<td>\n<p>Return all the information about the Master database<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlServer -sqlserver &lt;server\\instance&gt; |\r\nSelect  -ExpandProperty  Databases | \r\nWhere-Object {$_.name -eq 'Master'} |\r\nSelect -ExpandProperty tables <\/pre>\n<\/td>\n<td>\n<p>Return a list of the tables in Master<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt; | Get-SqlTable<\/pre>\n<\/td>\n<td>\n<p>Check for tables<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt; | Get-SqlStoredProcedure<\/pre>\n<\/td>\n<td>\n<p>Check for procedures<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt; | Get-sqlview <\/pre>\n<\/td>\n<td>\n<p>Check for views<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLPSXDatabase &lt;server\\instance&gt; &lt;database&gt; | Get-SqlTable | Get-SqlScripter <\/pre>\n<\/td>\n<td>\n<p>Creating the script of all the tables in the database<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlServerPermission -sqlserver &lt;server\\instance&gt;<\/pre>\n<\/td>\n<td>\n<p>Check the permissions<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-sqllogin -sqlserver &lt;server\\instance&gt; <\/pre>\n<\/td>\n<td>\n<p>List the logins<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-InvalidLogins -sqlserver &lt;server\\instance&gt;  <\/pre>\n<\/td>\n<td>\n<p>Check for invalid logins (from restore)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLErrorLog -sqlserver &lt;server\\instance&gt; -lognumber 1 <\/pre>\n<\/td>\n<td>\n<p>Check error Log<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SqlServerRole &lt;server\\instance&gt;<\/pre>\n<\/td>\n<td>\n<p>Return the server roles<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">$server = Get-SqlServer -sqlserver &lt;server\\instance&gt;\r\n$server.Configuration.XPCmdShellEnabled.ConfigValue\r\n$server.Configuration.XPCmdShellEnabled.RunValue  <\/pre>\n<\/td>\n<td>\n<p>Check to see if XP_CMDSHELL is enabled<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">$server = Get-SqlServer -sqlserver &lt;server\\instance&gt;\r\n$server.Configuration.DefaultBackupCompression.ConfigValue\r\n$server.Configuration.DefaultBackupCompression.runvalue<\/pre>\n<\/td>\n<td>\n<p>Check the default backup compression setting<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">$server = Get-SqlServer -sqlserver &lt;server\\instance&gt;\r\n$server.Configuration.PriorityBoost.ConfigValue\r\n$server.Configuration.PriorityBoost.runvalue <\/pre>\n<\/td>\n<td>\n<p>Check the priority boost setting<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">$server = Get-SqlServer -sqlserver &lt;server\\instance&gt;\r\n$server.Configuration.MaxServerMemory.ConfigValue\r\n$server.Configuration.MaxServerMemory.RunValue<\/pre>\n<\/td>\n<td>\n<p>Check the max server memory value<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">$server = Get-SqlServer -sqlserver &lt;server\\instance&gt;\r\n$server.Configuration.MaxDegreeOfParallelism.ConfigValue\r\n$server.ConfigurationMaxDegreeOfParallelism.runvalue <\/pre>\n<\/td>\n<td>\n<p>Check the max degree of parallelism setting<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-agentjob &lt;server\\instance&gt; |\r\n Where-Object {$_.lastrunoutcome -eq 'Failed'} <\/pre>\n<\/td>\n<td>\n<p>Get a list of failed SQL Agent jobs<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You can control the output of each command by using the format-table or format-list cmdlets, for instance:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">get-agentjob &lt;server\\instance&gt; | Where-Object {$_.lastrunoutcome -eq 'Failed'} |Format-list\r\nget-agentjob &lt;server\\instance&gt; | Where-Object {$_.lastrunoutcome -eq 'Failed'} | SELECT Parent, Name, LastRunDate | Format-table<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"852\" height=\"541\" class=\"wp-image-81428\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-148.png\" \/><\/p>\n<h2>SQL Maintenance with SQLPSX<\/h2>\n<p>DBAs should be spending time on more important work, not manually repeating the same tasks day after day. Automating much of this work using the SQLPSX library is possible. Here is a list of useful commands to make changes or perform maintenance:<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<h3>Command<\/h3>\n<\/td>\n<td>\n<h3>Description<\/h3>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Add-SqlLogin -sqlserver &lt;server\\instance&gt; -name &lt;loginname&gt; -password &lt;password&gt;\r\n -logintype SqlLogin -DefaultDatabase &lt;database&gt;<\/pre>\n<\/td>\n<td>\n<p>Adding a new login<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Add-SqlUser -sqlserver &lt;server\\instance&gt; -dbname &lt;database&gt; -name &lt;username&gt;\r\n -login &lt;loginname&gt;<\/pre>\n<\/td>\n<td>\n<p>And add a user with an existing login<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Remove-SqlUser  -sqlserver &lt;server\\instance&gt; -dbname &lt;database&gt;\r\n -name &lt;username&gt;<\/pre>\n<\/td>\n<td>\n<p>Or remove a user<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Remove-SqlLogin -sqlserver &lt;server\\instance&gt; -name &lt;loginname&gt; <\/pre>\n<\/td>\n<td>\n<p>And remove a login<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt; | \r\nGet-SqlTable -name &lt;table&gt; | \r\nGet-SQLIndex | \r\nGet-SQLIndexFragmentation | \r\nWhere-Object {$_.AverageFragmentation -ge 50} | \r\nSelect-Object      index_Name,\r\n                   Pages,\r\n                   Rows,\r\n                   IndexType,\r\n                   AverageFragmentation  <\/pre>\n<\/td>\n<td>\n<p>Checking index fragmentation &gt; 50% in one table<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt;| \r\nGet-SqlTable -name &lt;table&gt; | \r\nGet-SQLIndex | \r\nInvoke-SqlIndexDefrag  \r\nget-SqlpsxDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt; | \r\nGet-SqlTable -name &lt;table&gt; | \r\nGet-SQLIndex | \r\nInvoke-SqlIndexRebuild <\/pre>\n<\/td>\n<td>\n<p>And if it is bad, invoke a rebuild or reorg<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLPSXDatabase  &lt;server\\instance&gt;  &lt;database&gt; | \r\nGet-SqlTable -name &lt;table&gt; |\r\nGet-SqlStatistic <\/pre>\n<\/td>\n<td>\n<p>Or get the statistics<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLPSXDatabase  &lt;server\\instance&gt; &lt;database&gt; | \r\nGet-SqlTable -name &lt;table&gt; | \r\nGet-SqlStatistic | \r\nUpdate-SqlStatistic <\/pre>\n<\/td>\n<td>\n<p>And update statistics<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Add-SqlDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt; <\/pre>\n<\/td>\n<td>\n<p>Add a new database<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">Remove-SqlDatabase -sqlserver &lt;server\\instance&gt;  -dbname &lt;database&gt;<\/pre>\n<\/td>\n<td>\n<p>Remove a database<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver &lt;server\\instance&gt; -dbname &lt;database&gt; | Invoke-SqlDatabaseCheck <\/pre>\n<\/td>\n<td>\n<p>Perform a database check<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Send Results in an Email<\/h2>\n<p>You may have a process that returns the last backup date of the databases, and the output is using <code>write-host<\/code>, but what I have is a procedure that returns the last backup date of my databases and sends it by email to me.<\/p>\n<p>First export the list of backups to a CSV file and then email as an attachment.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">get-SqlpsxDatabase -sqlserver DESKTOP-SQEVVO1\\SQL2017  |  \r\nSelect name, lastbackupdate |\r\nexport-csv c:\\temp\\lastbackup.csv -NoTypeInformation -noclobber\r\n$SmtpServer = 'smtp.live.com'\r\n$SmtpUser = 'youremail@outlook.com'\r\n$smtpPassword = 'youroutlookpasword'\r\n$MailtTo = 'youremail@outlook.com'\r\n$MailFrom = 'youremail@outlook.com'\r\n$MailSubject = \"Last Backup\" \r\n$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $SmtpUser, $($smtpPassword | ConvertTo-SecureString -AsPlainText -Force) \r\nSend-MailMessage -To \"$MailtTo\" -from \"$MailFrom\" -Subject $MailSubject -SmtpServer $SmtpServer -UseSsl -Credential $Credentials -Attachments  c:\\temp\\lastbackup.csv <\/pre>\n<h2>Automating the Daily Checklist with SQLPSX<\/h2>\n<p>As you can see from the examples shown here, you can gather a large amount of information from SQL Server and perform many maintenance tasks using this library. One of the daily tasks of the DBA in charge is the daily checks. Every morning, by reviewing these, you have a way to guarantee that your environment is online and available. In the days of Kubernetes, Docker, CDI CDR, and large estates, DBAs should not do this kind of job manually. Well, in this article I will show how to automate some of those daily checklists.<\/p>\n<h3>Setting Up<\/h3>\n<p>Create a folder called <em>c:\\checklist<\/em> and a text file called <em>InstanceNames.txt<\/em> with all the SQL Servers listed. Create a text file called <em>hostnames.txt<\/em> with the name of the servers (instead of SQL Server instances here, add hostnames of the servers). The folder also will accommodate all the CSV files that will be created. The text file instance names are the SQL Server instance names to be checked, and the hostnames are the servers that need checks like disk space.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"220\" height=\"116\" class=\"wp-image-81429\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-149.png\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"207\" height=\"88\" class=\"wp-image-81430\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-150.png\" \/><\/p>\n<h3>Failed Jobs<\/h3>\n<p>Create a file called <em>failedjobs.ps1. <\/em>With SQLPSX, collecting this information becomes very simple: just check the property <code>lastrunoutcome<\/code>:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Import-Module agent\r\nimport-module sqlpsxserver\r\nget-content C:\\Checklist\\InstanceNames.txt |\r\nForEach-Object {\r\n    $servername = $_\r\n    $nameserver = $_ -replace ('\\\\','_')\r\n    #get the agentjobs\r\n    Remove-Item \"c:\\checklist\\$($nameserver)_failedjobs.csv\" -ErrorAction SilentlyContinue\r\n    get-agentjob $servername |\r\n    Where-Object {$_.lastrunoutcome -eq 'Failed'} |\r\n    foreach-object { \r\n       [pscustomobject][ordered]@{'Sever Name' = $servername\r\n                                  'Job Name ' = $_.name } |\r\n        Export-Csv \"c:\\checklist\\$($nameserver)_failedjobs.csv\" -NoTypeInformation -NoClobber -append\r\n        $Csvs +=\"c:\\checklist\\$($nameserver)_failedjobs.csv\"\r\n    } \r\n    \r\n}<\/pre>\n<h3>Disk Space<\/h3>\n<p>This step uses the code written by Aaron Nelson you can find <a href=\"https:\/\/sqlvariant.com\/2010\/11\/quick-blog-powershell-disk-and-mountpoint-check\/\">here<\/a>. Add the code to a file called <em>diskspace.ps1.<\/em><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Get-Content C:\\Checklist\\hostnames.txt |\r\nForEach-Object {\r\n    $servername = $_\r\n    Remove-Item \"c:\\checklist\\$($servername)_diskspace.csv\" -ErrorAction SilentlyContinue\r\n    $params = @{Computername = $servername\r\n                query = \"select SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label from Win32_Volume where DriveType = 2 or DriveType = 3\" }\r\n    \r\n    Get-WmiObject @params |\r\n    select SystemName ,\r\n           Name ,\r\n           @{Label=\"SizeIn(GB)\";Expression={\"{0:n2}\" -f($_.Capacity\/1GB)}} ,\r\n           @{Label=\"FreeIn(GB)\";Expression={\"{0:n2}\" -f($_.freespace\/1GB)}} ,\r\n           @{Label=\"PercentFree\";Expression={\"{0:n2}\" -f(($_.freespace \/ $_.Capacity) * 100)}} ,\r\n           Label |\r\n    Export-Csv \"c:\\checklist\\$($servername)_diskspace.csv\" -NoTypeInformation -noclobber -append\r\n} <\/pre>\n<h3>Databases Without a Backup in the Last 16 Hours<\/h3>\n<p>This script will record any database that hasn\u2019t been backed up for 16 hours. Add the code to <em>dbnobackup.ps1.<\/em><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">import-module sqlpsxserver\r\nGet-Content C:\\Checklist\\InstanceNames.txt |\r\nForEach-Object {\r\n    $servername = $_\r\n    $nameserver = $_ -replace ('\\\\','_')\r\n    Remove-Item \"c:\\checklist\\$($nameserver)_dbnobackup.csv\" -ErrorAction SilentlyContinue\r\n    Get-SqlPSXDatabase -sqlserver $servername |\r\n    Where-object { $_.status -eq 'Normal' -and $_.lastbackupdate -le (get-date).addhours(-16) } | \r\n    ForEach-Object {\r\n        [pscustomobject]@{\r\n            'Database Name' = $_.name\r\n            'Last Backup Date ' = $_.LastBackupDate\r\n            'Last Differential Backup Date' = $_.LastDifferentialBackupDate\r\n            'Last Log Backup Date' = $_.LastLogBackupDate\r\n        } |\r\n        Export-Csv \"c:\\checklist\\$($nameserver)_dbnobackup.csv\" -NoTypeInformation -NoClobber -append \r\n    } \r\n}<\/pre>\n<h3>Index Fragmentation<\/h3>\n<p>This script will return all indexes for rebuild if the fragmentation is &gt;= 30. If &gt;=10 and &lt; 30, it will show reorg. No action is done. It just reports the information to the CSV file. The name of the file is <em>indexfrag.ps1.<\/em><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">#Checking  index fragmentation \r\nGet-Content C:\\Checklist\\instancenames.txt |\r\nForEach-Object {\r\n    $ServerName= $_ \r\n    get-SqlpsxDatabase -sqlserver $_ -PipelineVariable DatabaseName |  \r\n    Get-SqlTable   |\r\n    ForEach-Object {\r\n        $tablename = $_\r\n        Get-SQLIndex $_  | \r\n        Get-SQLIndexFragmentation |\r\n        ForEach-Object {\r\n            $nametosave = $servername.Replace(\"\\\",\"_\")\r\n            if ($_.AverageFragmentation -ge 30 ) { \r\n                    [pscustomobject][ordered ]@{ \"Server Name\"=$servername\r\n                                                    \"Database Name\" = $databasename\r\n                                                    \"Table Name\"=$tablename\r\n                                                    \"index_Name\" = $_.index_name\r\n                                                    \"Pages\" = $_.pages          \r\n                                                    \"AverageFragmentation\"  = $_.AverageFragmentation\r\n                                                    \"Action\" = \"Rebuild\"} |\r\n                    Export-Csv  \"C:\\checklist\\$($nametosave)_fragmentation.csv\" -NoTypeInformation  -noclobber -append\r\n            } elseif ($_.AverageFragmentation -le 30 \u2013and $_.AverageFragmentation \u2013ge 10 ) {\r\n                    [pscustomobject][ordered ]@{ \"Server Name\"=$servername\r\n                                                \"Database Name\" = $databasename\r\n                                            \"Table Name\"=$tablename\r\n                                            \"index_Name\" = $_.index_name\r\n                                            \"Pages\" = $_.pages           \r\n                                            \"AverageFragmentation\"  = $_.AverageFragmentation\r\n                                                \"Action\" = \"Reorg\"} |\r\n                    Export-Csv \"C:\\checklist\\$($nametosave)_fragmentation.csv\" -NoTypeInformation  -noclobber -append\r\n            } \r\n        }\r\n    }\r\n}<\/pre>\n<h3>Data and Log Files<\/h3>\n<p>This script returns information about the space used and free in the data and log files. The name of the file is <em>DataLogFiles.ps1.<\/em><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">import-module sqlpsxserver\r\nGet-Content C:\\Checklist\\instancenames.txt |\r\nForEach-Object {\r\n    $servername = $_\r\n    $nameserver = $_ -replace ('\\\\','_')\r\n    \r\n    Remove-Item \"c:\\checklist\\$($nameserver)_LogFiles.csv\" -ErrorAction SilentlyContinue\r\n    Remove-Item \"c:\\checklist\\$($nameserver)_DAtaFiles.csv\" -ErrorAction SilentlyContinue\r\n     Get-SqlPSXDatabase -sqlserver $servername |\r\n     ForEach-Object {\r\n          $_.FileGroups.Files |\r\n            Select-Object Name,\r\n                          FileName,\r\n                          GrowthType,\r\n                          @{ N = 'Growth MB'; E = { [math]::Round(($_.Growth * 1024)\/1MB, 2) } },\r\n                          @{ N = 'File Size MB'; E = { [math]::Round(($_.Size * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Database Used Space MB'; E = { [math]::Round(($_.UsedSpace * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Max Size MB'; E = { [math]::Round(($_.MaxSize * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Free Space on Disk GB'; E = { [math]::Round(($_.VolumeFreeSpace * 1024)\/1GB, 2) } } |\r\n            Export-Csv \"c:\\checklist\\$($nameserver)_DataFiles.csv\" -NoTypeInformation -noclobber -append\r\n            $_.LogFiles |\r\n            Select-Object Name,\r\n                          FileName,\r\n                          GrowthType,\r\n                          @{ N = 'Growth MB'; E = { [math]::Round(($_.Growth * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Max Size MB'; E = { [math]::Round(($_.MaxSize * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Size MB'; E = { [math]::Round(($_.Size * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Used Space'; E = { [math]::Round(($_.UsedSpace * 1024)\/1MB, 2) } },\r\n                          @{ N = 'Free Space on Disk GB'; E = { [math]::Round(($_.VolumeFreeSpace * 1024)\/1GB, 2) } }   |\r\n            Export-Csv \"c:\\checklist\\$($nameserver)_LogFiles.csv\" -NoTypeInformation -noclobber -append\r\n        }\r\n} <\/pre>\n<h3>New Logins<\/h3>\n<p>Check for new logins created in the last 24 hours. The name of the file is <em>Newlogins.ps1.<\/em><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">import-module sqlpsxserver\r\nget-content C:\\Checklist\\InstanceNames.txt |\r\nForEach-Object {\r\n    $servername = $_\r\n    $nameserver = $_ -replace ('\\\\','_')\r\n \r\n    Remove-Item \"c:\\checklist\\$($nameserver)_newlogins.csv\" -ErrorAction SilentlyContinue\r\n    Get-SqlLogin -sqlserver $servername | \r\n    Where-Object {$_.creationdate -ge (get-date).adddays(-1)}|\r\n    export-csv \"c:\\checklist\\$($nameserver)_newlogins.csv\" -NoTypeInformation -NoClobber -append\r\n}<\/pre>\n<h3>After the checks<\/h3>\n<p>The last step will look for CSV files and send them by email. I am using Outlook\u2019s SMTP server but change to your credentials. This file will be called <em>sendemail.ps1.<\/em><\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$csvtosend = @()\r\n$csvtosend = (dir \"c:\\checklist\\*.csv\").fullname\r\n$SmtpServer = 'smtp.live.com'\r\n$SmtpUser = 'youremail@outlook.com'\r\n$smtpPassword = 'youroutlookpasword'\r\n$MailtTo = 'youremail@outlook.com'\r\n$MailFrom = 'youremail@outlook.com'\r\n$MailSubject = \"Daily Check\" \r\n$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $SmtpUser, $($smtpPassword | ConvertTo-SecureString -AsPlainText -Force) \r\nSend-MailMessage -To \"$MailtTo\" -from \"$MailFrom\" -Subject $MailSubject -SmtpServer $SmtpServer -UseSsl -Credential $Credentials -Attachments  $csvtosend <\/pre>\n<h3>Creating the jobs<\/h3>\n<p>Now it\u00b4s time to create the job and add the steps with the files. Create a job called <em>Checklist<\/em>. Use the name of the file for each step. The step type will be <em>Operating system (CMDEXEC)<\/em>. The command for each step will be:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">PowerShell.exe \u201cc:\\checklist\\&lt;nameofthecheck&gt;\u201d<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"690\" height=\"625\" class=\"wp-image-81431\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-151.png\" \/><\/p>\n<p>There should be one step for each check, and the final step will be the <em>sendemail.ps1.<\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"690\" height=\"625\" class=\"wp-image-81432\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/10\/word-image-152.png\" \/><\/p>\n<p>Be sure the account running the SQL Agent job has rights in the paths and, more important, Windows authentication in the SQL Server instances. Otherwise, you may want to use a Proxy account to run the job. If you have tons of SQL Server instances, a better approach is to store the results in tables and then create a report instead of emailing files.<\/p>\n<h2>Conclusion<\/h2>\n<p>As you can see, there are plenty of commands that can apply in the server, database, table, index level. Just use <code>get-command -Module sqlpsxserver<\/code> to check the available cmdlets of SQLPSX. As I said, the beauty of SQLPSX is the flexibility of the output. Of Course, you need a little bit more knowledge of PowerShell, but now that you have the tools, you can build whatever you need!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SQLPSX is a useful library you can add to PowerShell to automate many DBA tasks. In this article, Laerte Junior demonstrates several useful commands and shows you how to create a SQL Agent job using this library to run daily DBA checks.&hellip;<\/p>\n","protected":false},"author":221715,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527,53,35],"tags":[5842],"coauthors":[6819],"class_list":["post-81425","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","category-featured","category-powershell","tag-sql-monitor"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81425","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221715"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=81425"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81425\/revisions"}],"predecessor-version":[{"id":81451,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/81425\/revisions\/81451"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=81425"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=81425"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=81425"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=81425"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}