July 2011 - Posts

Outlook folders

The functions we’ve seen so far have involved iterating through the whole set of Outlook folders. That’s a lot of folders (I have 4 email accounts with lost of folders). The trick is to do this just once and then use the GetFolderFromID method at the Outlook.Session level to access individual mailboxes

The problem is that this method uses the EntryID and the StoreID for the folder.  Examples are shown below

StoreID : 0000000038A1BB1005E5101AA1BB08002B2A56C200006D736E636F6E2E646C6C
0000000000000000E92FEB759650448683B87DE522AA494800433A5C55736572735C526963
686172645C417070446174615C4C6F63616C5C4D6963726F736F66745C4F75746C6F6F6B5C
726963686172645F73696464617761795F686F746D61696C2E6F737400

EntryID : 000000000A9D24A8D4E32445825D043ADB203A6F0100D9539C2261A6BB45B9DA
B62C7081B3C101002100FFFF0000

Path    : \\emailaddress@hotmail.com\Deleted Items

Now I for one don’t want to be typing in the EntryID never mind the StoreID – chances of getting that right range from zero to about … zero

What we can do convert all of our Outlook functions (the two so far and the others I’ve got planned) into a module. In the psm1 file I can put a line that says

$folders = get-mailfolders

That creates a collection of objects containing the path, storeid and entryid for all of the folders. We can then search through that collection much faster than the outlook folders.

We create the collection like this

function get-mailfolders {            
$outlookfolders = @()            
$outlook = New-Object -ComObject Outlook.Application            
foreach ($folder in $outlook.Session.Folders){            
              
  foreach($mailfolder in $folder.Folders ) {            
   $olkf = New-Object PSObject -Property @{            
    Path = $($mailfolder.FullFolderPath)            
    EntryID = $($mailfolder.EntryID)            
    StoreID = $($mailfolder.StoreID)            
   }            
               
   $outlookfolders += $olkf            
               
  }             
}            
$outlookfolders            
}

Loop through the folders as we’ve already seen and create an object that has the path, storeid and entryid. Add the object to the collection and output the collection at the end of the function.

We’ll see how to use this collection in the revised clear-deletedmail function

PowerShell Cheat Sheet

This may be useful for people starting PowerShell. If you can’t remember what punctuation sign does what in PowerShell get a copy of this cheat sheet

http://blogs.lessthandot.com/index.php/DataMgmt/DBAdmin/a-cheat-sheet-for-all

Posted by RichardSiddaway | with no comments
Filed under:

Emptying the Deleted Items Folder

Continuing our look at scripting against Outlook & the hotmail connector

Many of the emails I get end up being read once and deleted. This leaves a ton of stuff in the deleted items folders. Periodically I’ll clean these up. This is one way how it can be accomplished.

function clear-deletedmail {            
            
$outlook = New-Object -ComObject Outlook.Application            
foreach ($folder in $outlook.Session.Folders){            
              
  foreach($mailfolder in $folder.Folders ) {            
    if ($mailfolder.Name -eq "Deleted Items" -and $mailfolder.Items.Count -gt 0){            
      foreach ($item in $mailfolder.Items){$item.Delete()}            
    }               
  }             
}            
}

All I’m doing is looping through my top level folders (email accounts) and for each of them finding a folder called Deleted Items.  If it has any content I will delete it.  Notice that the deletions have to occur on the item not at the folder level.

The contents of the folders can be tested using the function we saw earlier

get-mailitemcount | where {$_.Folder –like “Deleted*”}

A couple of notes on this function:

  1. Its inefficient iterating through all of the folders
  2. It doesn’t necessarily delete all of the items in one pass

The fix to this is use the get-mailitemcount function to test if we need to delete and keep calling the function until the folder is empty.  There is a way to access the folder directly that I will be testing next

Live Mesh Update

Couple of points worth noting if you are going to use Live Mesh

