{"id":761,"date":"2010-01-21T00:00:00","date_gmt":"2009-12-23T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/exceptional-powershell-dba-pt-2-the-morning-checklist\/"},"modified":"2021-08-24T13:40:32","modified_gmt":"2021-08-24T13:40:32","slug":"exceptional-powershell-dba-pt-2-the-morning-checklist","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/exceptional-powershell-dba-pt-2-the-morning-checklist\/","title":{"rendered":"Exceptional PowerShell DBA Pt 2 &#8211; The Morning Checklist"},"content":{"rendered":"<div id=\"PRETTY\">\n<p class=\"START\">I confess that when I started working with PowerShell, I envisioned functions as behaving the same way as Visual Basic or C#, i.e. everything being done within the single function. Thus it was big and full of conditions. Yet one of the great features of PowerShell is consistency; Everything in PowerShell is an object, and the output from one cmdlet can be easily &#8220;piped&#8221; into the input of another. This means your functions are generic, and over just two or three lines of code you can filter, apply summations, dictate output format, manage inserts into a SQL Server table etc&#8230;<\/p>\n<p>As a result, PowerShell is an incredibly versatile scripting language which makes proactive database administration potentially pain-free. In my previous article, I demonstrated how to deal with orphaned users using PowerShell and SMO. This time around, I&#8217;ll take you through some more of my daily checklists and management tasks which have been &#8216;PowerShelled&#8217; to make my life infinitely easier! Bear in mind that, as with my last article, my scripts require PowerShell 2.0, and all I&#8217;ll be giving you today are my scripts (which you can download from the top of this article) and examples on how to use them. I think they&#8217;re incredibly useful, and if you have any questions about any of them, feel free to leave a comment. So, without further ado:<\/p>\n<h1>Recording log information<\/h1>\n<p>Let&#8217;s start by talking a little more about Save-MSSQLMsg. It is one of most important functions here, because it will generate a log of all messages, be they error messages or simply information. As a result, I will always use this function in conjunction with others to ensure I know exactly what is going on in my servers. It takes 6 parameters, and only the first two (file and server names) are mandatory. The other 4 are not informed of the default values, as they are used by the script to insert information into the log files.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\r\nFunction Save-MSSQLMsg () \r\n{\r\n    #requires -Version 2\r\n    \r\n    <#\r\n        .SYNOPSIS\r\n        Save  log in file\r\n        .PARAMETER NamePS1\r\n        File Name \r\n        .PARAMETER Server\r\n        Server name\r\n        .PARAMETER databasename \r\n        DatabaseName\r\n        .PARAMETER Message default \u001c\u001d\r\n        Message To Log\r\n        .PARAMETER PathFileLog default \u001c\u001d\r\n        Path to generate file \r\n        Default = C:\\temp\r\n        .PARAMETER TodayDate\r\n        Date to Log default getdate\r\n    #>\r\n\r\n\r\n\r\n    [CmdletBinding()]\r\n    \r\n    Param (\r\n    [Parameter(position=1,Mandatory = $true )][String] $NamePS1,\r\n    [Parameter(position=2,Mandatory = $true )][String] $Server,\r\n    [Parameter(position=3,Mandatory = $false )][String] $DatabaseName = \",\r\n    [Parameter(position=4,Mandatory = $false )][String] $Message = \" ,\r\n    [Parameter(position=5,Mandatory = $false )][String] $PathFileLog = \r\n                                                              \"C:\\temp\",\r\n    [Parameter(position=6,Mandatory = $false )][String] $TodayDate = \r\n                                        (Get-Date -Format \"yyyyMMddhhmmss\")\r\n    )\r\n    process \r\n    {\r\n    \r\n        #Test if path wich will contains the error file exists. If not,\r\n        # create \r\n        \r\n        if (!(Test-Path -path $PathFileLog))\r\n        {\r\n            try {\r\n                New-Item $PathFileLog -itemtype directory -ErrorAction  \r\n                Stop  | Out-Null\r\n            }\r\n            catch {\r\n                Write-Host \"Can not create log file path\"\r\n                break;\r\n            }\r\n        } \r\n    \r\n    \r\n        $NameFileFull = Join-Path $PathFileLog \"$NamePS1$TodayDate.log\" \r\n        \r\n        $TDate = $TodayDate.Substring(0,4) + \"-\" + $TodayDate.Substring(4,2) \r\n                 + \"-\"     + $TodayDate.Substring(6,2) \r\n        \r\n        \"Server : \" + $Server + \" Database : \" + $DatabaseName + \" Date : \"  \r\n        + $TDate + \" Message: \"  + $Message | Out-file  $NameFileFull \r\n        -append \r\n    } \r\n}<\/pre>\n<p>So, for generating an error message or simply information, we use :<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Save-MSSQLMsg \"ScriptName1\" \"Server1\" \"Database1\" \"MessageToDisplay1\" \"C:\\TEMP\"<\/pre>\n<p class=\"ILLUSTRATION\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/900-logfile.jpg\" height=\"260\" width=\"630\" alt=\"900-logfile.jpg\" \/><\/p>\n<p>But if we want only one file to be generated, we must constantly pass the same script name and date to the function, i.e., we need to set this in the parameters at the beginning of the function, and then we can use the function like this:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\r\n$ScriptName = \"ScriptName1\" $TodaydateMSG = Get-Date -Format \"yyyyMMddhhmmss\"\r\n\r\nSave-MSSQLMsg $ScriptName \"Server1\" \"Database1\" \"MessageToDisplay1\" \"C:\\TEMP\" $TodaydateMSG\r\nSave-MSSQLMsg $ScriptName \"Server2\" \"Database2\" \"MessageToDisplay2\" \"C:\\TEMP\" $TodaydateMSG\r\nSave-MSSQLMsg $ScriptName \"Server3\" \"Database3\" \"MessageToDisplay3\" \"C:\\TEMP\" $TodaydateMSG<\/pre>\n<p class=\"ILLUSTRATION\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/900-logfile2.jpg\" height=\"289\" width=\"630\" alt=\"900-logfile2.jpg\" \/><\/p>\n<h1>Returning Backup Information<\/h1>\n<p class=\"INDENTED\"><em>&#8220;Laerte, I need all backup information about backups in our databases, and if we had some of them without backup&#8221;<\/em><\/p>\n<p class=\"INDENTED\"><em>&#8220;Yes boss. Right away. How do you want that information? Email ? Snail Mail ? Smoke Signal ? Drums ?&#8221;<\/em><\/p>\n<p>Not a problem; You&#8217;re an Exceptional DBA who has everything ready for your checks, and PowerShell under your command. As the heading suggests, this function returns information about your database backups. The properties return by the object are :<\/p>\n<table class=\"table--tight\" >\n<tbody>\n<tr>\n<td>\n<p>LineNumber<\/p>\n<\/td>\n<td>\n<p>(Int)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Date<\/p>\n<\/td>\n<td>\n<p>(DateTime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ServerName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DatabaseName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LastBackupDate<\/p>\n<\/td>\n<td>\n<p>(DateTime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LastDifferentialBackupDate<\/p>\n<\/td>\n<td>\n<p>(DateTime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LastLogBackupDate<\/p>\n<\/td>\n<td>\n<p>(DateTime)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Bear in mind that you can control which servers this script checks by supplying a list of targets in a flat text file &#8211; C:\\TEMP\\Servers.txt. If you don&#8217;t supply this file, the script will only check the default server. Take a look at the script below, and I&#8217;ll give some examples of how to use it after the jump&gt;<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\r\nFunction Get-MSSQLBackup () \r\n{\r\n#requires -Version 2\r\n\r\n<#\r\n    .SYNOPSIS\r\n    Returns information about backups\r\n    .DESCRIPTION\r\n    Write to Application LocalComputer Eventlog\r\n    .PARAMETER TxtServerList\r\n    List with servers, if not pass default server is used\r\n    .LINK\r\n    www.laertejuniordba.spaces.live.com\r\n\r\n#>\r\n\r\n\r\n[CmdletBinding()]\r\n\r\nPARAM    (\r\n            [Parameter(position=1,Mandatory=$false, \r\n            ValueFromPipelineByPropertyName=$true,HelpMessage=\"File Servers \r\n                                                               List\")]\r\n            [Alias(\"FullNameTXT\")]\r\n            [String] $TXTServersList = $env:COMPUTERNAME\r\n            \r\n        )\r\n\r\nbegin\r\n{\r\n    [reflection.assembly]::LoadWithPartialName(\"Microsoft.SqlServer.Smo\") \r\n    | out-null\r\n}\r\nProcess\r\n{\r\n\r\n    $verbosePreference=\"continue\"\r\n    [datetime] $TodayDate = get-date -Format \"yyyy\/MM\/dd hh:mm:ss\"\r\n    if ($TXTServersList.substring($TXTServersList.length -4,4) -eq \".TXT\")\r\n    {\r\n        try\r\n        {\r\n            $ServersList = get-content $TXTServersList    \r\n        } catch {\r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    break;\r\n        }\r\n    }    \r\n    else\r\n    {\r\n        $ServersList = $TXTServersList\r\n    }    \r\n    \r\n    \r\n    $LineNumber = 1\r\n    $FinalResult = @()\r\n\r\n    foreach ($svr in  $ServersList )\r\n    {\r\n        try \r\n        {\r\n        $DatabaseName = \"\r\n        $Server=New-Object \"Microsoft.SqlServer.Management.Smo.Server\" \r\n                           \"$svr\"\r\n        $Data = $Server.Databases| where-object {$_.IsAccessible } | \r\n                  foreach {\r\n    \r\n        [datetime] $LastBackupDate            = \"{0:yyyy\/MM\/dd hh:mm:ss}\" \r\n        -f [datetime] $_.LastBackupDate\r\n        [datetime]$LastDifferentialBackupDate = \"{0:yyyy\/MM\/dd hh:mm:ss}\" \r\n        -f [datetime] $_.LastDifferentialBackupDate  \r\n        [datetime]$LastLogBackupDate          = \"{0:yyyy\/MM\/dd hh:mm:ss}\" \r\n        -f [datetime] $_.LastLogBackupDate\r\n               [String] $DatabaseName = $_.name\r\n\r\n                $Object = New-Object PSObject -Property @{\r\n                   LineNumber                  = $LineNumber    \r\n                   Date                        = $TodayDate    \r\n                   ServerName                  = $svr\r\n                   DatabaseName                = $DatabaseName             \r\n                   LastBackupDate              = $LastBackupDate\r\n                   LastDifferentialBackupDate  = $LastDifferentialBackupDate\r\n                   LastLogBackupDate           = $LastLogBackupDate\r\n                                                          }\r\n\r\n                $FinalResult += $Object\r\n                $LineNumber ++\r\n                \r\n            }               \r\n        \r\n        }   catch    {\r\n              $msg = $error[0]\r\n              Write-Warning $msg\r\n              Save-MSSQLMsg \"Get-MSSQLBackup\" \"$svr\" \"$DatabaseName\" \"$msg\"\r\n              continue\r\n        }\r\n    }    \r\n    \r\n    Write-Output $FinalResult                \r\n}\r\n}<\/pre>\n<p>\n        <br \/>So, now that you&#8217;ve seen the function, here are some examples on how to use it :<\/p>\n<ul>\n<li>List all backup information on the default server:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-MSSQLBackup <\/pre>\n<ul>\n<li>List all backup information on target server[s] (supplied in the text file):<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-MSSQLBackup \"C:\\TEMP\\Servers.txt\"<\/pre>\n<ul>\n<li>List all databases which haven&#8217;t been backed up for one day:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">\r\nGet-MSSQLBackup \"C:\\TEMP\\servers1.txt\" | where-Object {( (get-Date) \r\n- ($_.LastBackupDate)).days -gt 1}  | Select LineNumber,Date,ServerName,\r\nDatabaseName,LastBackupDate,LastDifferentialBackupDate,LastLogBackupDate\r\n | Format-Table<\/pre>\n<ul>\n<li>List backup information for all databases, and store it in a SQL Server table:<\/li>\n<\/ul>\n<pre class=\"lang:tsql theme:ssms2012\">\r\ncreate table BackupsXML (XMLCOL XML)\r\n(Get-MSSQLBackup \"C:\\TEMP\\servers.txt\" | Select LineNumber,Date,ServerName,\r\nDatabaseName,LastBackupDate,LastDifferentialBackupDate,LastLogBackupDate \r\n| ConvertTo-Xml -NoTypeInformation).save(\"c:\\temp\\Backups.xml\") \r\n$XML = (Get-Content \"C:\\Temp\\Backups.xml\" ) -replace \"'\", \"''\"\r\n$SQL = \"insert into BackupsXML (XMLCol) values ('$XML')\" \r\ninvoke-sqlcmd -ServerInstance $env:COMPUTERNAME -database \"tempdb\" \r\n-query $sql<\/pre>\n<ul>\n<li>Generate TSQL to list all data from the &#8220;BackupsXML&#8221; table, where the ServerName condition = &#8220;Jupiter&#8221;:<\/li>\n<\/ul>\n<pre class=\"lang:tsql theme:ssms2012\">\r\nSelect t2.Col1.value('(Property[@Name=\"LineNumber\"]\/text())[1]', 'int') \r\n                    LineNumber,\r\n    t2.Col1.value('(Property[@Name=\"Date\"]\/text())[1]', 'nvarchar(255)') \r\n                 Date,\r\n    t2.Col1.value('(Property[@Name=\"ServerName\"]\/text())[1]', \r\n                 'nvarchar(max)') ServerName,\r\n    t2.Col1.value('(Property[@Name=\"DatabaseName\"]\/text())[1]', \r\n                 'nvarchar(255)') DatabaseName,\r\n    t2.Col1.value('(Property[@Name=\"LastBackupDate\"]\/text())[1]', \r\n                 'nvarchar(255)') LastBackupDate\r\nFROM dbo.BackupsXML\r\nCROSS APPLY xmlcol.nodes('\/Objects\/Object') As t2(Col1)\r\nwhere t2.Col1.value('(Property[@Name=\"ServerName\"]\/text())[1]', \r\n                   'nvarchar(max)') = 'Jupiter'<\/pre>\n<p>Now that we&#8217;ve got this script working, let&#8217;s generate a CSV file and email it to our beloved leader:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\r\nGet-MSSQLBackup \"C:\\TEMP\\Servers.txt\" | Select Date,ServerName,DatabaseName,LastBackupDate | Export-Csv -Path \"C:\\temp\\Backups.csv\"\r\n-NoTypeInformation &#226;&#226;&#172;\"Force\r\nSend-MailMessage -To \"YourBelovedBoss@contoso.com\" \r\n-Subject \"Database Without Backups My Beloved Boss\" -from \"ExceptionalPowershellDBA@contoso.com\" \r\n-Attachments \"C:\\TEMP\\backups.csv\" \r\n-SmtpServer \"10.0.0.10\"<\/pre>\n<p class=\"ILLUSTRATION\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/900-backupCSV.gif\" height=\"434\" width=\"630\" alt=\"900-backupCSV.gif\" \/><\/p>\n<h1>Returns Linked Servers Information and Connectivity<\/h1>\n<p class=\"INDENTED\"><em>&#8220;Laerteeeee (yes..he loves my name) , Some of our remote procedures are returning errors. What is happening?&#8221;<\/em><\/p>\n<p class=\"INDENTED\"><em>&#8220;I will check the linked servers again, my lord&#8221;<\/em><\/p>\n<p>I know..I know.. PowerShell is my middle name.<\/p>\n<p>This function returns an object with information about all your linked servers and your connectivity. As with the others functions, you can pass a file to the script with all the servers you want to test, or else leave it empty if you want the current server. Likewise, this script will also generate a log file if some server is off or another problem occurs.The properties returned are :<\/p>\n<table class=\"table--tight\">\n<tbody>\n<tr>\n<td>\n<p>LineNumber<\/p>\n<\/td>\n<td>\n<p>(int)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Date<\/p>\n<\/td>\n<td>\n<p>(datetime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ServerName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LinkedServerName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DataSource<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DateLastModified<\/p>\n<\/td>\n<td>\n<p>(datetime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>CollationCompatible<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DataAccess<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>RPC<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>RpcOut<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>UseRemoteCollation<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>CollationName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ConnectionTimeOut<\/p>\n<\/td>\n<td>\n<p>(int)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>QueryTimeOut<\/p>\n<\/td>\n<td>\n<p>(int)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Distributor<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Publisher<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Subscriber<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LazySchemaValidation<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>EnablePromotionofDistributedTransactionsForRPC<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ProviderName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ProductName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Connectivity<\/p>\n<\/td>\n<td>\n<p>(boolean)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre class=\"lang:ps theme:powershell-ise\">\r\nFunction Get-MSSQLLinkedServer()\r\n<#\r\n#requires -Version 2\r\n\r\n<#\r\n    .SYNOPSIS\r\n    Returns information about linkedservers. Also test connectivity too.\r\n    .DESCRIPTION\r\n    Returns information about linkedservers. Also test connectivity too.\r\n    .PARAMETER TxtServerList\r\n    List with servers, if not pass default server is used \r\n    .LINK\r\n    www.laertejuniordba.spaces.live.com\r\n\r\n#>\r\n\r\n\r\n#>\r\n{\r\n[CmdletBinding()]\r\n\r\nPARAM    (\r\n            [Parameter(position=1,Mandatory=$false, \r\n               ValueFromPipelineByPropertyName=$true,\r\n               HelpMessage=\"File Servers List\")]\r\n            [Alias(\"FullNameTXT\")]\r\n            [String] $TXTServersList = $env:COMPUTERNAME\r\n            \r\n        )\r\n\r\nbegin \r\n{\r\n    [reflection.assembly]::LoadWithPartialName(\"Microsoft.SqlServer.Smo\") \r\n     | out-null \r\n}\r\nProcess\r\n{\r\n\r\n    $verbosePreference=\"continue\" \r\n    [datetime] $TodayDate = get-date -Format \"yyyy\/MM\/dd hh:mm:ss\"\r\n    if ($TXTServersList.substring($TXTServersList.length -4,4) -eq \".TXT\")\r\n    {\r\n        try\r\n        {\r\n            $ServersList = get-content $TXTServersList    \r\n        } catch {\r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    break;\r\n        }\r\n    }    \r\n    else\r\n    {\r\n        $ServersList = $TXTServersList\r\n    }    \r\n    \r\n    \r\n    $LineNumber = 1\r\n    $FinalResult = @()\r\n\r\n    foreach ($svr in  $ServersList )\r\n    {\r\n        try \r\n        {\r\n            $Server=New-Object \"Microsoft.SqlServer.Management.Smo.Server\" \r\n                               \"$svr\"\r\n            $Data = $Server.linkedservers| where-object {$_.State \r\n                                                -eq \"Existing\"} | foreach {\r\n            \r\n                \r\n                \r\n                [datetime]$DateLastModified = \"{0:yyyy-MM-dd hh:mm:ss}\"\r\n                                         -f [datetime] $_.DateLastModified  \r\n                \r\n                try\r\n                {\r\n                    $_.testconnection() \r\n                    $Connectivity = $true\r\n                } catch {\r\n                    $Connectivity = $false\r\n                }\r\n\r\n                \r\n                $Object = New-Object PSObject -Property @{\r\n\r\nLineNumber                        = $LineNumber     \r\nDate                                  = $TodayDate     \r\nServerName                        = $svr\r\nLinkedServerName                  = $_.Name\r\nDataSource                        = $_.DataSource             \r\nDateLastModified                      = $DateLastModified\r\nCollationCompatible               = $_.CollationCompatible\r\nDataAccess                          = $_.DataAccess\r\nRPC                                 = $_.RPC\r\nRpcOut                              = $_.RPCOut\r\nUseRemoteCollation                  = $_.UseRemoteCollation\r\nCollationName                       = $_.CollationName        \r\nConnectionTimeOut                   = $_.ConnectTimeOut\r\nQueryTimeOut                        = $_.QueryTimeOut        \r\nDistributor                         = $_.Distributor            \r\nPublisher                           = $_.Publisher            \r\nSubscriber                          = $_.Subscriber            \r\nLazySchemaValidation                = $_.LazySchemaValidation\r\nEnablePromotionofDistributedTransactionsForRPC       \r\n                       = $_.IsPromotionofDistributedTransactionsForRPCEnable\r\nProviderName                        = $_.ProviderName            \r\nProductName                         = $_.ProductName\r\nConnectivity                        =  $Connectivity     }\r\n                \r\n                $FinalResult += $Object\r\n                $LineNumber ++ \r\n            } \r\n\r\n        \r\n        }    catch {        \r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    Save-MSSQLMsg \"Get-MSSQLLinkedServer\" \"$svr\" \" \"$msg\" \r\n                    continue\r\n        } \r\n        \r\n    }\r\n    \r\n    Write-Output $FinalResult\r\n}    \r\n\r\n}<\/pre>\n<p>&#8230; And just as before, some examples on how to use it:<\/p>\n<ul>\n<li>List All the LinkedServers information for the default Server:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLLinkedServer <\/pre>\n<ul>\n<li>&gt;List All the LinkedServers information for All target servers:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLLinkedServer \"c:\\temp\\servers.txt\" <\/pre>\n<ul>\n<li>List All the LinkedServers information for All target servers, and filter the data down to just the name and connectivity test data:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLLinkedServer \"c:\\temp\\servers.txt\" | select LineNumber,Date,ServerName,LinkedServerName,Connectivity |Format-Table<\/pre>\n<ul>\n<li>List All the LinkedServers information for All target servers, and filter the data down to just the name and connectivity test data for results where Connectivity = false):<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-SQLLinkedServer \"c:\\temp\\servers.txt\" | where-object {!$_.Connectivity}\r\n| select LineNumber,Date,ServerName,LinkedServerName,Connectivity \r\n|Format-Table<\/pre>\n<ul>\n<li>List All the LinkedServers information for All target servers, filter the data down to just the name and connectivity test results where Connectivity = false), and store the data in a &#8216;LinkerServers&#8217; SQL Server Table:<\/li>\n<\/ul>\n<pre class=\"lang:tsql theme:ssms2012\">TSQL create table LinkedServers (XMLCOL XML)\r\n(Get-SQLLinkedServer \"c:\\temp\\servers.txt\" | where-object \r\n{!$_.Connectivity} | select LineNumber,Date,ServerName,LinkedServerName,\r\nConnectivity | ConvertTo-Xml -NoTypeInformation)\r\n.save(\"c:\\temp\\LinkedServers.xml\") \r\n$XML = (Get-Content \"C:\\Temp\\LinkedServers.xml\" ) -replace \"'\", \"''\"\r\n$SQL = \"insert into LinkedServers (XMLCol) values ('$XML')\" \r\ninvoke-sqlcmd -ServerInstance $env:COMPUTERNAME -database \"tempdb\" \r\n-query $sql<\/pre>\n<ul>\n<li>Generate TSQL to list all the data from the &#8216;LinkerServers&#8217; table where the ServerName condition = &#8220;Jupiter&#8221;<\/li>\n<\/ul>\n<pre class=\"lang:tsql theme:ssms2012\">Select t2.Col1.value('(Property[@Name=\"LineNumber\"]\/text())[1]', 'int') \r\n                    LineNumber,\r\n    t2.Col1.value('(Property[@Name=\"Date\"]\/text())[1]', 'nvarchar(255)') \r\n                 Date,\r\n    t2.Col1.value('(Property[@Name=\"ServerName\"]\/text())[1]', \r\n                  'nvarchar(max)') ServerName,\r\n    t2.Col1.value('(Property[@Name=\"LinkedServerName\"]\/text())[1]', \r\n                  'nvarchar(255)') LinkedServerName,\r\n    t2.Col1.value('(Property[@Name=\"Connectivity\"]\/text())[1]', \r\n                  'nvarchar(255)') Connectivity\r\nFROM dbo.LinkedServers\r\nCROSS APPLY xmlCOL.nodes('\/Objects\/Object') As t2(Col1)\r\nwhere t2.Col1.value('(Property[@Name=\"ServerName\"]\/text())[1]', \r\n                    'nvarchar(max)') = 'Jupiter'<\/pre>\n<h1>Return Logins information<\/h1>\n<p class=\"INDENTED\"><em>&#8220;Laerte, my Padawan, we need to know all the logins created in our servers during last week&#8221;<\/em><\/p>\n<p class=\"INDENTED\"><em>&#8220;Yes my Master. I&#194;&#180;m an Exceptional DBA and The PowerShell is with me.&#8221;<\/em><\/p>\n<p>This function returns an object containing: <\/p>\n<table class=\"table--tight\">\n<tbody>\n<tr>\n<td>\n<p>LineNumber<\/p>\n<\/td>\n<td>\n<p>(int)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Date<\/p>\n<\/td>\n<td>\n<p>(datetime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ServerName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LoginName<\/p>\n<\/td>\n<td>\n<p>(string)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>CreateDate<\/p>\n<\/td>\n<td>\n<p>(Datetime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DateLastModified<\/p>\n<\/td>\n<td>\n<p>(Datetime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LoginType<\/p>\n<\/td>\n<td>\n<p>(String)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>AsymmetricKeys<\/p>\n<\/td>\n<td>\n<p>(String)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Certificate<\/p>\n<\/td>\n<td>\n<p>(String)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DefaultDatabase<\/p>\n<\/td>\n<td>\n<p>(String)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>DenyWindowsLogin<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>HasAccess<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>IsDisable<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>IsLocked<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>IsPassowordExpired<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>IsSystemObject<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Language<\/p>\n<\/td>\n<td>\n<p>(String)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>LanguageAlias<\/p>\n<\/td>\n<td>\n<p>(String)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>MustChangePassword<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>PasswordExpirationEnabled<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>PasswordPolicyEnforced<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&#8230; And the script itself looks like this:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">Function Get-MSSQLLogin()\r\n#requires -Version 2\r\n\r\n<#\r\n    .SYNOPSIS\r\n    Returns information about Logins\r\n    .DESCRIPTION\r\n    Returns information about Logins\r\n    .PARAMETER TxtServerList\r\n    List with servers, if not pass default server is used \r\n    .LINK\r\n    www.laertejuniordba.spaces.live.com\r\n\r\n#>\r\n{\r\n[CmdletBinding()]\r\n\r\nPARAM    (\r\n            [Parameter(position=1,Mandatory=$false, \r\n              ValueFromPipelineByPropertyName=$true,\r\n              HelpMessage=\"File Servers List\")]\r\n            [Alias(\"FullNameTXT\")]\r\n            [String] $TXTServersList = $env:COMPUTERNAME\r\n            \r\n        )\r\n\r\nbegin \r\n{\r\n    [reflection.assembly]::LoadWithPartialName(\"Microsoft.SqlServer.Smo\") \r\n    | out-null \r\n}\r\nProcess\r\n{\r\n\r\n    $verbosePreference=\"continue\" \r\n    [datetime] $TodayDate = get-date -Format \"yyyy\/MM\/dd hh:mm:ss\"\r\n    if ($TXTServersList.substring($TXTServersList.length -4,4) -eq \".TXT\")\r\n    {\r\n        try\r\n        {\r\n            $ServersList = get-content $TXTServersList    \r\n        } catch {\r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    break;\r\n        }\r\n    }    \r\n    else\r\n    {\r\n        $ServersList = $TXTServersList\r\n    }    \r\n    \r\n    $LineNumber = 1\r\n    $FinalResult = @()\r\n    \r\n\r\n\r\n    foreach ($svr in  $ServersList )\r\n    {\r\n        try \r\n        {\r\n            $Server=New-Object \"Microsoft.SqlServer.Management.Smo.Server\" \r\n                               \"$svr\"\r\n            $Data = $Server.logins | where-object {$_.State -eq \"Existing\"}\r\n                    | foreach {\r\n            \r\n                \r\n                \r\n                [datetime] $CreateDate = \"{0:yyyy\/MM\/dd hh:mm:ss}\" \r\n                                         -f [datetime] $_.CreateDate  \r\n                [datetime] $DateLastModified = \"{0:yyyy\/MM\/dd hh:mm:ss}\" \r\n                                         -f [datetime] $_.DateLastModified \r\n                \r\n                $Object = New-Object PSObject -Property @{\r\n\r\nLineNumber                  =$LineNumber     \r\nDate                        =$TodayDate     \r\nServerName                  =$svr\r\nLoginName                   =$_.Name\r\nCreateDate                  =$CreateDate \r\nDateLastModified            =$DateLastModified \r\nLoginType                   =$_.LoginType                    \r\nAsymmetricKeys              =$_.AsymmetricKeys\r\nCertificate                 =$_.Certificate\r\nDefaultDatabase             =$_.DefaultDatabase\r\nDenyWindowsLogin            =$_.DenyWindowsLogin\r\nHasAccess                   =$_.HasAccess\r\nIsDisable                   =$_.IsDisable                    \r\nIsLocked                    =$_.IsLocked\r\nIsPassowordExpired          =$_.IsPassowordExpired            \r\nIsSystemObject              =$_.IsSystemObject            \r\nLanguage                    =$_.Language\r\nLanguageAlias               =$_.LanguageAlias\r\nMustChangePassword          =$_.MustChangePassword\r\nPasswordExpirationEnabled   =$_.PasswordExpirationEnabled\r\nPasswordPolicyEnforced      =$_.PasswordPolicyEnforced }\r\n\r\n                $FinalResult += $Object\r\n                $LineNumber ++ \r\n            }                                                 \r\n        \r\n        }    catch {        \r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    Save-MSSQLMsg \"Get-MSSQLLogin\" \"$svr\" \" \"$msg\" \r\n                    continue\r\n        } \r\n        \r\n    }\r\n    \r\n    Write-Output $FinalResult            \r\n}    \r\n}<\/pre>\n<p>As before you can easily apply all the filters you want , this function works with logs, and the usage patterns are basically the same:<\/p>\n<ul>\n<li>List All Logins information on the default server:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-MSSQLLogin <\/pre>\n<ul>\n<li>List All Logins information on All target servers:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-MSSQLLogin \"c:\\temp\\servers.txt\" <\/pre>\n<ul>\n<li>List All Logins information on All target servers, filter the data down to just the name and CreateDate:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-MSSQLLogin \"c:\\temp\\servers.txt\" | select LineNumber,Date,ServerName, LoginName,CreateDate |Format-Table<\/pre>\n<ul>\n<li>List All Logins information on All target servers filter the data down to just the name and where the CreateDate is less than 7 days ago, and store the results in a SQL Server Table:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Get-MSSQLLogin | where-Object {((get-date) - ($_.createdate)).days -lt 7} | select LineNumber,Date,ServerName,LoginName,CreateDate |Format-Table\r\n\r\nTSQL create table Logins (XMLCOL XML):\r\n(Get-MSSQLLogin \"c:\\temp\\servers.txt\" | where-Object {((get-date) \r\n- ($_.createdate)).days -lt 7} | select LineNumber,Date,ServerName,LoginName,CreateDate | ConvertTo-Xml -NoTypeInformation).save(\"c:\\temp\\Logins.xml\") $XML = (Get-Content c:\\temp\\Logins ) -replace \"'\", \"''\"\r\n$SQL = \"insert into Logins (XML) values ('$XML')\" invoke-sqlcmd -ServerInstance $env:COMPUTERNAME -database \"tempdb\" -query $sql<\/pre>\n<ul>\n<li>Generate TSQL to list the data where the ServerName condition = &#8220;Jupiter&#8221;:<\/li>\n<\/ul>\n<pre class=\"lang:ps theme:powershell-ise\">Select t2.Col1.value('(Property[@Name=\"LineNumber\"]\/text())[1]', 'int')                      LineNumber,    t2.Col1.value('(Property[@Name=\"Date\"]\/text())[1]', 'nvarchar(255)')                  Date,    t2.Col1.value('(Property[@Name=\"ServerName\"]\/text())[1]', 'nvarchar(max)') ServerName,    t2.Col1.value('(Property[@Name=\"LoginName\"]\/text())[1]', 'nvarchar(255)') LoginName,    t2.Col1.value('(Property[@Name=\"CreateDate\"]\/text())[1]', \r\n                  'nvarchar(255)') CreateDateFROM dbo.LoginsCROSS APPLY xmlCOL.nodes('\/Objects\/Object') As t2(Col1)where t2.Col1.value('(Property[@Name=\"ServerName\"]\/text())[1]','nvarchar(max)') = 'Jupiter'<\/pre>\n<h1>Returning Jobs information<\/h1>\n<p class=\"INDENTED\"><em>&#8220;Laerte, I see that every morning when you get to work, you have an excel list in your inbox listing the problematic backups on all the servers in the environment. Out of curiosity, how do you do that? I couldn&#8217;t find Database Mail configured, checking procedures or any other mechanism in place to audit jobs in servers.&#8221;<\/em><\/p>\n<p>From my machine, with a few lines of code, I access all the servers&#8217; lists of the jobs that failed, in XLSX format, and send that information to my email. I don&#8217;t have any procedures or mechanism on the servers;. the function is uniform, repetitive and centralized; it returns the following information:<\/p>\n<table class=\"table--tight\" >\n<tbody>\n<tr>\n<td>\n<p>LineNumber<\/p>\n<\/td>\n<td>\n<p>(int)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Date<\/p>\n<\/td>\n<td>\n<p>(DateTime)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>ServerName<\/p>\n<\/td>\n<td>\n<p>(String) <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>JobName<\/p>\n<\/td>\n<td>\n<p>(String) <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Lastrunoutcome<\/p>\n<\/td>\n<td>\n<p>(String) <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Isenabled<\/p>\n<\/td>\n<td>\n<p>(Boolean)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>lastrundate<\/p>\n<\/td>\n<td>\n<p>(DateTime)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre class=\"lang:ps theme:powershell-ise\">Function Get-MSSQLJob()\r\n#requires -Version 2\r\n\r\n<#\r\n    .SYNOPSIS\r\n    Returns information about Jobs\r\n    .DESCRIPTION\r\n    Returns information about Jobs\r\n    .PARAMETER TxtServerList\r\n    List with servers, if not pass default server is used \r\n    .LINK\r\n    www.laertejuniordba.spaces.live.com\r\n\r\n#>\r\n{\r\n[CmdletBinding()]\r\n\r\nPARAM    (\r\n            [Parameter(position=1,Mandatory=$false, \r\n             ValueFromPipelineByPropertyName=$true,\r\n             HelpMessage=\"File Servers List\")]\r\n            [Alias(\"FullNameTXT\")]\r\n            [String] $TXTServersList = $env:COMPUTERNAME\r\n            \r\n        )\r\n\r\nbegin \r\n{\r\n    [reflection.assembly]::LoadWithPartialName(\"Microsoft.SqlServer.Smo\")\r\n     | out-null \r\n}\r\nProcess\r\n{\r\n\r\n    $verbosePreference=\"continue\" \r\n    [datetime] $TodayDate = get-date -Format \"yyyy\/MM\/dd hh:mm:ss\"\r\n    if ($TXTServersList.substring($TXTServersList.length -4,4) -eq \".TXT\")\r\n    {\r\n        try\r\n        {\r\n            $ServersList = get-content $TXTServersList    \r\n        } catch {\r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    break;\r\n        }\r\n    }    \r\n    else\r\n    {\r\n        $ServersList = $TXTServersList\r\n    }    \r\n    \r\n    \r\n    $LineNumber = 1\r\n    $FinalResult = @()\r\n    \r\n\r\n\r\n    foreach ($svr in  $ServersList )\r\n    {\r\n        try \r\n        {\r\n            $Server=New-Object \"Microsoft.SqlServer.Management.Smo.Server\"\r\n                               \"$svr\"\r\n            $Data = $Server.jobserver.jobs | foreach { \r\n            \r\n                $Object = New-Object PSObject -Property @{\r\n                    LineNumber  = $LineNumber    \r\n                    Date           = $TodayDate     \r\n                    ServerName = $svr\r\n                    JobName = $_.name\r\n                    lastrunoutcome = $_.lastrunoutcome \r\n                    isenabled = $_.isenabled\r\n                    lastrundate = $_.lastrundate \r\n                    }    \r\n                $FinalResult += $Object\r\n                $LineNumber ++                         \r\n            }        \r\n                \r\n                \r\n        }    catch {        \r\n                    $msg = $error[0]\r\n                    Write-Warning $msg\r\n                    Save-MSSQLMsg \"Get-MSSQLJob\" \"$svr\" \" \"$msg\" \r\n                    continue\r\n        }            \r\n    }\r\n    Write-Output $FinalResult    \r\n}\r\n}<\/pre>\n<p>And now, once we&#8217;ve collected all that iinformation, we can create a XLSX and send to our email:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">$excel = New-Object -ComObject Excel.Application\r\n$excel.Visible = $false\r\n$workbook = $excel.Workbooks.add()\r\n$workbook.WorkSheets.item(1).Name = \"Jobs\"\r\n$sheet = $workbook.WorkSheets.Item(\"Jobs \")\r\n\r\n$sheet.cells.item(1,1) = \"Date\"\r\n$sheet.cells.item(1,2) = \"ServerName\"\r\n$sheet.cells.item(1,3) = \"JobName\"\r\n$sheet.cells.item(1,4) = \"LastRunDate\"\r\n\r\n$Line = 2\r\n\r\nforeach ($Jobs in Get-MSSQLJob \"C:\\TEMP\\servers.txt\" \r\nwhere-Object {$_.lastrunoutcome \u2013eq \u201cFailed\u201d \u2013and  $_.isenabled} )\r\n \r\n{\r\n    $sheet.cells.item($line, 1) = $Jobs.Date -f \"{0:yyyy-MM-dd hh:mm:ss}\" \r\n                                             -f [datetime] \r\n    $sheet.cells.item($line, 2) = $Jobs.ServerName\r\n    $sheet.cells.item($line, 3) = $Jobs.JobName\r\n    $sheet.cells.item($line, 4) = $Jobs.LastRunDate\r\n    $Line++\r\n}\r\n\r\n$excel.Visible = $true\r\n\r\n$workbook.SaveAs(\"C:\\TEMP\\Jobs.xlsx\")\r\n$workbook.Close()\r\n$excel.Quit\r\n\r\nSend-MailMessage -To \" ExceptionalPowershellDBA@contoso.com \" \r\n-Subject \"Jobs With Problems\" \r\n-from \"AuditDBA@contoso.com\" \r\n-Attachments \"C:\\TEMP\\Jobs.xlsx\" \r\n-SmtpServer \"10.0.0.10\"<\/pre>\n<p>If you want to see more about PowerShell exporting data to Excel, be sure to read this excellent content on TechNet:<\/p>\n<ul>\n<li><a href=\"http:\/\/technet.microsoft.com\/en-us\/magazine\/2009.01.heyscriptingguy.aspx?pr=blog\">Hey, Scripting Guy!<\/a> <\/li>\n<li><a href=\"http:\/\/technet.microsoft.com\/en-us\/magazine\/2009.01.heyscriptingguy.aspx?pr=blog\">Export Windows PowerShell Data to Excel<\/a><\/li>\n<\/ul>\n<p>Now it only remains for you to schedule this script for a job in SQL Server, or even Windows Task Scheduler. Keep in mind that these functions are just examples, and do not return all the properties of the SMO objects used. If you want a complete list of properties, I suggest using MSDN and studying the classes in question.<\/p>\n<p>So, in this second part of my Exceptional PowerShell DBA posts you can see some of my daily checklist, and, more importantly, how PowerShell can help reduce the overhead of managing that checklist Please feel free to download my sample scripts from the top of this article and experiment with them. In the third and final part of these posts, we will see some more set ups we can do, like changing collation columns and rebuilding indexes. As always: don&#8217;t forget, you are an Exceptional DBA who loves PowerShell!<br \/>\n        \n    <\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Laerte Junior takes us further into the world of an Exceptional PowerShell DBA, showing us how he uses PowerShell 2.0 to take all the headaches out of even more of his daily checklist. What could be better than having your morning checklist run itself?&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],"tags":[4170,4659,5094,4635,4150],"coauthors":[6819],"class_list":["post-761","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-database-administration","tag-dba","tag-exceptional","tag-powershell","tag-sql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/761","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=761"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/761\/revisions"}],"predecessor-version":[{"id":92273,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/761\/revisions\/92273"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=761"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=761"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=761"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=761"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}