Archive for the ‘Scripting’ Category

In my Exchange 2010 environment, we’re using the thumbnailPhoto attribute in active directory, so that users photos appear in Outlook 2010. We also have a custom intranet page that features a company directory which pulls details such as phone numbers from AD. I wanted to also add in the image stored in thumbnailPhoto for each user, so started to create a way of extracting the image that could be used in a web page.

I ended up with a C# ASP page, called userPhoto.aspx with the following contents:

<%@ Page Language="C#" %>
<%@ OutputCache Duration="6000" VaryByParam="u" %>
<%@ Import Namespace="System.DirectoryServices" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">

    private void Page_Load(object sender, EventArgs e)
    {
        String myUser = Request.QueryString["u"];

        if (myUser == null)
            Response.Redirect("app_graphics/user.jpg");

        Response.ContentType = "image/jpeg";
        Response.Clear();
        Response.BufferOutput = true;

        DirectoryEntry de = new DirectoryEntry();
        de.Path = "LDAP://OU=Users,DC=domain,DC=local";

        DirectorySearcher search = new DirectorySearcher();
        search.SearchRoot = de;
        search.Filter = "(&(objectClass=user)(objectCategory=person)(sAMAccountName=" + myUser + "))";
        search.PropertiesToLoad.Add("samaccountname");
        search.PropertiesToLoad.Add("thumbnailPhoto");

        SearchResult user;
        user = search.FindOne();

        String userName;
        if (user == null)
            Response.Redirect("app_graphics/user.jpg");
        else
            userName = (String)user.Properties["sAMAccountName"][0];

        try
        {
            byte[] bb = (byte[])user.Properties["thumbnailPhoto"][0];
            Response.BinaryWrite(bb);
            Response.Flush();
        }
        catch
        {
            Response.Redirect("app_graphics/user.jpg");
        }

    }

</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>userImage</title>
</head>
<body>
    <form id="form1" runat="server">
    </form>
</body>
</html>

You can then use the file by passing a parameter of u on a HTTP GET request, containing the users sAMAccountName.

For example, <img src=’userPhoto.aspx?u=gkendal’/>. Also, don’t forget to set the OU of where your user objects are in AD on line 21, and to add a placeholder image for those users that dont have anything stored in thier thumbnailPhoto attribute!

We have a couple of requirements in our environment, firstly Admin users need access to all mailboxes, then all users need reviewer (Read only) access to everyone elses calendars.

To achieve this, I have the following powershell script scheduled to run overnight…

$userAccounts = get-mailbox -resultsize unlimited
ForEach ($user in $userAccounts)
{
	Add-MailboxPermission -Identity $user -User "DOMAIN\Domain Admins" -AccessRights FullAccess
	Add-MailboxFolderPermission -Identity ($user.UserPrincipalName + ":\Calendar") -User entire-company -AccessRights Reviewer
}

I initially tried using the ‘Default’ user for the calendar permissions, but this caused some odd results and errors in outlook, so I put that back to ‘AvailabilityOnly’ and used a mail-enabled active directory group that contained the whole company instead!

There might be a nicer way to set default mailbox permissions at the time of account creation, but I’ve yet to find this!

I have been looking at getting a script together for a while to mass change some shortcuts we have on our file server to point to a different location. After some research, and some playing around I found a really easy way to do this in Powershell:


# Call wscript com object
$shell = new-object -com wscript.shell

# Recurse through directories for .lnk files
dir "I:\" -filter *.lnk -recurse | foreach {
$lnk = $shell.createShortcut($_.fullname)
$oldPath= $lnk.targetPath

