diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:03:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:03:42 +0000 |
commit | 66cec45960ce1d9c794e9399de15c138acb18aed (patch) | |
tree | 59cd19d69e9d56b7989b080da7c20ef1a3fe2a5a /ansible_collections/lowlydba/sqlserver/plugins | |
parent | Initial commit. (diff) | |
download | ansible-66cec45960ce1d9c794e9399de15c138acb18aed.tar.xz ansible-66cec45960ce1d9c794e9399de15c138acb18aed.zip |
Adding upstream version 7.3.0+dfsg.upstream/7.3.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/lowlydba/sqlserver/plugins')
64 files changed, 6278 insertions, 0 deletions
diff --git a/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/attributes.py b/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/attributes.py new file mode 100644 index 00000000..76cf703c --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/attributes.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + + DOCUMENTATION = r''' +options: {} +''' + + CHECK_MODE = r''' +options: {} +attributes: + check_mode: + support: full + description: Can run in check_mode and return changed status prediction without modifying target. +''' + + CHECK_MODE_READ_ONLY = r''' +options: {} +attributes: + check_mode: + support: full + description: This module is "read only" and operates the same regardless of check mode. +''' + + PLATFORM_ALL = r''' +options: {} +attributes: + platform: + platforms: all + support: full + description: Target OS/families that can be operated against. +''' + + PLATFORM_WIN = r''' +options: {} +attributes: + platform: + platforms: Windows + support: full + description: Target OS/families that can be operated against. +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/sql_credentials.py b/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/sql_credentials.py new file mode 100644 index 00000000..1af7e9a4 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/sql_credentials.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Options for authenticating with SQL Authentication. + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class ModuleDocFragment(object): + + DOCUMENTATION = r''' +options: + sql_instance: + description: + - The SQL Server instance to modify. + type: str + required: true + sql_username: + description: + - Username for SQL Authentication. + type: str + required: false + sql_password: + description: + - Password for SQL Authentication. + type: str + required: false +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/state.py b/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/state.py new file mode 100644 index 00000000..d2ede689 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/doc_fragments/state.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Options for object state. + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class ModuleDocFragment(object): + + DOCUMENTATION = r''' +options: + state: + description: + - Whether or not the object should be C(present) or C(absent). + required: false + type: str + default: 'present' + choices: ['present', 'absent'] +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/module_utils/_SqlServerUtils.psm1 b/ansible_collections/lowlydba/sqlserver/plugins/module_utils/_SqlServerUtils.psm1 new file mode 100644 index 00000000..e45b7c74 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/module_utils/_SqlServerUtils.psm1 @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Private + +function Get-LowlyDbaSqlServerAuthSpec { + <# + .SYNOPSIS + Output the auth spec used by every module. + + .DESCRIPTION + Standardized way to access the common auth spec for modules. + Uses the recommended Ansible naming convention. + #> + @{ + options = @{ + sql_instance = @{type = 'str'; required = $true } + sql_username = @{type = 'str'; required = $false } + sql_password = @{type = 'str'; required = $false; no_log = $true } + } + required_together = @( + , @('sql_username', 'sql_password') + ) + } +} + +function Get-SqlCredential { + <# + .SYNOPSIS + Build a credential object for SQL Authentication. + + .DESCRIPTION + Standardized way to build a SQL Credential object that is required for SQL Authentication. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [ValidateScript({ $_.GetType().FullName -eq 'Ansible.Basic.AnsibleModule' })] + $Module + ) + try { + $sqlInstance = $module.Params.sql_instance + if ($null -ne $Module.Params.sql_username) { + [securestring]$secPassword = ConvertTo-SecureString $Module.Params.sql_password -AsPlainText -Force + [pscredential]$sqlCredential = New-Object System.Management.Automation.PSCredential ($Module.Params.sql_username, $secPassword) + } + else { + $sqlCredential = $null + } + return $sqlInstance, $sqlCredential + } + catch { + Write-Error ("Error building Credential for SQL Authentication spec.") + } +} + +function ConvertTo-SerializableObject { + <# + .SYNOPSIS + Transforms some members of a DbaTools result objects to be more serialization-friendly and prevent infinite recursion. + + .DESCRIPTION + Stringifies version properties so we don't get serialized [System.Version] objects which aren't very useful. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [Object] + $InputObject, + [Parameter()] + [string[]] + $ExcludeProperty = @( + <# + Returning a list of properties as a property is redundant. + #> + 'Properties', + <# + Urn is not useful. + #> + 'Urn', + <# + ExecutionManager can contain a login password in plain text. + #> + 'ExecutionManager', + <# + UserData is not useful. + #> + 'UserData', + <# + ParentCollection is redundant. + #> + 'ParentCollection', + <# + DatabaseEngineEdition is not useful. + #> + 'DatabaseEngineEdition', + <# + DatabaseEngineType is not useful. + #> + 'DatabaseEngineType', + <# + ServerVersion is not useful. + #> + 'ServerVersion', + <# + Server is redundant. + #> + 'Server', + <# + Parent is not useful. + #> + 'Parent' + ), + [bool]$UseDefaultProperty = $true + ) + + Begin { + # We need to remove this type data so that arrays don't get serialized weirdly. + # In some cases, an array gets serialized as an object with a Count and Value property where the value is the actual array. + # See: https://stackoverflow.com/a/48858780/3905079 + # This only affects Windows PowerShell. + # This has to come after the AnsibleModule is created, otherwise it will break the sanity tests. + if (-not $IsLinux) { + Remove-TypeData -TypeName System.Array -ErrorAction SilentlyContinue + } + } + + Process { + $defaultProperty = $InputObject.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames + if ($defaultProperty -and $UseDefaultProperty) { + $objectProperty = $InputObject.PSObject.Properties | Where-Object { $_.Name -in $defaultProperty -and $_.Name -notin $ExcludeProperty } + } + else { + $objectProperty = $InputObject.PSObject.Properties | Where-Object { $_.Name -notin $ExcludeProperty } + } + $properties = foreach ($p in $objectProperty) { + $pName = $p.Name + $pValue = $p.Value + + switch ($p) { + { $null -eq $pValue } { + @{ + Name = $pName + Expression = { $null }.GetNewClosure() + } + break + } + { $pValue -is [datetime] } { + @{ + Name = $pName + Expression = { $pValue.ToString('o') }.GetNewClosure() + } + break + } + { $pValue -is [enum] -or $pValue -is [type] } { + @{ + Name = $pName + Expression = { $pValue.ToString() }.GetNewClosure() + } + break + } + { $pValue.GetType().Name -like '*Collection' } { + @{ + Name = $pName + Expression = { [string[]]($pValue.Name) }.GetNewClosure() + } + break + } + { $pValue.GetType().Name -eq 'User' } { + @{ + Name = $pName + Expression = { [string[]]($pValue.Name) }.GetNewClosure() + } + break + } + default { $pName } + } + } + return $InputObject | Select-Object -Property $properties + } +} + +$exportMembers = @("Get-SqlCredential", "ConvertTo-SerializableObject", "Get-LowlyDbaSqlServerAuthSpec") +Export-ModuleMember -Function $exportMembers diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_listener.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_listener.ps1 new file mode 100644 index 00000000..dd377141 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_listener.ps1 @@ -0,0 +1,86 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + ag_name = @{type = 'str'; required = $true } + listener_name = @{type = 'str'; required = $true } + ip_address = @{type = 'str'; required = $false } + subnet_ip = @{type = 'str'; required = $false } + subnet_mask = @{type = 'str'; required = $false; default = '255.255.255.0' } + port = @{type = 'int'; required = $false; default = 1433 } + dhcp = @{type = 'bool'; required = $false; default = $false } + state = @{type = "str"; required = $false; default = "present"; choices = @("present", "absent") } + } +} +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$agName = $module.Params.ag_name +$listenerName = $module.Params.listener_name +$subnetIp = $module.Params.subnet_ip +$subnetMask = $module.Params.subnet_mask +$ipAddress = $module.Params.ip_address +$port = $module.Params.port +$dhcp = $module.Params.dhcp +$state = $module.Params.state +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ + "*:SqlInstance" = $sqlInstance + "*:SqlCredential" = $sqlCredential + "*:EnableException" = $true + "*:Confirm" = $false + "*:WhatIf" = $checkMode +} + +try { + $existingListener = Get-DbaAgListener -AvailabilityGroup $agName -Listener $listenerName + if ($state -eq "present") { + if ($null -eq $existingListener) { + $listenerParams = @{ + AvailabilityGroup = $agName + Name = $listenerName + Port = $port + Dhcp = $dhcp + SubnetMask = $subnetMask + } + if ($null -ne $ipAddress) { + $listenerParams.Add("IPAddress", $ipAddress) + } + if ($null -ne $subnetIp) { + $listenerParams.Add("SubnetIP", $subnetIp) + } + $output = Add-DbaAgListener @listenerParams + $module.Result.changed = $true + } + elseif ($existingListener.Port -ne $port) { + $output = Set-DbaAgListener -AvailabilityGroup $agName -Listener $listenerName -Port $port + $module.Result.changed = $true + } + } + elseif ($state -eq "absent") { + if ($null -ne $existingListener) { + $output = Remove-DbaAgListener -AvailabilityGroup $agName -Listener $listenerName + $module.Result.changed = $true + } + } + + if ($output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring availability group listener failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_listener.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_listener.py new file mode 100644 index 00000000..12f0ec63 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_listener.py @@ -0,0 +1,84 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: ag_listener +short_description: Configures an availability group listener +description: + - Creates an Availability Group Listener for an existing availability group. +version_added: 0.5.0 +options: + ag_name: + description: + - Name of the target availability group. + type: str + required: true + listener_name: + description: + - Name of the Listener to be configured. + type: str + required: true + ip_address: + description: + - IP address(es) of the listener. Comma separated if multiple. + type: str + required: false + subnet_ip: + description: + - Subnet IP address(es) of the listener. Comma separated if multiple. + type: str + required: false + subnet_mask: + description: + - Sets the subnet IP mask(s) of the availability group listener. Comma separated if multiple. + type: str + required: false + default: 255.255.255.0 + port: + description: + - Sets the port number used to communicate with the availability group. + type: int + required: false + default: 1433 + dhcp: + description: + - Indicates whether the listener uses DHCP. + type: bool + required: false + default: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create Availability Group + lowlydba.sqlserver.availability_group: + sql_instance: sql-01.myco.io + ag_name: AG_MyDatabase + +- name: Create AG Listener + lowlydba.sqlserver.ag_listener: + sql_instance_primary: sql-01.myco.io + ag_name: AG_MyDatabase + listener_name: aglMyDatabase + ip_address: 10.0.20.20,10.1.77.77 + subnet_ip: 255.255.252.0 + subnet_mask: 255.255.255.0 +''' + +RETURN = r''' +data: + description: Output from the C(Add-DbaAgListener) or C(Set-DbaAgListener) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_replica.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_replica.ps1 new file mode 100644 index 00000000..cd7d0930 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_replica.ps1 @@ -0,0 +1,193 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + sql_instance_replica = @{type = 'str'; required = $true } + sql_username_replica = @{type = 'str'; required = $false } + sql_password_replica = @{type = 'str'; required = $false; no_log = $true } + ag_name = @{type = 'str'; required = $true } + endpoint = @{type = 'str'; required = $false; default = 'hadr_endpoint' } + endpoint_url = @{type = 'str'; required = $false } + backup_priority = @{type = 'int'; required = $false; default = 50 } + failover_mode = @{ + type = 'str' + required = $false + default = 'Manual' + choices = @('Manual', 'Automatic') + } + availability_mode = @{ + type = 'str' + required = $false; default = 'AsynchronousCommit' + choices = @('SynchronousCommit', 'AsynchronousCommit') + } + seeding_mode = @{ + type = 'str' + required = $false + default = 'Automatic' + choices = @('Manual', 'Automatic') + } + connection_mode_in_primary_role = @{ + type = 'str' + required = $false + default = 'AllowAllConnections' + choices = @('AllowReadIntentConnectionsOnly', 'AllowAllConnections') + } + connection_mode_in_secondary_role = @{ + type = 'str' + required = $false + default = 'AllowNoConnections' + choices = @('AllowNoConnections', 'AllowReadIntentConnectionsOnly', 'AllowAllConnections') + } + read_only_routing_connection_url = @{ + type = 'str' + required = $false + } + read_only_routing_list = @{ + type = 'str' + required = $false + } + cluster_type = @{ + type = 'str' + required = $false + default = 'Wsfc' + choices = @('Wsfc', 'External', 'None') + } + configure_xe_session = @{ type = 'bool'; required = $false; default = $false } + session_timeout = @{ type = 'int'; required = $false } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } + required_together = @( + , @('sql_username_replica', 'sql_password_replica') + ) +} +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$readOnlyRoutingConnectionUrl = $module.params.read_only_routing_connection_url +$readOnlyRoutingList = $module.Params.read_only_routing_list +$failoverMode = $module.Params.failover_mode +$seedingMode = $module.Params.seeding_mode +$agName = $module.Params.ag_name +$clusterType = $module.Params.cluster_type +$availabilityMode = $module.Params.availability_mode +$replicaSqlInstance = $module.Params.sql_instance_replica +$connectionModeInPrimaryRole = $module.Params.connection_mode_in_primary_role +$connectionModeInSecondaryRole = $module.Params.connection_mode_in_secondary_role +$configureXESession = $module.Params.configure_xe_session +$state = $module.Params.state +[nullable[int]]$sessionTimeout = $module.Params.session_timeout +$endpoint = $module.Params.endpoint +$endpointUrl = $module.Params.endpoint_url +$backupPriority = $module.Params.backup_priority + +if ($null -ne $module.Params.sql_username_replica) { + [securestring]$replicaSecPassword = ConvertTo-SecureString $Module.Params.sql_password_replica -AsPlainText -Force + [pscredential]$replicaSqlCredential = New-Object System.Management.Automation.PSCredential ($Module.Params.sql_username_replica, $replicaSecPassword) +} +if ($null -eq $replicaSqlCredential) { + $replicaSqlCredential = $sqlCredential +} +$module.Result.changed = $false +$checkMode = $module.CheckMode +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $replicaInstance = Connect-DbaInstance -SqlInstance $replicaSqlInstance -SqlCredential $replicaSqlCredential + $allReplicas = Get-DbaAgReplica -SqlInstance $replicaSqlInstance -SqlCredential $replicaSqlCredential -AvailabilityGroup $agName + $existingReplica = $allReplicas | Where-Object Name -eq $replicaInstance.DomainInstanceName + + if ($state -eq "present") { + $addReplicaSplat = @{ + SqlInstance = $replicaSqlInstance + SqlCredential = $replicaSqlCredential + Endpoint = $endpoint + AvailabilityMode = $availabilityMode + FailoverMode = $failoverMode + BackupPriority = $backupPriority + ConnectionModeInPrimaryRole = $connectionModeInPrimaryRole + ConnectionModeInSecondaryRole = $connectionModeInSecondaryRole + SeedingMode = $seedingMode + ClusterType = $clusterType + } + if ($null -ne $readOnlyRoutingList) { + $addReplicaSplat.Add("ReadOnlyRoutingList", $readOnlyRoutingList) + } + if ($null -ne $readOnlyRoutingConnectionUrl) { + $addReplicaSplat.Add("ReadOnlyRoutingConnectionUrl", $readOnlyRoutingConnectionUrl) + } + if ($null -ne $endpointUrl) { + $addReplicaSplat.Add("EndpointUrl", $endpointUrl) + } + if ($configureXESession -eq $true) { + $addReplicaSplat.Add("ConfigureXESession", $true) + } + if ($null -ne $sessionTimeout) { + $addReplicaSplat.Add("SessionTimeout", $sessionTimeout) + } + + if ($null -eq $existingReplica) { + $availabilityGroup = Get-DbaAvailabilityGroup -SqlInstance $sqlInstance -SqlCredential $sqlCredential -AvailabilityGroup $agName + $output = $availabilityGroup | Add-DbaAgReplica @addReplicaSplat + $module.Result.changed = $true + } + else { + $compareReplicaProperty = @( + 'AvailabilityMode' + 'FailoverMode' + 'BackupPriority' + 'ConnectionModeInPrimaryRole' + 'ConnectionModeInSecondaryRole' + 'SeedingMode' + 'SessionTimeout' + 'EndpointUrl' + ) + $setReplicaSplat = @{} + $addReplicaSplat.GetEnumerator() | Where-Object Key -in $compareReplicaProperty | ForEach-Object { $setReplicaSplat.Add($_.Key, $_.Value) } + [string[]]$compareProperty = $setReplicaSplat.Keys + $replicaDiff = Compare-Object -ReferenceObject $setReplicaSplat -DifferenceObject $existingReplica -Property $compareProperty + $setReplicaSplat.Add("SqlInstance", $sqlInstance) + $setReplicaSplat.Add("SqlCredential", $sqlCredential) + $setReplicaSplat.Add("Replica", $existingReplica.Name) + $setReplicaSplat.Add("AvailabilityGroup", $agName) + if ($replicaDiff) { + $output = Set-DbaAgReplica @setReplicaSplat + $module.Result.changed = $true + } + } + } +} +catch { + $module.FailJson("Configuring Availability Group replica failed: $($_.Exception.Message)") +} +try { + if ($state -eq "absent") { + if ($null -ne $existingReplica) { + $output = $existingReplica | Remove-DbaAgReplica + $module.Result.changed = $true + } + } +} +catch { + $module.FailJson("Removing Availability Group replica failed: $($_.Exception.Message)") +} + +try { + if ($output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error parsing results - operation likely completed: $($_.Exception.Message)") +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_replica.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_replica.py new file mode 100644 index 00000000..d817d333 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/ag_replica.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: ag_replica +short_description: Configures an availability group replica +description: + - Configures an availability group replica. +version_added: 0.5.0 +options: + sql_instance_replica: + description: + - The SQL Server instance where of the replica to be configured. + type: str + required: true + sql_username_replica: + description: + - Username for SQL Authentication for the secondary replica. + type: str + required: false + sql_password_replica: + description: + - Password for SQL Authentication for the secondary replica. + type: str + required: false + ag_name: + description: + - Name of the Availability Group that will have the new replica joined to it. + type: str + required: true + endpoint: + description: + - By default, this command will attempt to find a DatabaseMirror endpoint. If one does not exist, it will create it. + type: str + required: false + default: 'hadr_endpoint' + endpoint_url: + description: + - By default, the property C(Fqdn) of C(Get-DbaEndpoint) is used as I(endpoint_url). + Use I(endpoint_url) if a different URL is required due to special network configurations. + type: str + required: false + backup_priority: + description: + - Sets the backup priority availability group replica. + type: int + default: 50 + failover_mode: + description: + - Whether the replica have Automatic or Manual failover. + type: str + required: false + default: 'Manual' + choices: ['Automatic', 'Manual'] + availability_mode: + description: + - Whether the replica should be Asynchronous or Synchronous. + type: str + required: false + default: 'AsynchronousCommit' + choices: ['AsynchronousCommit', 'SynchronousCommit'] + seeding_mode: + description: + - Default seeding mode for the replica. Should remain as the default otherwise manual setup may be required. + type: str + required: false + default: 'Automatic' + choices: ['Automatic', 'Manual'] + connection_mode_in_primary_role: + description: + - Which connections can be made to the database when it is in the primary role. + type: str + required: false + default: 'AllowAllConnections' + choices: ['AllowReadIntentConnectionsOnly','AllowAllConnections'] + connection_mode_in_secondary_role: + description: + - Which connections can be made to the database when it is in the secondary role. + type: str + required: false + default: 'AllowNoConnections' + choices: ['AllowNoConnections','AllowReadIntentConnectionsOnly','AllowAllConnections'] + read_only_routing_connection_url: + description: + - Sets the read only routing connection url for the availability replica. + type: str + required: false + read_only_routing_list: + description: + - Sets the read only routing ordered list of replica server names to use when redirecting read-only connections through this availability replica. + type: str + required: false + cluster_type: + description: + - Cluster type of the Availability Group. Only supported in SQL Server 2017 and above. + type: str + required: false + default: 'Wsfc' + choices: ['Wsfc', 'External', 'None'] + configure_xe_session: + description: + - Configure the AlwaysOn_health extended events session to start automatically as the SSMS wizard would do. + type: bool + default: false + session_timeout: + description: + - How many seconds an availability replica waits for a ping response from a connected replica before considering the connection to have failed. + type: int + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create Availability Group + lowlydba.sqlserver.availability_group: + sql_instance: sql-01.myco.io + ag_name: AG_MyDatabase + +- name: Add a DR replica + lowlydba.sqlserver.ag_replica: + ag_name: 'AG_MyDatabase' + sql_instance_primary: sql-01.myco.io + sql_instance_replica: sql-02.myco.io + failover_mode: 'Manual' + availability_mode: 'Asynchronous' + seeding_mode: 'Automatic' + connection_mode_in_primary_role: 'AllowAllConnections' + connection_mode_in_secondary_role: 'AllowNoConnections' +''' + +RETURN = r''' +data: + description: Output from the C(Add-DbaAgReplica) or C(Set-DbaAgReplica) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job.ps1 new file mode 100644 index 00000000..cf261f3e --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job.ps1 @@ -0,0 +1,133 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + job = @{type = 'str'; required = $true } + description = @{type = 'str'; required = $false; } + category = @{type = 'str'; required = $false; } + enabled = @{type = 'bool'; required = $false; default = $true } + owner_login = @{type = 'str'; required = $false; } + start_step_id = @{type = 'int'; required = $false; } + schedule = @{type = 'str'; required = $false; } + force = @{type = 'bool'; required = $false; default = $false } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$job = $module.Params.job +$description = $module.Params.description +$enabled = $module.Params.enabled +$ownerLogin = $module.Params.owner_login +$category = $module.Params.category +$schedule = $module.Params.schedule +[nullable[int]]$startStepId = $module.Params.start_step_id +$force = $module.Params.force +$state = $module.Params.state +$checkMode = $module.CheckMode +$module.Result.changed = $false + +# Configure Agent job +try { + $existingJob = Get-DbaAgentJob -SqlInstance $sqlInstance -SqlCredential $sqlCredential -Job $job -EnableException + $output = $existingJob + + if ($state -eq "absent") { + if ($null -ne $existingJob) { + $output = $existingJob | Remove-DbaAgentJob -Confirm:$false -WhatIf:$checkMode -EnableException + $module.Result.changed = $true + } + } + elseif ($state -eq "present") { + $jobParams = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Job = $job + WhatIf = $checkMode + Force = $force + EnableException = $true + } + + if ($enabled -eq $false) { + $jobParams.add("Disabled", $true) + } + + if ($null -ne $ownerLogin) { + $jobParams.add("OwnerLogin", $ownerLogin) + } + + if ($null -ne $schedule) { + $jobParams.add("Schedule", $schedule) + } + + if ($null -ne $category) { + $jobParams.add("Category", $category) + } + + if ($null -ne $description) { + $jobParams.add("Description", $description) + } + + if ($null -ne $startStepID) { + $jobParams.add("StartStepId", $startStepID) + } + + # Create new job + if ($null -eq $existingJob) { + try { + $null = New-DbaAgentJob @jobParams + # Explicitly fetch the new job to make sure results don't suffer from SMO / Agent stale data bugs + $output = Get-DbaAgentJob -SqlInstance $sqlInstance -SqlCredential $sqlCredential -Job $job -EnableException + } + catch { + $module.FailJson("Failed creating new agent job: $($_.Exception.Message)", $_) + } + $module.Result.changed = $true + } + # Job exists + else { + # Need to serialize to prevent SMO auto refreshing + $old = ConvertTo-SerializableObject -InputObject $existingJob -UseDefaultProperty $false + if ($enabled -eq $true) { + $jobParams.Add("Enabled", $true) + } + $output = Set-DbaAgentJob @jobParams + if ($null -ne $output) { + $compareProperty = @( + "Category" + "Enabled" + "Name" + "OwnerLoginName" + "HasSchedule" + "Description" + "StartStepId" + ) + $diff = Compare-Object -ReferenceObject $output -DifferenceObject $old -Property $compareProperty + if ($diff -or $checkMode) { + $module.Result.changed = $true + } + } + } + } + + if ($output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error configuring SQL Agent job: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job.py new file mode 100644 index 00000000..dbd3043f --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job.py @@ -0,0 +1,84 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: agent_job +short_description: Configures a SQL Agent job +description: + - Configure a SQL Agent job, including which schedules and category it belongs to. +version_added: 0.1.0 +options: + job: + description: + - The name of the target SQL Agent job. + type: str + required: true + description: + description: + - Description for the SQL Agent job. + type: str + required: false + category: + description: + - Category for the target SQL Agent job. Must already exist. + type: str + required: false + enabled: + description: + - Whether the SQL Agent job should be enabled or disabled. + type: bool + required: false + default: true + version_added: '0.4.0' + owner_login: + description: + - The owning login for the database. Will default to the current user if + the database is being created and none supplied. + type: str + required: false + start_step_id: + description: + - What step number the job should begin with when run. + type: int + required: false + schedule: + description: + - The name of the schedule the job should be associated with. Only one schedule per job is supported. + type: str + required: false + force: + description: + - If I(force=true), any job categories will be created if they don't exist already. + type: bool + default: false +author: "John McCall (@lowlydba)" +notes: + - On slower hardware, stale job component data may be returned (i.e., a previous or default job category). + Configuring each component (schedule, step, category, etc.) individually is recommended for this reason. +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a job + lowlydba.sqlserver.agent_job: + sql_instance: sql-01.myco.io + job: MyJob + force: true +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaAgentJob), C(Set-DbaAgentJob), or C(Remove-DbaAgentJob) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_category.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_category.ps1 new file mode 100644 index 00000000..1e5c033a --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_category.ps1 @@ -0,0 +1,64 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + category = @{type = 'str'; required = $true } + category_type = @{type = 'str'; required = $false; choices = @('LocalJob', 'MultiServerJob', 'None') } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$category = $module.Params.category +$categoryType = $module.Params.category_type +$state = $module.Params.state +$checkMode = $module.CheckMode +$module.Result.changed = $false + +try { + $agentJobCategorySplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Category = $category + EnableException = $true + } + if ($null -ne $categoryType) { + $agentJobCategorySplat.Add("CategoryType", $categoryType) + } + $existingCategory = Get-DbaAgentJobCategory @agentJobCategorySplat + + if ($state -eq "present") { + # Create new job category + if ($null -eq $existingCategory) { + $output = New-DbaAgentJobCategory @agentJobCategorySplat -WhatIf:$checkMode + $module.Result.changed = $true + } + } + elseif ($state -eq "absent") { + if ($null -ne $existingCategory) { + $output = $existingCategory | Remove-DbaAgentJobCategory -WhatIf:$checkMode -EnableException -Confirm:$false + $module.Result.changed = $true + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error configuring SQL Agent job category.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_category.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_category.py new file mode 100644 index 00000000..ce480c4c --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_category.py @@ -0,0 +1,48 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: agent_job_category +short_description: Configures a SQL Agent job category +description: + - Configures a SQL Agent job category. Creates if it does not exist, else does nothing. +version_added: 0.1.0 +options: + category: + description: + - Name of the category. + required: true + type: str + category_type: + description: + - The type of category. + required: false + type: str + choices: ['LocalJob', 'MultiServerJob', 'None'] +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a maintenance job category + lowlydba.sqlserver.agent_job_category: + sql_instance: sql-01.myco.io + category: "Index Maintenance" +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaAgentJobCategory) or C(Remove-DbaAgentJobCategory) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_schedule.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_schedule.ps1 new file mode 100644 index 00000000..6681dcff --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_schedule.ps1 @@ -0,0 +1,164 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + schedule = @{type = 'str'; required = $true } + job = @{type = 'str'; required = $true } + enabled = @{type = 'bool'; required = $false } + force = @{type = 'bool'; required = $false } + frequency_type = @{type = 'str'; required = $false + choices = @('Once', 'OneTime', 'Daily', 'Weekly', 'Monthly', 'MonthlyRelative', 'AgentStart', 'AutoStart', 'IdleComputer', 'OnIdle') + } + frequency_interval = @{type = 'str'; required = $false; } + frequency_subday_type = @{type = 'str'; required = $false; choices = @('Time', 'Seconds', 'Minutes', 'Hours') } + frequency_subday_interval = @{type = 'int'; required = $false } + frequency_relative_interval = @{type = 'str'; required = $false; choices = @('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last') } + frequency_recurrence_factor = @{type = 'int'; required = $false } + start_date = @{type = 'str'; required = $false } + end_date = @{type = 'str'; required = $false } + start_time = @{type = 'str'; required = $false } + end_time = @{type = 'str'; required = $false } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$schedule = $module.Params.schedule +$job = $module.Params.job +[nullable[bool]]$enabled = $module.Params.enabled +$force = $module.Params.force +$frequencyType = $module.Params.frequency_type +$frequencyInterval = $module.Params.frequency_interval +$frequencySubdayType = $module.Params.frequency_subday_type +[nullable[int]]$frequencySubdayInterval = $module.Params.frequency_subday_interval +$frequencyRelativeInterval = $module.Params.frequency_relative_interval +[nullable[int]]$frequencyRecurrenceFactor = $module.Params.frequency_recurrence_factor +$startDate = $module.Params.start_date +$endDate = $module.Params.end_date +$startTime = $module.Params.start_time +$endTime = $module.Params.end_time +$state = $module.Params.state +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +$scheduleParams = @{ + SqlInstance = $SqlInstance + SqlCredential = $sqlCredential + Force = $force + Schedule = $schedule +} + +if ($enabled -eq $false) { + $scheduleParams.add("Disabled", $true) +} +if ($null -ne $job) { + $scheduleParams.add("Job", $job) +} +if ($null -ne $startDate) { + $scheduleParams.add("StartDate", $startDate) +} +if ($null -ne $endDate) { + $scheduleParams.add("EndDate", $endDate) +} +if ($null -ne $startTime) { + $scheduleParams.add("StartTime", $startTime) +} +if ($null -ne $endTime) { + $scheduleParams.add("EndTime", $endTime) +} +if ($null -ne $frequencyType) { + $scheduleParams.add("FrequencyType", $frequencyType) +} +if ($null -ne $frequencyInterval) { + $scheduleParams.add("FrequencyInterval", $frequencyInterval) +} +if ($null -ne $frequencySubdayType) { + $scheduleParams.add("FrequencySubdayType", $frequencySubdayType) +} +if ($null -ne $frequencySubdayInterval) { + $scheduleParams.add("FrequencySubdayInterval", $frequencySubdayInterval) +} +if ($null -ne $frequencyRelativeInterval) { + $scheduleParams.add("FrequencyRelativeInterval", $frequencyRelativeInterval) +} +if ($null -ne $frequencyRecurrenceFactor) { + $scheduleParams.add("FrequencyRecurrenceFactor", $frequencyRecurrenceFactor) +} + +try { + $existingSchedule = Get-DbaAgentSchedule -SqlInstance $SqlInstance -SqlCredential $sqlCredential -Schedule $schedule + if ($state -eq "present") { + if ($enabled -eq $true) { + $scheduleParams.Add("Enabled", $true) + } + # Update schedule + if ($null -ne $existingSchedule) { + # Need to serialize to prevent SMO auto refreshing + $old = ConvertTo-SerializableObject -InputObject $existingSchedule -UseDefaultProperty $false + $output = Set-DbaAgentSchedule @scheduleParams + if ($null -ne $output) { + $compareProperty = @( + "ActiveEndDate" + "ActiveEndTimeOfDay" + "ActiveEndTimeOfDay" + "ActiveStartTimeOfDay" + "Description" + "FrequencyInterval" + "FrequencyRecurrenceFactor" + "FrequencyRelativeIntervals" + "FrequencySubDayInterval" + "FrequencySubDayTypes" + "FrequencyTypes" + "IsEnabled" + "ScheduleName" + ) + $diff = Compare-Object -ReferenceObject $output -DifferenceObject $old -Property $compareProperty + + # # Check if schedule was actually changed + # $modifiedSchedule = Get-DbaAgentSchedule -SqlInstance $SqlInstance -SqlCredential $sqlCredential -Schedule $ScheduleName -EnableException + # $scheduleDiff = Compare-Object -ReferenceObject $existingSchedule -DifferenceObject $modifiedSchedule + if ($diff -or $checkMode) { + $module.Result.changed = $true + } + } + } + # Create schedule + else { + $output = New-DbaAgentSchedule @scheduleParams + $module.Result.changed = $true + } + } + elseif ($state -eq "absent") { + # Nothing to remove + if ($null -eq $existingSchedule) { + $module.ExitJson() + } + # Remove schedule + else { + $output = $existingSchedule | Remove-DbaAgentSchedule -Force + $module.Result.changed = $true + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error configuring SQL Agent job schedule.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_schedule.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_schedule.py new file mode 100644 index 00000000..008efca2 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_schedule.py @@ -0,0 +1,142 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: agent_job_schedule +short_description: Configures a SQL Agent job schedule +description: + - Configures settings for an agent schedule that can be applied to one or more agent jobs. +version_added: 0.1.0 +options: + schedule: + description: + - The name of the schedule. + type: str + required: true + job: + description: + - The name of the job that has the schedule. + - Schedules and jobs can also be associated via M(lowlydba.sqlserver.agent_job). + - See U(https://docs.dbatools.io/New-DbaAgentSchedule) for more detailed usage. + type: str + required: true + enabled: + description: + - Whether the schedule is enabled or disabled. + type: bool + required: false + force: + description: + - The force option will ignore some errors in the options and assume defaults. + - It will also remove the any present schedules with the same name for the specific job. + type: bool + frequency_type: + description: + - A value indicating when a job is to be executed. + - If I(force=true) the default will be C(Once). + type: str + required: false + choices: ['Once', 'OneTime', 'Daily', 'Weekly', 'Monthly', 'MonthlyRelative', 'AgentStart', 'AutoStart', 'IdleComputer', 'OnIdle'] + frequency_interval: + description: + - The days that a job is executed. + - Allowed values for I(frequency_type=Daily) - C(EveryDay) or a number between C(1) and C(365) inclusive. + - >- + Allowed values for I(frequency_type=Weekly) - + C(Sunday), C(Monday), C(Tuesday), C(Wednesday), C(Thursday), C(Friday), C(Saturday), + C(Weekdays), C(Weekend) or C(EveryDay). + - Allowed values for I(frequency_type=Monthly) - Numbers C(1) through C(31) for each day of the month. + - If C(Weekdays), C(Weekend) or C(EveryDay) is used it overwrites any other value that has been passed before. + - If I(force=true) the default will be C(1). + type: str + required: false + frequency_subday_type: + description: + - Specifies the units for the subday I(frequency_interval). + type: str + required: false + choices: ['Time', 'Seconds', 'Minutes', 'Hours'] + frequency_subday_interval: + description: + - The number of subday type periods to occur between each execution of a job. + type: int + required: false + frequency_relative_interval: + description: + - A job's occurrence of I(frequency_interval) in each month. If I(frequency_interval=32) (C(MonthlyRelative)). + type: str + required: false + choices: ['Unused', 'First', 'Second', 'Third', 'Fourth', 'Last'] + frequency_recurrence_factor: + description: + - The number of weeks or months between the scheduled execution of a job. + - Required if I(frequency_type=Weekly), I(frequency_type=Monthly) or I(frequency_type=MonthlyRelative). + type: int + required: false + start_date: + description: + - The date on which execution of a job can begin. + - If I(force=true)the start date will be the current day. + - Format is C(YYYY-MM-DD). + type: str + required: false + end_date: + description: + - The date on which execution of a job can stop. + - If I(force=true) the end date will be C(9999-12-31). + - Format is C(YYYY-MM-DD). + type: str + required: false + start_time: + description: + - The time on any day to begin execution of a job. Format C(HHMMSS) (24 hour clock). + - If I(force=true) the start time will be C(00:00:00). + type: str + required: false + end_time: + description: + - The time on any day to end execution of a job. Format C(HHMMSS) (24 hour clock). + - If (force=true) the start time will be C(23:59:59). + type: str + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a job schedule + lowlydba.sqlserver.agent_job_schedule: + sql_instance: sql-01.myco.io + schedule: DailySchedule + force: true + enabled: true + start_date: 2020-05-25 # May 25, 2020 + end_date: 2099-05-25 # May 25, 2099 + start_time: 010500 # 01:05:00 AM + end_time: 140030 # 02:00:30 PM + state: present + +- name: Create a job with schedule + lowlydba.sqlserver.agent_job: + sql_instance: sql-01.myco.io + job: MyJob + force: true + schedule: DailySchedule +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaAgentJobSchedule) or C(Remove-DbaAgentJobSchedule) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_step.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_step.ps1 new file mode 100644 index 00000000..1bbbeb2e --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_step.ps1 @@ -0,0 +1,157 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + job = @{type = 'str'; required = $true } + step_id = @{type = 'int'; required = $false } + step_name = @{type = 'str'; required = $false } + database = @{type = 'str'; required = $false; default = 'master' } + subsystem = @{type = 'str'; required = $false; default = 'TransactSql' + choices = @('CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql') + } + command = @{type = 'str'; required = $false } + on_success_action = @{type = 'str'; required = $false; default = 'QuitWithSuccess' + choices = @('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep') + } + on_success_step_id = @{type = 'int'; required = $false; default = 0 } + on_fail_action = @{type = 'str'; required = $false; default = 'QuitWithFailure' + choices = @('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep') + } + on_fail_step_id = @{type = 'int'; required = $false; default = 0 } + retry_attempts = @{type = 'int'; required = $false; default = 0 } + retry_interval = @{type = 'int'; required = $false; default = 0 } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } + required_together = @( + , @('retry_attempts', 'retry_interval') + ) + required_one_of = @( + , @('step_id', 'step_name') + ) +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$job = $module.Params.job +$stepId = $module.Params.step_id +$stepName = $module.Params.step_name +$database = $module.Params.database +$subsystem = $module.Params.subsystem +$command = $module.Params.command +$onSuccessAction = $module.Params.on_success_action +[nullable[int]]$onSuccessStepId = $module.Params.on_success_step_id +$onFailAction = $module.Params.on_fail_action +[nullable[int]]$onFailStepId = $module.Params.on_fail_step_id +[int]$retryAttempts = $module.Params.retry_attempts +[nullable[int]]$retryInterval = $module.Params.retry_interval +$state = $module.Params.state +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +# Configure Agent job step +try { + $existingJobSteps = Get-DbaAgentJobStep -SqlInstance $SqlInstance -SqlCredential $sqlCredential -Job $job + $existingJobStep = $existingJobSteps | Where-Object Name -eq $stepName + + if ($state -eq "absent") { + if ($null -eq $existingJobStep) { + # try fetching name by id if we only care about removing + $existingJobStep = $existingJobSteps | Where-Object Id -eq $stepId + $stepName = $existingJobStep.Name + } + if ($existingJobStep) { + $removeStepSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Job = $job + StepName = $stepName + } + $output = Remove-DbaAgentJobStep @removeStepSplat + $module.Result.changed = $true + } + } + elseif ($state -eq "present") { + if (!($stepName) -or !($stepId)) { + $module.FailJson("Step name must be specified when state=present.") + } + $jobStepParams = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Job = $job + StepName = $stepName + Database = $database + SubSystem = $subsystem + OnSuccessAction = $onSuccessAction + OnSuccessStepId = $onSuccessStepId + OnFailAction = $onFailAction + OnFailStepId = $onFailStepId + RetryAttempts = $retryAttempts + RetryInterval = $retryInterval + WhatIf = $checkMode + } + if ($null -ne $command) { + $jobStepParams.Add("Command", $command) + } + + # No existing job step + if ($null -eq $existingJobStep) { + $jobStepParams.Add("StepId", $stepId) + $output = New-DbaAgentJobStep @jobStepParams + $module.Result.changed = $true + } + # Update existing + else { + # Validate step name isn't taken already - must be unique within a job + if ($existingJobStep.Name -eq $StepName -and $existingJobStep.ID -ne $stepId) { + $module.FailJson("There is already a step named '$StepName' for this job with an ID of $($existingJobStep.ID).") + } + + # Reference by old name in case new name differs for step id + $jobStepParams.StepName = $existingJobStep.Name + $jobStepParams.Add("NewName", $StepName) + + # Need to serialize to prevent SMO auto refreshing + $old = ConvertTo-SerializableObject -InputObject $existingJobStep -UseDefaultProperty $false + $output = Set-DbaAgentJobStep @jobStepParams + if ($null -ne $output) { + $compareProperty = @( + "Name" + "DatabaseName" + "Command" + "Subsystem" + "OnFailAction" + "OnFailActionStep" + "OnSuccessAction" + "OnSuccessActionStep" + "RetryAttempts" + "RetryInterval" + ) + $diff = Compare-Object -ReferenceObject $output -DifferenceObject $old -Property $compareProperty + } + if ($diff -or $checkMode) { + $module.Result.changed = $true + } + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error configuring SQL Agent job step.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_step.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_step.py new file mode 100644 index 00000000..1e3e9891 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/agent_job_step.py @@ -0,0 +1,118 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: agent_job_step +short_description: Configures a SQL Agent job step +description: + - Configures a step for an agent job. +version_added: 0.1.0 +options: + job: + description: + - The name of the job to which to add the step. + required: true + type: str + step_id: + description: + - The sequence identification number for the job step. Step identification numbers start at C(1) and increment without gaps. + - Required if I(state=present). + required: false + type: int + step_name: + description: + - The name of the step. Required if I(state=present). + required: false + type: str + database: + description: + - The name of the database in which to execute a Transact-SQL step. + required: false + type: str + default: 'master' + subsystem: + description: + - The subsystem used by the SQL Server Agent service to execute command. + required: false + type: str + default: 'TransactSql' + choices: ['CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql'] + command: + description: + - The commands to be executed by SQLServerAgent service through subsystem. + required: false + type: str + on_success_action: + description: + - The action to perform if the step succeeds. + required: false + type: str + default: 'QuitWithSuccess' + choices: ['QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep'] + on_success_step_id: + description: + - The ID of the step in this job to execute if the step succeeds and I(on_success_action=GoToStep). + required: false + type: int + default: 0 + on_fail_action: + description: + - The action to perform if the step fails. + required: false + type: str + default: 'QuitWithFailure' + choices: ['QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep'] + on_fail_step_id: + description: + - The ID of the step in this job to execute if the step fails and I(on_fail_action=GoToStep). + required: false + type: int + default: 0 + retry_attempts: + description: + - The number of retry attempts to use if this step fails. The default is C(0). + required: false + type: int + default: 0 + retry_interval: + description: + - The amount of time in minutes between retry attempts. + required: false + type: int + default: 0 +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a job + lowlydba.sqlserver.agent_job: + sql_instance: sql-01.myco.io + job: MyJob + force: true + +- name: Create a job step + lowlydba.sqlserver.agent_job_step: + sql_instance: sql-01.myco.io + job: MyJob + step_name: Step1 + step_id: 1 + command: "TRUNCATE TABLE dbo.TestData;" +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaAgentJobStep), C(Set-DbaAgentJobStep), or C(Remove-DbaAgentJobStep) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/availability_group.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/availability_group.ps1 new file mode 100644 index 00000000..177a6d10 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/availability_group.ps1 @@ -0,0 +1,235 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + sql_instance_secondary = @{type = "str"; required = $false } + sql_username_secondary = @{type = 'str'; required = $false } + sql_password_secondary = @{type = 'str'; required = $false; no_log = $true } + database = @{type = "str"; required = $false; aliases = @('database_name') } + ag_name = @{type = "str"; required = $true } + all_ags = @{type = "bool"; required = $false; } + shared_path = @{type = "str"; required = $false; default = $null } + dtc_support_enabled = @{type = "bool"; required = $false; } + basic_availability_group = @{type = "bool"; required = $false; } + database_health_trigger = @{type = "bool"; required = $false; } + is_distributed_ag = @{type = "bool"; required = $false; } + use_last_backup = @{type = "bool"; required = $false; } + healthcheck_timeout = @{type = "int"; required = $false; } + availability_mode = @{ + type = "str" + required = $false + default = "SynchronousCommit" + choices = @("SynchronousCommit", "AsynchronousCommit") + } + failure_condition_level = @{ + type = "str" + required = $false + choices = @( + "OnAnyQualifiedFailureCondition", + "OnCriticalServerErrors", + "OnModerateServerErrors", + "OnServerDown", + "OnServerUnresponsive" + ) + } + failover_mode = @{ + type = "str" + required = $false + default = "Automatic" + choices = @("Manual", "Automatic") + } + seeding_mode = @{ + type = "str" + required = $false + default = "Manual" + choices = @("Manual", "Automatic") + } + automated_backup_preference = @{ + type = "str" + required = $false + default = "Secondary" + choices = @("None", "Primary", "Secondary", "SecondaryOnly") + } + cluster_type = @{ + type = "str" + required = $false + default = "Wsfc" + choices = @("Wsfc", "External", "None") + } + allow_null_backup = @{type = "bool"; required = $false } + force = @{type = "bool"; required = $false } + state = @{type = "str"; required = $false; default = "present"; choices = @("present", "absent") } + } + required_together = @( + , @('sql_username_secondary', 'sql_password_secondary') + ) +} + +# Setup var +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$ProgressPreference = "SilentlyContinue" + +# Var +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$secondary = $module.Params.sql_instance_secondary +if ($null -ne $module.Params.sql_username_secondary) { + [securestring]$secondarySecPassword = ConvertTo-SecureString $Module.Params.sql_password_secondary -AsPlainText -Force + [pscredential]$secondarySqlCredential = New-Object System.Management.Automation.PSCredential ($Module.Params.sql_username_secondary, $secondarySecPassword) +} +$agName = $module.Params.ag_name +$database = $module.Params.database +$seedingMode = $module.Params.seeding_mode +$sharedPath = $module.Params.shared_path +$healthCheckTimeout = $module.Params.healthcheck_timeout +$availabilityMode = $module.Params.availability_mode +$failureConditionLevel = $module.Params.failure_condition_level +$failoverMode = $module.Params.failover_mode +$automatedBackupPreference = $module.Params.automated_backup_preference +$clusterType = $module.Params.cluster_type +$state = $module.Params.state +[nullable[bool]]$all_ags = $module.Params.all_ags +[nullable[bool]]$useLastBackup = $module.Params.use_last_backup +[nullable[bool]]$dtcSupportEnabled = $module.Params.dtc_support_enabled +[nullable[bool]]$basicAvailabilityGroup = $module.Params.basic_availability_group +[nullable[bool]]$databaseHealthTrigger = $module.Params.database_health_trigger +[nullable[bool]]$isDistributedAg = $module.Params.is_distributed_ag +[nullable[bool]]$force = $module.Params.force +[nullable[bool]]$allowNullBackup = $module.Params.allow_null_backup +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $existingAG = Get-DbaAvailabilityGroup -SqlInstance $sqlInstance -SqlCredential $sqlCredential -AvailabilityGroup $agName + + if ($state -eq "present") { + $agSplat = @{ + Primary = $sqlInstance + PrimarySqlCredential = $sqlCredential + Name = $agName + SeedingMode = $seedingMode + FailoverMode = $failoverMode + AvailabilityMode = $availabilityMode + AutomatedBackupPreference = $automatedBackupPreference + ClusterType = $clusterType + } + if ($null -ne $sharedPath -and $seedingMode -eq "Manual") { + $agSplat.Add("SharedPath", $sharedPath) + } + if ($useLastBackup -eq $true) { + $agSplat.Add("UseLastBackup", $useLastBackup) + } + if ($dtcSupportEnabled -eq $true) { + $agSplat.Add("DtcSupport", $dtcSupportEnabled) + } + if ($basicAvailabilityGroup -eq $true) { + $agSplat.Add("Basic", $basicAvailabilityGroup) + } + if ($databaseHealthTrigger -eq $true) { + $agSplat.Add("DatabaseHealthTrigger", $databaseHealthTrigger) + } + if ($null -ne $healthCheckTimeout) { + $agSplat.Add("HealthCheckTimeout", $healthCheckTimeout) + } + if ($null -ne $failureConditionLevel) { + $agSplat.Add("FailureConditionLevel", $failureConditionLevel) + } + if ($null -ne $database) { + $agSplat.Add("Database", $database) + } + if ($null -ne $secondary) { + $agSplat.Add("Secondary", $secondary) + } + if ($null -ne $secondarySqlCredential) { + $agSplat.Add("SecondarySqlCredential", $secondarySqlCredential) + } + if ($force -eq $true) { + $agSplat.Add("Force", $force) + } + + # Create the AG with initial replica(s) + if ($null -eq $existingAG) { + # Full backup requirement for new AG via automatic seeding + if ($seedingMode -eq "automatic" -and $null -ne $database) { + $dbBackup = Get-DbaLastBackup -SqlInstance $sqlInstance -SqlCredential $sqlCredential -Database $database + if ($null -eq $dbBackup.LastFullBackup -and $allowNullBackup -eq $true) { + $backupSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + FilePath = "NUL" + Type = "Full" + } + $null = Backup-DbaDatabase @backupSplat + } + } + $output = New-DbaAvailabilityGroup @agSplat + $module.Result.changed = $true + } + # Configure existing AG + else { + $setAgSplat = @{ + AutomatedBackupPreference = $automatedBackupPreference + ClusterType = $clusterType + } + if ($all_ags -eq $true) { + $setAgSplat.Add("AllAvailabilityGroups", $all_ags) + } + if ($dtcSupportEnabled -eq $true) { + $setAgSplat.Add("DtcSupportEnabled", $dtcSupportEnabled) + } + if ($basicAvailabilityGroup -eq $true) { + $setAgSplat.Add("BasicAvailabilityGroup", $basicAvailabilityGroup) + } + if ($databaseHealthTrigger -eq $true) { + $setAgSplat.Add("DatabaseHealthTrigger", $databaseHealthTrigger) + } + if ($null -ne $failureConditionLevel) { + $setAgSplat.Add("FailureConditionLevel", $failureConditionLevel) + } + if ($null -ne $healthCheckTimeout) { + $setAgSplat.Add("HealthCheckTimeout", $healthCheckTimeout) + } + if ($isDistributedAg -eq $true) { + $setAgSplat.Add("IsDistributedAvailabilityGroup", $isDistributedAg) + } + $compareProperty = ($existingAG.Properties | Where-Object Name -in $setAgSplat.Keys).Name + $agDiff = Compare-Object -ReferenceObject $existingAG -DifferenceObject $setAgSplat -Property $compareProperty + if ($null -ne $agDiff) { + $output = $existingAG | Set-DbaAvailabilityGroup @setAgSplat + $module.Result.changed = $true + } + } + } + elseif ($state -eq "absent") { + if ($null -ne $existingAG) { + if ($all_ags -eq $true) { + $existingAG | Remove-DbaAvailabilityGroup -AllAvailabilityGroups + } + else { + $existingAG | Remove-DbaAvailabilityGroup + } + $module.Result.changed = $true + } + } + + if ($output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring Availability Group failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/availability_group.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/availability_group.py new file mode 100644 index 00000000..15f78191 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/availability_group.py @@ -0,0 +1,157 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: availability_group +short_description: Configures availability group(s) +description: + - Configures SQL Server Availability Group(s) with up to one replica. +version_added: 0.4.0 +options: + sql_instance_secondary: + description: + - The secondary SQL Server instance for the new Availability Group. + type: str + required: false + sql_username_secondary: + description: + - Username for SQL Authentication for the secondary replica. + type: str + required: false + sql_password_secondary: + description: + - Password for SQL Authentication for the secondary replica. + type: str + required: false + database: + description: + - Name of the database to create the Availability Group for. + type: str + required: false + aliases: + - database_name + ag_name: + description: + - Name of the Availability Group. + type: str + required: true + all_ags: + description: + - Apply changes to all availability groups on the instance. Only used for configuring existing availability groups. + type: bool + required: false + shared_path: + description: + - The network share where the backups will be backed up and restored from. + type: str + required: false + dtc_support_enabled: + description: + - Enables Dtc support. + type: bool + required: false + basic_availability_group: + description: + - Indicates whether the availability group is Basic Availability Group. + type: bool + required: false + database_health_trigger: + description: + - Indicates whether the availability group triggers the database health. + type: bool + required: false + is_distributed_ag: + description: + - Indicates whether the availability group is distributed. + type: bool + required: false + healthcheck_timeout: + description: + - This setting used to specify the length of time, in milliseconds, + that the SQL Server resource DLL should wait for information returned by the C(sp_server_diagnostics) + stored procedure before reporting the Always On Failover Cluster Instance (FCI) as unresponsive. + - Changes that are made to the timeout settings are effective immediately and do not require a restart of the SQL Server resource. + type: int + required: false + failure_condition_level: + description: + - Specifies the different conditions that can trigger an automatic failover in Availability Group. + type: str + required: false + choices: ['OnAnyQualifiedFailureCondition', 'OnCriticalServerErrors', 'OnModerateServerErrors', 'OnServerDown', 'OnServerUnresponsive'] + cluster_type: + description: + - Cluster type of the Availability Group. Only supported in SQL Server 2017 and above. + type: str + required: false + default: 'Wsfc' + choices: ['Wsfc', 'External', 'None'] + failover_mode: + description: + - Whether the replica have Automatic or Manual failover. + type: str + required: false + default: 'Automatic' + choices: ['Automatic', 'Manual'] + availability_mode: + description: + - Whether the replica should be Asynchronous or Synchronous. + - Only used in creating a new availability group. + type: str + required: false + default: 'SynchronousCommit' + choices: ['AsynchronousCommit', 'SynchronousCommit'] + seeding_mode: + description: + - Default seeding mode for the replica. Should remain as the default otherwise manual setup may be required. + type: str + required: false + default: 'Manual' + choices: ['Automatic', 'Manual'] + automated_backup_preference: + description: + - How to handle backup requests by default. + type: str + required: false + default: 'Secondary' + choices: ['None', 'Primary', 'Secondary', 'SecondaryOnly'] + allow_null_backup: + description: + - Allow taking a full backup to C(NULL) if one does not exist and I(seeding_mode=Automatic). + type: bool + required: false + force: + description: + - Drop and recreate the database on remote servers using fresh backup. + type: bool + use_last_backup: + description: + - Use the last full and log backup of database. A log backup must be the last backup. + type: bool +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create Availability Group + lowlydba.sqlserver.availability_group: + sql_instance: sql-01.myco.io + ag_name: AG_MyDatabase +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaAvailabilityGroup) or C(Set-DbaAvailabilityGroup) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/backup.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/backup.ps1 new file mode 100644 index 00000000..163d4503 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/backup.ps1 @@ -0,0 +1,138 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + database = @{type = 'str'; required = $true } + path = @{type = 'str'; required = $false } + file_path = @{type = 'str'; required = $false } + increment_prefix = @{type = 'bool'; required = $false; default = $false } + replace_in_name = @{type = 'bool'; required = $false; default = $false } + copy_only = @{type = 'bool'; required = $false; default = $false } + type = @{type = 'str'; required = $false; default = 'database'; choices = @('full', 'log', 'differential', 'diff', 'database') } + timestamp_format = @{type = 'str'; required = $false } + encryption_certificate = @{type = 'str'; required = $false } + encryption_algorithm = @{type = 'str'; required = $false; choices = @('AES128', 'AES192', 'AES256', 'TRIPLEDES') } + create_folder = @{type = 'bool'; required = $false; default = $false } + file_count = @{type = 'int'; required = $false; default = 0 } + compress = @{type = 'bool'; required = $false; default = $false } + checksum = @{type = 'bool'; required = $false; default = $false } + verify = @{type = 'bool'; required = $false; default = $false } + no_recovery = @{type = 'bool'; required = $false; default = $false } + build_path = @{type = 'bool'; required = $false; default = $false } + max_transfer_size = @{type = 'int'; required = $false; default = 0 } + with_format = @{type = 'bool'; required = $false; default = $false } + initialize = @{type = 'bool'; required = $false; default = $false } + ignore_file_checks = @{type = 'bool'; required = $false; default = $false } + block_size = @{type = 'str'; required = $false; choices = @('0.5kb', '1kb', '2kb', '4kb', '8kb', '16kb', '32kb', '64kb') } + buffer_count = @{type = 'int'; required = $false; default = 0 } + azure_base_url = @{type = 'str'; required = $false } + azure_credential = @{type = 'str'; required = $false } + } + mutually_exclusive = @( + , @('path', 'azure_base_url') + ) + required_together = @( + , @('encryption_algorithm', 'encryption_certificate') + ) +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$path = $module.Params.path +$filePath = $module.Params.file_path +$incrementPrefix = $module.Params.increment_prefix +$replaceInName = $module.Params.replace_in_name +$copyOnly = $module.Params.copy_only +$type = $module.Params.type +$createFolder = $module.Params.create_folder +$fileCount = $module.Params.file_count +$compressBackup = $module.Params.compress_backup +$checkSum = $module.Params.checksum +$verify = $module.Params.verify +$maxTransferSize = $module.Params.max_transfer_size +$blockSize = $module.Params.block_size +$bufferCount = $module.Params.buffer_count +$noRecovery = $module.Params.no_recovery +$buildPath = $module.Params.build_path +$withFormat = $module.Params.with_format +$initialize = $module.Params.initialize +$timestampFormat = $module.Params.timestamp_format +$ignoreFileChecks = $module.Params.ignore_file_checks +$encryptionAlgorithm = $module.Params.encryption_algorithm +$encryptionCertificate = $modules.Params.encryption_certificate +$azureBaseUrl = $modules.Params.azure_base_url +$azureCredential = $modules.Param.azure_credential +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $backupSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + IncrementPrefix = $incrementPrefix + ReplaceInName = $replaceInName + CopyOnly = $copyOnly + Type = $type + CreateFolder = $createFolder + FileCount = $fileCount + CompressBackup = $compressBackup + Checksum = $checkSum + Verify = $verify + MaxTransferSize = $maxTransferSize + BufferCount = $bufferCount + NoRecovery = $noRecovery + BuildPath = $buildPath + WithFormat = $withFormat + Initialize = $initialize + IgnoreFileChecks = $ignoreFileChecks + } + if ($null -ne $path) { + $backupSplat.Add("Path", $path) + } + if ($null -ne $filePath) { + $backupSplat.Add("FilePath", $filePath) + } + if ($null -ne $blockSize) { + $backupSplat.Add("BlockSize", ($blockSize / 1)) + } + if ($null -ne $timestampFormat) { + $backupSplat.Add("TimestampFormat", $timestampFormat) + } + if ($null -ne $encryptionAlgorithm) { + $backupSplat.Add("EncryptionAlgorithm", $encryptionAlgorithm) + } + if ($null -ne $encryptionCertificate) { + $backupSplat.Add("EncryptionCertificate", $encryptionCertificate) + } + if ($null -ne $azureBaseUrl) { + $backupSplat.Add("AzureBaseURL", $azureBaseUrl) + } + if ($null -ne $azureCredential) { + $backupSplat.Add("AzureCredential", $azureCredential) + } + $output = Backup-DbaDatabase @backupSplat + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + $module.Result.changed = $true + } + $module.ExitJson() +} +catch { + $module.FailJson("Error backing up database: $($_.Exception.Message).", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/backup.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/backup.py new file mode 100644 index 00000000..962b0cfc --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/backup.py @@ -0,0 +1,197 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: backup +short_description: Performs a backup operation +description: + - Performs any type of database backup operation. +version_added: 0.8.0 +options: + database: + description: + - The database to process. + type: str + required: true + path: + description: + - Path in which to place the backup files. + - If not specified, the backups will be placed in the default backup location for SqlInstance. + type: str + required: false + file_path: + description: + - The name of the file to backup to. + - If no name is specified then the backup files will be named C(DatabaseName_yyyyMMddHHmm) (i.e. C(Database1_201714022131)) + type: str + required: false + increment_prefix: + description: + - If set, this will prefix backup files with an incrementing integer (ie; C(1-), C(2-)). + - Using this has been alleged to improved restore times on some Azure based SQL Database platforms. + type: bool + required: false + default: false + replace_in_name: + description: + - If set, the following list of strings will be replaced in the FilePath and Path strings. + C(instancename) - will be replaced with the instance Name + C(servername) - will be replaced with the server name + C(dbname) - will be replaced with the database name + C(timestamp) - will be replaced with the timestamp (either the default, or the format provided) + C(backuptype) - will be replaced with C(Full), C(Log), or C(Differential) as appropriate + type: bool + required: false + default: false + copy_only: + description: + - The backup will be CopyOnly. + type: bool + required: false + default: false + type: + description: + - The type of backup to perform. + type: str + required: false + default: 'database' + choices: ['full', 'log', 'differential', 'diff', 'database'] + create_folder: + description: + - If set, database is backed up to its own subfolder within the path. + type: bool + required: false + default: false + file_count: + description: + - The number of striped files to create the backup with. + type: int + required: false + default: 0 + compress: + description: + - If set, use compression when creating the backup if it is supported by the version and edition. + type: bool + required: false + default: false + checksum: + description: + - If set, the backup checksum will be calculated. + type: bool + required: false + default: false + verify: + description: + - If set, the backup will be verified via C(RESTORE VERIFYONLY) + type: bool + required: false + default: false + max_transfer_size: + description: + - Sets the size of the unit of transfer. Values must be a multiple of 64kb. + type: int + required: false + default: 0 + block_size: + description: + - Specifies block size to use. + type: str + required: false + choices: ['0.5kb','1kb','2kb','4kb','8kb','16kb','32kb','64kb'] + buffer_count: + description: + - Number of I/O buffers to use. + type: int + required: false + default: 0 + azure_base_url: + description: + - The URL to the base container of an Azure Storage account to write backups to. + type: str + required: false + azure_credential: + description: + - The name of the credential on the SQL instance that can write to the I(azure_base_url), + only needed if using Storage access keys If using SAS credentials, the command will look for a credential with a name matching the I(azure_base_url). + type: str + required: false + no_recovery: + description: + - If set, performs a tail log backup. + type: bool + required: false + default: false + build_path: + description: + - By default this command will not attempt to create missing paths, this switch will change the behaviour so that it will. + type: bool + required: false + default: false + with_format: + description: + - Formats the media as the first step of the backup operation. + type: bool + required: false + default: false + initialize: + description: + - Initializes the media as part of the backup operation. + type: bool + required: false + default: false + timestamp_format: + description: + - By default the command timestamps backups using the format C(yyyyMMddHHmm). Using this option this can be overridden. + type: str + required: false + ignore_file_checks: + description: + - If set, stops the function from checking path validity. + type: bool + required: false + default: false + encryption_algorithm: + description: + - Specifies the Encryption Algorithm to used. + type: str + required: false + choices: ['AES128','AES192','AES256','TRIPLEDES'] + encryption_certificate: + description: + - The name of the certificate to be used to encrypt the backups. + type: str + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Create striped full database backup in default dir + lowlydba.sqlserver.backup: + sql_instance: sql-01.myco.io + database: LowlyDB + type: full + file_count: 8 + +- name: Create t-log backup + lowlydba.sqlserver.backup: + sql_instance: sql-01.myco.io + database: LowlyDB + type: log +''' + +RETURN = r''' +data: + description: Modified output from the C(Backup-DbaDatabase) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/credential.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/credential.ps1 new file mode 100644 index 00000000..29631b11 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/credential.ps1 @@ -0,0 +1,117 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + identity = @{type = 'str'; required = $true } + name = @{type = 'str'; required = $false } + password = @{type = 'str'; required = $false; no_log = $true } + mapped_class_type = @{type = 'str'; required = $false; choices = @('CryptographicProvider', 'None') } + provider_name = @{type = 'str'; required = $false } + force = @{type = 'bool'; required = $false; default = $false } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$identity = $module.Params.identity +$name = $module.Params.name +if ($null -ne $module.Params.password) { + $secure_password = ConvertTo-SecureString -String $module.Params.password -AsPlainText -Force +} +$mapped_class_type = $module.Params.mapped_class_type +$provider_name = $module.Params.provider_name +$state = $module.Params.state +$force = $module.Params.force +$checkMode = $module.CheckMode + +$module.Result.changed = $false + +try { + $getCredendtialSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Identity = $identity + EnableException = $true + } + $existingCredential = Get-DbaCredential @getCredendtialSplat + + if ($state -eq "absent") { + # Remove credential if it exists + if ($null -ne $existingCredential) { + try { + $removeCredentialSplat = @{ + EnableException = $true + WhatIf = $checkMode + Confirm = $false + } + $output = $existingCredential | Remove-DbaCredential @removeCredentialSplat + $module.Result.changed = $true + } + catch { + $module.FailJson("Removing credential failed: $($_.Exception.Message)", $_) + } + } + } + elseif ($state -eq "present") { + # Create credential + if (($null -ne $existingCredential -and $force -eq $true) -or $null -eq $existingCredential) { + try { + $newCredentialSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Identity = $identity + EnableException = $true + WhatIf = $checkMode + Force = $force + Confirm = $false + } + if ($null -ne $name) { + $newCredentialSplat.Add("Name", $name) + } + if ($null -ne $secure_password) { + $newCredentialSplat.Add("SecurePassword", $secure_password) + } + if ($null -ne $mapped_class_type) { + $newCredentialSplat.Add("MappedClassType", $mapped_class_type) + } + if ($null -ne $provider_name) { + $newCredentialSplat.Add("ProviderName", $provider_name) + } + $output = New-DbaCredential @newCredentialSplat + $module.result.changed = $true + } + catch { + $module.FailJson("Creating credential failed: $($_.Exception.Message)", $_) + } + } + # Return existing credential if nothing is changed + elseif ($null -ne $existingCredential) { + $output = $existingCredential + } + } + try { + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() + } + catch { + $module.FailJson("Failure: $($_.Exception.Message)", $_) + } +} +catch { + $module.FailJson("Configuring credential failed: $($_.Exception.Message) ; $getCredendtialSplat", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/credential.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/credential.py new file mode 100644 index 00000000..e83b8ed3 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/credential.py @@ -0,0 +1,91 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: credential +short_description: Configures a credential on a SQL server +description: + - Creates, replaces, or removes a credential on a SQL server. +version_added: 1.3.0 +options: + identity: + description: + - The Credential Identity. + type: str + required: true + name: + description: + - The Credential name. + type: str + required: false + password: + description: + - Password used to authenticate the Credential Identity. + type: str + required: false + mapped_class_type: + description: + - Sets the class associated with the credential. + type: str + required: false + choices: ['CryptographicProvider','None'] + provider_name: + description: + - Specifies the cryptographic provider name for the Enterprise Key Management Provider. + type: str + required: false + force: + description: + - If this switch is enabled, the existing credential will be dropped and recreated. + type: bool + default: false +author: + - "Joe Krilov (@Joey40)" + - "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a credential with a password + lowlydba.sqlserver.credential: + sql_instance: sql-01.myco.io + identity: ad\\user + name: MyCredential + password : <Password> + +- name: Replace an existing credential + lowlydba.sqlserver.credential: + sql_instance: sql-01.myco.io + identity: MyIdentity + force: true + +- name: Create a credential using a SAS token for a backup URL + lowlydba.sqlserver.credential: + sql_instance: sql-01.myco.io + identity: SHARED ACCESS SIGNATURE + name: https://<azure storage account name>.blob.core.windows.net/<blob container> + password : <Shared Access Token> + +- name: Remove a credential + lowlydba.sqlserver.credential: + sql_instance: sql-01.myco.io + identity: MyIdentity + state: absent +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaDbCredential), C(Get-DbaDbCredential), or C(Remove-DbaDbCredential) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/database.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/database.ps1 new file mode 100644 index 00000000..4c3e3c9d --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/database.ps1 @@ -0,0 +1,242 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + database = @{type = 'str'; required = $true } + recovery_model = @{type = 'str'; required = $false; choices = @('Full', 'Simple', 'BulkLogged') } + data_file_path = @{type = 'str'; required = $false } + log_file_path = @{type = 'str'; required = $false } + owner = @{type = 'str'; required = $false; } + maxdop = @{type = 'int'; required = $false; } + secondary_maxdop = @{type = 'int'; required = $false; } + compatibility = @{type = 'str'; required = $false; } + rcsi = @{type = 'bool'; required = $false; } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$recoveryModel = $module.Params.recovery_model +$dataFilePath = $module.Params.data_file_path +$logFilePath = $module.Params.log_file_path +$owner = $module.Params.owner +$compatibility = $module.Params.compatibility +[nullable[bool]]$rcsiEnabled = $module.Params.rcsi +[nullable[int]]$maxDop = $module.Params.maxdop +[nullable[int]]$secondaryMaxDop = $module.Params.secondary_maxdop +$state = $module.Params.state +$checkMode = $module.CheckMode + +try { + # Get database status + try { + $server = Connect-DbaInstance -SqlInstance $sqlInstance -SqlCredential $sqlCredential + $getDatabaseSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + OnlyAccessible = $true + ExcludeSystem = $true + EnableException = $true + } + $existingDatabase = Get-DbaDatabase @getDatabaseSplat + $output = $existingDatabase + } + catch { + $module.FailJson("Error checking database status.", $_.Exception.Message) + } + + if ($state -eq "absent") { + if ($null -ne $existingDatabase) { + $existingDatabase | Remove-DbaDatabase -WhatIf:$checkMode -EnableException -Confirm:$false + $module.Result.changed = $true + } + $module.ExitJson() + } + elseif ($state -eq "present") { + # Create database + if ($null -eq $existingDatabase) { + try { + $newDbParams = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + WhatIf = $checkMode + EnableException = $true + } + if ($null -ne $dataFilePath) { + $newDbParams.Add("DataFilePath", $dataFilePath) + } + if ($null -ne $logFilePath) { + $newDbParams.Add("LogFilePath", $logFilePath) + } + if ($null -ne $owner) { + $newDbParams.Add("Owner", $owner) + } + $output = New-DbaDatabase @newDbParams + $module.Result.changed = $true + } + catch { + $module.FailJson("Creating database [$database] failed.", $_) + } + } + # Set Owner + elseif ($null -ne $owner) { + try { + if ($existingDatabase.Owner -ne $owner) { + $setDbParams = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + TargetLogin = $owner + WhatIf = $checkMode + EnableException = $true + } + $null = Set-DbaDbOwner @setDbParams + $output = Get-DbaDatabase @getDatabaseSplat + $module.Result.changed = $true + } + } + catch { + $module.FailJson("Setting database owner for [$database] failed.", $_) + } + } + + # Add non-standard fields to output + if ($null -ne $output) { + # Secondary MaxDop + [int]$existingSecondaryMaxDop = $server.Databases[$database].SecondaryMaxDop + $output | Add-Member -MemberType NoteProperty -Name "SecondaryMaxDop" -Value $existingSecondaryMaxDop + $output.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Add("SecondaryMaxDop") + + # MaxDop (exists, but is not in default display) + $existingMaxDop = $server.Databases[$database].MaxDop + $output.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Add("MaxDop") + + # RCSI + $existingRCSI = $server.Databases[$database].IsReadCommittedSnapshotOn + $output | Add-Member -MemberType NoteProperty -Name "RCSI" -Value $existingRCSI + $output.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Add("RCSI") + } + + # Recovery Model + if ($null -ne $recoveryModel) { + try { + if ($recoveryModel -ne $output.RecoveryModel) { + $recoveryModelSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + RecoveryModel = $recoveryModel + WhatIf = $checkMode + EnableException = $true + Confirm = $false + } + $null = Set-DbaDbRecoveryModel @recoveryModelSplat + $output.RecoveryModel = $recoveryModel + $module.Result.changed = $true + } + } + catch { + $module.FailJson("Setting recovery model for [$database] failed.", $_) + } + } + + # Compatibility Mode + if ($null -ne $compatibility) { + try { + $existingCompatibility = $output.Compatibility + if ($compatibility -ne $existingCompatibility) { + $compatSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + Compatibility = $compatibility + WhatIf = $checkMode + EnableException = $true + } + $null = Set-DbaDbCompatibility @compatSplat + $output.Compatibility = $compatibility + $module.Result.changed = $true + } + } + catch { + $module.FailJson("Setting Compatibility for [$database] failed.", $_) + } + } + + # RCSI + if ($null -ne $rcsiEnabled) { + try { + if ($rcsiEnabled -ne $existingRCSI) { + if (-not $checkMode) { + $server.Databases[$database].IsReadCommittedSnapshotOn = $rcsiEnabled + $server.Databases[$database].Alter() + $output.RCSI = $rcsiEnabled + } + $module.Result.changed = $true + } + } + catch { + $module.FailJson("Setting Read Commmitted Snapshot Isolation for [$database] failed.", $_) + } + } + + # Configure MAXDOPs + ## Database Scoped MaxDop + if ($null -ne $MaxDop) { + try { + if ($MaxDop -ne $existingMaxDop) { + if (-not $checkMode) { + $server.Databases[$database].MaxDop = $maxDop + $server.Databases[$database].Alter() + $output.MaxDop = $MaxDOP + } + $module.Result.changed = $true + } + } + catch { + $module.FailJson("Setting MAXDOP for [$database] failed.", $_) + } + } + + ## Secondary Mode MaxDop + if ($null -ne $secondaryMaxDOP) { + try { + if ($secondaryMaxDop -ne $existingSecondaryMaxDop) { + if (-not $checkMode) { + $server.Databases[$database].MaxDopForSecondary = $secondaryMaxDOP + $server.Databases[$database].Alter() + $output.SecondaryMaxDop = $secondaryMaxDop + } + $module.Result.changed = $true + } + } + catch { + $module.FailJson("Setting MaxDop for secondary mode failed.", $_) + } + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring database failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/database.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/database.py new file mode 100644 index 00000000..97551c71 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/database.py @@ -0,0 +1,94 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: database +short_description: Creates and configures a database +description: + - Adds a new database to an existing SQL Server instance. +version_added: 0.1.0 +options: + database: + description: + - Name of the target database. + type: str + required: true + recovery_model: + description: + - Choose the recovery model for the database. + type: str + required: false + choices: ['Full', 'Simple', 'BulkLogged'] + data_file_path: + description: + - Directory where the data files should be placed. Uses SQL Server's default if not supplied. + - Only used if database is being created. + type: str + required: false + log_file_path: + description: + - Directory where the log files should be placed. Uses SQL Server's default if not supplied. + - Only used if database is being created. + type: str + required: false + owner: + description: + - Database owner login. + type: str + required: false + maxdop: + description: + - MAXDOP value for the database. + required: false + type: int + secondary_maxdop: + description: + - MAXDOP value for the database when it is a non-primary replica in an availability group. + required: false + type: int + compatibility: + description: + - Compatibility mode for the database. Follows the format of C(Version90), C(Version100), and so on. + - String is validated by C(Set-DbaDbCompatibility). + required: false + type: str + rcsi: + description: + - Whether or not to enable Read Committed Snapshot Isolation. + required: false + type: bool +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create database + lowlydba.sqlserver.database: + sql_instance: sql-01.myco.io + database: LowlyDB + +- name: Create database with customizations + lowlydba.sqlserver.database: + sql_instance: sql-01.myco.io + database: LowlyDB + owner: sa + maxdop: 2 + recovery_model: Simple +''' + +RETURN = r''' +data: + description: Modified output from the C(New-DbaDatabase), C(Set-DbaDatabase), or C(Remove-DbaDatabase) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/dba_multitool.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/dba_multitool.ps1 new file mode 100644 index 00000000..ef52bdd6 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/dba_multitool.ps1 @@ -0,0 +1,65 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" +$spec = @{ + supports_check_mode = $true + options = @{ + branch = @{type = 'str'; required = $false; choices = @('master', 'development') } + local_file = @{type = 'str'; required = $false } + database = @{type = 'str'; required = $true } + force = @{type = 'bool'; required = $false; default = $false } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$branch = $module.Params.branch +$localFile = $module.Params.local_file +$force = $module.Params.force +$checkMode = $module.Checkmode +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +$multiToolSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + Force = $force +} +if ($null -ne $localFile) { + $multiToolSplat.LocalFile = $localFile +} +if ($null -ne $branch) { + $multiToolSplat.Branch = $branch +} + +try { + $output = Install-DbaMultiTool @multiToolSplat + $module.Result.changed = $true + + # output is an array for each stored proc, + # rollup output into a single result + $errorProcs = $output | Where-Object Status -eq "Error" + if ($errorProcs) { + $output = $errorProcs[0] | Select-Object -ExcludeProperty Name + } + else { + $output = $output[0] | Select-Object -ExcludeProperty Name + } + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Installing DBA-MultiTool failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/dba_multitool.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/dba_multitool.py new file mode 100644 index 00000000..6daeef3d --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/dba_multitool.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: dba_multitool +short_description: Install/update the DBA Multitool suite by John McCall +description: + - A wrapper for C(Install-DbaMultiTool) to fetch the latest version of the scripts, or install from a local cached version. +version_added: 0.7.0 +options: + sql_instance: + description: + - The target SQL Server instance or instances. Server version must be SQL Server version 2005 or higher. + type: str + required: true + local_file: + description: + - Specifies the path to a local file to install DBA MultiTool from. This should be the zip file as distributed by the maintainers. + - If this option is not specified, the latest version will be downloaded and installed from https://github.com/LowlyDBA/dba-multitool/. + type: str + required: false + branch: + description: + - Specifies an alternate branch of the DBA MultiTool to install. + type: str + required: false + choices: ['master', 'development'] + database: + description: + - Name of the target database. + type: str + required: true + force: + description: + - If this switch is enabled, the DBA MultiTool will be downloaded from the internet even if previously cached. + type: bool + default: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Install DBA MultiTool + lowlydba.sqlserver.dba_multitool: + sql_instance: test-server.my.company.com + database: dba_tools +''' + +RETURN = r''' +data: + description: Modified output from the C(Install-DbaMultitool) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/first_responder_kit.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/first_responder_kit.ps1 new file mode 100644 index 00000000..1e19ce62 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/first_responder_kit.ps1 @@ -0,0 +1,80 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" +$spec = @{ + supports_check_mode = $true + options = @{ + branch = @{type = 'str'; required = $false; choices = @('main', 'dev') } + local_file = @{type = 'str'; required = $false } + only_script = @{type = 'str'; required = $false; default = 'Install-All-Scripts.sql'; choices = @('Install-All-Scripts.sql' + 'Install-Core-Blitz-No-Query-Store.sql' + 'Install-Core-Blitz-With-Query-Store.sql' + 'sp_Blitz.sql' + 'sp_BlitzFirst.sql' + 'sp_BlitzIndex.sql' + 'sp_BlitzCache.sql' + 'sp_BlitzWho.sql' + 'sp_BlitzQueryStore.sql' + 'sp_BlitzAnalysis.sql' + 'sp_BlitzBackups.sql' + 'sp_BlitzInMemoryOLTP.sql' + 'sp_BlitzLock.sql' + 'sp_AllNightLog.sql' + 'sp_AllNightLog_Setup.sql' + 'sp_DatabaseRestore.sql' + 'sp_ineachdb.sql' + 'SqlServerVersions.sql' + 'Uninstall.sql') + } + database = @{type = 'str'; required = $true } + force = @{type = 'bool'; required = $false; default = $false } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$branch = $module.Params.branch +$onlyScript = $module.Params.only_script +$localFile = $module.Params.local_file +$force = $module.Params.force +$checkMode = $module.Checkmode +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +$firstResponderKitSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + Force = $force +} +if ($localFile) { + $firstResponderKitSplat.Add("LocalFile", $localFile) +} +if ($branch) { + $firstResponderKitSplat.Add("Branch", $branch) +} +if ($onlyScript) { + $firstResponderKitSplat.Add("OnlyScript", $onlyScript) +} + +try { + $output = Install-DbaFirstResponderKit @firstResponderKitSplat + $module.Result.changed = $true + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Installing First Responder Kit failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/first_responder_kit.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/first_responder_kit.py new file mode 100644 index 00000000..fba6fad9 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/first_responder_kit.py @@ -0,0 +1,84 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: first_responder_kit +short_description: Install/update the First Responder Kit scripts +description: + - A wrapper for C(Install-DbaFirstResponderKit) to fetch the latest version of the scripts, or install from a local cached version. +version_added: 0.10.0 +options: + local_file: + description: + - Specifies the path to a local file to install FRK from. This should be the zip file as distributed by the maintainers. + - If this option is not specified, the latest version will be downloaded and installed Github. + type: str + required: false + only_script: + description: + - Specifies the name(s) of the script(s) to run for installation. Wildcards are permitted. + - This way only part of the First Responder Kit can be installed. + type: str + required: false + default: 'Install-All-Scripts.sql' + choices: ['Install-All-Scripts.sql', + 'Install-Core-Blitz-No-Query-Store.sql', + 'Install-Core-Blitz-With-Query-Store.sql', + 'sp_Blitz.sql', + 'sp_BlitzFirst.sql', + 'sp_BlitzIndex.sql', + 'sp_BlitzCache.sql', + 'sp_BlitzWho.sql', + 'sp_BlitzQueryStore.sql', + 'sp_BlitzAnalysis.sql', + 'sp_BlitzBackups.sql', + 'sp_BlitzInMemoryOLTP.sql', + 'sp_BlitzLock.sql', + 'sp_AllNightLog.sql', + 'sp_AllNightLog_Setup.sql', + 'sp_DatabaseRestore.sql', + 'sp_ineachdb.sql', + 'SqlServerVersions.sql', + 'Uninstall.sql'] + branch: + description: + - Specifies an alternate branch of the First Responder Kit to install. + type: str + required: false + choices: ['main', 'dev'] + database: + description: + - Name of the target database. + type: str + required: true + force: + description: + - If this switch is enabled, the FRK will be downloaded from the internet even if previously cached. + type: bool + default: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Install FRK + lowlydba.sqlserver.first_responder_kit: + sql_instance: test-server.my.company.com + database: dba_tools +''' + +RETURN = r''' +data: + description: Modified output from the C(Install-DbaFirstResponderKit) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/hadr.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/hadr.ps1 new file mode 100644 index 00000000..473ad480 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/hadr.ps1 @@ -0,0 +1,71 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + username = @{type = 'str'; required = $false } + password = @{type = 'str'; required = $false; no_log = $true } + enabled = @{type = 'bool'; required = $false; default = $true } + force = @{type = 'bool'; required = $false; default = $false } + } + required_together = @( + , @('username', 'password') + ) +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +if ($null -ne $Module.Params.username) { + [securestring]$secPassword = ConvertTo-SecureString $Module.Params.password -AsPlainText -Force + [pscredential]$credential = New-Object System.Management.Automation.PSCredential ($Module.Params.username, $secPassword) +} +$enabled = $module.Params.enabled +$force = $module.Params.force +$checkMode = $module.CheckMode +$module.Result.changed = $false + +try { + $server = Connect-DbaInstance -SqlInstance $sqlInstance -SqlCredential $sqlCredential + $existingHadr = $server | Get-DbaAgHadr -EnableException + $output = $existingHadr + if ($existingHadr.IsHadrEnabled -ne $enabled) { + $setHadr = @{ + Credential = $credential + WhatIf = $checkMode + Force = $force + Confirm = $false + EnableException = $true + } + if ($enabled -eq $false) { + $output = $server | Disable-DbaAgHadr @setHadr + } + else { + $output = $server | Enable-DbaAgHadr @setHadr + } + + if ($force -ne $true) { + $output | Add-Member -MemberType NoteProperty -Name "RestartRequired" -Value $true + } + $module.Result.changed = $true + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error configuring Hadr.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/hadr.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/hadr.py new file mode 100644 index 00000000..0fc91d22 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/hadr.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: hadr +short_description: Enable or disable HADR +description: + - Enable or disable the High Availability Disaster Recovery (HADR) feature. +version_added: 0.4.0 +options: + sql_instance: + description: + - The SQL Server instance to modify. + type: str + required: true + username: + description: + - Username for alternative credential to authenticate with Windows. + type: str + required: false + password: + description: + - Password for alternative credential to authenticate with Windows. + type: str + required: false + enabled: + description: + - Flag to enable or disable the feature. + type: bool + required: false + default: true + force: + description: + - Restart SQL Server and SQL Agent services automatically. + type: bool + required: false + default: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_win +''' + +EXAMPLES = r''' +- name: Enable hadr with service restart + lowlydba.sqlserver.hadr: + sql_instance: sql-01.myco.io + enabled: true + force: true +''' + +RETURN = r''' +data: + description: + - Output from the C(Enable-DbaAgHadr) or C(Disable-DbaAgHadr) function. + - RestartRequired returned if the setting requires a service restart to take effect. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/install_script.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/install_script.ps1 new file mode 100644 index 00000000..5c26588e --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/install_script.ps1 @@ -0,0 +1,86 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } +#Requires -Modules @{ ModuleName="dbops"; ModuleVersion="0.8.0" } + +$spec = @{ + supports_check_mode = $true + options = @{ + database = @{type = 'str'; required = $true } + path = @{type = 'str'; required = $true } + deployment_method = @{type = 'str'; required = $false; default = 'NoTransaction' + choices = @('NoTransaction', 'SingleTransaction', 'TransactionPerScript') + } + schema_version_table = @{type = 'str'; required = $false } + no_log_version = @{type = 'bool'; required = $false; default = $false } + connection_timeout = @{type = 'int'; required = $false; default = 30 } + execution_timeout = @{type = 'int'; required = $false; default = 0 } + output_file = @{type = 'str'; required = $false } + create_database = @{type = 'bool'; required = $false; default = $false } + no_recurse = @{type = 'bool'; required = $false; default = $false } + match = @{type = 'str'; required = $false } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$schemaVersionTable = $module.Params.schema_version_table +$path = $module.Params.path +$outputFile = $module.Params.output_file +$match = $module.Params.match +$connectionTimeout = $module.Params.connection_timeout +$executionTimeout = $module.Params.execution_timeout +$createDatabase = $module.Params.create_database +$noRecurse = $module.Params.no_recurse +$noLogVersion = $module.Params.no_log_version +$checkMode = $module.Checkmode +$PSDefaultParameterValues = @{ "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $installSplat = @{ + SqlInstance = $sqlInstance + Credential = $sqlCredential + Database = $database + Path = $path + ConnectionTimeout = $connectionTimeout + ExecutionTimeout = $executionTimeout + CreateDatabase = $createDatabase + NoRecurse = $noRecurse + Silent = $true + Type = "SqlServer" + } + if ($schemaVersionTable) { + $installSplat.Add("SchemaVersionTable", $schemaVersionTable) + } + if ($outputFile) { + $installSplat.Add("OutputFile", $outputFile) + } + if ($match) { + $installSplat.Add("Match", $match) + } + if ($noLogVersion) { + $installSplat.SchemaVersionTable = $null + } + + $output = Install-DboScript @installSplat + if ($output.DeploymentLog[-1] -ne "No new scripts need to be executed - completing.") { + $module.Result.changed = $true + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + } + + $module.ExitJson() +} +catch { + $module.FailJson("Failed migration script(s) execution: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/install_script.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/install_script.py new file mode 100644 index 00000000..4dbf78f4 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/install_script.py @@ -0,0 +1,104 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: install_script +short_description: Runs migration scripts against a database +description: + - Uses the module C(DBOps) to run C(Dbo-InstallScript) against a target SQL Server database. +version_added: 0.11.0 +options: + database: + description: + - Name of the target database. + required: true + type: str + path: + description: + - Directory where targeted sql scripts are stored. + type: str + required: true + schema_version_table: + description: + - A table that will hold the history of script execution. This table is used to choose what scripts are going to be + run during the deployment, preventing the scripts from being execured twice. + type: str + required: false + deployment_method: + description: + - C(SingleTransaction) - wrap all the deployment scripts into a single transaction and rollback whole deployment on error. + - C(TransactionPerScript) - wrap each script into a separate transaction; rollback single script deployment in case of error. + - C(NoTransaction) - deploy as is. + type: str + required: false + default: 'NoTransaction' + choices: ['NoTransaction', 'SingleTransaction', 'TransactionPerScript'] + no_log_version: + description: + - If set, the deployment will not be tracked in the database. That will also mean that all the scripts + and all the builds from the package are going to be deployed regardless of any previous deployment history. + type: bool + default: false + required: false + connection_timeout: + description: + - Database server connection timeout in seconds. Only affects connection attempts. Does not affect execution timeout. + type: int + default: 30 + required: false + execution_timeout: + description: + - Script execution timeout. The script will be aborted if the execution takes more than specified number of seconds. + type: int + default: 0 + required: false + output_file: + description: + - Log output into specified file. + type: str + required: false + create_database: + description: + - Will create an empty database if missing. + type: bool + default: false + required: false + no_recurse: + description: + - Only process the first level of the target path. + type: bool + required: false + default: false + match: + description: + - Runs a regex verification against provided file names using the provided string. + type: str + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module + - L(dbops,https://github.com/dataplat/dbops) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Migrate a database + lowlydba.sqlserver.install_script: + sql_instance: test-server.my.company.com + database: AdventureWorks + path: migrations +''' + +RETURN = r''' +data: + description: Modified output from the C(Install-DboScript) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/instance_info.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/instance_info.ps1 new file mode 100644 index 00000000..a99ea01d --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/instance_info.ps1 @@ -0,0 +1,57 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{} +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$module.Result.changed = $false + +# Fetch instance information +try { + $getSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + EnableException = $true + } + $output = Get-DbaConnection @getSplat | Select-Object -Property "ComputerName", "SqlInstance", "InstanceName" -First 1 + + if ($null -ne $output) { + # Add additional fields + $extraProperties = @( + "BuildNumber" + "Language" + "VersionMajor" + "VersionMinor" + "VersionString" + "Collation" + "ProductLevel" + "IsClustered" + "LoginMode" + ) + foreach ($prop in $extraProperties) { + $value = (Get-DbaInstanceProperty @getSplat -InstanceProperty $prop).Value + $output | Add-Member -MemberType NoteProperty -Name $prop -Value $value + } + + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error fetching instance information: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/instance_info.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/instance_info.py new file mode 100644 index 00000000..2bfdbf15 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/instance_info.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: instance_info +short_description: Returns basic information for a SQL Server instance +description: + - Returns basic information for a SQL Server instance. +version_added: 0.2.0 +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode_read_only + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Get basic info for an instance + lowlydba.sqlserver.instance_info: + sql_instance: sql-01.myco.io +''' + +RETURN = r''' +data: + description: Instance level properties of the SQL Server instance. + returned: always + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/login.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/login.ps1 new file mode 100644 index 00000000..d509ba79 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/login.ps1 @@ -0,0 +1,144 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + login = @{type = 'str'; required = $true } + password = @{type = 'str'; required = $false; no_log = $true } + enabled = @{type = 'bool'; required = $false; default = $true } + default_database = @{type = 'str'; required = $false } + language = @{type = 'str'; required = $false } + password_must_change = @{type = 'bool'; required = $false } + password_policy_enforced = @{type = 'bool'; required = $false } + password_expiration_enabled = @{type = 'bool'; required = $false } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$login = $module.Params.login +if ($null -ne $module.Params.password) { + $secPassword = ConvertTo-SecureString -String $module.Params.password -AsPlainText -Force +} +$enabled = $module.Params.enabled +$defaultDatabase = $module.Params.default_database +$language = $module.Params.language +[nullable[bool]]$passwordMustChange = $module.Params.password_must_change +[nullable[bool]]$passwordExpirationEnabled = $module.Params.password_expiration_enabled +[nullable[bool]]$passwordPolicyEnforced = $module.Params.password_policy_enforced +$state = $module.Params.state +$checkMode = $module.CheckMode + +$module.Result.changed = $false + +try { + $getLoginSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Login = $login + ExcludeSystemLogin = $true + EnableException = $true + } + $existingLogin = Get-DbaLogin @getLoginSplat + + if ($state -eq "absent") { + if ($null -ne $existingLogin) { + $output = $existingLogin | Remove-DbaLogin -WhatIf:$checkMode -EnableException -Force -Confirm:$false + $module.Result.changed = $true + } + } + elseif ($state -eq "present") { + $setLoginSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Login = $login + WhatIf = $checkMode + EnableException = $true + Confirm = $false + } + if ($null -ne $defaultDatabase) { + $setLoginSplat.add("DefaultDatabase", $defaultDatabase) + } + if ($null -ne $passwordExpirationEnabled) { + if ($existingLogin.PasswordExpirationEnabled -ne $passwordExpirationEnabled) { + $changed = $true + } + if ($passwordExpirationEnabled -eq $true) { + $setLoginSplat.add("PasswordExpirationEnabled", $true) + } + } + if ($null -ne $passwordPolicyEnforced) { + if ($existingLogin.PasswordPolicyEnforced -ne $passwordPolicyEnforced) { + $changed = $true + } + if ($passwordPolicyEnforced -eq $true) { + $setLoginSplat.add("PasswordPolicyEnforced", $true) + } + } + if ($true -eq $passwordMustChange) { + if ($existingLogin.PasswordMustChange -ne $passwordMustChange) { + $changed = $true + } + if ($passwordMustChange -eq $true) { + $setLoginSplat.add("PasswordMustChange", $true) + } + } + if ($null -ne $secPassword) { + $setLoginSplat.add("SecurePassword", $secPassword) + } + + # Login already exists + if ($null -ne $existingLogin) { + if ($enabled -eq $false) { + $disabled = $true + $setLoginSplat.add("Disable", $true) + } + else { + $disabled = $false + $setLoginSplat.add("Enable", $true) + } + # Login needs to be modified + if (($changed -eq $true) -or ($disabled -ne $existingLogin.IsDisabled) -or ($secPassword)) { + $output = Set-DbaLogin @setLoginSplat + $module.result.changed = $true + } + } + # New login + else { + if ($null -ne $language) { + $setLoginSplat.add("Language", $language) + } + if ($enabled -eq $false) { + $setLoginSplat.add("Disabled", $true) + } + $output = New-DbaLogin @setLoginSplat + $module.result.changed = $true + } + # If not in check mode, add extra fields we can change to default display set + if ($null -ne $output) { + $output.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Add("DefaultDatabase") + $output.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Add("Language") + } + + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring login failed: $($_.Exception.Message) ; $setLoginSplat", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/login.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/login.py new file mode 100644 index 00000000..a570d8cf --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/login.py @@ -0,0 +1,89 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: login +short_description: Configures a login for the target SQL Server instance +description: + - Creates, modifies, or removes a Windows or SQL Authentication login on a SQL Server instance. +version_added: 0.1.0 +options: + login: + description: + - Name of the login to configure. + type: str + required: true + password: + description: + - Password for the login, if SQL Authentication login. + type: str + required: false + enabled: + description: + - Whether the login is enabled or disabled. + type: bool + required: false + default: true + version_added: '0.4.0' + default_database: + description: + - Default database for the login. + type: str + required: false + language: + description: + - Default language for the login. Only used when creating a new login, not when modifying an existing one. + type: str + required: false + password_must_change: + description: + - Enforces user must change password at next login. + - When specified will enforce I(password_expiration_enabled) and I(password_policy_enforced) as they are required. + type: bool + required: false + password_policy_enforced: + description: + - Enforces password complexity policy. + type: bool + required: false + password_expiration_enabled: + description: + - Enforces password expiration policy. Requires I(password_policy_enforced=true). + type: bool + required: false +author: "John McCall (@lowlydba)" +notes: + - Module will always return changed if a password is supplied. +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a login + lowlydba.sqlserver.login: + sql_instance: sql-01.myco.io + login: TheIntern + password: ReallyComplexStuff12345! + +- name: Disable a login + lowlydba.sqlserver.login: + sql_instance: sql-01.myco.io + login: TheIntern + enabled: false +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaLogin), C(Set-DbaLogin), or C(Remove-DbaLogin) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/maintenance_solution.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/maintenance_solution.ps1 new file mode 100644 index 00000000..48f9c90a --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/maintenance_solution.ps1 @@ -0,0 +1,105 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + backup_location = @{type = 'str'; required = $false } + cleanup_time = @{type = 'int'; required = $false; default = 0 } + output_file_dir = @{type = 'str'; required = $false } + replace_existing = @{type = 'bool'; required = $false; } + log_to_table = @{type = 'bool'; required = $false; default = $false } + solution = @{type = 'str'; required = $false; choices = @('All', 'Backup', 'IntegrityCheck', 'IndexOptimize'); default = 'All' } + install_jobs = @{type = 'bool'; required = $false; default = $false } + local_file = @{type = 'str'; required = $false } + database = @{type = 'str'; required = $true } + force = @{type = 'bool'; required = $false; default = $false } + install_parallel = @{type = 'bool'; required = $false; default = $false } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$backupLocation = $module.Params.backup_location +$outputFileDirectory = $module.Params.output_file_dir +$cleanupTime = $module.Params.cleanup_time +$replaceExisting = $module.Params.replace_existing +$solution = $module.Params.solution +$installJobs = $module.Params.install_jobs +$installParallel = $module.Params.install_parallel +$logToTable = $module.Params.log_to_table +$localFile = $module.Params.local_file +$force = $module.Params.force +$checkMode = $module.CheckMode +$module.Result.changed = $false + +try { + $maintenanceSolutionSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + LogToTable = $logToTable + Solution = $solution + InstallJobs = $installJobs + InstallParallel = $installParallel + WhatIf = $checkMode + Force = $force + Confirm = $false + EnableException = $true + } + if ($null -ne $localFile) { + $maintenanceSolutionSplat.LocalFile = $localFile + } + if ($null -ne $backupLocation) { + $maintenanceSolutionSplat.BackupLocation = $backupLocation + } + if ($null -ne $outputFileDirectory) { + $maintenanceSolutionSplat.OutputFileDirectory = $outputFileDirectory + } + if ($installJobs -eq $true -and $null -ne $cleanupTime) { + $maintenanceSolutionSplat.CleanupTime = $cleanupTime + } + # Only pass if true, otherwise removes warning that is used to track changed=$false + if ($replaceExisting -eq $true) { + $maintenanceSolutionSplat.ReplaceExisting = $replaceExisting + } + + try { + $output = Install-DbaMaintenanceSolution @maintenanceSolutionSplat + $module.Result.changed = $true + } + catch { + $errMessage = $_.Exception.Message + if ($errMessage -like "*Maintenance Solution already exists*") { + $server = Connect-DbaInstance -SqlInstance $sqlInstance -SqlCredential $sqlCredential + $output = [PSCustomObject]@{ + ComputerName = $server.ComputerName + InstanceName = $server.ServiceName + SqlInstance = $server.DomainInstanceName + Results = "Success" + } + } + else { + Write-Error -Message $errMessage + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Installing Maintenance Solution failed.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/maintenance_solution.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/maintenance_solution.py new file mode 100644 index 00000000..1df314ff --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/maintenance_solution.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: maintenance_solution +short_description: Install/update Maintenance Solution by Ola Hallengren +description: + - A wrapper for C(Install-DbaMaintenanceSolution) to fetch the latest version of the Ola Hallengren's Maintenance Solution, + or install from a local cached version. +version_added: 0.1.0 +options: + backup_location: + description: + - Location of the backup root directory. If this is not supplied, the default backup directory will be used. + type: str + required: false + cleanup_time: + description: + - Time in hours, after which backup files are deleted. + type: int + required: false + default: 0 + output_file_dir: + description: + - Specify the output file directory where the Maintenance Solution will write to. + type: str + required: false + replace_existing: + description: + - If this switch is enabled, objects already present in the target database will be dropped and recreated. + type: bool + required: false + log_to_table: + description: + - If this switch is enabled, the Maintenance Solution will be configured to log commands to a table. + type: bool + required: false + default: false + solution: + description: + - Specifies which portion of the Maintenance Solution to install. + type: str + required: false + default: 'All' + choices: ['All', 'Backup', 'IntegrityCheck', 'IndexOptimize'] + install_jobs: + description: + - If this switch is enabled, the corresponding SQL Agent Jobs will be created. + type: bool + required: false + default: false + install_parallel: + description: + - If this switch is enabled, the C(Queue) and C(QueueDatabase) tables are created, for use when C(@DatabasesInParallel = 'Y') are set in the jobs. + type: bool + required: false + default: false + local_file: + description: + - Specifies the path to a local file to install Ola's solution from. This should be the zip file as distributed by the maintainers. + - If this option is not specified, the latest version will be downloaded and installed + from the L(Maintenance Solution Github,https://github.com/olahallengren/sql-server-maintenance-solution). + type: str + required: false + database: + description: + - Name of the target database. + type: str + required: true + force: + description: + - If this switch is enabled, the Maintenance Solution will be downloaded from the internet even if previously cached. + type: bool + default: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Install/Update Maintenance Solution + lowlydba.sqlserver.multitool: + sql_instance: sql-01.myco.io + database: main + replace_existing: true +''' + +RETURN = r''' +data: + description: Output from the C(Install-MaintenanceSolution) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/memory.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/memory.ps1 new file mode 100644 index 00000000..1b6d2bed --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/memory.ps1 @@ -0,0 +1,50 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + max = @{type = "int"; required = $false; default = 0 } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$max = $module.Params.max +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +# Set max memory for SQL Instance +try { + # Set max memory + $setMemorySplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Max = $max + } + $output = Set-DbaMaxMemory @setMemorySplat + + if ($output.PreviousMaxValue -ne $output.MaxValue -or $checkMode) { + $module.Result.changed = $true + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Error setting max memory.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/memory.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/memory.py new file mode 100644 index 00000000..65b6b327 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/memory.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: memory +short_description: Sets the maximum memory for a SQL Server instance +description: + - Sets the maximum memory for a SQL Server instance. +version_added: 0.1.0 +options: + max: + description: + - The maximum memory in MB that the SQL Server instance can utilize. C(0) will automatically calculate the ideal value. + type: int + required: false + default: 0 +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Automatically configure SQL max memory + lowlydba.sqlserver.memory: + sql_instance: sql-01.myco.io + +- name: Manually configure SQL max memory + lowlydba.sqlserver.memory: + sql_instance: sql-01.myco.io + max: 10240 +''' + +RETURN = r''' +data: + description: Output from the C(Set-DbaMaxMemory) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/nonquery.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/nonquery.ps1 new file mode 100644 index 00000000..fdc34d17 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/nonquery.ps1 @@ -0,0 +1,51 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + database = @{type = 'str'; required = $true } + nonquery = @{type = 'str'; required = $true } + query_timeout = @{type = 'int'; required = $false; default = 60 } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$nonquery = $module.Params.nonquery +$queryTimeout = $module.Params.query_timeout +$checkMode = $module.CheckMode + +$module.Result.changed = $false + +try { + $invokeQuerySplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + Query = $nonquery + QueryTimeout = $queryTimeout + EnableException = $true + } + if ($checkMode) { + $invokeQuerySplat.Add("NoExec", $true) + } + $null = Invoke-DbaQuery @invokeQuerySplat + + $module.Result.changed = $true + $module.ExitJson() +} +catch { + $module.FailJson("Executing nonquery failed.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/nonquery.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/nonquery.py new file mode 100644 index 00000000..9f56c7bc --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/nonquery.py @@ -0,0 +1,48 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: nonquery +short_description: Executes a generic nonquery +description: + - Execute a nonquery against a database. Does not return a resultset. Ideal for ad-hoc configurations or DML queries. +version_added: 0.1.0 +options: + database: + description: + - Name of the database to execute the nonquery in. + type: str + required: true + nonquery: + description: + - The nonquery to be executed. + type: str + required: true + query_timeout: + description: + - Number of seconds to wait before timing out the nonquery execution. + type: int + required: false + default: 60 +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Update a table value + lowlydba.sqlserver.nonquery: + sql_instance: sql-01-myco.io + database: userdb + nonquery: "UPDATE dbo.User set IsActive = 1;" +''' + +RETURN = r''' # ''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/resource_governor.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/resource_governor.ps1 new file mode 100644 index 00000000..aa0ade5e --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/resource_governor.ps1 @@ -0,0 +1,69 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + enabled = @{type = 'bool'; required = $false; default = $true } + classifier_function = @{type = 'str'; required = $false } + } +} + +# Get Csharp utility module +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) + +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$enabled = $module.Params.enabled +$classifierFunction = $module.Params.classifier_function +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $rg = Get-DbaResourceGovernor -SqlInstance $sqlInstance -SqlCredential $sqlCredential + $rgClassifierFunction = $rg.ClassifierFunction + + if ($rg.Enabled -ne $enabled) { + $change = $true + } + if ($classifierFunction -ne "NULL" -and $rgClassifierFunction -ne $classifierFunction) { + $change = $true + } + if ($classifierFunction -eq "NULL" -and $null -ne $rgClassifierFunction) { + $change = $true + } + + if ($change) { + $rgSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + ClassifierFunction = $classifierFunction + } + if ($enabled) { + $rgSplat.Add("Enabled", $true) + } + else { + $rgSplat.Add("Disabled", $true) + } + $output = Set-DbaResourceGovernor @rgSplat + $module.Result.changed = $true + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Setting resource governor failed.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/resource_governor.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/resource_governor.py new file mode 100644 index 00000000..66b789d2 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/resource_governor.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: resource_governor +short_description: Configures the resource governor on a SQL Server instance +description: + - Enables or disables and optionally sets the classifier function for the resource governor feature. +version_added: 0.1.0 +options: + enabled: + description: + - Whether to enable or disable resource governor. + type: bool + required: false + default: true + classifier_function: + description: + - The name of the classifier function that resource governor will use. To clear the function, use the string C(NULL). + type: str + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Enable resource governor + lowlydba.sqlserver.resource_governor: + sql_instance: sql-01.myco.io + enabled: true +''' + +RETURN = r''' +data: + description: Output from the C(Set-DbaResourceGovernor) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/restore.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/restore.ps1 new file mode 100644 index 00000000..73c9690f --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/restore.ps1 @@ -0,0 +1,170 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + database = @{type = 'str'; required = $false } + path = @{type = 'str'; required = $true } + destination_data_directory = @{type = 'str'; required = $false } + destination_log_directory = @{type = 'str'; required = $false } + destination_filestream_directory = @{type = 'str'; required = $false } + restore_time = @{type = 'str'; required = $false } + with_replace = @{type = 'bool'; required = $false; default = $false } + keep_replication = @{type = 'bool'; required = $false; default = $false } + xp_dirtree = @{type = 'bool'; required = $false } + no_xp_dir_recurse = @{type = 'bool'; required = $false; default = $false } + verify_only = @{type = 'bool'; required = $false; default = $false } + maintenance_solution_backup = @{type = 'bool'; required = $false; default = $false } + ignore_log_backup = @{type = 'bool'; required = $false; default = $false } + ignore_diff_backup = @{type = 'bool'; required = $false; default = $false } + use_destination_default_directories = @{type = 'bool'; required = $false } + reuse_source_folder_structure = @{type = 'bool'; required = $false } + destination_file_prefix = @{type = 'str'; required = $false } + restored_database_name_prefix = @{type = 'str'; required = $false } + directory_recurse = @{type = 'bool'; required = $false; default = $false } + standby_directory = @{type = 'str'; required = $false } + replace_db_name_in_file = @{type = 'bool'; required = $false } + destination_file_suffix = @{type = 'str'; required = $false } + keep_cdc = @{type = 'bool'; required = $false } + stop_before = @{type = 'bool'; required = $false; default = $false } + stop_mark = @{type = 'str'; required = $false } + stop_after_date = @{type = 'str'; required = $false } + no_recovery = @{type = 'bool'; required = $false; default = $false } + max_transfer_size = @{type = 'int'; required = $false; default = 0 } + block_size = @{type = 'str'; required = $false; choices = @('0.5kb', '1kb', '2kb', '4kb', '8kb', '16kb', '32kb', '64kb') } + buffer_count = @{type = 'int'; required = $false; default = 0 } + azure_credential = @{type = 'str'; required = $false } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$path = $module.Params.path +$destinationDataDirectory = $module.Params.destination_data_directory +$destinationLogDirectory = $module.Params.destination_log_directory +$destinationFilestreamDirectory = $module.Params.destination_filestream_directory +$restoreTime = $module.Params.restore_time +$withReplace = $module.Params.with_replace +$keepReplication = $module.Params.keep_replication +$xpDirTree = $module.Params.xp_dirtree +$noXpDirRecurse = $module.Params.no_xp_dir_recurse +$verifyOnly = $module.Params.verify_only +$maintenanceSolutionBackup = $module.Params.maintenance_solution_backup +$ignoreLogBackup = $module.Params.ignore_log_backup +$ignoreDiffBackup = $module.Params.ignore_diff_backup +$useDestinationDefaultDirectories = $module.Params.use_destination_default_directories +$reuseSourceFolderStructure = $module.Params.reuse_source_folder_structure +$destinationFilePrefix = $module.Params.destination_file_prefix +$restoredDatabaseNamePrefix = $module.Params.restored_database_name_prefix +$directoryRecurse = $module.Params.directory_recurse +$standbyDirectory = $module.Params.standby_directory +$replaceDbNameInFile = $module.Params.replace_db_name_in_file +$destinationFileSuffix = $module.Params.destination_file_suffix +$keepCDC = $module.Params.keep_cdc +$stopBefore = $module.Params.stop_before +$stopMark = $module.Params.stop_mark +$stopAfterDate = $module.Params.stop_after_date +$noRecovery = $module.Params.no_recovery +$maxTransferSize = $module.Params.max_transfer_size +$blockSize = $module.Params.block_size +$bufferCount = $module.Params.buffer_count +$azureCredential = $modules.Param.azure_credential +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $restoreSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Path = $path + WithReplace = $withReplace + KeepReplication = $keepReplication + NoXpDirRecurse = $noXpDirRecurse + VerifyOnly = $verifyOnly + MaintenanceSolutionBackup = $maintenanceSolutionBackup + IgnoreLogBackup = $ignoreLogBackup + IgnoreDiffBackup = $ignoreDiffBackup + DirectoryRecurse = $directoryRecurse + StopBefore = $stopBefore + NoRecovery = $noRecovery + MaxTransferSize = $maxTransferSize + BufferCount = $bufferCount + } + if ($null -ne $database) { + $restoreSplat.Add("DatabaseName", $database) + } + if ($null -ne $destinationDataDirectory) { + $restoreSplat.Add("DestinationDataDirectory", $destinationDataDirectory) + } + if ($null -ne $destinationLogDirectory) { + $restoreSplat.Add("DestinationLogDirectory", $destinationLogDirectory) + } + if ($null -ne $destinationFilestreamDirectory) { + $restoreSplat.Add("DestinationFilestreamDirectory", $destinationFilestreamDirectory) + } + if ($null -ne $restoreTime) { + $restoreSplat.Add("RestoreTime", $restoreTime) + } + if ($null -ne $destinationFilePrefix) { + $restoreSplat.Add("DestinationFilePrefix", $destinationFilePrefix) + } + if ($null -ne $restoredDatabaseNamePrefix) { + $restoreSplat.Add("RestoredDatabaseNamePrefix", $restoredDatabaseNamePrefix) + } + if ($null -ne $standbyDirectory) { + $restoreSplat.Add("StandbyDirectory", $standbyDirectory) + } + if ($null -ne $destinationFileSuffix) { + $restoreSplat.Add("DestinationFileSuffix", $destinationFileSuffix) + } + if ($null -ne $stopAfterDate) { + $restoreSplat.Add("StopAfterDate", $stopAfterDate) + } + if ($null -ne $stopMark) { + $restoreSplat.Add("StopMark", $stopMark) + } + if ($null -ne $blockSize) { + $restoreSplat.Add("BlockSize", ($blockSize / 1)) + } + if ($null -ne $azureCredential) { + $restoreSplat.Add("AzureCredential", $azureCredential) + } + if ($null -ne $xpDirTree) { + $restoreSplat.Add("xpDirTree", $xpDirTree) + } + if ($null -ne $reuseSourceFolderStructure) { + $restoreSplat.Add("reuseSourceFolderStructure", $reuseSourceFolderStructure) + } + if ($null -ne $replaceDbNameInFile) { + $restoreSplat.Add("replaceDbNameInFile", $replaceDbNameInFile) + } + if ($null -ne $useDestinationDefaultDirectories) { + $restoreSplat.Add("useDestinationDefaultDirectories", $useDestinationDefaultDirectories) + } + if ($null -ne $keepCDC) { + $restoreSplat.Add("KeepCDC", $keepCDC) + } + $output = Restore-DbaDatabase @restoreSplat + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + $module.Result.changed = $true + } + $module.ExitJson() +} +catch { + $module.FailJson("Error restoring database: $($_.Exception.Message).", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/restore.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/restore.py new file mode 100644 index 00000000..5899ef18 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/restore.py @@ -0,0 +1,230 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: restore +short_description: Performs a restore operation +description: + - Performs a database restore operation. +version_added: 0.9.0 +options: + database: + description: + - The database to process. + type: str + required: false + path: + description: + - Path to SQL Server backup files. + - Paths passed in as strings will be scanned using the desired method, default is a recursive folder scan. + - Accepts multiple paths separated by C(,). + type: str + required: true + destination_data_directory: + description: + - Path to restore the SQL Server backups to on the target instance. + - If only this option is specified, then all database files (data and log) will be restored to this location + type: str + required: false + destination_log_directory: + description: + - Path to restore the database log files to. + - This option can only be specified alongside I(destination_data_directory). + type: str + required: false + destination_filestream_directory: + description: + - Path to restore FileStream data to. + - This option can only be specified alongside I(destination_data_directory). + type: str + required: false + restore_time: + description: + - Specify a datetime string C(HH:MM:SS MM/DD/YYYY) to which you want the database restored to. + - Default is to the latest point available in the specified backups. + type: str + required: false + with_replace: + description: + - Indicates if the restore is allowed to replace an existing database. + type: bool + required: false + default: false + keep_replication: + description: + - Indicates whether replication configuration should be restored as part of the database restore operation. + type: bool + required: false + default: false + xp_dirtree: + description: + - Switch that indicated file scanning should be performed by the SQL Server instance using C(xp_dirtree). + - This will scan recursively from the passed in path. + - You must have sysadmin role membership on the instance for this to work. + type: bool + required: false + no_xp_dir_recurse: + description: + - If specified, prevents the C(XpDirTree) process from recursing (its default behaviour). + type: bool + required: false + default: false + verify_only: + description: + - Indicates the restore should be verified only. + type: bool + required: false + default: false + maintenance_solution_backup: + description: + - Switch to indicate the backup files are in a folder structure as created by Ola Hallengreen's maintenance scripts. + - This allows for faster file parsing. + type: bool + required: false + default: false + ignore_log_backup: + description: + - Indicates to skip restoring any log backups. + type: bool + required: false + default: false + ignore_diff_backup: + description: + - Indicates to skip restoring any differential backups. + type: bool + required: false + default: false + use_destination_default_directories: + description: + - Switch that tells the restore to use the default Data and Log locations on the target server. + - If they don't exist, the function will try to create them. + type: bool + required: false + reuse_source_folder_structure: + description: + - By default, databases will be migrated to the destination Sql Server's default data and log directories. + - You can override this by using C(reuse_source_folder_structure). + type: bool + required: false + destination_file_prefix: + description: + - This value will be prefixed to B(all) restored files (log and data). + type: str + required: false + restored_database_name_prefix: + description: + - A string which will be prefixed to the start of the restore Database's name. + type: str + required: false + directory_recurse: + description: + - If specified the specified directory will be recursed into (overriding the default behaviour). + type: bool + required: false + default: false + standby_directory: + description: + - If a directory is specified the database(s) will be restored into a standby state, + with the standby file placed into this directory (which must exist, and be writable by the target Sql Server instance). + type: str + required: false + replace_db_name_in_file: + description: + - If switch set any occurrence of the original database's name in a data or log file + will be replace with the name specified in the I(database_name) option. + type: bool + required: false + destination_file_suffix: + description: + - This value will be suffixed to B(all) restored files (log and data). + type: str + required: false + keep_cdc: + description: + - Indicates whether CDC information should be restored as part of the database. + type: bool + required: false + stop_before: + description: + - Switch to indicate the restore should stop before I(stop_mark) occurs, default is to stop when mark is created. + type: bool + required: false + default: false + stop_mark: + description: + - Marked point in the transaction log to stop the restore at. + type: str + required: false + stop_after_date: + description: + - By default the restore will stop at the first occurence of I(stop_mark) found in the chain, + passing a datetime string C(HH:MM:SS MM/DD/YYYY) will cause it to stop the first I(stop_mark) after that datetime. + type: str + required: false + no_recovery: + description: + - Indicates if the databases should be recovered after last restore. + type: bool + required: false + default: false + max_transfer_size: + description: + - Sets the size of the unit of transfer. Values must be a multiple of 64kb. + type: int + required: false + default: 0 + block_size: + description: + - Specifies block size to use. + type: str + required: false + choices: ['0.5kb','1kb','2kb','4kb','8kb','16kb','32kb','64kb'] + buffer_count: + description: + - Number of I/O buffers to use. + type: int + required: false + default: 0 + azure_credential: + description: + - The name of the SQL Server credential to be used if restoring from an Azure hosted backup using Storage Access Keys. + type: str + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Restore a Database + lowlydba.sqlserver.restore: + sql_instance: sql-01.myco.io + database: LowlyDB + +- name: Restore a Database and allow future T-Log restores + lowlydba.sqlserver.restore: + sql_instance: sql-01.myco.io + database: LowlyDB1 + no_recovery: true + +- name: Verify backup files, no restore + lowlydba.sqlserver.restore: + sql_instance: sql-01.myco.io + database: LowlyDB2 + verify_only: true +''' + +RETURN = r''' +data: + description: Modified output from the C(Restore-DbaDatabase) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_resource_pool.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_resource_pool.ps1 new file mode 100644 index 00000000..a6b7ff9b --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_resource_pool.ps1 @@ -0,0 +1,118 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + resource_pool = @{type = 'str'; required = $true } + type = @{type = 'str'; required = $false; default = 'Internal'; choices = @('Internal', 'External') } + max_cpu_perc = @{type = 'int'; required = $false; } + min_cpu_perc = @{type = 'int'; required = $false; } + cap_cpu_perc = @{type = 'int'; required = $false; } + max_mem_perc = @{type = 'int'; required = $false; } + min_mem_perc = @{type = 'int'; required = $false; } + min_iops_per_vol = @{type = 'int'; required = $false; } + max_iops_per_vol = @{type = 'int'; required = $false; } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$state = $module.Params.state +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$options = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + ResourcePool = $module.Params.resource_pool + Type = $module.Params.type + MaximumCpuPercentage = $module.Params.max_cpu_perc + MinimumCpuPercentage = $module.Params.min_cpu_perc + CapCpuPercentage = $module.Params.cap_cpu_perc + MaximumMemoryPercentage = $module.params.max_mem_perc + MinimumMemoryPercentage = $module.Params.min_mem_perc + MinimumIOPSPerVolume = $module.params.min_iops_per_vol + MaximumIOPSPerVolume = $module.params.max_iops_per_vol +} +$checkMode = $module.CheckMode +$module.Result.changed = $false + +[System.Collections.ArrayList]$compareProperty = @( + 'MaximumCpuPercentage', + 'MinimumCpuPercentage', + 'CapCpuPercentage', + 'MinimumMemoryPercentage', + 'MaximumMemoryPercentage', + 'MinimumIOPSPerVolume', + 'MaximumIOPSPerVolume' +) + +$optionsToRemove = @() +foreach ($item in $options.GetEnumerator() ) { + if ($null -eq $item.Value) { + $optionsToRemove += $item.Name + } +} +foreach ($item in $optionsToRemove) { + $options.Remove($item) + $compareProperty.Remove($item) +} + +try { + $getPoolParams = @{ + SqlInstance = $options.SqlInstance + SqlCredential = $options.SqlCredential + Type = $options.Type + EnableException = $true + } + $existingResourcePool = Get-DbaRgResourcePool @getPoolParams | Where-Object Name -eq $options.ResourcePool + + if ($state -eq "absent") { + if ($null -ne $existingResourcePool) { + $removePoolParams = @{ + SqlInstance = $options.SqlInstance + SqlCredential = $options.SqlCredential + Type = $options.Type + ResourcePool = $options.ResourcePool + WhatIf = $checkMode + EnableException = $true + Confirm = $false + } + $output = Remove-DbaRgResourcePool @removePoolParams + $module.Result.changed = $true + } + } + elseif ($state -eq "present") { + $options.Add("WhatIf", $checkMode) + if ($null -ne $existingResourcePool) { + # Check for value parity + $diff = Compare-Object -ReferenceObject $options -DifferenceObject $existingResourcePool -Property $compareProperty + if ($null -ne $diff) { + # Set to new values + $output = Set-DbaRgResourcePool @options -EnableException + $module.Result.changed = $true + } + } + else { + # Create a resource pool + $output = New-DbaRgResourcePool @options -EnableException + $module.Result.changed = $true + } + } + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring resource pool failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_resource_pool.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_resource_pool.py new file mode 100644 index 00000000..08b7aef2 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_resource_pool.py @@ -0,0 +1,90 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: rg_resource_pool +short_description: Configures a resource pool for use by the Resource Governor +description: + - Creates or modifies a resource pool to be used by the Resource Governor. Default values are handled by the Powershell functions themselves. +version_added: 0.1.0 +options: + resource_pool: + description: + - Name of the target resource pool. + type: str + required: true + type: + description: + - Specify the type of resource pool. + type: str + required: false + default: 'Internal' + choices: ['Internal', 'External'] + max_cpu_perc: + description: + - Maximum CPU Percentage able to be used by queries in this resource pool. + type: int + required: false + min_cpu_perc: + description: + - Minimum CPU Percentage able to be used by queries in this resource pool. + type: int + required: false + cap_cpu_perc: + description: + - Cap CPU Percentage able to be used by queries in this resource pool. + type: int + required: false + max_mem_perc: + description: + - Maximum Memory Percentage able to be used by queries in this resource pool. + type: int + required: false + min_mem_perc: + description: + - Minimum Memory Percentage able to be used by queries in this resource pool. + type: int + required: false + max_iops_per_vol: + description: + - Maximum IOPS/volume able to be used by queries in this resource pool. + type: int + required: false + min_iops_per_vol: + description: + - Minimum IOPS/volume able to be used by queries in this resource pool. + type: int + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Enable resource governor + lowlydba.sqlserver.resource_governor: + sql_instance: sql-01.myco.io + enabled: true + +- name: Create rg resource pool + lowlydba.sqlserver.rg_resource_pool: + sql_instance: sql-01.myco.io + resource_pool: "rpLittle" + max_cpu_perc: 5 +''' + +RETURN = r''' +data: + description: Output from the C(Set-DbaRgResourcePool), C(New-DbaRgResourcePool), or C(Remove-DbaRgResourcePool) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_workload_group.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_workload_group.ps1 new file mode 100644 index 00000000..0b225fd1 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_workload_group.ps1 @@ -0,0 +1,112 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + workload_group = @{type = 'str'; required = $true; } + resource_pool = @{type = 'str'; required = $true; } + resource_pool_type = @{type = 'str'; required = $false; default = 'Internal'; choices = @('Internal', 'External') } + group_max_requests = @{type = 'int'; required = $false; } + importance = @{type = 'str'; required = $false; choices = @('Low', 'Medium', 'High') } + max_dop = @{type = 'int'; required = $false; } + request_max_cpu_time = @{type = 'int'; required = $false; } + request_max_mem_grant_perc = @{type = 'int'; required = $false; } + request_mem_grant_timeout_sec = @{type = 'int'; required = $false; } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +# Get Csharp utility module +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$options = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + WorkloadGroup = $module.Params.workload_group + ResourcePool = $module.Params.resource_pool + ResourcePoolType = $module.Params.resource_pool_type + GroupMaximumRequests = $module.Params.group_max_requests + Importance = $module.Params.importance + MaximumDegreeOfParallelism = $module.Params.max_dop + RequestMaximumCpuTimeInSeconds = $module.Params.request_max_cpu_time + RequestMaximumMemoryGrantPercentage = $module.Params.request_max_mem_grant_perc + RequestMemoryGrantTimeoutInSeconds = $module.Params.request_mem_grant_timeout_sec +} +$state = $module.Params.state +$module.Result.changed = $false +$checkMode = $module.CheckMode +[System.Collections.ArrayList]$compareProperty = @( + 'GroupMaximumRequests', + 'Importance', + 'MaximumDegreeOfParallelism', + 'RequestMaximumCpuTimeInSeconds', + 'RequestMemoryGrantTimeoutInSeconds', + 'RequestMaximumMemoryGrantPercentage' +) + +# Remove unsupplied params - the dbatools functions handle default value checks +$optionsToRemove = @() +foreach ($item in $options.GetEnumerator() ) { + if ($null -eq $item.Value) { + $optionsToRemove += $item.Name + } +} +foreach ($item in $optionsToRemove) { + $options.Remove($item) + $compareProperty.Remove($item) +} + +try { + $getResourcePoolSplat = @{ + SqlInstance = $options.SqlInstance + SqlCredential = $options.SqlCredential + Type = $options.ResourcePoolType + } + $existingResourcePool = Get-DbaRgResourcePool @getResourcePoolSplat | Where-Object Name -eq $options.ResourcePool + if ($null -eq $existingResourcePool) { + $module.FailJson("Failed to lookup parent resource pool '$($options.ResourcePool)'.", $_) + } + $existingWorkloadGroup = $existingResourcePool.WorkloadGroups | Where-Object Name -eq $options.WorkloadGroup + + if ($state -eq "absent") { + if ($null -ne $existingResourcePool) { + $output = $existingWorkloadGroup | Remove-DbaRgWorkloadGroup -WhatIf:$checkMode -EnableException + $module.Result.changed = $true + } + } + elseif ($state -eq "present") { + if ($null -ne $existingWorkloadGroup) { + # Check for value parity + $diff = Compare-Object -ReferenceObject $existingWorkloadGroup -DifferenceObject $options -Property $compareProperty + # Set to new values + if ($null -ne $diff) { + $output = Set-DbaRgWorkloadGroup @options -WhatIf:$checkMode -EnableException + $module.Result.changed = $true + } + } + else { + # Create a new workload group + $output = New-DbaRgWorkloadGroup @options -WhatIf:$checkMode -EnableException + $module.Result.changed = $true + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Failed to configure workload group: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_workload_group.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_workload_group.py new file mode 100644 index 00000000..21cf8261 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/rg_workload_group.py @@ -0,0 +1,100 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: rg_workload_group +short_description: Configures a workload group for use by the Resource Governor +description: + - Creates or modifies a workload group to be used by the Resource Governor. Default values are handled by the Powershell functions themselves. +version_added: 0.1.0 +options: + workload_group: + description: + - Name of the target workload group. + type: str + required: true + resource_pool: + description: + - Name of the resource pool for the workload group. + type: str + required: true + resource_pool_type: + description: + - Specify the type of resource pool. + type: str + required: false + default: 'Internal' + choices: ['Internal', 'External'] + group_max_requests: + description: + - Specifies the maximum number of simultaneous requests that are allowed to execute in the workload group. + type: int + required: false + importance: + description: + - Specifies the relative importance of a request in the workload group. + type: str + required: false + choices: ['Low', 'Medium', 'High'] + max_dop: + description: + - Specifies the maximum degree of parallelism (MAXDOP) for parallel query execution. + type: int + required: false + request_max_cpu_time: + description: + - Specifies the maximum amount of CPU time, in seconds, that a request can use. + type: int + required: false + request_max_mem_grant_perc: + description: + - Specifies the maximum amount of memory that a single request can take from the pool. + type: int + required: false + request_mem_grant_timeout_sec: + description: + - Specifies the maximum time, in seconds, that a query can wait for a memory grant (work buffer memory) to become available. + type: int + required: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Enable resource governor + lowlydba.sqlserver.resource_governor: + sql_instance: sql-01.myco.io + enabled: true + +- name: Create rg resource pool + lowlydba.sqlserver.rg_resource_pool: + sql_instance: sql-01.myco.io + resource_pool: "rpLittle" + max_cpu_perc: 5 + +- name: Create rg workload group + lowlydba.sqlserver.rg_workload_group: + sql_instance: sql-01.myco.io + workload_group: rgMyGroup + resource_pool: rpLittle + resource_pool_type: Internal + max_dop: 2 + state: present +''' + +RETURN = r''' +data: + description: Output from the C(Set-DbaRgWorkloadGroup), C(New-DbaRgWorkloadGroup), or C(Remove-DbaRgWorkloadGroup) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/sa.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/sa.ps1 new file mode 100644 index 00000000..38042bc6 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/sa.ps1 @@ -0,0 +1,101 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + new_name = @{type = 'str'; required = $false; } + password = @{type = 'str'; required = $false; no_log = $true } + enabled = @{type = 'bool'; required = $false; default = $true } + password_must_change = @{type = 'bool'; required = $false } + password_policy_enforced = @{type = 'bool'; required = $false } + password_expiration_enabled = @{type = 'bool'; required = $false } + } +} +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$newName = $module.Params.new_name +if ($null -ne $module.Params.password) { + $secPassword = ConvertTo-SecureString -String $module.Params.password -AsPlainText -Force +} +$enabled = $module.Params.enabled +[nullable[bool]]$passwordMustChange = $module.Params.password_must_change +[nullable[bool]]$passwordExpirationEnabled = $module.Params.password_expiration_enabled +[nullable[bool]]$passwordPolicyEnforced = $module.Params.password_policy_enforced +$checkMode = $module.CheckMode +$module.Result.changed = $false + +try { + $sa = Get-DbaLogin -SqlInstance $SqlInstance -SqlCredential $sqlCredential -EnableException | Where-Object ID -eq 1 + + $setLoginSplat = @{ } + + if ($null -ne $newName) { + $setLoginSplat.Add("NewName", $newName) + if ($sa.Name -ne $newName) { + $changed = $true + } + } + if ($null -ne $passwordExpirationEnabled) { + if ($sa.PasswordExpirationEnabled -ne $passwordExpirationEnabled) { + $changed = $true + } + if ($passwordExpirationEnabled -eq $true) { + $setLoginSplat.add("PasswordExpirationEnabled", $true) + } + } + if ($null -ne $passwordPolicyEnforced) { + if ($sa.PasswordPolicyEnforced -ne $passwordPolicyEnforced) { + $changed = $true + } + if ($passwordPolicyEnforced -eq $true) { + $setLoginSplat.add("PasswordPolicyEnforced", $true) + } + } + if ($true -eq $passwordMustChange) { + if ($sa.PasswordMustChange -ne $passwordMustChange) { + $changed = $true + } + if ($passwordMustChange -eq $true) { + $setLoginSplat.add("PasswordMustChange", $true) + } + } + if ($null -ne $secPassword) { + $setLoginSplat.add("SecurePassword", $secPassword) + } + if ($enabled -eq $false) { + $disabled = $true + $setLoginSplat.add("Disable", $true) + } + else { + $disabled = $false + $setLoginSplat.add("Enable", $true) + } + + # Check for changes + if (($changed -eq $true) -or ($disabled -ne $sa.IsDisabled) -or ($secPassword)) { + $output = $sa | Set-DbaLogin @setLoginSplat -WhatIf:$checkMode -EnableException + $module.Result.changed = $true + } + else { + $output = $sa + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.exitJson() +} +catch { + $module.FailJson("Configuring 'sa' login failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/sa.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/sa.py new file mode 100644 index 00000000..6aa45fe3 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/sa.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: sa +short_description: Configure the C(sa) login for security best practices +description: + - Rename, disable, and reset the password for the C(sa) login on a SQL Server instance per best practices. +options: + password: + description: + - Password for the login. + type: str + required: false + new_name: + description: + - The new name to rename the C(sa) login to. + type: str + required: false + enabled: + description: + - Whether the login is enabled or disabled. + type: bool + required: false + default: true + version_added: '0.4.0' + password_must_change: + description: + - Enforces user must change password at next login. + - When specified, will enforce I(password_expiration_enabled) and I(password_policy_enforced) as they are required. + type: bool + required: false + password_policy_enforced: + description: + - Enforces password complexity policy. + type: bool + required: false + password_expiration_enabled: + description: + - Enforces password expiration policy. Requires I(password_policy_enforced=true). + type: bool + required: false +version_added: 0.3.0 +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Disable sa login + lowlydba.sqlserver.sa: + sql_instance: sql-01.myco.io + disable: true + +- name: Rename sa login + lowlydba.sqlserver.sa: + sql_instance: sql-01.myco.io + new_name: 'notthesayourelookingfor' +''' + +RETURN = r''' +data: + description: Output from the C(Set-DbaLogin) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_configure.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_configure.ps1 new file mode 100644 index 00000000..f4ac80fd --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_configure.ps1 @@ -0,0 +1,59 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# (c) 2021, Sudhir Koduri (@kodurisudhir) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + name = @{type = 'str'; required = $true } + value = @{type = 'int'; required = $true } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$name = $module.Params.name +$value = $module.Params.value +$checkMode = $module.CheckMode +$module.Result.changed = $false + +try { + $existingConfig = Get-DbaSpConfigure -SqlInstance $sqlInstance -SqlCredential $sqlCredential -Name $name -EnableException + + if ($existingConfig.ConfiguredValue -ne $value) { + $setSpConfigureSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Name = $name + Value = $value + WhatIf = $checkMode + EnableException = $true + } + $output = Set-DbaSpConfigure @setSpConfigureSplat + + if ($existingConfig.IsDynamic -eq $false) { + $output | Add-Member -MemberType NoteProperty -Name "RestartRequired" -Value $true + } + $module.Result.changed = $true + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} + +catch { + $module.FailJson("sp_configure change failed.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_configure.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_configure.py new file mode 100644 index 00000000..91e060e8 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_configure.py @@ -0,0 +1,50 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# (c) 2021, Sudhir Koduri (@kodurisudhir) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: sp_configure +short_description: Make instance level system configuration changes via C(sp_configure) +description: + - Read instance level system configuration for a given configuration and update to a new value as provided. +version_added: 0.1.0 +options: + name: + description: + - Name of the configuration that will be changed. + type: str + required: true + value: + description: + - New value the configuration will be set to. + type: int + required: true +author: "Sudhir Koduri (@kodurisudhir)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Enable remote DAC connection + lowlydba.sqlserver.sp_configure: + sql_instance: sql-01.myco.io + name: RemoteDacConnectionsEnabled + value: 1 +''' + +RETURN = r''' +data: + description: + - Output from the C(Set-DbaSpConfigure) function. + - RestartRequired returned if the setting requires a service restart to take effect. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_whoisactive.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_whoisactive.ps1 new file mode 100644 index 00000000..45fc5ebd --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_whoisactive.ps1 @@ -0,0 +1,57 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + database = @{type = 'str'; required = $true } + local_file = @{type = 'str'; required = $false } + force = @{type = 'bool'; required = $false; default = $false } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$database = $module.Params.database +$localFile = $module.Params.local_file +$force = $module.Params.force +$checkMode = $module.CheckMode +$module.Result.changed = $false + +$whoIsActiveSplat = @{ + SqlInstance = $SqlInstance + SqlCredential = $SqlCredential + Database = $Database + WhatIf = $checkMode + Force = $force + Confirm = $false + EnableException = $true +} +if ($null -ne $LocalFile) { + $whoIsActiveSplat.LocalFile = $LocalFile +} + +try { + $output = Install-DbaWhoIsActive @whoIsActiveSplat + $module.Result.changed = $true + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} + +catch { + $module.FailJson("Installing sp_WhoIsActive failed.", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_whoisactive.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_whoisactive.py new file mode 100644 index 00000000..8aed1036 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/sp_whoisactive.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: sp_whoisactive +short_description: Install/update C(sp_whoisactive) by Adam Mechanic +description: + - A wrapper for C(Install-DbaWhoIsActive) to fetch the latest version of the script, or install from a local cached version. +version_added: 0.1.0 +options: + local_file: + description: + - Specifies the path to a local file to install sp_WhoisActive from. + - This can be either the zip file as distributed by the website or the expanded SQL script. + - If this option is not specified, the latest version will be downloaded and installed from https://github.com/amachanic/sp_whoisactive/releases + type: str + required: false + database: + description: + - Name of the target database. + type: str + required: true + force: + description: + - If this switch is enabled, then C(sp_WhoisActive) will be downloaded from the internet even if previously cached. + type: bool + default: false +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Install/Update sp_whoisactive + lowlydba.sqlserver.sp_whoisactive: + sql_instance: sql-01.myco.io + database: lowlydb +''' + +RETURN = r''' +data: + description: Output from the C(Install-DbaWhoIsActive) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/spn.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/spn.ps1 new file mode 100644 index 00000000..12c953bc --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/spn.ps1 @@ -0,0 +1,69 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# (c) 2021, Sudhir Koduri (@kodurisudhir) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + computer_username = @{ type = 'str'; required = $false } + computer_password = @{ type = 'str'; required = $false; no_log = $true; } + computer = @{ type = 'str'; required = $true } + service_account = @{ type = 'str'; required = $true; } + state = @{ type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } + required_together = @( + , @('computer_username', 'computer_password') + ) +} +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) +if ($null -ne $module.Params.computer_username) { + [securestring]$secPassword = ConvertTo-SecureString $Module.Params.computer_password -AsPlainText -Force + [pscredential]$computerCredential = New-Object System.Management.Automation.PSCredential ($Module.Params.computer_username, $secPassword) +} +$computer = $module.Params.computer +$serviceAccount = $module.Params.service_account +$serviceClass = "MSSQLSvc" +$spn = "$serviceClass\$computer" +$state = $module.Params.state +$checkMode = $module.CheckMode +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $existingSPN = Get-DbaSpn -ComputerName $computer -Credential $computerCredential -AccountName $serviceAccount | Where-Object spn -eq $spn + + if ($state -eq "present") { + if ($null -ne $existingSPN) { + # SPNs can only be added and removed, not modified + $module.ExitJson() + } + elseif ($null -eq $existingSPN) { + $output = Set-DbaSpn -SPN $spn -ServiceAccount $serviceAccount -Credential $computerCredential + $module.Result.changed = $true + } + } + elseif ($state -eq "absent") { + if ($null -ne $existingSPN) { + $output = Remove-DbaSpn -SPN $spn -ServiceAccount $serviceAccount -Credential $computerCredential + $module.Result.changed = $true + } + } + + if ($output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring SPN failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/spn.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/spn.py new file mode 100644 index 00000000..0fabb934 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/spn.py @@ -0,0 +1,70 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: spn +short_description: Configures SPNs for SQL Server +description: + - Configures SPNs for SQL Server. +version_added: 0.6.0 +options: + computer_username: + description: + - Username of a credential to connect to Active Directory with. + type: str + required: false + computer_password: + description: + - Password of a credential to connect to Active Directory with. + type: str + required: false + computer: + description: + - The host or alias to configure the SPN for. Can include the port in the format C(host:port). + type: str + required: true + service_account: + description: + - The account you want the SPN added to. Will be looked up if not provided. + type: str + required: true +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.state + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_win +''' + +EXAMPLES = r''' +- name: Add server SPN + lowlydba.sqlserver.spn: + computer: sql-01.myco.io + service_account: myco\sql-svc + +- name: Create an AG Listener + lowlydba.sqlserver.ag_listener: + sql_instance_primary: sql-01.myco.io + ag_name: AG_MyDatabase + listener_name: aglMyDatabase + ip_address: 10.0.20.20,10.1.77.77 + subnet_ip: 255.255.252.0 + subnet_mask: 255.255.255.0 + +- name: Add SPN for new AG listener on port 1433 + lowlydba.sqlserver.spn: + computer: aglMyDatabase.myco.io:1433 + service_account: myco\sql-svc +''' + +RETURN = r''' +data: + description: Output from the C(Set-DbaSpn) or C(Remove-DbaSpn) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/tcp_port.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/tcp_port.ps1 new file mode 100644 index 00000000..bff87bd9 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/tcp_port.ps1 @@ -0,0 +1,69 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# (c) 2021, Sudhir Koduri (@kodurisudhir) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + username = @{type = 'str'; required = $false } + password = @{type = 'str'; required = $false; no_log = $true } + port = @{type = 'int'; required = $true } + ip_address = @{type = 'str'; required = $false } + force = @{type = 'bool'; required = $false; default = $false } + } + required_together = @( + , @('username', 'password') + ) +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +if ($null -ne $module.Params.username) { + [securestring]$secPassword = ConvertTo-SecureString $Module.Params.password -AsPlainText -Force + [pscredential]$credential = New-Object System.Management.Automation.PSCredential ($Module.Params.username, $secPassword) +} +$port = $module.Params.port +$ipAddress = $module.Params.ip_address +$checkMode = $module.CheckMode +$force = $module.Params.force +$module.Result.changed = $false +$PSDefaultParameterValues = @{ "*:EnableException" = $true; "*:Confirm" = $false; "*:WhatIf" = $checkMode } + +try { + $tcpPortSplat = @{ + SqlInstance = $SqlInstance + Credential = $credential + Port = $port + Force = $force + } + if ($ipAddress) { + $tcpPortSplat.Add("IPAddress", $ipAddress) + } + $output = Set-DbaTcpPort @tcpPortSplat + + if ($output.Changes.Count -gt 0 -or $checkMode) { + $module.Result.changed = $true + if ($force -ne $true) { + $output | Add-Member -MemberType NoteProperty -Name "RestartRequired" -Value $true + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring TCP port failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/tcp_port.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/tcp_port.py new file mode 100644 index 00000000..f97013f8 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/tcp_port.py @@ -0,0 +1,70 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: tcp_port +short_description: Sets the TCP port for the instance +description: + - Sets the TCP port for a SQL Server instance. +version_added: 0.10.0 +options: + username: + description: + - Username for alternative credential to authenticate with Windows. + type: str + required: false + password: + description: + - Password for alternative credential to authenticate with Windows. + type: str + required: false + port: + description: + - Port for SQL Server to listen on. + type: int + required: true + ip_address: + description: + - IPv4 address. + type: str + required: false + force: + description: + - Restart SQL Server and SQL Agent services automatically. + type: bool + required: false + default: false + version_added: 1.2.0 +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_win +''' + +EXAMPLES = r''' +- name: Set the default port + lowlydba.sqlserver.tcp_port: + sql_instance: sql-01.myco.io + port: 1433 + +- name: Set a non-standard default port + lowlydba.sqlserver.tcp_port: + sql_instance: sql-01.myco.io + port: 1933 +''' + +RETURN = r''' +data: + description: + - Output from the C(Set-DbaTcpPort) function. + - RestartRequired returned if the change requires a service restart to take effect. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/traceflag.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/traceflag.ps1 new file mode 100644 index 00000000..9e0a1434 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/traceflag.ps1 @@ -0,0 +1,67 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# (c) 2021, Sudhir Koduri (@kodurisudhir) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +# Get Csharp utility module +$spec = @{ + supports_check_mode = $true + options = @{ + trace_flag = @{type = 'int'; required = $true } + enabled = @{type = 'bool'; required = $true } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$traceFlag = $module.Params.trace_flag +$enabled = $module.Params.enabled +$checkMode = $module.CheckMode +$module.Result.changed = $false + +try { + $traceFlagSplat = @{ + SqlInstance = $SqlInstance + SqlCredential = $sqlCredential + TraceFlag = $traceFlag + EnableException = $true + } + $existingFlag = Get-DbaTraceFlag @traceFlagSplat + + if ($enabled -eq $true) { + if (-not $checkMode) { + $enabled = Enable-DbaTraceFlag @traceFlagSplat + $output = $enabled | Select-Object -Property InstanceName, SqlInstance, TraceFlag + } + if ($existingFlag.TraceFlag -notcontains $traceFlag) { + $module.Result.changed = $true + } + } + elseif ($enabled -eq $false) { + + if (-not $checkMode) { + $disabled = Disable-DbaTraceFlag @traceFlagSplat + $output = $disabled | Select-Object -Property InstanceName, SqlInstance, TraceFlag + } + if ($existingFlag.TraceFlag -contains $traceFlag) { + $module.Result.changed = $true + } + } + + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Configuring trace flag failed: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/traceflag.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/traceflag.py new file mode 100644 index 00000000..c8139e51 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/traceflag.py @@ -0,0 +1,55 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# (c) 2021, Sudhir Koduri (@kodurisudhir) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: traceflag +short_description: Enable or disable global trace flags on a SQL Server instance +description: + - Enable\Disable global trace flag on a SQL Instance. This trace flag takes affect immediately and does not require SQL Instance restart. + - This setting does not persist after restart. +version_added: 0.1.0 +options: + trace_flag: + description: + - Trace Flag number. + type: int + required: true + enabled: + description: + - Flag to enable or disable the trace flag. + type: bool + required: true +author: "Sudhir Koduri (@kodurisudhir)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all +''' + +EXAMPLES = r''' +- name: Eliminate successful backup information from SQL Error Log + lowlydba.sqlserver.traceflag: + sql_instance: sql-01.myco.io + trace_flag: 3226 + enabled: true + +- name: Disable trace flag + lowlydba.sqlserver.traceflag: + sql_instance: sql-01.myco.io + trace_flag: 3226 + enabled: false +''' + +RETURN = r''' +data: + description: Output from the C(Enable-DbaTraceFlag) or C(Disable-DbaTraceFlag) function. + returned: success, but not in check_mode. + type: dict +''' diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/user.ps1 b/ansible_collections/lowlydba/sqlserver/plugins/modules/user.ps1 new file mode 100644 index 00000000..29bbd69b --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/user.ps1 @@ -0,0 +1,142 @@ +#!powershell +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#AnsibleRequires -PowerShell ansible_collections.lowlydba.sqlserver.plugins.module_utils._SqlServerUtils +#Requires -Modules @{ ModuleName="dbatools"; ModuleVersion="1.1.112" } + +$ErrorActionPreference = "Stop" + +$spec = @{ + supports_check_mode = $true + options = @{ + login = @{type = 'str'; required = $true } + database = @{type = 'str'; required = $true } + username = @{type = 'str'; required = $true } + default_schema = @{type = 'str'; required = $false ; default = 'dbo' } + external_provider = @{type = 'bool'; required = $false } + state = @{type = 'str'; required = $false; default = 'present'; choices = @('present', 'absent') } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec, @(Get-LowlyDbaSqlServerAuthSpec)) +$sqlInstance, $sqlCredential = Get-SqlCredential -Module $module +$login = $module.Params.login +$username = $module.Params.username +$database = $module.Params.database +$defaultSchema = $module.Params.default_schema +[nullable[bool]]$externalProvider = $module.Params.external_provider +$state = $module.Params.state +$checkMode = $module.CheckMode + +$module.Result.changed = $false + +$getUserSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + User = $username + Login = $login + Database = $database + EnableException = $true +} +$existingUser = Get-DbaDbUser @getUserSplat + +if ($state -eq "absent") { + if ($null -ne $existingUser) { + try { + $removeUserSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + User = $username + Database = $database + EnableException = $true + WhatIf = $checkMode + Force = $true + Confirm = $false + } + $output = Remove-DbaDbUser @removeUserSplat + $module.Result.changed = $true + } + catch { + $module.FailJson("Removing user failed: $($_.Exception.Message)", $_) + } + } +} +elseif ($state -eq "present") { + # User exists + if ($null -ne $existingUser) { + if ($defaultSchema -ne $existingUser.DefaultSchema) { + try { + # No Set-DbaDbUser command exists, use SMO + $getSchemaSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Database = $database + Schema = $defaultSchema + IncludeSystemDatabases = $true + IncludeSystemSchemas = $true + EnableException = $true + } + $existingSchema = Get-DbaDbSchema @getSchemaSplat + + if ($null -ne $existingSchema) { + # do schema change + if (-not($checkMode)) { + $existingUser.DefaultSchema = $defaultSchema + $existingUser.Alter() + $output = $existingUser + } + $module.result.changed = $true + } + else { + $module.FailJson("Schema '$defaultSchema' not found in [$database].") + } + } + catch { + $module.FailJson("Configuring user failed: $($_.Exception.Message)", $_) + } + } + } + # New User + else { + try { + $newUserSplat = @{ + SqlInstance = $sqlInstance + SqlCredential = $sqlCredential + Username = $username + Login = $login + Database = $database + DefaultSchema = $defaultSchema + EnableException = $true + WhatIf = $checkMode + Force = $true + Confirm = $false + } + if ($externalProvider -eq $true) { + $newUserSplat.add("ExternalProvider", $true) + } + $output = New-DbaDbUser @newUserSplat + $module.result.changed = $true + } + catch { + $module.FailJson("Creating user failed: $($_.Exception.Message)", $_) + } + } + # If not in check mode, add extra fields we can change to default display set + if ($null -ne $output) { + $output.PSStandardMembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Add("DefaultSchema") + } +} +try { + if ($null -ne $output) { + $resultData = ConvertTo-SerializableObject -InputObject $output + $module.Result.data = $resultData + } + $module.ExitJson() +} +catch { + $module.FailJson("Failure: $($_.Exception.Message)", $_) +} diff --git a/ansible_collections/lowlydba/sqlserver/plugins/modules/user.py b/ansible_collections/lowlydba/sqlserver/plugins/modules/user.py new file mode 100644 index 00000000..b9c36d06 --- /dev/null +++ b/ansible_collections/lowlydba/sqlserver/plugins/modules/user.py @@ -0,0 +1,81 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2022, John McCall (@lowlydba) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: user +short_description: Configures a user within a database +description: + - Creates, modifies, or removes a user in a database. +version_added: 1.1.0 +options: + login: + description: + - Name of the login that the user is mapped to. + type: str + required: true + database: + description: + - Database for the user. + type: str + required: true + username: + description: + - Name of the user. + type: str + required: true + external_provider: + description: + - Specifies that the user is for Azure AD Authentication. Only used when creating a new user, this cannot be modified for an existing user. + type: bool + required: false + default_schema: + description: + - The default database schema for the user. + type: str + required: false + default: "dbo" +author: "John McCall (@lowlydba)" +requirements: + - L(dbatools,https://www.powershellgallery.com/packages/dbatools/) PowerShell module +extends_documentation_fragment: + - lowlydba.sqlserver.sql_credentials + - lowlydba.sqlserver.attributes.check_mode + - lowlydba.sqlserver.attributes.platform_all + - lowlydba.sqlserver.state +''' + +EXAMPLES = r''' +- name: Create a user + lowlydba.sqlserver.user: + sql_instance: sql-01.myco.io + login: TheIntern + username: TheIntern + database: InternProject1 + +- name: Change user's schema + lowlydba.sqlserver.login: + sql_instance: sql-01.myco.io + login: TheIntern + username: TheIntern + database: InternProject1 + default_schema: dev + +- name: Remove a user + lowlydba.sqlserver.login: + sql_instance: sql-01.myco.io + login: TheIntern + username: TheIntern + database: InternProject1 + state: absent +''' + +RETURN = r''' +data: + description: Output from the C(New-DbaDbUser), C(Get-DbaDbUser), or C(Remove-DbaDbUser) function. + returned: success, but not in check_mode. + type: dict +''' |