Prefixing the names of all SQL Server Agent jobs with dbatools - sql-server

I have several hundred disabled jobs on several servers that we are going to rename zzzz-(exisisting job name). I am trying to use dbatools:
Find-DbaAgentJob -SqlInstance sql1 -LastUsed 60 -IsDisabled -jobname test*
| Set-DbaAgentJob -SqlInstance sql1 -NewName ('zzz' + $_.Name) -whatif
This will rename both all disabled jobs to 'zzz', but I cannot concatenate it with the $_.Name.
Ideally, the result should be: test1 renamed to zzztest1

Related

How to synchronize SQL Server Agent jobs across availability group replicas on Linux?

I have two SQL Server 2019 instances running on Linux. These two instances both contain a single database which is synchronized using AlwaysOn Availability Group. Data in the database is synchronized, but the problem is that the SQL Agent jobs are not part of the database itself.
Therefore, when I create a SQL Server Agent job on the primary replica, this configuration does not copy to the secondary replica. So, after creating each job, I always have to also go to the secondary and create the job there as well. And I have to keep track of all the changes I make all the time.
Is there a built-in way to automate this cross-replica synchronization of SQL Server jobs on Linux when using availability groups? Job synchronization across AG replicas seems like something that should already be natively supported by SQL Server/SQL Server Agent tools, but I found nothing from Microsoft, only a third-party tool for called DBA Tools that I can use to write my own automation scripts in PowerShell.
After some trial and error, I ended up with this script that works on Ubuntu Linux 18.04. Big thanks to Derik Hammer and his blog for the base of the script and also to David Söderlund for his reply.
For the script to work, you will need to install PowerShell for Linux and both DBATools and SqlCmd2 PowerShell modules. You will also have to store sql credentials in a file somewhere. I chose /var/opt/mssql/secrets/creds.xml for mine and changed access rights to root only. Script can sync logins, DBmail settings, SQL Agent categories, jobs, operators and schedules from primary replica to all secondaries (uncomment what you need, but be careful, order matters and some things cannot be synched in one connection, i.e operators and jobs), skipping configuration replicas if you have any.
You can set up scheduled execution as root with output logged into file using CRON. To set this up, run:
sudo crontab -e
and adding this line to the file:
*/5 * * * * pwsh /<PATH>/sync-sql-objects.ps1 >> /<PATH>/sync-sql-objects.log
Script:
<#
.DESCRIPTION
This script will detect your Availability Group replicas and copy all of its instance level objects from primary replica to secondary replicas within the Availability Group. It will skip any configuration replicas.
.EXAMPLE
sudo pwsh sync-sql-objects.ps1
.NOTES
One limitation of this script is that it assumes you only have one availability group. This script should run on your configuration replica server.
.LINK
https://www.sqlhammer.com/synchronizing-server-objects-for-availability-groups/
DEBUG
To see logs on Ubuntu Linux, install Postfix Mail Transfer Agent and then go to see mails in /var/mail/<username>
#>
Write-Output ("Sync started: " + (Get-Date -Format G))
#Error handling
$ErrorActionPreference = "stop";
Trap
{
$err = $_.Exception
while ( $err.InnerException )
{
$err = $err.InnerException
Write-Output $err.Message
};
}
# Prerequisites
try
{
Write-Output "Valiating prerequisites."
# You need to have these modules installed in advance, otherwise the import will fail
if ((Get-Module -Name dbatools) -eq $null)
{
Import-Module dbatools | Out-Null
}
if ((Get-Module -Name Invoke-SqlCmd2) -eq $null)
{
Import-Module Invoke-SqlCmd2 | Out-Null
}
Write-Output "Prerequisites loaded."
}
catch
{
Write-Error $_.Exception.Message -EA Continue
Write-Error "One or more of the prerequisites did not load. Review previous errors for more details." -EA Continue
return
}
# Detect Availability Group replicas
Write-Output "Begin query for Availability Group replicas"
$ConfigurationMode = "CONFIGURATION_ONLY"
$Hostname = hostname
$Credentials = Import-CliXml -Path /var/opt/mssql/secrets/creds.xml
$ReplicasQuery = #"
SELECT replica_server_name,
availability_mode_desc,
primary_replica
FROM sys.availability_replicas AR
INNER JOIN sys.dm_hadr_availability_group_states HAGS
INNER JOIN sys.availability_groups AG ON AG.group_id = HAGS.group_id
ON HAGS.group_id = AR.group_id;
"#
$Replicas = Invoke-Sqlcmd2 -ServerInstance $Hostname -Query $ReplicasQuery -ConnectionTimeout 30 -Credential $Credentials
if(([DBNull]::Value).Equals($Replicas[0].primary_replica))
{
Write-Error "Availability Group query returned no results. Confirm that you connected to a SQL Server instance running an Availability Group. No work was accomplished."
return
}
Write-Output "Completed query of Availability Group replicas"
foreach($replica in $Replicas)
{
# Skip if destination replica is primary replica itself
if($replica.primary_replica.CompareTo($replica.replica_server_name) -eq 0)
{
continue
}
# Skip configuration replicas
if($replica.availability_mode_desc.CompareTo($ConfigurationMode) -eq 0)
{
continue
}
#Connect
$PrimaryReplica = Connect-DbaInstance $replica.primary_replica -ClientName 'ConfigurationReplica' -SqlCredential $Credentials
$SecondaryReplica = Connect-DbaInstance $replica.replica_server_name -ClientName 'ConfigurationReplica' -SqlCredential $Credentials
Write-Output "Copying instance objects from $sourceReplica to $replica"
# Copy objects
# Write-Output "Copying Logins."
# Copy-DbaLogin -Source $PrimaryReplica -Destination $SecondaryReplica
# Write-Output "Copying DBMail."
# Copy-DbaDbMail -Source $PrimaryReplica -Destination $SecondaryReplica -Force
# Write-Output "Copying Agent Categories."
# Copy-DbaAgentJobCategory -Source $PrimaryReplica -Destination $SecondaryReplica -Force
# Write-Output "Copying Agent Schedules."
# Copy-DbaAgentSchedule -Source $PrimaryReplica -Destination $SecondaryReplica -Force
# Write-Output "Copying Operators."
# Copy-DbaAgentOperator -Source $PrimaryReplica -Destination $SecondaryReplica -Force
Write-Output "Copying Jobs."
Copy-DbaAgentJob -Source $PrimaryReplica -Destination $SecondaryReplica -Force
Write-Output "Copy complete from $PrimaryReplica to $SecondaryReplica"
}
Write-Output "SQL Instance object sync complete."
Enjoy!
dbatools can sync them but I haven't tried it on an AG running on linux. Let me know if it works or not!
The first parameter is the name of your AG, the second is the virtual network name of your cluster.
param($AvailabilityGroup, $SqlInstance)
try {
$replicas = Get-DbaAgReplica -AvailabilityGroup $AvailabilityGroup -SqlInstance $SqlInstance
$primary = $replicas | Where-Object Role -EQ Primary | Select-Object -ExpandProperty Name
$secondaries = $replicas | Where-Object Role -EQ Secondary | Select-Object -ExpandProperty Name
$primaryInstanceConnection = Connect-DbaInstance $primary -ClientName 'ScriptBorrowedFromStackOverFlow'
$secondaries | ForEach-Object {
$secondaryInstanceConnection = Connect-DbaInstance $_ -ClientName 'ScriptBorrowedFromStackOverFlow'
Copy-DbaAgentJob -Source $primaryInstanceConnection -Destination $secondaryInstanceConnection -Force
}
}
catch {
$msg = $_.Exception.Message
Write-Error "Error while syncing jobs for Availability Group '$($AvailabilityGroup): $msg'"
}

