Sunday, December 18, 2011

Converting a Clustered Shared Volume (CSV) from MBR to GPT without data loss

Today i converted a MBR disk to GPT without data loss. The goal is to overcome the 2TB volume limit
The disk was a CSV disk in a Windows 2008 R2 Hyper-V Cluster.

Here is the procedure :

  • Export the VM on your CSV or check your VM backups first in case something goes wrong
  • Put HA VMs offline in order to quiesce I/O on the CSV for additional security.
  • Put your CSV on maintenance mode. Check the node on which the CSV resource is online
  • Download GPT fdisk at http://sourceforge.net/projects/gptfdisk/ and extract on the node on wich the CSV is online
  • On the said node, run GDISK on a command prompt
  • Say yes to the prompt to accept MBR to GPT conversion
  • Type w to commit changes to the disk. It will not overwrite your partitions in the sense that it will erase data on it, so say yes again
  • Rescan disks in Disk management. Check the disk properties, volume, and verify that the disk type is now GPT
  • Disable maintenance mode on the cluster and disable redirected access, if unable to disable redirected access,take the CSV offline and back online
  • Put your HA VM back online. to test everything is OK before extending the Windows volume
  • Try to move your VM (ideally using live migration) from one node to the other and back. If unable to do so, stop the cluster service on the offending node and start it again using the cluster console. Retry moving VMs. It should work now.
  • Now you will be able to extend your Windows CSV volume beyond 2TB (you can stop VMs and put back on maintenance mode and extend on the node that owns the CSV disk if you want total security)
Notes : MBR to GPT should work fine in most scenarios (in my case 1 primary partition, large offset  (2048 sectors) to the beginning of the first partition to fit MBR structures, plenty of space at the end of the partition (since i was preparing an extension)

Thursday, September 22, 2011

D2D Backup revisited, this time using HP Smart Array and Windows 2008 R2

Here is an example of D2D backup with the possibility of vaulting (externalization) of the destination drive.
For this to work you will need a number of RAID0 disks that you will rotate by plugging/unplugging them.


On HP smart arrays there is no option to remove gracefully a RAID0 unit, and moreover it will place the unit in a "dirty" configuration upon reconnect, even if there was no I/O at the time of disk removal. This is quite a shame, so if you need a more robust solution, go for a D2D disk solution based on LSI 3Ware for instance.

Basically, there are two scheduled tasks, one for removal, and the other for reconnect. The first script will :
  1. Unmount the drive from the system using diskpart
The second task will call a script in ordrer to :
  1. Tell the HP smart array to ignore the status of the disk and make it online anyway (you can check the status in ACU before and after the first subtask execution).
  2. Make it online and mount it in Windows (using diskpart)
So here are the scripts, first to disconnect the drive :
dim oshell
set oshell = createobject("wscript.shell")

oshell.run "diskpart /s diskpart-remove.script",0, true

The diskpart "diskpart-remove.script" file :

select volume 3
remove
select disk 1
offline disk

And for reconnect :

dim oshell
set oshell = createobject("wscript.shell")

oshell.run chr(34) & "C:\Program Files (x86)\Compaq\Hpacucli\Bin\hpacucli.exe" & chr(34) & " ctrl slot=0 ld 2 modify reenable forced",0, true
wscript.sleep 60000
oshell.run "diskpart /s diskpart-add.script",0, true
wscript.sleep 10000
oshell.run "diskpart /s diskpart-add2.script",0, true


The diskpart "diskpart-add.script" file :

rescan
select disk 1
online disk

and finally, the diskpart "diskpart-add2.script" file :

rescan
select volume 3
assign

Note :
On Windows 2008 R2 (did not test on 2008) the diskpart "assign" command will mount the volume on the same drive letter it had at the moment of the diskpart "remove" command.

Do not forget to change the disk/volume IDs according to your configuration, and make sure that there are no I/O (backups) at the moment of drive disconnect !

I hope you will find these scripts useful !

Monday, August 1, 2011

Scripts for monitoring session logon, connect, logout, lock/unlock events across domain

Ok, here is a nice script to use for security purposes, time tracking or whatever fancy name you give to this solution
It is possible since Windows 2008 to use triggers like session lock, unlock, remote connect (RDP) in scheduled tasks. Moreover, the tasks run in the context of the user that triggered the event.
So basically we have to deploy tasks on all Vista/7/2008 R2 computers, tasks that will run when these events are triggered and will, for instance, insert in a database the user various session events (in our case we report by sending an HTTP request with additional data to a linux web server)
Additional data that is available is : user samaccountname, computer name where the event fired, client computer name in a RDP connect/logon event, type of event, of course event time is calculated at the database level.
To make all this work, we must first deploy the tasks on all computers. That will be done with a startup batch script in a GPO
but first you have to create all the tasks that will run our master script (the one that reports events) on a reference computer :
do not forget :
One task = one trigger = one command line with the type of event as a script argument
and do not forget to allow an adequate user group to run the task. Windows will run the task in the context of a user if he is member of the group.
now define the scheduled task action :
Easy : path to the script + EVENT TYPE as argument
(I could not find a way to know during script runtime what trigger fired the task) so we have to create as much scripts than the events we wish to monitor.
command line exemple :
c:\windows\scripts\user-event-monitor.vbs RCONNECT
You may use any moniker, i use these :
LOGON (session logon)
LOGOFF (session logoff / task trigger DOES NOT EXIST, you have to use a GPO logoff script))
RCONNECT (remote, RDP connect)
RDISCONNECT (RDP disconnect)
LCONNECT (connect to a user session by console, when switching users on Vista/7)
LDISCONNECT (see above, disconnect)
LOCK (session lock)
UNLOCK (session unlock)
So it makes a bunch of tasks, now you have to export their definition using SCHTASKS and the XML switch and create a batch to import them on all client computers and RDS servers. (by GPO as said above)
Here is the deployment batch :

