Setting a database offline with powershell - sql-server

I'm working on an automated database restore script in powershell and trying to harden it. At a high level, it goes something like this:
$s = new-object microsoft.sqlserver.management.smo.server '.';
$db = $s.Databases['myDb'];
$db.SetOffline();
# start restore here
The problem here is that if there are any connections in the database, the SetOffline() command gets blocked. I know that there are KillDatabase() and KillAllProcesses() methods on the Server object, but the former sets me up to have to zero out the log file again and the latter has a race condition on a busy server. What I'm looking for is the SMO equivalent of alter database [myDb] set offline with rollback immediate;.
Does such a thing exist? I could resort to doing some ExecuteNonQuery() shenanigans, but it seems like there should be a more elegant way.

So what if you block the access to SQL in the firewall first and then KillAllProcesses?
Then nothing will be able to reconnect and there will be no race condition...
New-NetFirewallRule -DisplayName BlockSQL -Service MSSQL -Direction Inbound -Action Block -Enabled True
You would of course need to call the SetOffline method locally then since MSSQL is blocked from any comunication over the network.
And when you're done taking the DB:s offline, just delete the firewall rule...
Remove-NetFirewallRule -DisplayName BlockSQL

Related

Restricting a user to only have access in a ssis package

We are running SQL Server 2016. For in-house political reasons, I can't restrict access the way I want to. Assume that I can't change the premise.
We have a user that is used in SSIS packages. Unfortunately, some devs are logging directly into the db with ssms using this user. I need to prevent this without changing the password or something. What I need is to be able to allow a user access to the database ONLY if it is running from an SSIS package and NOT if it is coming in any other way.
I am not looking for other suggestions of how to fix this issue. I understand most of them already, I am stuck because of management decisions that I cannot change.
Can anyone tell me how to restrict a user in such a way?
An approach is to use a LOGON trigger
A first blush approach might be to reject any process that look's like the SSMS application
CREATE OR ALTER TRIGGER logon_developer_check
ON ALL SERVER
FOR LOGON
AS
BEGIN
IF (ORIGINAL_LOGIN() = 'triskydeveloper'
and EXISTS
(
SELECT
*
FROM
sys.sysprocesses AS S
WHERE
S.spid = ##SPID
AND S.program_name LIKE 'Microsoft SQL Server Management%'
)
BEGIN
ROLLBACK
END
END
But developers, being devious little buggers, will then write their own .NET application or use SQLCMD so you'd fall into a rat race trying to identify all the program_names that might show up.
I would instead look at the hostname column on sys.sysprocesses - if the connection isn't coming from the server itself, just reject it. Unless you have to deal with developers able to RDP onto the server.
Oh and if you mangle the logon trigger and it's rejecting everything, use SQLCMD and the dedicated admin console, DAC, and
sqlcmd.exe -S localhost -d master -Q "DISABLE TRIGGER ALL ON SERVER" -A

How to Clone SQL Azure DBs in VSTS - The databases 'A' in server 'sql01' and 'B' in server 'sql01' are already in a replication relation

I am trying to configure my release process in VSTS to clone my live database to my staging environment before deploying the buld to staging. FOllowing guidelines here...
http://community.idera.com/blog/b/community_blog/posts/how-to-make-a-copy-of-a-database-on-windows-azure-sql-database
and here...
https://alexandrebrisebois.wordpress.com/2013/02/07/creating-a-staging-database-instance-from-a-production-instance-on-windows-azure-sql-database/
I have got it largely working though I get the following error when I run the command...
CREATE DATABASE [Staging] AS COPY OF [Operational]
The databases 'Operational' in server 'sql01' and 'Staging' in server
'sql01' are already in a replication relation.
This is preceeded by...
DROP DATABASE IF EXISTS [Staging]
I can configure the build server to continue regardless but I still get nasty yellow warnings coming up. I'm looking to either suppress this in some way as a workaround or to do whatever is necessary to stop the error from coming up in the first place. The end result is as desired, the SQL database is successfully copied.
It feels like there's some other cleanup I need to do beforehand other than simply dropping the original database (if it exists!)
After the step above I then do this, which forces the release process to 'wait' until the Database is ready...
DECLARE #COPY_DATA int
SET #COPY_DATA = SELECT COUNT(*) FROM sys.dm_database_copies
WHILE (#COPY_DATA > 0)
BEGIN
WAITFOR DELAY '00:00:10'
SET #COPY_DATA = SELECT COUNT(*) FROM sys.dm_database_copies
END
...which can take some time. This completes with no errors at all.
To compound my woes, as time has gone on I have been unable to consistently ensure completion at each stage, leading to spurious fails. Sometimes the DROP won't finish. If I check it's existence in Master then It's gone but Create will fall over because it's still there.
For those reading on later, what follows is the solution I ended up with that was the most reliable.
NOTES
- Powershell is more reliable than SqlCommand
- Azure's API and the underlying database sometimes get out os sync for a while leading to a conflicting state between the two
In order to clone my production to staging I did the following...
Step 1: Azure Powershell Task (Clear Staging)
Remove-AzureRmSqlDatabase -ResourceGroupName "Production" -ServerName "sql01" -DatabaseName "Staging" -ErrorAction SilentlyContinue
Step 2: Azure SQL Task (Drop Database) - Just to be sure!
DROP DATABASE IF EXISTS [Staging]
Step 3: Azure Powershell Task (Recreate Staging from Live)
New-AzureRmSqlDatabaseCopy -ResourceGroupName "Production" -ServerName "sql01" -DatabaseName "Operational" -CopyResourceGroupName "Production" -CopyServerName "sql01" -CopyDatabaseName "Staging"
Step 4: Azure SQL Task (Await Sync) - Just in case!
DECLARE #COPY_DATA int
SET #COPY_DATA = SELECT COUNT(*) FROM sys.dm_database_copies
WHILE (#COPY_DATA > 0)
BEGIN
WAITFOR DELAY '00:00:10'
SET #COPY_DATA = SELECT COUNT(*) FROM sys.dm_database_copies
END
Step 5: Azure Powershell Task (Generate BacPac)
(I'm not going into that here as it's a whole other topic).
Once the release moves to Production I call the cleardown process again so Staging is only ever transient.
You can use Remove-​Azure​Rm​Sql​Database and New-​Azure​Rm​Sql​Database​Copy command to delete a database and then create a database copy.
Add an "Azure PowerShell Script" task in your build definition and configure it to use "Azure Resource Manager" to connect to your Azure Account like following:
The PowerShell script I use:
Remove-AzureRmSqlDatabase -ResourceGroupName "resourcegourpname" -ServerName "servername" -DatabaseName "databasecopy"
New-AzureRmSqlDatabaseCopy -ResourceGroupName "resourcegourpname" -ServerName "servername" -DatabaseName "database" -CopyResourceGroupName "resourcegourpname" -CopyServerName "servername" -CopyDatabaseName "databasecopy"
Since the copy process hasn’t finished, it will throw the error that you get. The workaround is as you said that you can wait the process finishing.
On the other hand, you can try to use New-AzureRMSqlDatabaseCopy cmdlet and check the result.
The New-AzureRmSqlDatabaseCopy cmdlet creates a copy of an Azure SQL
Database that uses the snapshot of the data at the current time

Can I create MSDTC Transaction in PowerShell?

I am using Powershell 4 to do some deployment tasks including running a number of sql files into a database server.
http://technet.microsoft.com/en-us/library/dn464259.aspx
I have found the above cmdlets for MSDTC and I can see one for creating a transaction, rollback and commit. However, I cant find them and I assume its because we are not running Windows 8 and/or Server 2012 R2.
Is there a way that I can create a transaction scope using MSDTC so I can execute all of my scripts knowing that if any of them fail, that they will all roll back?
We are running Windows 7 & Server 2008 R2.
I don't think that you should try using PowerShell with MSDTC directly in the way that you are thinking. Instead I would just use plain old SQL TRANSACTION statements: BEGIN COMMIT and ROLLBACK...
I think what you really are looking for is the SQL statement BEGIN DISTRIBUTED TRANSACTION
See: MSDN: BEGIN DISTRIBUTED TRANSACTION
This will use MSDTC to execute several scripts over the same session and be able to commit, or rollback the transaction.
If Network DTC Access is enabled, you can use a simple try/catch and invoke .NET's TransactionScope object.
try {
$scope = New-Object -TypeName System.Transactions.TransactionScope
# do stuff
} catch {
$_.exception.message
} finally {
$scope.Dispose()
}
Calling Dispose() on the TransactionScope will either commit or rollback the changes.

Is there an equivalent of ping for checking connectivity to SQL Server?

Is there an equivalent of ping for checking connectivity to SQL Server?
I'm finding our BizTalk Admin Console that during some long operations, e.g. importing a big bindings file, the "connection" is being lost, i.e. the red box appears on the console. Eventually connectivity comes back. The SQL Server is on a different machine from BizTalk.
Also saw an issue where connection to SSO db was lost for a minute or so ... worse, this was production environment!
SQL DBA has checked and SQL is fine, showing no network issues ...
I can do a ping -t to see if anything happens to the connection between the two machines, but is there an equivalent function to check ongoing connectivity to SQL Server itself?
And if there is such a function, is there someway to automate its checking so I can have it flag any occurance of disconnect ... just sending an email to ops would be good first step
You can always use PowerShell
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
$query = "SELECT top 1* from bts_application"
Invoke-Sqlcmd -Query $query -ServerInstance '.' -Database 'BizTalkMgmtDb'

Powershell SQL Server database restore (with CDC)

I am trying to create a Powershell script to restore database to my laptop from my desktop. I have a script which creates the backup files and have almost got the restore script working apart from a strange error I get with a CDC enabled database. What I end up with is an off-line single user database. I have to bring it back on-line and change it back to multi-user manually. Here are the relevant bits of my powershell code ...
$instance = "(local)"
$server = New-Object Microsoft.SqlServer.Management.Smo.Server $instance
$restore = New-Object Microsoft.SqlServer.Management.Smo.Restore
$restore.Action = "Database"
$restore.Database = $dbname
$restore.NoRecovery = $false
$restore.ReplaceDatabase = $true
$restore.Devices.AddDevice($filename, "File")
$restore.SqlRestore($server)
I get an error message saying ...
*System.Data.SqlClient.SqlException: Could not update the metadata that indicates database xxxxx is not enabled for Change Data Capture. The failure occurred when executing the command '[sys].[sp_MScdc_ddl_database triggers 'drop''. The error returned was 15517: 'Cannot execute as the database principal because the principal "dbo" does not exist, this type of principal cannot be impersonated, or you do not have permission'*
and a bit further down ...
*The database has been left offline. See the topic MSSQL_ENG003165 in SQL Server Books Online.*
further down ...
Converting database 'xxxxx' from version 655 to the current version 661.Database 'xxxxx' running the upgrade step from version 655 to version 660.Database 'xxxxx' running the upgrade step from version 660 to 661.
While I can get the database back to a useable state, I would ideally like to have it completely scripted. The idea of this is that I can run the backup script on my desktop and then run the restore script on my laptop, which then restores the databases on my laptop so I have a working copy of the same database for when I need to work remotely.
Any insights would be great, even better if someone has come across and solved the same problem.
When I had to set an explicit CDC setting in a restore script, I did something like:
$script_lines = $restore.script( $server )
$script_lines += ', keep_cdc'
$script = ''
foreach ($line in $script_lines) {
$script += $line
}
$script
invoke-sqlcmd -ServerInstance $server.name -Query $script -QueryTimeout 65535

Resources