(1) File paths longer than 269 characters (folder paths > 248) won’t sync  - shorten the path to fix

(2) PST files won’t sync – even if they are not in use

 

By default Mesh wants to put the target folder into your Documents area.  The way round it is:

  1. On the source machine open Live Mesh select the folder to synchronise e.g.  c:\scripts
  2. On the target machine create c:\scripts
  3. Open Live Mesh on the target machine
  4. On the folder you want to sync select sync this folder
  5. Select the folder you just created

The synchonisation now happens to the folder you want rather than your profile area.

Otherwise it works as advertised

Posted by RichardSiddaway | with no comments
Filed under: ,

Outlook Connector & mail folder item count

On my home laptop I use Live Mail to aggregate my hotmail accounts. On my travelling netbook I decided to try the Outlook Connector

http://office.microsoft.com/en-us/outlook-help/microsoft-office-outlook-hotmail-connector-overview-HA010222518.aspx

This add in enables you to access hotmail accounts from Outlook & because I’m using Outlook 2010 I can have multiple mailboxes open at once.

This leads to some interesting PowerShell opportunities as having multiple mailboxes open means that some of the standard approaches won’t work – most scripts you see assume that you are connecting to an Exchange mailbox and that there is only one of them.

As an example consider getting a list of all of the mail folders and the number of items in each

function get-mailitemcount {            
Add-Type -Assembly Microsoft.Office.Interop.Outlook            
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]            
            
$outlook = New-Object -ComObject Outlook.Application            
foreach ($folder in $outlook.Session.Folders){            
              
  foreach($mailfolder in $folder.Folders ) {            
               
    New-Object -TypeName PSObject -Property @{            
      Mailbox = $($folder.Name)            
      Folder = $($mailfolder.Name)            
      ItemCount = $($mailfolder.Items.Count)            
    } | select Mailbox, Folder, ItemCount            
  }             
}            
}

The first two lines add the interop assembly and create an object representing the default folders. If these are used directly all we get is the folders for the default (first) mailbox.

Create an object for the outlook application and iterate through the folders in the session – these are the individual mailboxes.

For each of those create an object and set the properties. The select ensures the object properties display in the order we need.

I used foreach instead of foreach-object so that I could use a variable rather than $_ as it would have confusing with nested foreachs

There is quite a bit to play with here and some very interesting PowerShell issues.

Live Mesh

One problem I’ve had for a while is how to keep my script library synchronised across a number of machines.  I have a laptop I use for my development work at home and I have two netbooks – at least one of which is always with my when I’m away from home.

I’ve tried using robocopy and keeping everything on a USB stick but it just doesn’t work.

Live Mesh may be the answer

http://explore.live.com/windows-live-mesh?os=other

I can designate folders to sync and they will sync, including subfolders, across all three machines. The sync is in both directions.

Good price point as well!

Not something to contemplate for the enterprise but a good solution for private use.

This doesn’t necessarily take away the need for a backup but it does reduce the pressure on backups as I can accept rebuilding the machine and re-installing software. Its the data I can’t afford to lose.

Posted by RichardSiddaway | with no comments
Filed under: ,

Network Adapter speed and duplex

One thing that can cause problems on the network is the duplex setting on the network adapter.  If this doesn’t match the switch port then at best you will get performance issues and at worst no connectivity.

The speed of the connection can be obtained from Win32_NetworkAdapter but for the duplex setting we need to go to the registry.

