Create SQL Server certificate on multiple servers - sql-server

I have created a certificate on SQL server for encrypting my database backups. I used the following article for instructions.
https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/backup-encryption?view=sql-server-2016
I need to restore this database to multiple servers. I can use the following code to import/create the certificate on another server.
-- Copy Certificate to target then create
CREATE CERTIFICATE myCertificate
FROM FILE = 'C:\Temp\CertBackup.cer'
WITH PRIVATE KEY (FILE = 'C:\Temp\CertBackup.pvk',
DECRYPTION BY PASSWORD = 'myPassword');
GO
I would like to be able to import this certificate to many servers at once instead of running this command on each server. Should I script this with Powershell or is there a better way?

DECLARE #password VARCHAR(40) = 'f00bar!23'
select name, 'create certificate ' + QUOTENAME(name) + ' from binary = '
+ CONVERT(VARCHAR(MAX), CERTENCODED(CERT_ID(name)), 1)
+ ' with private key ( binary = '
+ CONVERT(VARCHAR(MAX), CERTPRIVATEKEY(CERT_ID(name), #password), 1)
+ ', decryption by password = ''' + #password + ''')'
FROM sys.[certificates] AS [c]
WHERE name = '«cert name here»';
Run that and it will generate a T-SQL statement that will allow you to create the certificate without having to use a certificate backup. The above assumes that the private key for the certificate is protected by a database master key. If it's protected by a password, you'll have to modify the code accordingly (left as an exercise for the reader).
Note: #password is only used to protect the certificate in transit. That is, it is not the password for the certificate itself.

Related

Adding a Database User for each Customer

I'm creating a database that has two main parts: a front end which the customer sees and a backend that only the company can see.
It is an email subscriber system so I want the customer to be able to view, add, edit and delete their entries in the email subscriber table, but all the admin-related tables should be hidden from them.
As I'm fairly new to SQL Server 2017, I'm still getting my head around logins, users and roles.
The main question I wanted to ask is what is the best way of doing this? I know I can manually set up database users and give them grant/deny permissions, but how do I do this automatically so that every time a customer's details are added to a new row in the customer table, a new database user in the sidebar is added.
Try this
public static void AddUsersToDatabase(string databaseserver, string databasename, string usertobeadded)
{
using (SqlConnection conn = new SqlConnection("server=" + databaseserver + "; database=" + databasename + "; User ID=WPDOMAIN\\spdev; Integrated Security=SSPI; password=Password123;"))
{
conn.Open();
string password = "Password123";
string sql = "CREATE LOGIN " + usertobeadded + " WITH PASSWORD = '" +
password + "'; USE " + databasename + "; CREATE USER " + usertobeadded + " FOR LOGIN " + usertobeadded + ";";
SqlCommand cmd = new SqlCommand(sql);
cmd.ExecuteNonQuery();
conn.Close();
}
}

How to create a Database user in Entity Framework

Been working on my first Entity Framework project. As part of the project I am going to be creating a number of SSRS reports. In order to connect to the database I need to have a Reports user that will only access to the specific database on the server. In the past i have always written a script to add Database users but I want to know is there a way that i can do this using Entity Framework instead?
Assuming your user already has a login defined at the SQL Server level (Security > Logins), you can call the following method from your DB initializer seed method to add the user to the database:
private void AddDbUser(MyDataContext myDB)
{
string accountDomainName = "AccountDomainName"; // replace with user's login domain
string accountLoginID = "AccountLoginID"; // replace with user's login ID
string sql =
"USE [MyDB]" +
"CREATE USER [MyNewUser] FOR LOGIN [" + accountDomainName + "\\" + accountLoginID + "]" +
"ALTER AUTHORIZATION ON SCHEMA::[db_datareader] TO [" + accountLoginID + "]" +
"ALTER AUTHORIZATION ON SCHEMA::[db_datawriter] TO [" + accountLoginID + "]" +
"EXEC sp_addrolemember N'db_datawriter', N'" + accountLoginID + "'" +
"EXEC sp_addrolemember N'db_datareader', N'" + accountLoginID + "'";
myDB.Database.ExecuteSqlCommand(sql);
}
The exact SQL needed is dependent on your configuration. To have SQL Server generate the SQL for your scenario, you could open the add user dialog in SSMS (Database > Users > New User...), fill out the fields, and click the "Script" button at the top instead of hitting OK at the bottom. Note that any "GO" lines will need to be removed from the generated script before pasting it into the method above.
You would need to tell EF to use the appropriate stored procedures to do so. You could also wrap these up in a sproc of your own that wraps the relevant commands. There is no native "CreateReportsUser" type method within EF that I know of.
Edit: I probably should have provided this reference to be a "complete" answer. Apologies.
Here's how you can do what I recommend: How to call Stored Procedure in Entity Framework 6 (Code-First)?

extraction data from file.mdf using pyodbc

I have an application for managing pharmacies (just an example ) this application is developed so that it relates to a single pharmacy . This application map on a Micrsoft DBMS MSSDE
each pharmacy generates database file ( pharmacy.mdf , pharmacy_log.mdf )
So after getting these files from different pharmacies, I wrote a script to automate data extraction from these all files
As the application is running, the script do the following tasks:
- Stops the server MSSQL server
- copy the two files from folder files recover and paste them in the path of the application
- Restart the server
- exctract the desired data
the connection with the database in question is using the python pyodbc Module
Once the extraction is run at a certain time pyodbc crash
i gotte this error:
cursor.execute ('select * from Pha.dbo.table_test ')
pyodbc.Error : ( '01000 ',' [ 01000 ] [ Microsoft] [ODBC SQL Server Driver] [ Shared Memory] ConnectionWrite ( ( send () ( 10054 ) ( SQLExecDirectW ) ')
i wrote this code for connection:
log = os.path.join(path,c,"SIC_log.ldf")
mdf = os.path.join(path,c,"SIC.mdf")
print(log)
print(mdf)
subprocess.call('net stop mssqlserver')
time.sleep(2)
os.system('copy "' + log + '" "' + MSSQL_DIR+'"')
os.system('copy "' + mdf + '" "' + MSSQL_DIR+'"')
time.sleep(2)
subprocess.call('net start mssqlserver')
time.sleep(2)
# Open a connection with the database
cnxn = pyodbc.connect('DSN=SIC_ODBC')
time.sleep(2)
extract_clo(cnxn, wb, ws)
cnxn.close()

Unable to attach with sqlcipher encrypted database to another one

I'm working on an app that is using a database that is encrypted by sqlcipher. The passwort for this encryption is stored by cacheword.
To make a backup of my database I used the following code:
// ggf. Datenbank öffnen
openGuard();
mDb.execSQL("ATTACH DATABASE '" + outFileName + "' AS backup KEY 'asdfghjkl';");
mDb.rawExecSQL("SELECT sqlcipher_export('backup');");
mDb.execSQL("DETACH DATABASE backup;");
The method openGuard() is used to check if the database is opend yet and if not do this.
I've checked the backup by using an empty key to make an unencrypted copy of my database. Then I can use it on adb shell and use sql-statements to get the wanted data.
My problem now for a long time is that I'm not able to use my backup to restore the database of my app. I tried this code:
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(backupFile, "asdfghjkl", null);
db.execSQL("ATTACH DATABASE '" + dbFile + "' AS encrypted KEY '" + mCacheWord.getEncryptionKey() + "';");
db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
db.rawExecSQL("DETACH DATABASE encrypted;");
Alternatively I tried
db.execSQL("ATTACH DATABASE '" + dbFile + "' AS encrypted KEY '" + encodeRawKey(mCacheWord.getEncryptionKey()) + "';");
But in both cases I get the following error message:
10-30 00:56:42.845: I/Database(14407): sqlite returned: error code = 26, msg = statement aborts at 5: [ATTACH DATABASE '/data/data/.../databases/database.db' AS encrypted KEY '[B#42082da0';] file is encrypted or is not a database
10-30 00:56:42.845: E/Database(14407): Failure 26 (file is encrypted or is not a database) on 0x63bdedb0 when executing 'ATTACH DATABASE '/data/data/.../databases/database.db' AS encrypted KEY '[B#42082da0';'
Is there anyone how can help me with my problem?
Looks like you've gotten accidentally "hung up" trying to decrypt by supplying the key via the ATTACH DATABASE statement. Looking at Example 2: Decrypt a SQLCipher database to a Plaintext Database of the API, there's a comment that reads -- empty key will disable encryption. So, presumably your initial decryption code attempt should first execute the PRAGMA key = statement (to supply the key) and then the ATTACH DATABASE statement with empty key (to decrypt) something like this:
db.execSQL("PRAGMA key = '" + mCacheWord.getEncryptionKey() + "';");
db.execSQL("ATTACH DATABASE '" + dbFile + "' AS encrypted KEY '';");
db.rawExecSQL("SELECT sqlcipher_export('encrypted')");
db.rawExecSQL("DETACH DATABASE encrypted;");
Another brief example about this can be seen in this the SQLCipher Users mailing list discussion.
I believe the problem may have to do with the fact that you are using CacheWord, which manages the encryption key separately from SQLCipher. You should verify the format of the string returned from getEncryptionKey() and ensure that it matches the proper format for a raw key in SQLCIpher.

Batch file SQLCMD Restore Loop - Files Disappear

So I've searched and found a lot of info in similar areas, but nothing that quite hits the issue I'm having.
I'm working in a test environment and need to restore from the same SQL database backup (.bak) to many named instances of SQL server. All the sql instances are pre-installed and running.
I played around with different ideas, but a batch file using sqlcmd seems to be the best suited for the job.
So I created a batch file (.bat) that asks for the starting and stopping instance numbers and then should restore from the backup to each SQL named instance incrementing the instance number along the way.
When it runs the sqlcmd appears to work fine. At the end it prints out
RESTORE DATABASE successfully processed X pages in Y seconds
Also the files (.mdf, .ndf, .ldf) are in the directory as expected and then it moves on to the next.
The problem is that when it moves on to the next, the files that were just restored disappear from the directory.
If anyone has any ideas it would certainly be appreciated.
Here's the batch...
ECHO OFF
ECHO Enter starting instance number for restore db
SET /P _IntStart=
ECHO Enter number of last instance for restore db
SET /P _IntStop=
SET /a _IntStop=_IntStop+1
:RestoreDb
If %_IntStart% GEQ %_IntStop% goto:EOF
ECHO Display Instance Number... IntStart = %_IntStart%
sqlcmd -e -s localhost\instance%_IntStart% -d master -U user -P password -Q "Use [master]; RESTORE DATABASE DBName1 FROM DISK = 'C:\DBName1.bak'WITH REPLACE, MOVE 'DBName1' TO 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE%_IntStart%\MSSQL\DATA\DBName1.mdf', MOVE 'DBName1_log' TO 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE%_IntStart%\MSSQL\DATA\DBName1_log.LDF', MOVE 'ftrow_DBName1Catalog' TO 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE%_IntStart%\MSSQL\DATA\DBName1_1.ndf';"
SET /a _IntStart=_IntStart+1
GOTO:RestoreDb
PAUSE
EXIT
=========================================
From SQL Mgmt. Studio I've also tried the below. It works if I comment out the loop and run it each time manually bumping the instance number. It will create separate copies of the db and files. The problem here is SQLCMD doesn't appear to like concatenation in Mgmt. Studio so I can't increment the instance number in the :CONNECT. It ends up trying to connect to localhost\instance$(SCintnum).
Declare #intnum int
Set #intnum = 1
Declare #intstr NVARCHAR(255)
Set #intstr = #intnum
Declare #PathName1 NVARCHAR(255)
Declare #PathName2 NVARCHAR(255)
Declare #PathName3 NVARCHAR(255)
Set #PathName1 = 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE' + #intstr + '\MSSQL\DATA\DBName1.mdf'
Set #PathName2 = 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE' + #intstr + '\MSSQL\DATA\DBName1_log.LDF'
Set #PathName3 = 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE' + #intstr + '\MSSQL\DATA\DBName1_1.ndf'
While #intnum < 51
:SETVAR SCintnum 1
:CONNECT localhost\instance$(SCintnum) -U user -P password
Use [master];
RESTORE DATABASE DBName1 FROM DISK = 'C:\DBName1.bak'
WITH REPLACE,
MOVE 'DBName1' TO #PathName1,
MOVE 'DBName1_log' TO #PathName2,
MOVE 'ftrow_DBName1Catalog' TO #PathName3;
:SETVAR SCintnum $(SCintum)+1
Set #intnum = #intnum+1
Set #intstr = #intnum
Set #PathName1 = 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE' + #intstr + '\MSSQL\DATA\DBName1.mdf'
Set #PathName2 = 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE' + #intstr + '\MSSQL\DATA\DBName1_log.LDF'
Set #PathName3 = 'E:\Microsoft SQL Server\MSSQL10_50.INSTANCE' + #intstr + '\MSSQL\DATA\DBName1_1.ndf'
===================================================================
This is an example of what I ended up using in c#.....
///Set SQL Connection
SqlConnection myConnection = new SqlConnection("user id=sa;" +
"password="+ sapassword+";server="+servername+"\\instance"+currentinstancenum+";" +
"Trusted_Connection=yes;" +
"database=master; " +
"connection LifeTime=0; connection Timeout=30");
///Set SQL Command
string thesqlcommand = "USE [master]; RESTORE DATABASE " + dbname + " FROM DISK = '" + backuplocation + "' WITH REPLACE, MOVE '" + dbname + "' TO " + #PathName1 + ", MOVE '" + dbname + "_log' TO " + #PathName2 + ", MOVE 'ftrow_" + dbname + "Catalog' TO " + #PathName3 + ";";
SqlCommand myCommand = new SqlCommand(thesqlcommand, myConnection);
///Set SQL Command TimeOut, open connection, execute command, close command
myCommand.CommandTimeout = 180;
myCommand.Connection.Open();
myCommand.ExecuteNonQuery();
myConnection.Close();
It is doing what you asked...
MOVE 'DBName1'
and
MOVE 'DBName1_log'
Ended up creating a little utility in C# to do this. Wish I had started there as it was far simpler. I added an example to the bottom of the original post.

Resources