Saturday, June 11, 2011

if you hate tape backups and need to vault (externalize), this script is for you !

If you are an happy owner of a decent RAID HBA like 3Ware, and you cannot stand tape backups, there are options : USB2 drives, ok, too slow. let's wait for USB3 compliant servers. The other option are e-SATA drives, faster, but somewhat big (the enclosure), and such a chore to take it with you.

Instead, you can choose a DTD backup (disk to disk) with the externalized set of disks manage by your internal HBA (of course you need a disk in a tray, hot pluggable) the majority of entry and middle servers have it so it should not be a problem.

So the strategy is : Backup at non production hours (if possible) to a RAID5 array.
Create a duplication job to a RAID0 1TB or 2TB disk (only one disk)
After the duplication is finished, issue a windows command to unmount the disk.
When it is done, issue a HBA CLI command to REMOVE the single RAID0 disk (not DELETE) The following script also manages rotation on five disks to ensure that the correct disk is inserted based on day.

Works well on Windows 2003 R1 R2, but on some servers i had an issue : the disk GUID would not change after replacing a disk, so test it well in you environment !
PS : tested with Symantec backup Exec

Here comes the code :



GUID_Lundi = "\\?\Volume{b33c7f05-0faa-497b-be6c-27e9be089cea}\"
GUID_Mardi = "\\?\Volume{5530f634-8fa9-11dd-99e0-40001abd57ea}\"
GUID_Mercredi = "\\?\Volume{5530f641-8fa9-11dd-99e0-40001abd57ea}\"
GUID_Jeudi = "\\?\Volume{e2a17ddf-955a-11dd-8103-4000a7d5a89c}\"
GUID_Vendredi = "\\?\Volume{e2a17de9-955a-11dd-8103-4000a7d5a89c}\"

Set ofso = createobject("scripting.filesystemobject")
set oshell = createobject("wscript.shell")
set ofile = ofso.opentextfile("c:\scripts\log.txt",8,true)



Dim Day

Day = DatePart("w", now)

ofile.writeline "script start, inventory of disks - day " & Day & " of week"
oshell.run "c:\scripts\tw_cli.exe maint rescan c0" , 0 , true
ofile.writeline "rescan controller done, waiting 30 secs"

wscript.sleep 30000

strComputer = "."