function test-duplex {            
[CmdletBinding()]            
param (            
 [string]$computer="."            
)            
BEGIN {            
 $HKLM = 2147483650            
 $reg = [wmiclass]"\\$computer\root\default:StdRegprov"            
 $keyroot = "SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"            
}            
            
PROCESS {            
            
Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $computer -Filter "IPEnabled='$true'" |            
foreach {            
            
$data = $_.Caption -split "]"            
$suffix = $data[0].Substring(($data[0].length-4),4)            
$key = $keyroot + "\$suffix"            
            
$value = "*PhysicalMediaType"            
$pmt = $reg.GetDwordValue($HKLM, $key, $value)  ## REG_DWORD            
            
## 0=Unspecified, 9=Wireless, 14=Ethernet            
if ($pmt.uValue -eq 14){            
            
$nic = $_.GetRelated("Win32_NetworkAdapter") | select Speed, NetConnectionId            
            
$value = "*SpeedDuplex"            
$dup = $reg.GetStringValue($HKLM, $key, $value)  ## REG_SZ            
            
switch ($dup.sValue) {            
 "0" {$duplex = "Auto Detect"}            
 "1" {$duplex = "10Mbps \ Half Duplex"}            
 "2" {$duplex = "10Mbps \ Full Duplex"}            
 "3" {$duplex = "100Mbps \ Half Duplex"}            
 "4" {$duplex = "100Mbps \ Full Duplex"}            
}             
            
New-Object -TypeName PSObject -Property @{            
  NetworkConnector = $($nic.NetConnectionID )            
  DuplexSetting = $duplex            
  Speed = $($nic.Speed)            
}            
            
} #if            
} #foreach            
} #process            
} #function

Start by defining the information we need to read the registry

Get the Win32_NetworkAdapterConfiguration for those adapters that are IP enabled. We then test the physical media type and if its ethernet we get the duplex setting. The nework connectioid is retrieved to identify the card and an object is created to pull the output together

The New IT ?

A recent headline asked "How will you fit into the New IT?"

My answer is what new IT?

I have been working in IT for well over 20 years and in that time I can't remember a period when there wasn't a significant change coming:

  • introduction of PCs
  • growth of networks
  • Internet
  • Rise of Windows and fall of Novell
  • Token ring giving way to Ethernet
  • Viruses and other malware
  • Virtualisation

The so called new IT is the "cloud" and all its ramifications. We've been hosting applications elsewhere for years but all of a sudden its the only way to run your IT shop - at least according to the experts in the computer press. When was the last time one of these pundits actually worked in IT - if they ever did.

IT is constantly changing - if you don't realise that and can't live with it you shouldn't be in the industry. An IT professional's job is to work out which of the changes are beneficial to their organisation and which are a distraction.

No matter what the "experts" tell you there is no single answer that fits every organisation - pick whats needed and disregard the rest.

The only constant is change

Posted by RichardSiddaway | with no comments
Filed under:

Kindle for PC fix?

I appear to have fixed my problem. In the hope that this may help others in similar position this is what I did:

  1. uninstall Kindle for PC
  2. clean out all folders and content including the My Kindle Content folder. Make sure the C:\Users\Richard\username\Local\Amazon folder is deleted as well as C:\Program Files\Amazon
  3. clean out all registry keys related to Amazon/Kindle - I did a registry search on Amazon
  4. Shut down the machine. A restart does not seem to do the job - it needs to be a cold start
  5. Download the Kindle for PC application. Do not try and run it from the website
  6. Right click the application, select properties and Unblock.
  7. Right click application and select Troubleshoot compatibility
  8. select Try recommended settings
  9. click start program
  10. The install should work and allow you to register the installation
  11. Right click the icon you use to start Kindle and set compatibility to windows XP SP 2
  12. delete the content of the My Kindle content folder

It should now work. Once your content ahas re-synchronised you can remove the compatibility setting

I also found that downloading the installer and performing an install over the top of v1.5 seems to work OK.

Good luck. This is not a well behaved application and there is no excuse for having to go through these hoops especially as Amazon claim the application is compatible with Windows 7

Posted by RichardSiddaway | with no comments
Filed under:

Kindle for PC Update

Just checked my other two machines and they are running v1.5 with NO problem.  The latest version is 1.6.1 and that is the one showing a problem.

