Backup folder issues - c#

When running the code below, I keep getting "access is denied", but i have full administrative rights on the folder.
{"Cannot open backup device '\\\\networkDrive\backups\\'. Operating system error 5(Access is denied.).\r\nBACKUP DATABASE is terminating abnormally."}
the folder target is #"\networkDrive\backups";
I've also tried #"C:\backups"\ and #"C:\backups"
private static string publicConString = "server=myServer;Trusted_Connection=Yes;persist security info=False;connection timeout=120";
public static void BackupDatabase(String databaseName, String userName, String password, String serverName, String destinationPath)
{
Backup sqlBackup = new Backup();
sqlBackup.Action = BackupActionType.Database;
sqlBackup.BackupSetDescription = "ArchiveDataBase:" +
DateTime.Now.ToShortDateString();
sqlBackup.BackupSetName = "Archive";
sqlBackup.Database = databaseName;
BackupDeviceItem deviceItem = new BackupDeviceItem(destinationPath, DeviceType.File);
SqlConnection sqlCon = new SqlConnection(publicConString);
ServerConnection connection = new ServerConnection(sqlCon);
Server sqlServer = new Server(connection);
Database db = sqlServer.Databases[databaseName];
sqlBackup.Initialize = true;
sqlBackup.Checksum = true;
sqlBackup.ContinueAfterError = true;
sqlBackup.Devices.Add(deviceItem);
sqlBackup.Incremental = false;
sqlBackup.ExpirationDate = DateTime.Now.AddDays(3);
//sqlBackup.LogTruncation = BackupTruncateLogType.Truncate;
sqlBackup.FormatMedia = false;
sqlBackup.SqlBackup(sqlServer);

From your description, I think you would like to back up database to a network share folder, right?
Based on the error message, I think the SQL Server service account doesn’t have sufficient permission on the share folder. Please ensure the SQL Server service account is a domain account and it has sufficient permissions.
Please refer to this info for more information.
You cannot back up databases to a network drive if your account have not sufficient permissions to access the network drive.

Related

How to backup a database in WPF with C# and SQL Server? [duplicate]

This question already has answers here:
Backup Permissions
(5 answers)
Closed 1 year ago.
I'm trying to back up my database using this C# code How to backup and restore SQL Server in WPF with C# and Entity Framework
private static void CreateBackup(string databaseName, string backupFilePath)
{
GlobalConfig gb = new GlobalConfig();
string connectionString = gb.GetConnectionString();
backupFilePath = backupFilePath + "\\" + databaseName + ".bak";
backupFilePath = #""+backupFilePath;
var backupCommand = "BACKUP DATABASE #databaseName TO DISK = #backupFilePath";
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(backupCommand, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#databaseName", databaseName);
cmd.Parameters.AddWithValue("#backupFilePath", backupFilePath);
cmd.ExecuteNonQuery();
}
}
CreateBackup("Test","C:\Desktop\Backup\\Test.bak");
But I got this error :
Cannot open backup device 'C:\Desktop\Backup\Test.bak'. Operating system error 5(Access is denied.).
What I'm doing wrong with this code?
How can I fix this error?
The SQL Server process typically does not run with the permissions of the currently logged in user, therefore it cannot access the users desktop (nor most of the folders of the user or any network folders). It is not possible to freely choose the folder for the backup.
Your best solution is to export to a folder where the server process has access to (i.e. the system temp folder) and then copy the backup from there to wherever you want it.
private static void CreateBackup(string databaseName, string backupFilePath)
{
GlobalConfig gb = new GlobalConfig();
string connectionString = gb.GetConnectionString();
// Create the backup in the temp directory (the server should have access there)
var backup = Path.Combine(Path.GetTempPath(), "TemporaryBackup.bak");
var backupCommand = "BACKUP DATABASE #databaseName TO DISK = #backup";
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(backupCommand, conn))
{
conn.Open();
cmd.Parameters.AddWithValue("#databaseName", databaseName);
cmd.Parameters.AddWithValue("#backup", backup);
cmd.ExecuteNonQuery();
}
File.Copy(backup, backupFilePath); // Copy file to final location
}
I have successfully backed up SQL Server databases using Microsoft.SqlServer.Management.Smo.Backup, might want to try that. Mine was in VB years ago but it is still working today. Here is the VB code if it helps:
Dim mySourceServer As New Server(My.Settings.SQLInstance)
Dim bkpDBFullWithCompression As New Backup()
' Specify whether you want to back up database or files or log
Me.Cursor = Cursors.WaitCursor()
bkpDBFullWithCompression.Action = BackupActionType.Database
' Specify the name of the database to back up
bkpDBFullWithCompression.Database = _sBackupDatabaseName
bkpDBFullWithCompression.CompressionOption = BackupCompressionOptions.[On]
bkpDBFullWithCompression.Devices.AddDevice(_sBackupFilePath, DeviceType.File)
bkpDBFullWithCompression.BackupSetName = _sBackupDatabaseName + " database Backup - Compressed"
bkpDBFullWithCompression.BackupSetDescription = _sBackupDatabaseName + " database - Full Backup"
Try
bkpDBFullWithCompression.SqlBackup(mySourceServer)
Catch ex As SmoException
blSuccess = False '
Me.Cursor = Cursors.Default
End Try
This solution ( Check Local System account instead of This account ) worked for me, but I didn't have any idea if it's a good solution for security or no.
You can find the LogOn tab setting under this :
Services -> SQL Server -> Properties -> Log on

