Categories

15493

Create a calendar item

Continuing the occasional look at Outlook automation its time to see how we create a calendar item

function new-calendaritem {            
param (            
            
 [string]$mailbox,            
             
 [datetime]$start,            
             
 [datetime]$end,            
             
 [string]$subject,            
             
 [string]$location,            
             
 [string]$body            
            
)            
$outlook = New-Object -ComObject Outlook.Application            
            
$folder = get-mailfolders |             
where {$_.Path -like "*calendar*" -and $_.Path -notlike "*birthday*" -and $_.Path -like "*$mailbox*"}            
            
$calendar = $outlook.Session.GetFolderFromID($folder.EntryID, $folder.StoreID)            
             
$entry = $calendar.Items.Add(1)            
$entry.Start = $start            
             
$entry.End = $end            
             
$entry.Subject = $subject            
             
$entry.Location = $location            
             
$entry.Body = $body             
             
$entry.Save()            
            
}


 



The appropriate calendar is identified – remember I have 4 to choose from. The Add method is used from the calendar items collection and the properties populated.



I always enter dates like this “14 September 2011 11:00” as it removes any problems with culture. In the UK this would be “14/9/2011 11:00” but when entering in this style I have to remember to use the US format “9/14/2011 11:00”



The mailbox parameter helps identify which calendar the entry goes

Controlling Firewall Rules

I decide that for this module I wanted some functions that control specific rule that I could be working with often e.g. Enable/Disable WMI rules and then I want a generic function for everything else.

Lets start with some specifics. If we look at the WMI rules

get-rule | where{$_.Name -like "*wmi*"}

we get this output.

Action    : Allow
Name      : Windows Management Instrumentation (WMI-Out)
Profile   : {Private, Public}
Direction : Outbound
Protocol  : TCP

Action    : Allow
Name      : Windows Management Instrumentation (WMI-In)
Profile   : {Private, Public}
Direction : Inbound
Protocol  : TCP

Action    : Allow
Name      : Windows Management Instrumentation (WMI-Out)
Profile   : {Domain}
Direction : Outbound
Protocol  : TCP

Action    : Allow
Name      : Windows Management Instrumentation (WMI-In)
Profile   : {Domain}
Direction : Inbound
Protocol  : TCP

As they are enabled we’ll start by looking at disabling them.