After removing all of the Kindle stuff including a registry I did get it to install and register.  It then starts to show the error at the second start.  Bah.

Come on Amazon you’ve bust it – its time you fixed it!

Posted by RichardSiddaway | with no comments
Filed under:

Now this is odd

I have three machines running Windows 7 32 bit

  • an HP laptop running Ultimate
  • an HP netbook running starter edition
  • a Dell duo running Home Premium

I have installed the Kindle for PC reader on each of them.  It installed and worked without any problems - - until today.

I added two books to the Kindle application on the Dell and it suddenly stopped working. Every time I open it I get a Application has stopped working message.

Tried uninstalling & re-installing - still bust

Tried the steps given here - http://blogs.msdn.com/b/saveenr/archive/2010/10/30/stopping-the-amazon-kindle-for-pc-application-from-crashing-on-windows-7.aspx - nope still doesn't work.

How can application go from working fine to unusable? Evidently this problem has been around since autumn last year. Just didn't notice it because it didn't affect me.

I've standardised on Kindle format for ebooks.  This isn't going to be good if the Kindle application stops working on my other laptops

There is no excuse for an application to suddenly decide it isn't going to work.  I have other ebook readers installed and they are working with no problems.

Posted by RichardSiddaway | with no comments
Filed under:

Just a thought

I’ve working with WMI a lot recently and frequently seen things like this

Get-WmiObject -Class Win32_OperatingSystem -ComputerName dc02

Get-WmiObject -Class Win32_ComputerSystem -ComputerName dc02

Get-WmiObject -Class Win32_LogicalDisk -ComputerName dc02

Each of these has to create a connection

is

$sb = {
Get-WmiObject -Class Win32_OperatingSystem
Get-WmiObject -Class Win32_ComputerSystem
Get-WmiObject -Class Win32_LogicalDisk
}

Invoke-Command -ScriptBlock $sb -ComputerName dc02

more efficient

In some cases yes depending on what you want to do with the information. Especially if need to filter data

AD account Expiry date

In many organisations AD accounts for temporary works are set to expiry when their contract ends.  Its also a good practice to put an expiry date on any one who is leaving – that way you know their account won’t available.

Setting the expiry date for all users in an OU is done like this

$date = "01/01/2012 00:00:00"            
            
$ou = [adsi]"LDAP://ou=test,dc=manticore,dc=org"            
            
$search = [System.DirectoryServices.DirectorySearcher]$ou            
$search.Filter = "(&(objectclass=user)(objectcategory=user))"            
$search.SizeLimit = 3000            
$results = $search.FindAll()            
            
foreach ($result in $results){            
            
 $target = $result.GetDirectoryEntry()            
 $target.AccountExpirationDate = $date            
 $target.SetInfo()            
}

 

The date shows the start of a day but the account expires at the end of the previous day.

The attribute is set correctly in ADSIEdit but on my Windows 2008 R2 system AD Users and Computers showed a date 1 day earlier but AD Administrative center shows the correct date!

GPO cmdlets

One thing new I found out today – if you install the Remote Server Administration Tools (RSAT) on Windows 7 you can use the GPO cmdlets (GroupPolicy module) against a Windows 2003 domain controller.

The cmdlets enable all GPO management tasks apart from modifying settings.

Useful

Just enough

One thing that never seems to stated is that you don’t need to use the full criteria when searching for something. As an example I wanted to know the COM related services running on a system

Get-Service | where {$_.DisplayName -like "*com*"}

supplies

Status   Name               DisplayName
------   ----               -----------
Running  Browser            Computer Browser
Stopped  COMSysApp          COM+ System Application
Running  DcomLaunch         DCOM Server Process Launcher
Running  EventSystem        COM+ Event System
Running  PcaSvc             Program Compatibility Assistant Ser...

which is exactly what I wanted.

Start vague and narrow down rather spending a lot of time trying to refine the criteria up front.  I’ve found its a faster way to get to the results