Import CSV from PowerShell to SQL Server

I was trying to import a CSV file from PowerShell over to my SQL Server database. I've already created the database and tables with columns. I got the data on a CSV file that I need to import.
I tried to research and found a bit of code I modified so it should be working, but when I run the code I get the error:
Import-CsvToSql : exeption calling "writetoserver" with "1"
argument(s): "The transaction is iether not associated with the
connection or has been completed"
But I hasn't imported the data to the table, so I don't know what's wrong.
Here is the code I've got so far:
Import-Module csvsqlimport
Import-CsvToSql -Csv C:\Users\Tim\Desktop\POWERSHELL\AutoParts.csv `
-SqlServer DHCP_SERVER -Database FeilAuto4 `
-Table dbo.Reservedele -FirstRowColumns -Delimiter ";" -Truncate
You may want to try the "SqlServer" module as it is being kept up to date by Microsoft and has multiple SQL cmdlets. The only downside is that you will have to separate the script into multiple commands based on their cmdlets.
## Install module if not installed, this is a one time install.
Install-Module SqlServer
## Input Variables
$csvPath = "C:\Users\Tim\Desktop\POWERSHELL\AutoParts.csv"
$csvDelimiter = ";"
$serverName = "DHCP_SERVER"
$databaseName = "FeilAuto4"
$tableSchema = "dbo"
$tableName = "Reservedele"
## Truncate Table
Invoke-Sqlcmd -ServerInstance $serverName -Database $databaseName -Query "TRUNCATE TABLE $tableSchema.$tableName"
## Import CSV into SQL
Import-Csv -Path $csvPath -Delimiter $csvDelimiter | Write-SqlTableData -ServerInstance $serverName -DatabaseName $databaseName -SchemaName $tableSchema -TableName $tableName -Force