function disable-wmi {             
[CmdletBinding()]             
param (            
             
 [parameter(ParameterSetName="Inbound")]            
 [switch]$in,            
             
 [parameter(ParameterSetName="Outbound")]            
 [switch]$out,            
             
 [switch]$domain,            
 [switch]$private,            
 [switch]$public            
)             
BEGIN{}#begin             
PROCESS{            
$fw = New-Object -ComObject HNetCfg.FwPolicy2            
            
switch ($psCmdlet.ParameterSetName) {            
 "Inbound"  {$direction = 1 }            
 "Outbound"  {$direction = 2 }            
 default {Write-Host "Error!!! Should not be here" }            
}            
            
            
$fw.Rules | where {$_.Name -like "Windows Management Instrumentation*" -and $_.Direction -eq $direction} |            
foreach {            
 #$_            
 if($domain -and ($_.profiles -eq 1)) {$_.Enabled = $false}             
 if($private -and ($_.profiles -eq 2)) {$_.Enabled = $false}              
 if($public -and ($_.profiles -eq 4)) {$_.Enabled = $false}              
            
 if($domain -and ($_.profiles -band 1)) {$_.Enabled = $false}             
 if($private -and ($_.profiles -band 2)) {$_.Enabled = $false}              
 if($public -and ($_.profiles -band 4)) {$_.Enabled = $false}              
            
            
}            
            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Disables WMI through firewall

.DESCRIPTION
Disables WMI through firewall
 

.EXAMPLE
disable-wmi -in -domain

.EXAMPLE
disable-wmi -in -domain -pp

.EXAMPLE
disable-wmi -out  -pp

#>            
            
}


I’ve used parameter sets to distinguish in and outbound rules. The network types are set as switches.



The rules are scanned and depending on the network type and profile the Enabled property is set to $false. I’ve tested the profile as an equals and as a –band to catch the singleton and mixed profile

Firewall rules (OK)

The really important thing about our firewall is the rules that are configured.

function get-rule {             
[CmdletBinding()]             
param ()             
BEGIN{}#begin             
PROCESS{            
$fw = New-Object -ComObject HNetCfg.FwPolicy2            
$fw.Rules |            
foreach {            
            
$profiles = @()            
$ruleprofile = $_.Profiles            
            
@(1,2,4, 1073741824) |             
foreach {            
  if ($_ -band $ruleprofile){$profiles += [ProfileType]($_)}            
}            
              
 $rule = New-Object -TypeName PSObject -Property @{            
     Name = $_.Name            
     Protocol = [Protocol]($_.Protocol)            
     Direction = [Direction]($_.Direction)            
     Profile = $profiles            
     Action = [Action]($_.Action)            
   }            
 $rule.PSTypeNames[0] = "FirewallRule"            
 $rule            
            
}            
            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Displays firewall rules

.DESCRIPTION
Displays firewall rules.
Properties are:
 Name
 Protocol
 Direction
 Profile
 Action 
 

.EXAMPLE
get-rule

get-rule | format-table -AutoSize -Wrap
#>            
            
}


Start with the HNetCfg.FwPolicy2   object. Put the Rules collection onto the pipeline. check each of the possible profiles and add them to the array.



An output object is created with the properties converted from their numeric values to descriptive values using a few more enumerations.



Suggested usage:



get-rule



get-rule | format-table -AutoSize -Wrap

Windows firewall

I normally leave the Windows firewall enabled in my test environment. It ensures I don’t forget it when testing. My recent session for the TechEd:Australia PowerShell conference involved me disabling various firewall rules on the subject machine to set up part of the demo. I had to use the GUI tools to do this. I'’ve moaned to myself that I needed some PowerShell tools for working with the firewall especially as the netsh syntax has changed in Windows 2008 R2 – the session has prompted me to do something about it.

James O’Neill has written a very nice Configurator module for setting up servers. I’ve borrowed some of his ideas but as I’m just working with the firewall I thought I’d be a bit more verbose in the way I do things.

First off I want to know which network types are enabled on my machine. I need to consider them when working with  firewall rules.

I’m creating these functions as a module so I can define my networks (firewall profiles) in the .psm1 file

## types            
Add-type @"
public enum ProfileType {
 Domain   = 1,
 Private  = 2,
 Public   = 4,
 All      = 1073741824
} 
"@            
            
## functions            
. $psScriptRoot/Get-Profile.ps1


I can use this enum in the function to display the firewall profiles



function get-profile {             
[CmdletBinding()]             
param ()             
BEGIN{}#begin             
PROCESS{            
$fw = New-Object -ComObject HNetCfg.FwPolicy2            
$fwtypes = $fw.CurrentProfileTypes            
            
@(1,2,4) |             
foreach {            
 $cpt = New-Object -TypeName PSObject -Property @{            
     Enabled = $false            
     Profile = [ProfileType]($_)             
   }            
 $cpt.PSTypeNames[0] = "FirewallProfile"            
             
 if ($_ -band $fwtypes){$cpt.Enabled = $true}            
 $cpt             
}            
            
}#process             
END{}#end            
            
<# 
.SYNOPSIS
Determines active firewall profiles

.DESCRIPTION
Determines active firewall profiles.
Possible values are:
 Domain
 Private 
 Public

.EXAMPLE
get-profile

#>            
            
}


The function gets the firewall COM object. For the three network types we create an object that gives the name and that its disabled. We change the object type and then if test if the value and the firewall CurrentProfileTypes property can band. If they do we set the profile to enabled ($true). The object is displayed.



Output looks like this



PS> get-profile | ft -a



Profile Enabled
------- -------
Domain   False
Private    True
Public    True

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.

IE history to CSV

Back in April last year I wrote a post about viewing IE history

http://msmvps.com/blogs/richardsiddaway/archive/2010/04/13/ie-history.aspx

I had a comment left asking how this could be put into a CSV file

We’ll start by turning the script into an advanced function that outputs an object

function get-iehistory {            
[CmdletBinding()]            
param ()            
            
$shell = New-Object -ComObject Shell.Application            
$hist = $shell.NameSpace(34)            
$folder = $hist.Self            
            
$hist.Items() |             
foreach {            
 if ($_.IsFolder) {            
   $siteFolder = $_.GetFolder            
   $siteFolder.Items() |             
   foreach {            
     $site = $_            
             
     if ($site.IsFolder) {            
        $pageFolder  = $site.GetFolder            
        $pageFolder.Items() |             
        foreach {            
           $visit = New-Object -TypeName PSObject -Property @{            
               Site = $($site.Name)            
               URL = $($pageFolder.GetDetailsOf($_,0))            
               Date = $( $pageFolder.GetDetailsOf($_,2))            
           }            
           $visit            
        }            
     }            
   }            
 }            
}            
}


The main changes are to stop writing strings to the output and to create an object to put the data into.



Run the function by dot sourcing the file you have the script in – its easier to do this in ISE Then try these commands



get-iehistory
get-iehistory | export-csv iehistory.csv -TypeInformation
Import-Csv iehistory.csv

Windows Updates: remote machines

 

My main blog is now at http://msmvps.com/blogs/RichardSiddaway/Default.aspx but I also maintain http://richardspowershellblog.wordpress.com/ as a mirror and in case I want the two to diverge at some point.

My recent posts on accessing Windows updates – especially when testing for available updates have raised a number of comments

http://msmvps.com/blogs/richardsiddaway/archive/2011/06/12/windows-updates-4-tidy-up-get-update.aspx

http://richardspowershellblog.wordpress.com/2011/06/12/windows-updates-4-tidy-up-get-update/

One very interesting question is about getting information on the state of updates on remote machines.

We are using COM classes in these functions. It is possible for programmers to access COM objects on remote machines but it isn’t an easy proposition in PowerShell. The best way round this I can think of is to do something like this

get-qadcomputer |
foreach {
invoke-command -filepath c:\scripts\getupdates.ps1 -ComputerName $_.Name

}

The getupdates script would contain this code

$session = New-Object -ComObject Microsoft.Update.Session            
$searcher = $session.CreateUpdateSearcher()            
            
$result = $searcher.Search("IsInstalled=0 and Type='Software' and ISHidden=0" )            
            
if ($result.Updates.Count -gt 0){            
 $result.Updates |             
 select Title, IsHidden, IsDownloaded, IsMandatory,             
 IsUninstallable, RebootRequired, Description            
}            
else {            
 Write-Host " No updates available"            
}


Alternatively leave it as a function and add a call to the function as the last line of the script. I’ll be returning to this idea in a little while as I want to do some more on automating updates across my test environment

Viewing favourites

I recently copied my favourites between machines which started me thinking about viewing favourites. Like many people I have generated a lot of favourites over the years – do I really need them all. Don’t know because I don’t know what they are.  Its easy to correct that

$favs = New-Object -ComObject Shell.Application            
Get-ChildItem -Path $favs.NameSpace(0x6).Self.Path -Recurse |            
where {-not $_.PSIsContainer} |            
foreach {            
 $fav = New-Object -TypeName PSObject -Property @{            
  Name = $_.BaseName             
 }            
             
 Get-Content -Path $_.FullName | foreach {            
  if ($_.StartsWith("URL=")) {            
   $fav | Add-Member -MemberType NoteProperty -Name URL -Value $_.Replace("URL=","")}            
 }             
 $fav            
}


The script use the Shell COM object to access the favourites special folder. We then iterate through the favourites and get the content of each favourite. These are just text files so we can pick out the URL. An object is created to hold the favourite name and URL and then displayed

Windows Updates: 3 Installing Updates

Now we have discovered the updates we have available we can think about installing them.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037

function install-update {

$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()

$result = $searcher.Search("IsInstalled=0 and Type='Software' and ISHidden=0" )

if ($result.Updates.Count -eq 0
) {
 
Write-Host "No updates to install"
}
else {
$result.Updates | select Title
}

$downloads = New-Object -ComObject Microsoft.Update.UpdateColl

foreach ($update in $result.
Updates){
 
$downloads.Add($update
)
}
 

$downloader = $session.CreateUpdateDownLoader()
$downloader.Updates = 
$downloads
$downloader
.Download()

$installs = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($update in $result.
Updates){
 
if ($update.
IsDownloaded){
  
$installs.Add($update
)
 }
}


$installer = $session.CreateUpdateInstaller()
$installer.Updates = 
$installs
$installresult
 = $installer.Install()
$installresult

}

 

The function looks for non-hidden software updates that aren’t hidden.  At the moment I want some control over driver updates. If there are updates available they are added to a download collection and then downloaded.

The update collection returned from our original search is automatically updated with the fact the update is now downloaded.

We create an installer, add the updates to it and install.

The function is still a bit rough but it works and better still illustrates how the COM objects work.

Next jobs are to tidy up the functions and extend the functionality to remote machines!

Windows Updates: 2 Installed updates

Testing for installed updates is a variation on what we saw last time. This will show updates that Windows updates has installed – Get-Hotfix will show all updates that have been installed.

001
002
003
004
005
006
007
008

function get-installedupdate {
$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()

$result = $searcher.Search("IsInstalled=1 and Type='Software'" )

$result.Updates | select Title, LastDeploymentChangeTime
}

 

The main difference is that we now search on IsInstalled=1 (true) rather than IsInstalled=0 (false)

The title and date are displayed.