Posted by RichardSiddaway | with no comments
Filed under:

Moving Windows

In answer to a forum question I started to look at how you could move the PowerShell window from within PowerShell. Its not straight forward as we have to dig into the Win32 APIs

I came up with this code

function move-window {            
param(            
 [int]$newX,            
 [int]$newY            
)             
BEGIN {            
$signature = @'

[DllImport("user32.dll")]
public static extern bool MoveWindow(
    IntPtr hWnd,
    int X,
    int Y,
    int nWidth,
    int nHeight,
    bool bRepaint);

[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
public static extern bool GetWindowRect(
    HandleRef hWnd,
    out RECT lpRect);

public struct RECT
{
    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner
}

'@            
            
Add-Type -MemberDefinition $signature -Name Wutils -Namespace WindowsUtils             
            
}            
PROCESS{            
 $phandle = [WindowsUtils.Wutils]::GetForegroundWindow()            
            
 $o = New-Object -TypeName System.Object            
 $href = New-Object -TypeName System.RunTime.InteropServices.HandleRef -ArgumentList $o, $phandle            
            
 $rct = New-Object WindowsUtils.Wutils+RECT            
            
 [WindowsUtils.Wutils]::GetWindowRect($href, [ref]$rct)            
             
 $width = $rct.Right - $rct.Left            
 $height = 700            
<#
 $height = $rct.Bottom = $rct.Top
 
 $rct.Right
 $rct.Left
 $rct.Bottom
 $rct.Top
 
 $width
 $height
#>             
 [WindowsUtils.Wutils]::MoveWindow($phandle, $newX, $newY, $width, $height, $true)            
            
}             
}

We start by creating a piece of inline C# that creates a .NET class we can use to call the Win32 API functions. The names of the these functions are self explanatory

The move is accomplished by getting the handle of the foreground window and then creating a handle reference. The current window size is obtained using GetWindowRect – on my Windows 7 machine it doesn’t report the height correctly so I hard code that but calculate the width.

The MoveWindow method can be used to perform the move.  Remember that 0,0 is top left corner of the screen

I’m intrigued as to why the height isn’t reported correctly but haven’t found a good reason

Can I? Should?–copying VBscripts

One of the major errors I see PowerShell newcomers performing is copying script structures and syntax of VBScript into PowerShell. Let me give you an example

This piece of VBScript is borrowed from the Windows 2000 scripting guide

 

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colSettings = objWMIService.ExecQuery _
("SELECT * FROM Win32_OperatingSystem")
For Each objOperatingSystem in colSettings
Wscript.Echo "OS Name: " & objOperatingSystem.Name
Wscript.Echo "Version: " & objOperatingSystem.Version
Wscript.Echo "Service Pack: " & _
objOperatingSystem.ServicePackMajorVersion _
& "." & objOperatingSystem.ServicePackMinorVersion
Wscript.Echo "OS Manufacturer: " & objOperatingSystem.Manufacturer
Wscript.Echo "Windows Directory: " & _
objOperatingSystem.WindowsDirectory
Wscript.Echo "Locale: " & objOperatingSystem.Locale
Wscript.Echo "Available Physical Memory: " & _
objOperatingSystem.FreePhysicalMemory
Wscript.Echo "Total Virtual Memory: " & _
objOperatingSystem.TotalVirtualMemorySize
Wscript.Echo "Available Virtual Memory: " & _
objOperatingSystem.FreeVirtualMemory
Wscript.Echo "OS Name: " & objOperatingSystem.SizeStoredInPagingFiles
Next

It uses the Win32_OperatingSystem class to display information about the computers OS. As you can see the bulk of the script is concerned with formatting. Note the error in the script regarding OS Name

 

What often happens is that this will be translated directly, line by line, into PowerShell – which gives us something like this

$strComputer = "."            
            
$colSettings = Get-WmiObject -Query "SELECT * FROM Win32_OperatingSystem" -ComputerName $strComputer            
foreach ($objOperatingSystem in $colSettings){            
 Write-Host "OS Name: "  $objOperatingSystem.Name            
 Write-Host "Version: "  $objOperatingSystem.Version            
 Write-Host "Service Pack: "  $objOperatingSystem.ServicePackMajorVersion "." $objOperatingSystem.ServicePackMinorVersion            
 Write-Host "OS Manufacturer: "  $objOperatingSystem.Manufacturer            
 Write-Host "Windows Directory: "  $objOperatingSystem.WindowsDirectory            
 Write-Host "Locale: "  $objOperatingSystem.Locale            
 Write-Host "Available Physical Memory: "  $objOperatingSystem.FreePhysicalMemory            
 Write-Host "Total Virtual Memory: "  $objOperatingSystem.TotalVirtualMemorySize            
 Write-Host "Available Virtual Memory: " $objOperatingSystem.FreeVirtualMemory            
}

This is less code but still seems like a lot of work

 

lets put it into PowerShell as if we had written it from scratch

$strComputer = "."            
            
Get-WmiObject -Class Win32_OperatingSystem -ComputerName $strComputer |            
select Name, Version, ServicePackMajorVersion, ServicePackMinorVersion,            
Manufacturer, WindowsDirectory, Locale, FreePhysicalMemory,             
TotalVirtualMemorySize, FreeVirtualMemory

 

Now that is much easier. Quicker to code and easier to understand.

 

By all means use VBscripts as references for using WMI classes (I do) but re-write into PowerShell its much, much easier.

This isn’t a contradiction of my previous post as I advocated leaving legacy VBScript alone until you needed to  modify it

Dell duo tablet

Having bought the Dell Duo a few weeks ago I’ve been giving its tires a good kick. The duo is a combination netbook and tablet. It opens and works like a conventional netbook but the screen flips round and so you can close the lid, have the screen on the outside and use it as a tablet.

As a netbook its great. 1366x768 resolution makes for a very sharp screen. Its fast enough for the applications I want to use – mainly Office & reading PDFs. Keyboard is OK and the touchpad mouse works better than my HP netbook. The lack of an Ethernet port and external video feed are irritants but they can be overcome. Performance is more than enough for what I use it for.

As a tablet it works. Windows 7 isn’t an iPad but it you want  a tablet experience that runs your business applications and is good enough then its OK. I’ve found it especially useful in meetings for scrolling through notes. As the tablet is flat on the table there isn’t the feeling of anything getting in the way that you have when everyone has a laptop open.

All in all its a good buy and a useful addition to my hardware set. I’m tending to use it as a machine for work to carry all my reference material at which it is superb.

Posted by RichardSiddaway | with no comments
Filed under: ,

Can I? Should I?–examples–legacy scripts

In this post http://msmvps.com/blogs/richardsiddaway/archive/2011/07/17/can-i-should-i.aspx I stated that PowerShell isn’t necessarily the right answer to every problem.

I was left a comment asking if I could expand.  I’ll do that over a series of short posts as I think of examples.

One of the first that comes to mind is legacy scripts.

VBScript never really caught on as a mainstream administration tool. There were a number of reasons for this:

  • non interactive
  • harder to use and debug
  • less information
  • less flexible
  • admins addicted to the GUI
  • less pressure on people i.e. more admins

However, a number of organisations created a significant number of scripts and performed some very clever stuff.

Now, the question for those organisations is this -

“PowerShell has appeared. Do I convert all those scripts, that work really well to PowerShell?”

 

My answer would be no!  Learn PowerShell first . Get really proficient. Develop new stuff in PowerShell and migrate the legacy scripts when they need an over haul (or you get some free timeSurprised smile) that way you get the best of both worlds and run the smallest risk when you come to migrate the legacy scripts.

Posted by RichardSiddaway | with no comments
More Posts Next page »