Copy Files via a Mapped Drive using Powershell

Here’s a quick script to copy files via a mapped drive in powershell.  In my case, this was copying a bunch of .bak files located in separate directories located in the $path.  It only copies the latest .bak file. 

I should also warn you, this is kind of hard to test.  I believe there is a bug in the wscript.network that will error out (I forget the text of the error, but you’ll see it soon enough) that will keep erroring when you run it additional times. 

When this is run as a job, say a couple times daily it seems to work fine, but this makes debugging & stepping through the code a frustrating endeavor.  I don’t know if Powershell 3 has an easier way to do this.  I may have to investigate.

As always, use at your own risk.  Worked for me, may not for you.

$path = "c:\SourceDirectory\"
$destPath = "\\192.168.1.1\TargetDirectory"
$destDrive = "x:"

function CopyLastBackup
{
    $net = new-object -ComObject WScript.Network
    try
    {
        $net.MapNetworkDrive($destDrive, $destPath, $false, "UserName", "Password")
        $dirs = Get-ChildItem $path | where {$_.PSIsContainer} | where {$_.GetFiles().Count -ne 0}
        foreach($dir in $dirs)
        {
            #write-host $dir.FullName
			$file = Get-ChildItem -LiteralPath $dir.FullName -include "*.bak" | sort CreationTime -Descending | select -First 1
        	Copy-Item -LiteralPath $file.FullName -Destination $destDrive -force
        }
        $net.RemoveNetworkDrive($destDrive,"true","true")
        return 0;
    }
    catch
    {
        write-host $error[0]
        return 1
        #return ("Error: {0}" -f $error[0])
    }
    finally
    {
        $net.RemoveNetworkDrive($destDrive,"true","true")
    }
    
}

$ret = CopyLastBackup
if($ret -eq 1)
{
    throw "Failure"
}

File Free Space Query

Quick post on how to check how much space is free in your sql data files.

SELECT
	a.FILEID,
	[FILE_SIZE_MB] = 
		CONVERT(DECIMAL(12,2),ROUND(a.size/128.000,2)),
	[SPACE_USED_MB] =
		CONVERT(DECIMAL(12,2),ROUND(FILEPROPERTY(a.name,'SpaceUsed')/128.000,2)),
	[FREE_SPACE_MB] =
		CONVERT(DECIMAL(12,2),ROUND((a.size-FILEPROPERTY(a.name,'SpaceUsed'))/128.000,2)) ,
	NAME = LEFT(a.NAME,30),
	FILENAME = LEFT(a.FILENAME,60)
FROM
	dbo.sysfiles a

Sql Server Black Box Trace

The black-box trace is a trace that is used by Microsoft when trying to diagnose problems.  It’s a pretty lightweight trace that captures only the  SP:Starting, SQL:BatchStarting, Exception, and Attention events (see here).

This trace however, does not survive a restart of the instance.  You must re-start the trace if the instance bounces. 

Here’s how you declare the trace

--Declare the trace
DECLARE 
	@trcID INT;

EXEC sp_trace_create @trcID OUTPUT, @options = 8;
SELECT @trcID AS TraceID
GO

In the @options, 8 means this will be a black-box trace.  Query the trace to ensure that it was created successfully.  Mind you, even though you’ve created the trace, it does not start when created.  You need to start it manually later using sp_trace_setstatus (see below).

--Get trace info
SELECT * FROM fn_trace_getinfo (2);
GO

Here are my results: 

image

Here are the properties returned by the trace:

  • Property 1:  Trace options.  Rather than me explain them here, see this link.
  • Property 2:  File name.  This is not configurable for a black-box trace.
  • Property 3:  Max File size in MB.  Again, not configurable for a black-box trace.
  • Property 4:  The stop time for the trace.  Null means run until the trace is stopped manually.
  • Property 5:  The traces’ current status.  1 = ON, 0 = OFF.

I’m focusing specifically on capturing DML statements here, so let’s start the trace:

--Start the trace
EXEC sp_trace_setstatus @traceid = 2, @status = 1;
GO

The @status = 1 indicates that you want the trace started.  If you want to stop the trace, simply replace the @status = 1 to @status = 0.

Now, let’s look the information being brought back in the trace for the events:

--You can get the path from the 'Get trace info' select above
SELECT * FROM fn_trace_gettable (
'G:\Sql_2008_Data\blackbox.trc',
DEFAULT);
GO

I’ll refrain from posting a screenshot.  If you ran the SELECT statement above, you’ll see that it captures quite a lot of data.  More than enough for you to be able to mine whatever data you’re looking for, in a general sense.  Like I said, this is a high-level lightweight trace. 

So, focusing on DML.  I’ve created a table called TestTable in my database consisting of an ID field and a Value field of type VARCHAR(100).  Let’s insert 100 rows into this table:

--Create table & test data
CREATE TABLE dbo.TestTable(
	ID		INT NOT NULL PRIMARY KEY IDENTITY(1,1),
	Value	VARCHAR(100) NOT NULL
)

INSERT INTO dbo.TestTable(Value)
SELECT 'Test'
GO 100

Now, let’s see if we can see this in the trace:

SELECT * FROM fn_trace_gettable (
'G:\Sql_2008_Data\blackbox.trc',
DEFAULT)
WHERE TextData LIKE '%CREATE TABLE%'
ORDER BY StartTime DESC
GO

Yup, got it:

image

Now, let’s delete some rows and see if we can capture that:

--Do some DML
DELETE FROM dbo.TestTable
WHERE ID > 50

Yup, got that too:

image

The black-box trace is a very handy for capturing basic info for auditing on your servers.  Should you leave it on all the time?  That’s up to you.  I don’t keep it running at all times, but there are times that I wish I had.  You do have other resources to find (for example, DML) operations that have been run lately on your sql servers without having the default trace enabled, as Jack Vamvas (blog | twitter) points out in his blog post, but these depend upon CDC or Change Tracking to be enabled.  I believe most people don’t have either of these enabled on all their sql servers.  The fn_dblog() function is available on all servers, but unfortunately it can tell you what was deleted (although you do have to do some digging to parse it out…hmm…next blog post methinks), it cannot tell you who deleted it.  Just FYI, I’m in no way slamming Jack’s wonderful post.  He’s got one of my favorite blogs to follow.  Concise, to the point, and no fluff.  I hate fluff.  Winking smile

Oh, what’s that?  I made you create all these traces and whatnot and you don’t know how to get rid of them?  Sigh, here you go.  Be a good citizen and always clean up after yourself.

--Stop the trace
EXEC sp_trace_setstatus @traceid = 2, @status = 0;
GO

--Delete the trace
EXEC sp_trace_setstatus @traceid = 2, @status = 2;
GO

 

@status = 0 stops the trace, @status = 2 deletes the trace.  If you try to delete a trace without stopping it first, you’ll get an error:

image