# If match text, perform operation
if($oldpath -match "\\serverold\share1")
{
write-host "Match: " + $_.fullname
remove-item $_.fullname
$lnknew = $shell.createShortcut($_.fullname)
$lnknew.targetPath = "`"\\newserver\share1`""
$lnknew.IconLocation = "%SystemRoot%\system32\SHELL32.dll,4"
$lnknew.Save()
}
}
Write-Host "End..."

From time to time our Citrix users end up with multiple sessions on our Citrix farm, especially when accessing the farm via Citrix access gateway and they loose connectivity. Most of these are fixed by setting sessions in the disconnected state to be automatically reset after 15 mins. (We allow this time  incase users are moving to a meeting room for example).

The big problem for us is that you can’t run multiple instances of Lotus Notes, so the disconnected session needs to be reset before the user can fire up Lotus Notes again, otherwise they will see an error like:

“You cannot use the Administration program while the Domino Server is running. Either shut down the Domino Server (buy keep the file server running) or choose the icon labeled ‘Lotus Notes’ instead.”

In order to try and make things a bit easier on the helpdesk, I created a script that will allow users to reset their own Citrix sessions. If you want to use it you’ll need to populate the first array with a list of all the servers in your farm, then create a shortcut:

cscript /nologo citrixreset.vbs

I’ve set mine to run minimized so that they don’t see random command windows popping up.

It should also be noted, that the script resets any disconnected sessions first, so that it can still carry on and kill the active one at the end! I know it’s not the slickest of scripts (Lots of splitting arrays and capturing command output), but I couldnt find any nice objects to do it properly with!

 citrixServers = array("CXS38", "CXS39", "CXS40", "CXS41")

Set objShell = CreateObject("WScript.Shell")

Sub resetSession(s)
    count = count + 1
    sessionID = trim(mid(s,48,5))
    citrixServer = trim(mid(s,1,6))
    wscript.echo "RESET SESSION " & sessionID & " /SERVER:" & citrixServer
    Set oExec = objShell.Exec("RESET SESSION " & sessionID & " /SERVER:" & citrixServer)
End Sub

userName = objShell.ExpandEnvironmentStrings("%UserName%")

For Each citrixServer In citrixServers

    wscript.echo "QUERY SESSION " & userName & " /SERVER:" & citrixServer
    Set oExec = objShell.Exec("QUERY SESSION " & userName & " /SERVER:" & citrixServer)

    Do While oExec.Status = 0
        WScript.Sleep 100
    Loop

    Do While oExec.StdOut.AtEndOfStream <> True
        sessionOutput = sessionOutput & vbCrLf & citrixServer & oExec.StdOut.ReadLine
    Loop

Next

count = 0
sessions = split(sessionOutput, vbCrLf)
wscript.echo

For Each session In sessions
    if InStr(session, "wdica") and InStr(session, "Disc") then wscript.echo session
Next

For Each session In sessions
    if InStr(session, "wdica") and InStr(session, "Active") then wscript.echo session
Next

For Each session In sessions
    if InStr(session, "wdica") and InStr(session, "Disc") then resetSession(session)
Next

For Each session In sessions
    if InStr(session, "wdica") and InStr(session, "Active") then resetSession(session)
Next

msgbox "Reset " & count & " Citrix session(s) for " & userName

Shadow copies are a brilliant time-saver… I can recover a deleted/overwritten/corruped file in a matter of seconds – or better still, the end user can do it! We currently have a schedule that takes a snapshot of our main fileserver at 7am and noon. While this is quite good, it quickly eats up diskspace, then all the snapshots get ditched and we have to start collecting them again. I wished there was some more complex scheduling and management for the shadows - I would find it far more useful to have more of the recent snapshots, and less of the older ones. Unfortunatly there’s no inbuilt functionality to do this, so I thought it was time to write a script…

I put together the following, it looks through all the snapshots on the system, and will delete those that match the following criteria:

- Older than 3 days and created after 7am
- Older than 7 days and not created on a Monday
- Older than 31 days

Scheduled to run daily, it should work a treat. The amount of snapshots should also stay constant, as once they are older then 31 days they will be removed.

  option explicit

  Dim objWMI, snapshots, snapshot, sDate, vDate, deleteSnapshot

  Set objWMI = GetObject("winmgmts://localhost/root/cimv2")
  Set snapshots = objWMI.ExecQuery("select * from Win32_ShadowCopy")
  set sDate = CreateObject("WbemScripting.SWbemDateTime")

  WScript.echo "Searching for all snapshots..."

  for each snapshot in snapshots

      sDate.Value = snapshot.InstallDate
      vDate = sDate.GetVarDate(True)
    
      WScript.echo vbCrLf & "Found snapshot... Created " & vDate
      'WScript.echo snapshot.VolumeName

      if (DateDiff("d", vDate, Date) > 7) then
          if (DatePart("w", vDate) <> 2) then
              WScript.echo "Older than 7 days & Not created on a Monday"
              deleteSnapshot = true
          end if
      end if

      if (DateDiff("d", vDate, Date) > 3) then
          if (hour(vDate) > 7) then
              WScript.Echo "Older than 3 days & Created after 7AM"
              deleteSnapshot = true
          end if
      end if

      if (DateDiff("d", vDate, Date) > 31) then
          WScript.Echo "Older than 31 days"
          deleteSnapshot = true
      end if

      if (deleteSnapshot) then
          WScript.echo "*** Deleting snapshot ***"
          snapshot.Delete_()
          deleteSnapshot = false
      else
          WScript.echo "*** Keeping snapshot ***"
      end if

  next

You’ll sometimes have a server that has loads of scheduled tasks that need to run as a specific user account, and sometimes you might need to update the account they run as, or reset the password.

I recently had to change loads of these on our Altiris server, and didn’t really fancy doing it by hand! The following script will update the runas user/password for all scheduled tasks on your system…

@echo off

set Password=password
set Username=DOMAIN\username

cd /d "%SystemRoot%\tasks
for %%a in (*.job) do call :ChangePW %%a
goto :eof

:ChangePW
set TaskName=%*
set TaskName=%TaskName:~0,-4%
SCHTASKS /Change /RU %Username% /RP %password% /TN "%TaskName%"

Massive JPEGs

May 15th, 2008 4 Comments

I really hate it how digital cameras now create files that are stupidly big, every photo that I use - and most of those that others use only ever stay on the screen. So why do we need them saved at 5120×3825 eating about 4Mb each?!

In a large multi-user environment these massive images quickly eat up alot of disk space. I figured you could save alot of disk space by automatically resizing the images down if they are bigger than a certain size, so I got cracking on a VBScript to do this for me. Read the rest of this entry »