Replace backup location in SQL jobs

We are trying to replace a backup location in a SQL Backup Jobs step (running power shell through several servers)
Below is a PS script i would like to use it:
# $Server is a file with SERVERNAME names
$Jobs = Get-SQLAgentJob -ServerInstance
$Servers Foreach ($job in $Jobs.Where{$_.Name -like 'DatabaseBackup' -and $_.isenabled -eq $true}) {
foreach ($Step in $Job.jobsteps.Where{$_.Name -like 'DatabaseBackup'}) {
$Step.Command = $Step.Command.Replace("Directory = N'C:\Backup\oldname1\oldname2\SERVERNAME'", "Directory = N'C:\Backup2\newname1\newname2\SERVERNAME'")
$Step.Alter()
}
}
It seems like this should work. The only potential problems I see are the following:
named SQL instances: The $servers variable will need to have the servername\instancename format if not using the default instance name
Job and step names: If your job names and job step names are not exactly databasebackup, case excluded, then the -like operator combined with the exact string will not find a match. If the names contain the databasebackup string, you will be safer to use -match "databasebackup" or -like with asterisks on both sides of the string.
Otherwise, this code should just work provided there are not network connectivity or permissions issues.

Automating Database Counts for All SQL Servers

I need database counts for every SQL server instance (PROD/Non PROD) in an environment.
If I logged into each and every SQL Server then it is very tedious task for me, so I need to automate it.
Is there any t-sql or powershell script from which I can get consolidated report for database counts for all servers at one place.
You can use Azure Functions with Timer schedule to automate what you want.
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer
And you can use stored procedures with azure functions.
Take a look at the SqlServer module documentation, specifically the Get-SqlDatabase cmdlet. You can import your server names from a file or define them in an array and then iterate through them.
#get your credential
$credentials = get-credential
#read server names from a file
$servers = get-content "C:\Some\Path\servers.txt"
#use calculated properties
$servers | select-object `
#{Name='ServerName';Expression={$_}},
#{Name='DatabaseCount';Expression={
#force to array to ensure .count exists
#(Get-SqlInstance -MachineName $_ -Credential $credential | Get-SqlDatabase).Count
}}
If you are unable to install the SqlServer module you can do it with the SMO
#load the assembly
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")
#read server names from a file
$servers = get-content "C:\Some\Path\servers.txt"
#use calculated properties
$servers | select-object `
#{Name='ServerName';Expression={$_}},
#{Name='DatabaseCount';Expression={
(New-Object Microsoft.SqlServer.Management.SMO.Server $_).Databases.Count
}}
Output:
ServerName DatabaseCount
---------- -------------
ServerOne 12
ServerTwo 22
ServerThree 6
Note that there is no error checking and I am assuming that you are running in a context that has rights to the server and DB engine.