How to restore backup form one linked server to another linked server using C# SMO

Want to restore a database backup from linked server A to a database located on linked-server B using C#. prefer to use SMO.
I can restore from my local backup to local machine.
{
conn = new ServerConnection
{
ConnectionString = #"Data Source =
(localdb)\MSSQLLocalDb;Initial Catalog=master;Integrated Security=true",
};
try
{
//Restore Full
srv = new Server(conn);
//lsrv = srv.LinkedServers[#"DEVSQL\ALPHA"]; need to figure out how to restore to linked server instead of local.
//srv.KillAllProcesses("G4TestNew");
var res = new Restore();
res.Database = "G4TestNew";
res.Action = RestoreActionType.Database;
filePath = #"\\CABCSERVER\Database\Temp\Full.bak";
res.Devices.AddDevice(filePath, DeviceType.File);
res.ReplaceDatabase = true;
res.NoRecovery = true;
var dataFile = new RelocateFile("G4Test", #"C:\TBD\G4Test.mdf");
var logFile = new RelocateFile("G4Test_log", #"C:\TBD\G4TestNew.ldf");
res.RelocateFiles.Add(dataFile);
res.RelocateFiles.Add(logFile);
res.SqlRestore(srv);
}
EDIT(Adding more detail):.
In this case the linked servers are accessed via 'sql server authentication' and application does not have access to the credential required to connect directly and can use 'Integrated Security' to connect to localdb only.
In SMO you would not connect to one server and then administer a linked server. Instead connect directly to the target server. eg:
ConnectionString = #"Data Source =
DEVSQL\ALPHA;Initial Catalog=master;Integrated Security=true",
srv = new Server(conn);

WinSCP Session.PutFiles with Remove=true gets "Error deleting file System Error. Code: 5" when called from SQLCLR but works in SSIS script task

I am creating an assembly file so that get files and put files can be called as a Stored Procedure from SQL Server. Below is the method for put files. When I pass SqlBoolean DeleteLocalFolder=true, WinSCP errors out with the below error message:
Error: WinSCP.SessionRemoteException: Error deleting file System
Error. Code: 5
[SqlProcedure]
public static void PutFiles(SqlString HostName, SqlString UserName, SqlString Password,
SqlString SshHostKeyFingerprint, SqlString SshPrivateKeyPath,
SqlString SshPrivateKeyPassphrase, SqlString LocalFolderPath,
SqlString RemoteFolderPath,SqlBoolean DeleteLocalFolder,
SqlString FileMask, out SqlString strMessage)
{
// Declare Variables to hold the input parameter values
string hostName = HostName.Value;
string userName = UserName.Value;
string password = Password.Value;
string sshFingerPrint = SshHostKeyFingerprint.Value;
string sshPrivateKey = SshPrivateKeyPath.Value;
string sshPassPhrase = SshPrivateKeyPassphrase.Value;
bool delateLocalFolder = DeleteLocalFolder.Value;
//Local Directory
DirectoryInfo dir = new DirectoryInfo(LocalFolderPath.Value);
string folderName = dir.Name;
//Begin connection to SFTP **Always uses default SFTP port
try
{
string FtpFolderPath = RemoteFolderPath.Value;
SessionOptions options = new SessionOptions()
{
Protocol = Protocol.Sftp,
HostName = hostName,
UserName = userName,
Password = password,
SshHostKeyFingerprint = sshFingerPrint,
SshPrivateKeyPath = sshPrivateKey,
SshPrivateKeyPassphrase = sshPassPhrase
};
//Open the Remote session
using (Session session = new Session())
{
session.ExecutablePath = ExecutablePath+"WinSCP.exe";
session.SessionLogPath = ExecutablePath+"WinscpLog.log";
session.DisableVersionCheck = true;
session.Open(options);
session.ExecutableProcessUserName = "username to log into the sql server instance";
SecureString _Password = new SecureString();
foreach (char _PasswordChar in "password to log in sql instance")
{
_Password.AppendChar(_PasswordChar);
}
session.ExecutableProcessPassword = _Password;
// Upload files
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
transferOptions.FileMask = FileMask.Value;
TransferOperationResult transferoperationalResult;
transferoperationalResult = session.PutFiles(LocalFolderPath.Value, FtpFolderPath, false, transferOptions);
transferoperationalResult.Check();
if (transferoperationalResult.IsSuccess)
{
if (dir.Exists == true)
{
string sourcePath = FtpFolderPath + folderName + "//";
session.MoveFile(sourcePath + "*.*", FtpFolderPath);
session.RemoveFiles(sourcePath);
}
}
}
strMessage = "Upload of files successful";
}
catch (Exception e)
{
//Console.WriteLine("Error: {0}", e);
strMessage = "Error: "+ e;
}
}
I have written custom code in SSIS script task and have called the Session.PutFiles method by passing the argument bool remove = true. It works with out any problem. Can anyone please explain why it errors only in the custom assembly?
By default, processes that reach outside of SQL Server use the security context of the service account of the MSSQLSERVER (or MSSQL.InstanceName) service. When using this default setup, this service account would need to have write / modify access to the folder that the file is in (and possibly to the file itself if the file permissions are using inheritance).
Alternatively, you can use impersonation to change the security context of the external process to that of the Login (within SQL Server) executing the stored procedure, but only if the Login is a Windows Login. SQL Server logins do not have an SID known to the OS, so their security context would use the default, which again is that of the service account for the main SQL Server Database Engine process.
using System.Security.Principal;
public class stuff
{
[SqlProcedure]
public static void PutFiles()
{
using (WindowsImpersonationContext __ImpersonationIdentity =
SqlContext.WindowsIdentity.Impersonate())
{
... {do stuff} ...
__ImpersonationIdentity.Undo();
}
}
}
Also, Martin Prikryl commented on this answer to mention that:
WinSCP .NET assembly supports impersonation on its own. See Session.ExecutableProcessUserName and Session.ExecutableProcessPassword
The idea being ( I assume, though I have not tested):
using System.Security;
...
using (Session session = new Session())
{
session.ExecutableProcessUserName = "some_user_name";
SecureString _Password = new SecureString();
foreach (char _PasswordChar in "some_password")
{
_Password.AppendChar(_PasswordChar);
}
session.ExecutableProcessPassword = _Password;
... other session stuff...
session.Open(options);
...more stuff...
_Password.Dispose();
} /* using() */
In the example above, the username and password are string literals, but in practice those values can come from input parameters to the stored procedure or even from the sqlservr.exe.Config file. Or since the Assembly has a PERMISSION_SET of UNSAFE in order to reference the COM object, you can really get the values from anywhere, including the registry, etc.
So perhaps that is something to try. I am not sure of the timing of when that takes effect, so try one option and if it doesn't work, then try the other.
Please note that, at the very least, the difference between the options is:
Using impersonation via SQLCLR as I provided code for above only allows for the calling Login to be impersonated (and only if that Login is a Windows Login), not any random Login. But, this doesn't require a password either.
Using WinSCP for the impersonation allows for flexibility in what Login / account is being impersonated, BUT it requires passing in a password.
The WinSCP impersonation will allow impersonation to be used by a SQL Server Login, so that is an interesting option there

Connecting to SQL Server from EXE file on shared drive

Our software uses a SqlConnection object to get data from SQL Server. It is running fine for many sites, but for one specific user - it cannot connect to the server. I get an exception:
A network-related or instance-specific error occurred while
establishing a connection to SQL Server.
On this PC the executable file is located on a shared drive. If we copy the executable to a local drive (the desktop) and run it from there, the connection is successful.
Here is what I use to connect to SQL Server:
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
builder.DataSource = SQLServerName;
builder.InitialCatalog = SQLDBName;
builder.WorkstationID = SQLServerName;
builder.PacketSize = 4096;
builder.IntegratedSecurity = true;
builder.PersistSecurityInfo = true;
builder.ApplicationName = "App";
builder.MultipleActiveResultSets = true;
using (SqlConnection myDbConn = new SqlConnection())
{
myDbConn.ConnectionString = builder.ConnectionString;
try
{
myDbConn.Open();
if (myDbConn.State != System.Data.ConnectionState.Open)
{
myDbConn.Close();
return false;
}
myDbConn.Close();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
myDbConn.Close();
return false;
}
}
return true;
We have this problem only for a specific user on a specific PC (running Windows 7). I can't figure why moving the program from a network drive to a local drive would change its behavior. I tried adding trust assemblies full control over the shared drive with no results.

Restore Sqlserver Database Using C# Showing Error

I am trying to backup and restore sql server 2008r2 database, and i successfully backup my database , But the problem is when i am trying to restore my DB its showing the following error
Restore failed for Server 'XXXX-PC\SQLEXPRESS'
And Another important thing is that , When i Close the DB Connection (From server Explorer [right-click->Close Connection]) its working Normally And Correctly
Below is my Backup And Restore Code
Please help me to fix the problem
public static void BackupDatabase(string backUpFile)
{
ServerConnection con = new ServerConnection(#"XXXX-PC\SQLEXPRESS");
Server server = new Server(con);
Backup source = new Backup();
source.Action = BackupActionType.Database;
source.Database = "testBD";
BackupDeviceItem destination = new BackupDeviceItem(backUpFile, DeviceType.File);
source.Devices.Add(destination);
//source.Devices.AddDevice(#"G:\MyBackUp.bak", DeviceType.File);
source.SqlBackup(server);
con.Disconnect();
}
public static void RestoreDatabase(string backUpFile)
{
ServerConnection con = new ServerConnection(#"XXXX-PC\SQLEXPRESS");
Server server = new Server(con);
Restore destination = new Restore();
destination.Action = RestoreActionType.Database;
destination.Database = "testBD";
BackupDeviceItem source = new BackupDeviceItem(backUpFile, DeviceType.File);
destination.Devices.Add(source);
destination.ReplaceDatabase = true;
con.Disconnect();
server.ConnectionContext.Disconnect();
destination.SqlRestore(server);
}
This is the error :
This is the error message in detail:
My guess is that your restore process can't get exclusive access to the database. The Server SMO object has a KillAllProcesses method that takes a database as an argument and does what it says on the tin.

Categories

Resources