14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a

## [Unreleased]

## [2.5.0] - 2019-03-26

## Added

- Bolt task to start SQL agent jobs ([MODULES-8610](https://tickets.puppetlabs.com/browse/MODULES-8610)).

## Fixed

- Missing type declaration for the get_sqlagent_job.json
- Make job_name param required for the start_sql_agent_job task ([MODULES-8749](https://tickets.puppetlabs.com/browse/MODULES-8749))

## [2.4.0] - 2019-03-12

### Added
Expand Down Expand Up @@ -222,7 +233,8 @@ attempts to reinstall an instance that already exists ([MODULES-6022](https://ti

Initial release.

[Unreleased]: https://github.com/puppetlabs/puppetlabs-sqlserver/compare/2.4.0..master
[Unreleased]: https://github.com/puppetlabs/puppetlabs-sqlserver/compare/2.5.0..master
[2.5.0]: https://github.com/puppetlabs/puppetlabs-sqlserver/compare/2.4.0..2.5.0
[2.4.0]: https://github.com/puppetlabs/puppetlabs-sqlserver/compare/2.3.0..2.4.0
[2.3.0]: https://github.com/puppetlabs/puppetlabs-sqlserver/compare/2.2.0..2.3.0
[2.2.0]: https://github.com/puppetlabs/puppetlabs-sqlserver/compare/2.1.1..2.2.0
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,48 @@ Use this parameter — especially when using the `fuzzy_match` parameter — to
Set this to true to change the behavior of the `job_name` parameter so that only jobs with names exactly matching one of the provided job_names is returned in the result set.
This is an optional parameter which accepts either `true` or `false`. It's default value is false.

#### sqlserver::start_sqlagent_jobs

##### parameters

* **instance_name**

The name of the instance. The value defaults to the default instance on the machine.
Refer to named instances by either the short name of the instance or by `<COMPUTERNAME>\<INSTANCE_NAME>`.
Specifying an instance name will access only that instance.
Pass the values `.`, `MSSQLSERVER`, the node name, or leave blank which will refer to the default instance.
This is an optional parameter which accepts a single string or an array of strings as input.

* **job_name**

The name of a job. By default, the job names must be exact.
See the `fuzzy_match` parameter below for an example of pattern matching job names.
This parameter is required and accepts a string or an array of strings as input.

* **fuzzy_match**

Modifies the behavior of the `job_name` parameter so that the value given is matched to the job name pattern.
The `fuzzy_match` paremeter searches using the PowerShell `-match` operator.
For example, when `fuzzy_match` is set to true, if you pass the string `backup` to the `job_name` parameter, jobs such as `Daily Backup` and `System Backup` will be matched and executed.
This is an optional parameter which accepts either `true` or `false`. Its default value is false.

* **step**

The step number you want to execute. Leave blank to execute the first step.
Note that these job step numbers refer to a zero based array of job steps.
To execute a job starting at step 5, pass step 4 to this parameter.
This is because contrary to the SQLServer user interface, the programming objects
this task leverages are zero based.

* **wait**

This parameter tells the task to wait until a set of jobs has been completed before returning the job data.
All matched jobs start concurrently. The task waits in a loop for all of the jobs to complete.
If this parameter is not set to true, matched jobs are started and the task returns job data — indicating that the job has started.
Note that if a job completes quickly, it may finish before the task has finished executing.
This could result in false information that the job is idle. If this happens, check the `lastRunDate` property of the job to see if it was executed successfully.
This is a boolean parameter that defaults to false.

### Microsoft SQL Server terms

Terminology differs somewhat between various database systems; please refer to this list of terms for clarification.
Expand Down
303 changes: 303 additions & 0 deletions files/shared_task_functions.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
function Write-BoltError {
param(
$message,
$errorObject
)

$errorReturn = @{
_error = @{
msg = $message
kind = 'puppetlabs.task/task-error'
details = @{
detailedInfo = $errorObject.exception.message
exitcode = 1
}
}
}

$errorReturn | ConvertTo-JSON -depth 99
}

function Select-LoginName {
param(
[PSObject]$login,
[string[]]$namesToMatch,
[switch]$exact_match
)

# This function takes a single SQLServer login object and compares it against
# the list of names passed into the -login_name parameter of the script to
# determine if this is a login the user is interested in seeing. If it does
# not pass the filter represented by that parameter the login is discarded.

foreach ($paramLogin in $namesToMatch) {
if ($exact_match) {
if ($paramLogin -eq $login.name) {
$login
}
}
else {
# Match is a regex operator, and it doesn't like the '\' in domain names.
if ($login.name -match [regex]::escape($paramLogin)) {
$login
}
}
}
}

function Select-LoginNameStrict {
param(
[PSObject]$login,
[string[]]$namesToMatch,
[switch]$exact_match
)

<#
This function takes a single SQLServer login object and compares it against
the list of names passed into the -login_name parameter of the script to
determine if this is a login the user is interested in seeing. If it does
not pass the filter represented by that parameter the login is discarded.
#>

foreach ($paramLogin in [string[]]$namesToMatch) {
if (-not $fuzzy_match) {
if ($paramLogin -eq $login.name) {
$login
}
}
else {
# Match is a regex operator, and it doesn't like the '\' in domain names.
if ($login.name -match [regex]::escape($paramLogin)) {
$login
}
}
}
}

function Get-SQLInstances {
param(
[string[]]$instance_name
)

$instancesHolder = New-Object System.Collections.Generic.List[System.Object]
$stringsToReturn = New-Object System.Collections.Generic.List[System.Object]

# The default instance is referred to in its service name as MSSQLSERVER. This
# leads many SQLSERVER people to refer to it as such. They will also connect
# to it using just a '.'. None of these are its real name. Its real instance
# name is just the machine name. A named instances real name is the machine
# name a, '\', and the instance name. This little foreach ensures that we are
# referring to these instances by their real names so that proper filtering
# can be done.

foreach ($name in $instance_name) {
switch ($name) {
{($_ -eq 'MSSQLSERVER') -or ($_ -eq '.')} { [void]$instancesHolder.add($env:COMPUTERNAME) }
{$_ -eq $env:COMPUTERNAME} { [void]$instancesHolder.add($_) }
{$_ -notmatch '\\'} { [void]$instancesHolder.add("$env:COMPUTERNAME\$_") }
default { [void]$instancesHolder.add($name) }
}
}

$instanceStrings = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances

# The registry key does not return the real instance names. Again we must
# normalize these names into their real names so that comparisons can be done
# properly.

foreach ($string in $instanceStrings) {
switch ($string) {
'MSSQLSERVER' { $string = $env:COMPUTERNAME }
Default {$string = "$env:COMPUTERNAME\$string"}
}

if ((-not [string]::IsNullOrEmpty($instancesHolder))-and(-not [string]::IsNullOrWhiteSpace($instancesHolder))) {
foreach ($instance in $instancesHolder) {
if ($instance -eq $string) {
[void]$stringsToReturn.add($string)
}
}
}
else {
[void]$stringsToReturn.add($string)
}
}

if($stringsToReturn.count -gt 0){
$stringsToReturn
} else {
throw "No instances were found by the name(s) $instance_name"
}
}

function Get-SQLInstancesStrict {
param(
[string[]]$instance_name
)

$instancesHolder = New-Object System.Collections.Generic.List[System.Object]
$stringsToReturn = New-Object System.Collections.Generic.List[System.Object]

# The default instance is referred to in its service name as MSSQLSERVER. This
# leads many SQLSERVER people to refer to it as such. They will also connect
# to it using just a '.'. None of these are it's real name. Its real instance
# name is just the machine name. A named instances real name is the machine
# name a '\' and the instance name. This little foreach ensures that we are
# referring to these instances by their real names so that proper filtering
# can be done.

foreach ($name in $instance_name) {
switch ($name) {
{($_ -eq 'MSSQLSERVER') -or ($_ -eq '.')} { [void]$instancesHolder.add($env:COMPUTERNAME) }
{$_ -eq $env:COMPUTERNAME} { [void]$instancesHolder.add($_) }
{$_ -notmatch '\\'} { [void]$instancesHolder.add("$env:COMPUTERNAME\$_") }
default { [void]$instancesHolder.add($name) }
}
}

if($instancesHolder.count -eq 0){
[void]$instancesHolder.add($env:computername)
}

$instanceStrings = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances

# The registry key does not return the real instance names. Again we must
# normalize these names into their real names so that comparisons can be done
# properly.

foreach ($string in $instanceStrings) {
switch ($string) {
'MSSQLSERVER' { $string = $env:COMPUTERNAME }
Default {$string = "$env:COMPUTERNAME\$string"}
}

foreach ($instance in $instancesHolder) {
if ($instance -eq $string) {
[void]$stringsToReturn.add($string)
}
}
}

if($stringsToReturn.count -gt 0){
$stringsToReturn
} else {
throw "No instances were found by the name(s) $instance_name"
}
}

function Get-ServerObject {
param(
[string]$instance
)

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

New-Object Microsoft.SqlServer.Management.Smo.Server -ArgumentList $instance

}

function Select-Job {
param(
[PSObject]$job,
[string[]]$jobsToMatch,
[switch]$exact_match
)


# This function takes a single SQLServer job object and compares it against
# the list of names passed into the -job_name parameter of the script to
# determine if this is a job the user is interested in seeing. If it does
# not pass the filter represented by that parameter the job is discarded.

foreach ($paramJob in $jobsToMatch) {
if ($exact_match) {
if ($paramJob -eq $job.name) {
$job
}
}
else {
# Match is a regex operator, and it doesn't like the '\' in domain names.
if ($job.name -match [regex]::escape($paramJob)) {
$job
}
}
}
}

function Select-JobStrict {
param(
[PSObject]$job,
[string[]]$jobsToMatch,
[switch]$fuzzy_match
)


# This function takes a single SQLServer job object and compares it against
# the list of names passed into the -job_name parameter of the script to
# determine if this is a job the user is interested in seeing. If it does
# not pass the filter represented by that parameter the job is discarded.

foreach ($paramJob in $jobsToMatch) {
if (!$fuzzy_match) {
if ($paramJob -eq $job.name) {
$job
}
}
else {
# Match is a regex operator, and it doesn't like the '\' in domain names.
if ($job.name -match [regex]::escape($paramJob)) {
$job
}
}
}
}

function New-CustomJobObject {
param(
[psobject]$job
)

$customObject = @{
name = $job.name
description = $job.description
enabled = $job.isEnabled
ownerLoginName = $job.ownerLoginName
instance = $job.parent.name
lastRunDate = $job.lastRunDate.ToString('s')
lastRunOutcome = [string]$job.lastRunOutcome
currentRunStatus = [string]$job.currentRunStatus
currentRunStep = $job.currentRunStep
startStepID = $job.startStepID
currentRunRetryAttempt = $job.currentRunRetryAttempt
nextRunDate = $job.nextRunDate.ToString('s')
dateCreated = $job.dateCreated.ToString('s')
dateLastModified = $job.dateLastModified.ToString('s')
emailLevel = $job.emailLevel
operatorToEmail = $job.operatorToEmail
operatorToNetSend = $job.operatorToNetSend
operatorToPage = $job.operatorToPage
category = $job.category
steps = New-Object System.Collections.Generic.List[System.Object]
}

foreach($step in $job.jobSteps){
$step = @{
name = $step.name
type = [string]$step.subsystem
databaseName = $step.DatabaseName
lastRunDate = $step.lastRunDate.toString('s')
lastRunDurationSeconds = $step.LastRunDuration
lastRunOutcome = [string]$step.LastRunOutcome
lastRunRetries = $step.lastRunRetries
onFailAction = [string]$step.onFailAction
onFailStep = $step.onFailStep
onSuccessAction = [string]$step.onSuccessAction
onSuccessStep = $step.onSuccessStep
retryAttempts = $step.retryAttempts
retryIntervalMinutes = $step.retryInterval
}
[void]$customObject.steps.add($step)
}

$customObject
}
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-sqlserver",
"version": "2.4.0",
"version": "2.5.0",
"author": "Puppet Inc",
"summary": "The `sqlserver` module installs and manages MS SQL Server 2012, 2014, 2016, 2017, and 2019 on Windows systems.",
"license": "proprietary",
Expand Down
3 changes: 2 additions & 1 deletion tasks/get_sql_logins.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
"description": "Return more detailed information from the server instead of the default summary information",
"type": "Optional[Boolean]"
}
}
},
"files": ["sqlserver/files/shared_task_functions.ps1"]
}
Loading