Powershell script scripts on dbachecks to compare the MaxMemory of server listed in a table

Run checks against servers
Import-Module dbatools
Import-Module dbachecks
$Server = "AMCB123"
$Database = "DBA"
# Create recordset of servers to evaluate
$sconn = new-object System.Data.SqlClient.SqlConnection("server=$Server;Trusted_Connection=true");
$q = "SELECT DISTINCT servername FROM DBA.[dbo].[Server_Group] WHERE ID =1;"
$sconn.Open()
$cmd = new-object System.Data.SqlClient.SqlCommand ($q, $sconn);
$cmd.CommandTimeout = 0;
$dr = $cmd.ExecuteReader();
# Loop through the servers and build an array
while ($dr.Read()) {
Get-DbaMaxMemory -SqlServer $dr.GetValue(0) | Format-Table
}
$dr.Close()
$sconn.Close()
I have Listed the sql server(stage, prod, DR servers in a table as per the groups), Now I want to compare the servers with group id's to check wethere the servers(stage,prod, DR) with same group id is having same MAXMemory cofiguration or not.
For this I'm using the below powershell script can you please help me with this, I have created a table with all the servewith grop id.
Request to please help me with the loop thorugh the servers and build an array, so that I can run the MAXMEMORY powershell command to compare it using the group id for all servers.
I have collected all the servers details into a table dbo.server groups
the powershell script should iterate through the table by using the ID and check whether the servers in the ID group has same MAXMEMORY configuration ID server_name Environment
1 ABC0123 prod
1 ABC5123 stage
1 ABC4123 DR
2 DEF0123 prod
2 DEF5123 stage
2 DEF4123 DR
I'm trying to use a powershell script which will check and compare the MAXMEMORY configuration as per the ID(to check whether stage, prod, DR server of the same group_id have similar setting or not), if not then it will display a warning/message as group_ids servers are not configured similarly.
Please help me with the script
You're making this script longer than it needs to be. Also, you're using Format-Table prematurely - you should only use the Format-* functions for displaying final information to the user; they output strings, not properly typed data/variables that can be used down the line.
Use the tools that PowerShell and dbatools give you to get your server list, and then pass that list to Get-DbaMaxMemory as a collection.
import-module dbatools
$ServerList = Invoke-DbaSqlQuery -ServerInstance $Server -query "select distinct servername from dba.dbo.server_group where group_id = 1" | Select-Object -ExpandProperty servername;
Get-DbaMaxMemory -ServerInstance $ServerList | Select-Object SqlInstance, SqlMaxMB;
This will give you a list of your SQL instances and the memory they're configured to use. What you do after that...it's hard to say as you haven't clearly defined what you're looking for.
But this may not tell the full story. Wouldn't it be better to check the configured values and what you're currently running with? You can do that with Get-DbaSpConfigure.
import-module dbatools
$ServerList = Invoke-DbaSqlQuery -ServerInstance $Server -query "select distinct servername from dba.dbo.server_group where group_id = 1" | Select-Object -ExpandProperty servername;
Get-DbaSpConfigure -ServerInstance $ServerList | Select-Object ServerName,ConfiguredValue,RunningValue;
You can even create a computed column in that final Select-Object to tell you if the configured & running values differ.
If you just wanted to use dbachecks (which uses dbatools in the background) you can use
$ServerList = (Invoke-DbaSqlQuery -ServerInstance $Server -query "select distinct servername from dba.dbo.server_group where group_id = 1").servername
and
Invoke-DbcCheck -SQlInstance $ServerList -Check MaxMemory
Or you can set the configuration item app.computername and app.sqlinstance to your server list using
Set-DbcConfig -Name app.sqlinstance -Value $serverlist
Set-DbcConfig -Name app.computername -Value $serverlist
and then you can run this (or any other checks) using
Invoke-DbcCheck -Check MaxMemory

Resources