PSC: Move share
I have decided what the Power of scripting script should do! If you move a share by moving it in windows explorer the share will stop work. If you move the folder with File System Object from vbscript the share will still be there but will be pointing at the old path that no longer exists. So how to solve this?
I know a few ways that you don't. First attempt was to use the winnt://localhost/lanmanserver/ object to try updating the path of the share. No luck the property is read only!
Next attempt was using WMI instead. Same thing, unable to update the path. So i thought I delete the share and then create it again. Well enough that worked how ever I didn't manage to get the rights for the share to come along to the new one. There is methods to extract the access mask for the share and supply it when creating a new one, so I think it can be solved. I didn't have the time to do so this time and I think I proved my point about the power of scripting.
At the same time I have learned or realized a few things that I didn't thought about. Most scripts I have written in the past has been for controlled environments. By that I mean a system where a specific task was asked from the script and the run was always with the same parameters. Here I'm writing a script that should run on different versions of the operating system and with several different variables.
So some of the limitations I ran into can be expressed in the words: Access Denied! It was issues with running some of the commands from the script without running it with elevation. This is no issue running it from the command prompt with cscript moveshare.vbs as long as you start the cmd with elevation (right-click and select Run As Administrator). There are ways around this even with the "double-click execution" of scripts that are discussed in detail here.
So the window version of the script is pretty useless with a standard setup. How ever if you run the script with elevation from the cmd you will be able to move shares around on the disk. The script is pretty ruff around the edges and some more error handling and stuff needs to be added. There probably will be solvable to get the share to keep it's access list as well. This script should be seen as a proof of concept.
'******************************************************************************************** '* '* Power of scripting challange! '* '* moveshare.vbs '* '* Purpose: Move a shared folder from one location to another '* without losing the share! '* '* Written by Kristofer Källsbo '* '* http://www.hackviking.com '* '******************************************************************************************** Option explicit Dim FSO 'Get the filesystem object ready Set FSO = CreateObject("Scripting.FileSystemObject") 'First check if we are running in console mode or window mode If (InStr(1, WScript.FullName, "cscript", vbTextCompare)) Then InitConsole ElseIf (InStr(1, WScript.FullName, "wscript", vbTextCompare)) Then InitWindow Else ' Hope this never fires!wscript.echo "How the hell did you start this script?" End If 'Clean up Set FSO = Nothing 'Handles the command line run of the script Sub InitConsole() 'Check if we have any parameters If (Wscript.Arguments.Count < 2) Then 'Display help wscript.echo "Move share script" wscript.echo "" wscript.echo "Use: cscript moveshare.vbs {source} {destination}" wscript.echo "" wscript.echo "source = complete path of the shared folder" wscript.echo "destination = full path of the folder where the shared folder should be moved" wscript.echo "" wscript.echo "moveshare.vbs Kristofer Källsbo http://www.hackviking.com" Else 'Verrify the parameters Dim sourcePath, destPath sourcePath = Wscript.Arguments(0) destPath = Wscript.Arguments(1) If Not (FSO.FolderExists(sourcePath)) Then 'Display error wscript.echo "Source path was not found!" Exit Sub End If If Not (FSO.FolderExists(destPath)) Then 'Display error wscript.echo "Destination path was not found!" 'End this execution run Exit Sub End If 'Do the move wscript.echo MoveShare(sourcePath, destPath) End If End Sub 'Handles the double click run of the script Sub InitWindow() Dim sourcePath, destPath, msgboxRetval 'Get source path from user sourcePath = InputBox("This script will move an entire shared folder to a new location withou losing the share!" & vbNewLine & vbNewLine & "Enter path of share:", "Move Share Script") 'Validate source path If (sourcePath = "") Then 'Display error and end it msgboxRetval = MsgBox("Source path not entered or cancel was clicked!", vbOKOnly+vbCritical, "Source path error!") Exit Sub Else 'Check that path exists If Not (FSO.FolderExists(sourcePath)) Then 'Display error msgboxRetval = MsgBox("Source path was not found!", vbRetryCancel+vbCritical+vbDefaultButton1, "Source path error!") 'Check if user wants to retry If (msgboxRetval = 4) Then InitWindow End If 'End this execution run Exit Sub End If End if 'Get destination path from user destPath = InputBox("Enter destination path of share:", "Move share script") 'Validate destination path If (destPath = "") Then 'Display error and end it msgboxRetval = MsgBox("Destination path not entered or cancel was clicked!", vbOKOnly+vbCritical, "Destination path error!") Exit Sub Else 'Check that path exists If Not (FSO.FolderExists(destPath)) Then 'Display error msgboxRetval = MsgBox("Destination path was not found!", vbRetryCancel+vbCritical+vbDefaultButton1, "Destination path error!") 'Check if user wants to retry If (msgboxRetval = 4) Then InitWindow End If 'End this execution run Exit Sub End If End If 'We are all good to go, lets move wscript.echo MoveShare(sourcePath, destPath) End Sub Function MoveShare(sourcePath, destPath) Dim retval, objWMI, objInstances, objInstance, sourceFolder, sourceName, AccessMask, AllowMaximum, Caption, Description, MaximumAllowed, Name, shareType, delRetval, objNewShare, createRetval 'Add a \ to the destpath, making shure it will be treated as a folder destPath = destPath & "\" 'Get the shares Set objWMI = GetObject("winmgmts://./root\cimv2") Set objInstances = objWMI.InstancesOf("Win32_Share",48) 'Get the correct folder path with upper and lower cases Set sourceFolder = FSO.GetFolder(sourcePath) sourcePath = sourceFolder.Path sourceName = sourceFolder.Name 'Get the share 'On Error Resume Next For Each objInstance in objInstances If(objInstance.Path = sourcePath) Then 'Store all the options With objInstance AccessMask = .AccessMask AllowMaximum = .AllowMaximum Caption = .Caption Description = .Description MaximumAllowed = .MaximumAllowed Name = .Name shareType = .Type End With 'Delete the share delRetval = objInstance.Delete() 'Check that delete worked If Not(delRetval = 0) Then retval = "Error: Unable to delete share!" & vbNewLine & "Error code: " & delRetval MoveShare = retval Exit Function End If End If Next On Error Goto 0 'Move the folder FSO.MoveFolder sourcePath, destPath 'Setup the share in the new location Set objNewShare = objWMI.Get("Win32_Share") createRetval = objNewShare.Create(destPath & sourceName, Name, shareType, MaximumAllowed, Description, null, null) 'Check that the new share was created If Not(createRetval = 0) Then retval = "Error: New share not created!" End If 'Return result MoveShare = retval End Function
PSC: Basic script
So the challenge was accepted! Follow it here! One of the conditions was that the script has to perform like a program or command line tool to be exact! But why go for one when you can go for both! Still not sure what the final script will do but I have to detect how the script is started and act accordingly.
I always use Option explicit, makes troubleshooting so much easier!
Option explicit
Check if the script is running from console or windows mode by checkking the WScript.FullName string.
If InStr(1, WScript.FullName, "cscript", vbTextCompare) Then InitConsole ElseIf InStr(1, WScript.FullName, "wscript", vbTextCompare) Then InitWindow Else ' Hope this never fires!wscript.echo "How the hell did you start this script?" End If
Sub for handling the command line version. Checks for command line arguments, if none displays help.
Sub InitConsole() 'Check if we have any parameters If Wscript.Arguments.Count = 0 then 'Display help wscript.echo "You didn't supply any command line parameters... At least one parameter is required!" Else 'Verrify the parameters wscript.echo "You supplied " & Wscript.Arguments.Count & " arguments!" End If End Sub
Sub for handling if the user double clicks the script...
Sub InitWindow() wscript.echo "You are running this by dubble click..." End Sub
Stay tuned for the next version!
Power of scripting challange
I was speaking at an IT-consultant company kick-off today. Explained to the server maintenance guys how important scripting is! Not only power shell but classic vbscript. A server tech that can't script is behind the guy who can. Scripting tech can do twice the work in half the time in a big server park. They complained back and forth and sad "No way I am going to learn that, all the good stuff you can do needs a real program anyway!". WRONG!
So I took a bet with them! I will produce script that can do something real. Something that you actually do every day as a server tech more or less.
The conditions finally came to:
- Has to be pure vbscript, only allowed to use operative system built in objects like File System Object.
- Has to perform a vital task, something that usually claims several manual steps to complete.
- Has to work like any other command line tool, menus, help and so forth.
- I got until Monday 213-03-25 to complete the script!
Hey guys... Follow me!
Google Maps searchbox with autocomplete
I was sitting trying to do some mods on a Queclink GL200 GPS transmitter for the MPS project. After many hours of no luck at all I gave up for the day. If anyone have some input on that please contact me!
So i started messing around with the Google Maps API demo that I made for them instead. Adding some auto complete to the search form instead. I thought I would share what I managed to do. The challange is to do a mach up of Google Maps API and jQuery to get it to work good.
The trick is to attach the jQuery handler to the object. Why?
You have to create the search box dynamically in order to push it on top of the Google Maps canvas.
Entire demo can be found here: http://jsfiddle.net/kallsbo/XgsC6/
First initialize the map and all it's settings:
var map; var addressField; var geocoder; $(document).ready(function () { // Define map options var mapOptions = { center: new google.maps.LatLng(57.698254, 12.037024), zoom: 16, mapTypeId: google.maps.MapTypeId.HYBRID, panControl: true, zoomControl: true, mapTypeControl: true, scaleControl: true, streetViewControl: true, overviewMapControl: true }; // Define map map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); // Define Gecoder geocoder = new google.maps.Geocoder(); // Init searchbox initSearchBox(); }); function initSearchBox() { // Add searchbox var searchControlDiv = document.createElement('div'); var searchControl = new SearchControl(searchControlDiv, map); searchControlDiv.index = 1; map.controls[google.maps.ControlPosition.TOP_CENTER].push(searchControlDiv); }
As you can see we initialize the search box control and put it in a div at the top of the canvas. This is how we create the control and it's auto complete function:
function SearchControl(controlDiv, map) { // Set CSS styles for the DIV containing the control // Setting padding to 5 px will offset the control // from the edge of the map. controlDiv.style.padding = '5px'; // Set CSS for the control border. var controlUI = document.createElement('div'); controlUI.style.backgroundColor = 'white'; controlUI.style.borderStyle = 'solid'; controlUI.style.borderWidth = '2px'; controlUI.style.cursor = 'pointer'; controlUI.style.textAlign = 'center'; controlUI.title = 'Sök ex: gatunamn, stad'; controlDiv.appendChild(controlUI); // Create the search box var controlSearchBox = document.createElement('input'); controlSearchBox.id = 'search_address'; controlSearchBox.size = '80'; controlSearchBox.type = 'text';
So when you have gotten this far in the code you have the search input box as a VAR. Now whe can use that VAR to attache the function for the auto complete to it:
// Initiat autocomplete $(function () { $(controlSearchBox).autocomplete({ source: function (request, response) { if (geocoder == null) { geocoder = new google.maps.Geocoder(); } geocoder.geocode({ 'address': request.term }, function (results, status) { if (status == google.maps.GeocoderStatus.OK) { var searchLoc = results[0].geometry.location; var lat = results[0].geometry.location.lat(); var lng = results[0].geometry.location.lng(); var latlng = new google.maps.LatLng(lat, lng); var bounds = results[0].geometry.bounds; geocoder.geocode({ 'latLng': latlng }, function (results1, status1) { if (status1 == google.maps.GeocoderStatus.OK) { if (results1[1]) { response($.map(results1, function (loc) { return { label: loc.formatted_address, value: loc.formatted_address, bounds: loc.geometry.bounds } })); } } }); } }); }, select: function (event, ui) { var pos = ui.item.position; var lct = ui.item.locType; var bounds = ui.item.bounds; if (bounds) { map.fitBounds(bounds); } } }); });
Then finish up creating the object and push it the the Google Maps Canvas as a custom control:
// Set CSS for the control interior. var controlText = document.createElement('div'); controlText.style.fontFamily = 'Arial,sans-serif'; controlText.style.fontSize = '12px'; controlText.style.paddingLeft = '4px'; controlText.style.paddingRight = '4px'; controlText.appendChild(controlSearchBox); controlUI.appendChild(controlText); }
Missing People Sweden Techsupport
I'm currently involved in a project for Missing People Sweden (MPS). MPS is organizes volunteers to do big search operation for lost people. I was one of the volunteers last fall and saw first hand what a huge task it was to organize all the people and search area. So I went online the same night and wrote a blog entry on my Swedish blog. I questioned why no one could help this organisation with some technical support and build them a system to take care of all this. That's how Missing People Sweden Techsupport was born. First we got put of by the organisation due to an other company that wanted to build it for them. A few month later they delivered a system that was unusable!
So once again I reached out to my contacts in the business and asked them to join me in my quest for this. Unfortunately many of them was busy on other projects or didn't have the drive anymore after being put of the first time. But i managed to get the best project manager I ever worked with to join in. We got a MVC programmer and a jQuery guy on board. We had a few meetings with the, not so technical guys, at MPS. So they din't know tech so well but they are really good at finding people. So we asked them what they needed, what would help them. A few meetings in I felt that we needed to show them something, show that we do stuff not just talk. So I made a quick demo of how we could help them do search segments on a map. Using Google Maps API and some spare time I made a really easy prof of concept.
The other system delivered to them, that they discarded, was 1000 man hours of development. This demo I made was 2 hours of work one night. It has now been used several times to do maps for them when actually looking for real lost people. My point here is to really listen to the customer and give them what they need not what you think they need or whats cool to do with tech!
Demo: http://mpsdemo.kallsbo.se/googlemaps.htm
RoundCube Webmail login case sensitive
I got an issue reported to me that a user of a RoundCube Webmail setup we have had lost all his contacts. I logged in to his account and all the contacts where gone. Checked the database and found that all the contacts where still in the database. After some digging i found that there where 2 accounts in the RoundCube database. One sad User@ourdomain.com the other sad user@ourdomain.com. When I logged on to the User account all the contacts where there! So i realized that RoundCube logon are case sensitive. He will still see his e-mail because the RoundCube doesn't keep track of his password, it only tries to logon to the IMAP server and if logon is successful it creates it own DB-entries to keep track of contacts and other information. The IMAP server however isn't case sensitive and User or user is the same thing for it. But there is an easy way to fix it!
RoundCube Settings
I'm running RoundCube on a IIS server so I go in to the wwwroot and on to the config folder. In the file main.inc.php I look for the line $rcmail_config['login_lc'] = false; and change it like below:
// If users authentication is not case sensitive this must be enabled. // You can also use it to force conversion of logins to lower case. // After enabling it all user records need to be updated, e.g. with query: // UPDATE users SET username = LOWER(username); $rcmail_config['login_lc'] = true;
Then I do a IIS reset.
Database changes
If the user have all the contacts on the lowercase account your all good. Just delete the User account and it will all be good. But if the contacts where created on the User account like in my case you have to do like this. Delete the lowercase account and then run:
UPDATE users SET username = LOWER(username) WHERE user_id = {id of uppercase account};
If you don't delete the lowercase account first you will get an error like this:
UPDATE users SET username = LOWER(username) WHERE user_id = {user_id} Error Code: 1062. Duplicate entry '{username}' for key 'username'
If the user have spread his contacts over both accounts just change the contact table like this:
UPDATE contacts SET user_id = {id of lowercase account} WHERE user_id = {id of uppercase account};
Then delete the uppercase account and your all good!
Script: List users homedir size
I compiled a vbscript that loops through all the sub folders of a given folder and print out the size. The script takes the folder name and searches the Active Directory for a corresponding user. If found it prints the name of the user in the output.
Use:
cscript homedirsize.vbs
Three input boxes will appear...
- Path where the homedir folders are located. Ex. d:\home
- Width, in characters, of the first output column.
- Width, in characters, of the second output column.
Download script here:
'**************************************************************************************** ' ' Name: Homedirsize.vbs ' ' Retrieves the size of each subdirectory and matches them to an AD account. ' Outputs a list of directories, username and size. ' ' Written by: Kristofer Källsbo ' 2013-02-26 - http://www.hackviking.com ' '**************************************************************************************** Option explicit dim path, column1, column2, objRoot, domainname, fso, partline, i, rootFolder, folder ' get path of homedirs path = inputbox("Enter path of homedirs:") ' get column widths column1 = Cint(inputbox("Enter width of first output column:")) column2 = Cint(inputbox("Enter width of second output column:")) ' get the current domain Set objRoot = GETOBJECT("LDAP://RootDSE") domainname = objRoot.GET("defaultNamingContext") ' get the file system object Set fso = CreateObject("Scripting.FileSystemObject") ' print description lines wscript.echo "homedirsize.vbs runned on " & Date & " - " & Time wscript.echo "" wscript.echo LeftJustified("Foldername", column1) & LeftJustified("Username", column2) & "Size (Mb)" for i = 0 to column1 + column2 + 8 partline = partline & "-" next wscript.echo partline ' start looping all the subfolders Set rootFolder = fso.GetFolder(path) For Each folder in rootFolder.SubFolders Dim folderSize folderSize = folder.Size wscript.echo LeftJustified(folder.Name, column1) & LeftJustified(FindUser(folder.Name, domainname), column2) & FormatNumber(((folderSize/1024)/1024),2) & " Mb" Next Set fso = Nothing FUNCTION FindUser(BYVAL UserName, BYVAL Domain) Dim cn,cmd,rs SET cn = CREATEOBJECT("ADODB.Connection") SET cmd = CREATEOBJECT("ADODB.Command") SET rs = CREATEOBJECT("ADODB.Recordset") cn.open "Provider=ADsDSOObject;" cmd.activeconnection=cn cmd.commandtext="SELECT Name FROM 'LDAP://" & Domain & _ "' WHERE sAMAccountName = '" & UserName & "'" SET rs = cmd.EXECUTE IF err<>0 THEN FindUser = 2 wscript.echo "Error connecting to Active Directory Database:" & err.description ELSE IF NOT rs.BOF AND NOT rs.EOF THEN rs.MoveFirst FindUser = rs.Fields("Name").Value ELSE FindUser = "N/A" END IF END IF cn.close END FUNCTION Function LeftJustified(ColumnValue, ColumnWidth) If(ColumnWidth < Len(ColumnValue) OR ColumnWidth = Len(ColumnValue)) then LeftJustified = Left(ColumnValue, ColumnWidth - 1) & " " else LeftJustified = ColumnValue & Space(ColumnWidth - Len(ColumnValue)) End if End Function
View Members Dynamic E-mail Group – Exchange 2010
Always nice to be able to check who a dynamic group contains. Easy to do from the powershell with these two commands:
$DynGroup = Get-DynamicDistributionGroup "{name of dynamic distribution group}"
Get-Recipient -RecipientPreviewFilter $Dyn.RecipientFilter -OrganizationalUnit $DynGroup.RecipientContainer
Script: NTFS rights on user home directories
Have a normal Windows setup where the user have a home folder on the file server. All the users is connected to there \\fileserver\home$\%username% via GPO on logon. How ever we found that some of the folders had rights that where messed up. So i wrote a quick script that loopes through all folders and checks if there is a user account in the domain if not it will move the directory to __unconnected__ folder. For all know users it uses cacls command to set rights for the user and admins only. If you need something else you can just edit the cacls command before you run it! Script is provided as is and feel free to modify it...
Download script here:
Option Explicit 'ON ERROR RESUME NEXT Dim path, objRoot, domainname, fso, rootFolder, folder, objShell, intRunError path = inputbox("Enter path of homedirs:") ' Get current domain IF domainname = "" THEN SET objRoot = GETOBJECT("LDAP://RootDSE") domainname = objRoot.GET("defaultNamingContext") END IF ' Setup FSO connection Set fso = CreateObject("Scripting.FileSystemObject") Set rootFolder = fso.GetFolder(path) Set objShell = WScript.CreateObject( "WScript.Shell" ) ' Go through all homedir folders For Each folder in rootFolder.SubFolders if(FindUser(folder.Name, domainname) = 1) Then ' Folder found reset the permissions wscript.echo folder.Name + " - has a user connected! Reseting the permissions..." intRunError = objShell.Run("%COMSPEC% /c Echo Y| cacls " & folder.Path & " /t /c /g Administrators:F ""Domain Admins"":F " & folder.Name & ":F", 1, True) If intRunError <> 0 Then wscript.echo folder.Name + " - ERROR assigning rights!" wscript.echo intRunError else wscript.echo folder.Name + " - Rights asigned!" End If elseif(FindUser(folder.Name, domainname) = 0) then ' This folder isn't connected move it If(folder.Name <> "__unconnected__") then wscript.echo folder.Name + " - doesn't have a user connected! Moving to .\__unconnected__" fso.MoveFolder folder.Path, rootFolder.Path + "\__unconnected__\" End If else wscript.echo "ERROR: Connection to AD failed!" End If Next Set objRoot = Nothing Set fso = Nothing Set rootFolder = Nothing Set objShell = Nothing ' Function to check if user exists FUNCTION FindUser(BYVAL UserName, BYVAL Domain) Dim cn,cmd,rs SET cn = CREATEOBJECT("ADODB.Connection") SET cmd = CREATEOBJECT("ADODB.Command") SET rs = CREATEOBJECT("ADODB.Recordset") cn.open "Provider=ADsDSOObject;" cmd.activeconnection=cn cmd.commandtext="SELECT ADsPath FROM 'LDAP://" & Domain & _ "' WHERE sAMAccountName = '" & UserName & "'" SET rs = cmd.EXECUTE IF err<>0 THEN FindUser = 2 wscript.echo "Error connecting to Active Directory Database:" & err.description ELSE IF NOT rs.BOF AND NOT rs.EOF THEN rs.MoveFirst FindUser = 1 ELSE FindUser = 0 END IF END IF cn.close END FUNCTION
Viewing Axis webcam from iPhone
I just installed an axis webcam at home to keep tabs on my home when I'm not there. I tried a few apps for my iPhone to use the cam as easy as possible. I found four free apps for Axis cameras but only one that actually was usable.
Netcamviewer - Don't support SSL.
CamControl - Works great! Or at least the only one that supports SSL.
CamViewer - Looks really unprofessional, don't support SSL.
CamSee - Don't support SSL.