mkdir %windir%\scripts
xcopy file://domain.local/NETLOGON/User-Monitor.vbs %windir%\scripts\User-Monitor.vbs
xcopy file://domain.local/NETLOGON/User-Monitor-RConnect.xml %windir%\scripts\User-Monitor-RConnect.xml
xcopy file://domain.local/NETLOGON/User-Monitor-LConnect.xml %windir%\scripts\User-Monitor-LConnect.xml
xcopy file://domain.local/NETLOGON/User-Monitor-RDisconnect.xml %windir%\scripts\User-Monitor-RDisconnect.xml
xcopy file://domain.local/NETLOGON/User-Monitor-LDisconnect.xml %windir%\scripts\User-Monitor-LDisconnect.xml
xcopy file://domain.local/NETLOGON/User-Monitor-Logon.xml %windir%\scripts\User-Monitor-Logon.xml
xcopy file://domain.local/NETLOGON/User-Monitor-Lock.xml %windir%\scripts\User-Monitor-Lock.xml
xcopy file://domain.local/NETLOGON/User-Monitor-Unlock.xml %windir%\scripts\User-Monitor-Unlock.xml
schtasks /create /xml %windir%\scripts\User-Monitor-LConnect.xml /tn "User-Monitor-LConnect"
schtasks /create /xml %windir%\scripts\User-Monitor-RConnect.xml /tn "User-Monitor-RConnect"
schtasks /create /xml %windir%\scripts\User-Monitor-LDisonnect.xml /tn "User-Monitor-LDisconnect"
schtasks /create /xml %windir%\scripts\User-Monitor-RDisconnect.xml /tn "User-Monitor-RDisconnect"
schtasks /create /xml %windir%\scripts\User-Monitor-Logon.xml /tn "User-Monitor-Logon"
schtasks /create /xml %windir%\scripts\User-Monitor-Lock.xml /tn "User-Monitor-Lock"
schtasks /create /xml %windir%\scripts\User-Monitor-Unlock.xml /tn "User-Monitor-Unlock"

and here is the master reporting script :

on error resume next
set onet = createobject("wscript.network")
set oshell = createobject("wscript.shell")
Set objWinHttp = CreateObject("WinHttp.WinHttpRequest.5.1")
set oargs = wscript.arguments
set regex = new regexp
lngTimeout = 59000
regex.pattern = ">rdp-tcp#\d+\s+\S+\s+(\d+)"
set oexec = oshell.exec("query session " & onet.username)
oexec.stdout.readline
line = oexec.stdout.readline
set omatches = regex.execute(line)
set submatches = omatches(0).submatches
sessionid =  submatches(0)
clientname = oshell.regread("HKCU\Volatile Environment\" & sessionid & "\CLIENTNAME")
objWinHttp.SetTimeouts lngTimeout, lngTimeout, lngTimeout, lngTimeout
objWinHttp.open "GET" , "https://example.local/usermonitor.php?nav_user=" & onet.username & "&nav_file=" & oargs(0) & "&nav_module=" & onet.computername & "&nav_session=" & clientname
objwinhttp.send


I had to make some strange code in order to get the RDS "CLIENTNAME" variable. It is specific to Windows 2008 / R2  i never had trouble to get the var on Windows 2003
Also, the logoff event is not managed by a task since the trigger DOES NOT EXIST, the solution is to create a GPO with a logoff script.
Have a nice day !

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