Set objWMIService = GetObject("winmgmts:" _

& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colItems = objWMIService.ExecQuery("Select * from Win32_Volume")



For Each objItem In colItems



ofile.writeline "Found disk:"

ofile.Writeline "Device ID: " & objItem.DeviceID

ofile.Writeline "Drive Letter: " & objItem.DriveLetter

ofile.Writeblanklines 1

Select case objItem.DeviceID

Case Guid_Lundi


if day = 2 then


oshell.run "mountvol E: " & Guid_Lundi

ofile.Writeline "monday disk found - mount attempted"

wscript.sleep 30000

oshell.run "label E: BACKUP-LUNDI"

ofile.Writeline "labelling disk BACKUP-LUNDI"

Else


oshell.run "mountvol E: " & Guid_Lundi & " /d"

ofile.Writeline "wrong disk found - unmount attempted"
wscript.sleep 30000

oshell.run "c:\scripts\tw_cli.exe maint remove c0 u2",0, true

ofile.Writeline "wrong disk found - remove disk command executed"

End if



Case Guid_Mardi

if day = 3 then


oshell.run "mountvol E: " & Guid_Mardi

ofile.Writeline "tuesday disk found - mount attempted"


wscript.sleep 30000

oshell.run "label E: BACKUP-MARDI"

ofile.Writeline "labelling disk BACKUP-MARDI"

Else

oshell.run "mountvol E: " & Guid_Mardi & " /d"

ofile.Writeline "wrong disk found - unmount attempted"


wscript.sleep 30000

oshell.run "c:\scripts\tw_cli.exe maint remove c0 u2",0, true

ofile.Writeline "wrong disk found - remove disk command executed"


End if


Case Guid_Mercredi


if day = 4 then


oshell.run "mountvol E: " & Guid_Mercredi

ofile.Writeline "wednesday disk found - mount attempted"


wscript.sleep 30000

oshell.run "label E: BACKUP-MERCREDI"

ofile.Writeline "labelling disk BACKUP-MERCREDI"


Else


oshell.run "mountvol E: " & Guid_Mercredi & " /d"

ofile.Writeline "wrong disk found - unmount attempted"


wscript.sleep 30000

oshell.run "c:\scripts\tw_cli.exe maint remove c0 u2",0, true

ofile.Writeline "wrong disk found - remove disk command executed"


End if

Case Guid_Jeudi

if day = 5 then


oshell.run "mountvol E: " & Guid_Jeudi

ofile.Writeline "thursday disk found - mount attempted"


wscript.sleep 30000

oshell.run "label E: BACKUP-JEUDI"

ofile.Writeline "labelling disk BACKUP-JEUDI"


Else


oshell.run "mountvol E: " & Guid_Jeudi & " /d"

ofile.Writeline "wrong disk found - unmount attempted"


wscript.sleep 30000

oshell.run "c:\scripts\tw_cli.exe maint remove c0 u2",0, true

ofile.Writeline "wrong disk found - remove disk command executed"


End if


Case Guid_Vendredi



if day = 6 then

oshell.run "mountvol E: " & Guid_Vendredi

ofile.Writeline "friday disk found - mount attempted"

wscript.sleep 30000

oshell.run "label E: BACKUP-VENDREDI"

ofile.Writeline "labelling disk BACKUP-VENDREDI"

Else


oshell.run "mountvol E: " & Guid_Vendredi & " /d"

ofile.Writeline "wrong disk found - unmount attempted"

wscript.sleep 30000

oshell.run "c:\scripts\tw_cli.exe maint remove c0 u2",0, true

ofile.Writeline "wrong disk found - remove disk command executed"


End if


Case Else


ofile.writeline "not a listed removable backup drive, ignoring"

End Select


Next
ofile.close

Friday, June 10, 2011

Ghetto style mailbox sync between two exchange 2003 servers

Hello folks, some 5 years ago I figured out that it could be easy to synchronize the contents of mailboxes from one server to the other. i came up with a rather clumsy way of doing it : using ExIFS (R.I.P. exifs ! this feature is now absent from Exchange 2K7 and Exchange 2K10)

So if you're broke and cant afford Exchange 2007 / 2010 and their fancy SCR, CCR, DAG features, this is for you.

So what's the big idea : All you need is a little registry tweak in order to make exifs available thru the M: drive (like on Exchange 2000)
The robocopy tool and a little VBS script in a scheduled task.

Alas, it seems that it works only well with tiny mailboxes (less than 200 MB, 5000 mails) , but i didn't try all the robocopy switches, so maybe there is a way to avoid partial replications (missing mails).

Some drawbacks : folders must have the same names (useful to edit them on the passive server with outlook /resetfoldernames or just rename thru Exifs) And contacts are not happy with this method of syncing

And finally do not forget to put the send as / read as for your users to access their backup mbx, and also for the identity that run the script not to be blocked by "access denied"

So here is the script :



on error resume next


Const ADS_PROPERTY_CLEAR = 1
Const ADS_PROPERTY_UPDATE = 2
Const ADS_PROPERTY_APPEND = 3
Const ADS_PROPERTY_DELETE = 4

Dim AliasExchange
Dim IsMailSync

Set oshell = createobject("wscript.shell")
Set oFSO = createobject("scripting.filesystemobject")

Set oFile = oFSO.opentextfile("c:\scripts\mailsyncSRV1toSRV2.log",8,true)

ofile.writeline "##############début de synchronisation SRV1 vers SRV2 :" & Date() & " " & Time() & "#####################"
ofile.writeblanklines 2


set objParent = GetObject("LDAP://OU=allusers,DC=domain,DC=local")
objParent.Filter = Array("user")


for each objUser in objParent

AliasExchange = objuser.mailnickname


objuser.getinfo
err.clear
IsMailSync = objuser.get("extensionAttribute4")

if err.number <> 0 then isMailSync = ""


err.clear

if IsMailSync = "NoMailSync" then

ofile.writeline "Synchro de boite aux lettres desactivée pour : " & aliasexchange

Else

CheckFolderNames(aliasexchange)

ofile.writeline "Synchronisation de : " & aliasexchange & " debutée à : " & date() & " " & time()

oshell.run """C:\Program Files\Windows Resource Kits\Tools\robocopy.exe"" ""\\srv1\MBX$\" & AliasExchange & """ ""M:\domain.com\MBX\sec." & AliasExchange & """ /XD ""\\srv1\MBX$\" & AliasExchange & "\Contacts" & """ /E /MIR /XC /XA:H /R:0 /W:0 /NP /NFL /LOG+:c:\Scripts\sync-mbx-srv1-to-srv2.log", 0

ofile.writeline "Synchronisation de : " & aliasexchange & " finie à : " & date() & " " & time()

if err.number <> 0 then ofile.writeline err.number & " " & err.description

end if

Next

ofile.writeline "##############Fin de synchronisation SRV1->SRV2 :" & Date() & " " & Time() & "#####################"
ofile.writeblanklines 2

ofile.close


Sub CheckFolderNames(Alias)

on error resume next

set ofso = createobject("scripting.filesystemobject")
dim ofolder
dim odestfolder
dim erra
dim errb

err.clear
set ofolder = ofso.getfolder("\\srv1\MBX$\" & alias & "\Éléments envoyés")
set ofolder = ofso.getfolder("\\srv1\MBX$\" & alias & "\Éléments supprimés")
erra = err.number

err.clear
set ofolder = ofso.getfolder("\\srv1\MBX$\" & alias & "\Eléments supprimés")
set ofolder = ofso.getfolder("\\srv1\MBX$\" & alias & "\Eléments envoyés")
errb = err.number
err.clear

if (erra = 0) and (errb = 0) then exit sub

if erra = 0 then

set odestfolder = ofso.getfolder("M:\DOMAIN.COM\MBX\sec." & alias & "\Eléments envoyés")
odestfolder.name = "Éléments envoyés"
set odestfolder = ofso.getfolder("M:\DOMAIN.COM\MBX\sec." & alias & "\Eléments supprimés")
odestfolder.name = "Éléments supprimés"
err.clear

elseif errb = 0 then


set odestfolder = ofso.getfolder("M:\DOMAIN.COM\MBX\sec." & alias & "\Éléments envoyés")
odestfolder.name = "Eléments envoyés"
set odestfolder = ofso.getfolder("M:\DOMAIN.COM\MBX\sec." & alias & "\Éléments supprimés")
odestfolder.name = "Eléments supprimés"
err.clear

end if

End Sub

Tuesday, June 7, 2011

How to import PST disseminated on Outlook clients on a Exchange mailbox by script

the First script of the chain is missing : it involves creating a SQL express database to fecth all PSTs existing on client computers by the means of an INSERT SQL statement in a logon script, the only catch is that it supposes that all PST are stored on their default profile locations !

The last script is a powershell one and imports the PST in exchange mailbox based if the PST has the name of SamACCountName.

PS with exchange
you cannot have an import PST folder with multiple PST of the same user, so you have to dispatch them !!

on error resume next

Set WShell = CreateObject("WScript.Shell")
Set WSHnetwork = CreateObject("WScript.Network")
Set oFSO = CreateObject("scripting.filesystemobject")
Set oLogFile = OFSO.CreateTextFile("c:\IMPORT3\log.txt")
Dim strcomputer
strcomputer = lcase(WshNetwork.computername)

'############# RECUP PST #############

ologfile.writeline "***SCRIPT START***"
ologfile.writeline date & " " & time


Set Conn = Wscript.CreateObject("ADODB.Connection")

Conn.ConnectionTimeout = 10
Conn.CommandTimeout = 10

err.Clear
Conn.Open "Provider=SQLOLEDB;Data Source=SRVINFO\SQLEXPRESS,1433;Trusted_Connection=yes;Initial Catalog=PST_stats;APP=PST Stats"
ologfile.writeline "Connection opened, error=" & err.number

Set Command = Wscript.CreateObject("ADODB.Command")
Set Command.ActiveConnection = Conn
Command.commandtext = "SELECT * FROM TablePST WHERE skip IS NULL ORDER BY username,path"
'Command.commandtext = "SELECT * FROM TablePST WHERE username = 'user_to_skip' AND skip IS NULL ORDER BY username,path"

err.clear
Set rst = Command.execute
ologfile.writeline "Command executed, error=" & err.number

Dim previoususer
previoususer = ""
Dim i
i = 0

While Not(rst.EOF)

user = rst("username")
computer = rst("computer")
path = rst("path")
size = rst("size")

ologfile.Writeline vbcrlf
ologfile.writeline "**found record:**"
ologfile.writeline date & " " & time
ologfile.Writeline "User=" & user
ologfile.Writeline "computer=" & computer
ologfile.Writeline "path=" & path
ologfile.Writeline "size=" & size

ologfile.Writeline "Comparing user and previoususer user=" & user & " previoususer=" & previoususer

If user = previoususer then
i = i + 1
Else
i = 0
End if

uncpath = "\\" & computer & "\" & Replace(path,":","$")

pos = Instrrev(uncpath,"\")
filename = mid(uncpath, pos + 1, len(uncpath))

uncpath = left(uncpath, pos - 1)

destpath = "C:\IMPORT3\PST" & cstr(i) & "\" & user
ologfile.Writeline "uncpath=" & uncpath
ologfile.writeline "destpath=" & destpath
ologfile.Writeline "filename=" & filename

err.clear
if not OFSO.FolderExists("C:\IMPORT3\PST" & cstr(i) & "\" & user) then
OFSO.CreateFolder "C:\IMPORT3\PST" & cstr(i)
OFSO.CreateFolder "C:\IMPORT3\PST" & cstr(i) & "\" & user
End if

ologfile.writeline "created folder c:\IMPORT3\PST" & cstr(i) & "\" & user & " with error:" & err.number

Wshell.run "robocopy.exe " & chr(34) & uncpath & chr(34) & " " & chr(34) & destpath & chr(34) & " " & filename & " /NP /R:0 /W:0 /LOG+:C:\IMPORT3\" & user & cstr(i) & ".txt", 0, false
ologfile.writeline "ran command ROBOCOPY"

Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
wscript.sleep 2000
err.clear
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process where Name='robocopy.exe'")
numofprocesses = colProcess.count
ologfile.writeline "Number of ROBOCOPY processes=" & numofprocesses & " with error:" & err.number & " " & err.description

do while numofprocesses > 9

wscript.sleep 2000
err.clear
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process where Name='robocopy.exe'")
numofprocesses = colProcess.count
ologfile.writeline "Too many ROBOCOPY processes, sleeping 2000 msec=" & numofprocesses & " with error:" & err.number


loop

previoususer = user


rst.MoveNext
ologfile.Writeline "Moving to next record"
Wend


ologfile.writeline "***SCRIPT STOP***"
ologfile.writeline date & " " & time

rst.close
Conn.close

Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process where Name='robocopy.exe'")
numofprocesses = colProcess.count

do while numofprocesses > 0

wscript.sleep 2000
err.clear
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process where Name='robocopy.exe'")
numofprocesses = colProcess.count
ologfile.writeline "Waiting for ROBOCOPY processes to finish, sleeping 2000 msec=" & numofprocesses & " with error:" & err.number

loop



'#### COPIE FICHIERS ####

Set oFSO = CreateObject("scripting.filesystemobject")


set oRootFolder = OFSO.Getfolder("C:\IMPORT3")

For each oPSTFolder in oRootFolder.subfolders

For each oUserFolder in oPSTFolder.subfolders

UserFolderName = oUserFolder.name

For each oPSTFile in oUserFolder.files

oPSTFile.move oPSTFolder.path & "\" & UserFolderName & ".pst"

Next


Next

For each oUserFolder in oPSTFolder.subfolders

oUserFolder.delete

Next

Next


set oRootFolder = OFSO.Getfolder("C:\IMPORT3")

For each oPSTFolder in oRootFolder.subfolders

ologofile "Running import on:" & oPSTFolder.name
wshell.run "C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe c:\temp\importmbx.ps1 " & oPSTFolder.path, 3 , true

Next

ologfile.close




Anf finally the powershell script to import in the exchange mailboxes !
The scripts are not perfect, so you have suggestions, feel free to comment


Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin
Dir $args[0] | Import-Mailbox -Confirm:$false -BadItemLimit 10000 -debug -verbose