Archive for Powershell

A couple of days ago Anders Bengtsson over at contoso.se asked me if I could help out and create a solution that could be used to help automate the task of saving file attachments to the file system. After spending some time messing around with memory streams in PowerShell I’ve managed to come up with a script that can be used to save all files “attached” to a work item or configuration item in Service Manager.

#
#Author: Patrik Sundqvist, www.litware.se
#Description: Can be used to archive attached files
#
 
param([Guid] $Id = $(throw "Parameter `$Id is required. This should be the internal id of a work item or config item which attached files you want to archive."),
[string]$ArchiveRootPath = $(throw "Parameter `$ArchiveRootPath is required. A folder containing all file attachments will be created in this folder."),
[string]$ComputerName = "localhost")
 
$WIhasAttachMent = "aa8c26dc-3a12-5f88-d9c7-753e5a8a55b4"
$CIhasAttachMent = "095ebf2a-ee83-b956-7176-ab09eded6784"
 
#Adjust path
$ArchiveRootPath = $ArchiveRootPath.TrimEnd("\")
 
#Make sure path exists
if(!(Test-Path $ArchiveRootPath))
{
    Write-Error "Provided archive path $ArchiveRootPath doesn't exists" -ErrorAction Stop
}
 
#Making sure smlets is loaded
if(!(get-module smlets))
{
    import-module smlets -Force -ErrorAction Stop    
}
 
#Get Emo
$Emo = Get-SCSMObject -Id $Id -ComputerName $ComputerName
 
 
#Check if this is a work item or config item
$WIhasAttachMentClass = Get-SCSMRelationshipClass -Id $WIhasAttachMent -ComputerName $ComputerName
$WIClass = Get-SCSMClass System.WorkItem$ -ComputerName $ComputerName
#Figure out if this is a work item or a config item to make sure we use the correct relationship
if($Emo.IsInstanceOf($WIClass))
{
    $files = Get-SCSMRelatedObject -SMObject $Emo -Relationship $WIhasAttachMentClass -ComputerName $ComputerName
}
else
{
    $CIhasAttachMentClass = Get-SCSMRelationshipClass -Id $CIhasAttachMent -ComputerName $ComputerName
    $CIClass = Get-SCSMClass System.ConfigItem$ -ComputerName $ComputerName
    if($Emo.IsInstanceOf($CIClass))
    {
        $files = Get-SCSMRelatedObject -SMObject $Emo -Relationship $CIhasAttachMentClass -ComputerName $ComputerName
    }
    else
    {
        Write-Error "Instance isn't of supported type" -ErrorAction Stop
    }
}
 
#For each file, archive to entity folder
if($files -ne $Null)
{
    #Create archive folder
    $nArchivePath = $ArchiveRootPath + "\" + $Emo.Id
    New-Item -Path ($nArchivePath) -ItemType "directory" -Force|Out-Null
 
    $files|%{
            Try
            {
                $_.DisplayName
                $fs = [IO.File]::OpenWrite(($nArchivePath + "\" + $_.DisplayName))
                $memoryStream = New-Object IO.MemoryStream
                $buffer = New-Object byte[] 8192
                [int]$bytesRead|Out-Null
                while (($bytesRead = $_.Content.Read($buffer, 0, $buffer.Length)) -gt 0)
                {
                    $memoryStream.Write($buffer, 0, $bytesRead)
                }        
                $memoryStream.WriteTo($fs)
            }
            Finally
            {
                $fs.Close()
                $memoryStream.Close()
            }
    }
}

Get-FileAttachments.ps1 requires “smlets” which can be downloaded at smlets.codeplex.com.

Remember to unblock the script after downloading, if you want to be able to run it that is :)

Using smlets you can simply search for configuration items or work items and pass on their internal ids to the script to have all their attachments archived nice and easy in a folder of your choice. In the example below I’m searching for all incidents in Service Manager that has a title containing the word “computer”. I pass on their internal id, a folder to use for archiving and the computer name of my Service Manager management server to the script. The script then checks for attachments and, in the case of finding attachments, creates a folder for each incident that has attachments and save the files in them.

?View Code POWERSHELL
Import-module smlets
$class = get-scsmclass system.workitem.incident$ -ComputerName sm2
$incidents = Get-SCSMObject -Class $class -Filter "DisplayName -like '*test*'" -computerName sm2
foreach ($incident in $incidents)
{
    .\Get-FileAttachments.ps1 -Id $incident.Get_Id() -ArchiveRootPath "C:\SCSM\Archive" -computername sm2    
}
Comments (10)
Feb
24

SMLets – Enum and wildcards

Posted by: | Comments (11)

I’ve seen the question two times now, and that’s two times too many :)
The script below will show you how you can locate all Incident (as an example) that has an Classification value matching a Incident Classification that has a display name that matches *problem*.

?View Code POWERSHELL
Import-module smlets -Force
 
$class = get-scsmclass system.workitem.incident$
 
#Try to narrow the next search down by getting the root value for the enums you're interested in.
$rootEnum = Get-SCSMEnumeration IncidentClassificationEnum$
 
#Might be performance heavy depending on how many child enums the parent enum has.
$matchingEnums = get-scsmchildenumeration -Enumeration $rootEnum|?{$_.DisplayName -like '*problem*'}
 
$Filter = ""
$matchingEnums|%{$Filter +="Classification -eq '$($_.Id)' -or " }
$Filter = $Filter.TrimEnd("-or ")
 
Get-SCSMObject $class -Filter $Filter

Remember that the display name used when matching the filter is the localized display name used by the current process (if you run this in a workflow).
If you haven’t already got it, you’ll find SMLets at http://smlets.codeplex.com

When using smlets (smlets.codeplex.com) to work with Service Manager using PowerShell, small details can have huge impact on performance.

Server side vs. Client side filtering

The number one thing is to use server side filtering as far as possible. This has been described before but cannot be told too many times.

Example of client side filtering:

?View Code POWERSHELL
$class = Get-SCSMclass system.workitem.incident$ 
Get-SCSMObject -class $class|?{$_.Title -like '*printer*'}

Example of server side filtering:

?View Code POWERSHELL
$class = Get-SCSMclass system.workitem.incident$
Get-SCSMObject $class -Filter "Title -like *printer*"

In my test lab the server side filtering above is 50 times faster than client side. The reason for that is that client side filtering in the example above, in fact means bringing over all objects of the specific class to the client side and finding the matches there. Not a good idea!

Read more about the gain and tips about server side filtering in these excellent blog posts from Travis Wright and Jim Truher:
Properly Querying SCSM Using SMLets Get-SCSMObject cmdlet
Retrieving projection data with PowerShell

Choosing the lighter type projection

What I want to add to the above is how choosing the proper type projection will affect the performance.

As an example, let’s say we want to retrieve all Incidents that are currently Active and Un-Assigned (assigned to user is not set).

We can do this by using the following PowerShell commands:

?View Code POWERSHELL
#Get type projection
$IncidentProjection = Get-SCSMTypeProjection System.WorkItem.Incident.ProjectionType$
#Create criteria
$criteria = new-object $CTYPE $cc,$IncidentProjection.__Base,$IncidentProjection.managementgroup
#Get incidents matching the criteria
$incidents = Get-SCSMObjectProjection -Criteria $criteria

Note: I cut out the part of defining the criteria “$cc”, you can find it in the download available in the bottom of the post.

Depending on which type projection you choose the time for execution of the commands can differ greatly. The reason for this is that different type projections bring a different amount of data back from the server. A type projection is a definition of what data you want to wrap around the seed class instance (in this case the Incident). When choosing which type projection to use we have to think about how we want to define the criteria and which data we want to retrieve. In our example above we just want to see which incidents are Active and Un-assigned. This basically means that we only require a type projection (read more about type projections here) that has the seed class “Incident” and has one component, the AssignedUser. Let’s look at the effect of choosing two different type projections in the example above.

To see which type projections are available for a specific type you can use the following command:

?View Code POWERSHELL
Get-scsmtypeprojection|?{$_.TargetType.Name -eq "System.WorkItem.Incident"}

Note: Client side filtering is perfectly fine in usage like this of course. Another great way to study type projections is to export management packs and look at the xml directly. Also, if you do not find a suitable type projection you can also define your own, but that’s a different story.

After looking at the type projections let’s say that we choose to use the one called “System.WorkItem.Incident.ProjectionType” since this one contains all the components we’ll ever need. Actually it’s the projection that is used out of the box for the Incident form, which means it contains all the components used by the form like: Assigned User, Primary User, Activities, Affected User etc.

Executing the query for all Incidents with status Active which are Un-Assigned, using the type projection above, in my lab environment (over a medium slow connection) takes about 30s.

Now we might be perfectly fine with that, but let’s take a look at what would happen if we would have chosen a “lighter” type projection (fewer components wrapping the incident). As discussed earlier this scenario really only required one component, the AssignedUser, since we’re using the AssignedUser in our criteria.

The out-of-the-box type projection that has the least components and includes the AssignedUser is the projection called “System.WorkItem.Incident.View.ProjectionType”. This projection actually only contains two components: AssignedUser and AffectedUser. Using this projection for the same query for data as the previous, with the same setup takes 10s.

So by choosing a more suitable type projection we managed to cut two thirds of the time used for retrieving the incidents we were looking for.

So remember folks:

  • Use server side filtering
  • Use the type projection with the least components that fulfill your needs.
    Based on components used by your criteria and what data you want to retrieve.

One last thing, when creating views in Service Manager you can use the exact same reasoning regarding which components are required to achieve as good performance as possible. This has been described on its own in this great blog post by Travis Wright: Why is My Custom Incident View So Slow?

You can download my measurement script here: smletsDataQueryTest.ps1

Comments (0)

Sorry all non-Swedish speaking people out there… And congrats for those who understand Swedish! There is a series of online seminars about System Center 2012 taking place this spring with some great Swedish speakers. The seminars will be covering:

  • PowerShell
  • Configuration Manager 2012
  • Operations Manager 2012
  • Service Manager 2012 (my seminar)
  • Data Protection Manager 2012
  • Orchestrator 2012
  • Application Controller
  • How to build your automated data center with System Center 2012

For more information and registration: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032502587&Culture=sv-SE&WT.mc_id=nlc-n-se-loc–SystemCenter2012onlineseminars

 

Comments (0)
Nov
18

Affected User SMTP Address

Posted by: | Comments (6)

Last week I got a question on how to get the smtp address of the affected user in an incident using PowerShell. The scenario where the question surfaced was when a friend was trying to execute a PowerShell script that would send an email message to the affected user of an incident in a custom console task (targeting the Incident class).

After some investigation I came up with the following PowerShell script which should do the trick.

?View Code POWERSHELL
# Author: Patrik Sundqvist
# Author blog: http://blogs.litware.se
# Requires: SCSM PowerShell Cmdlets (Beta 1 or later)
 
#Pass the id of the incident you want to get the affected user smtp address for as command line argument to the script
#Example: "GetAffectedUserSMTP.ps1 IR58"
param($incidentId)
 
Import-module smlets -Force
 
$affectedUserRelClass = Get-SCSMRelationshipClass System.WorkItemAffectedUser$
$userPref = Get-SCSMRelationshipClass System.UserHasPreference$
 
#Update done 2011-04-05 based on blog post comment from German Minicucci, thanks for the feedback!
#Locate the incident you want to find the affected user smtp address for
$incident = Get-SCSMIncident -ID $incidentId |%{$_.Object}
 
#Make sure there was an incident located with the given id
if($incident -eq $null)
{
    Write-Host 'No incident exists with given id'
}
else
{
    #Get the affected user in the located incident
    $affectedUser = Get-SCSMRelatedObject -SMObject $incident -Relationship $AffectedUserRelClass
 
    #Make sure there is an affected user specified in the incident, else abort
    if($affectedUser -eq $null)
    {
        Write-Host 'No affected user is given for the incident'$incident.DisplayName
    }
    else
    {
        #Get the endpoint related to the affected user that specifies the smtp address
        $endPoint = Get-SCSMRelatedObject -SMObject $affectedUser -Relationship $userPref|?{$_.DisplayName -like '*SMTP'}
        if($endPoint -eq $null)
        {
            Write-Host 'There is no smtp channel declared for the affected user'$affectedUser.DisplayName
        }
        else
        {
        if($endPoint.TargetAddress.length -gt 0)
            {
                #Output the located smtp address
                Write-Host $endPoint.TargetAddress
            }
            else
            {
                #No valid smtp address located for the user in the CMDB
            	Write-Host 'No valid smtp address found for user'$affectedUser.DisplayName
            }
        }
    }
}

The result can be seen in the picture!

Enjoy!

Updated 2012-02-17

  • The performance of the script has been greatly improved.
  • When comparing time I had missed to convert to UTC which is what is stored in the database.

A big THANK YOU goes out to Andrew Gault for testing and finding the UTC bug.


A common request for Service Manager is to be able to notify upon Incidents breaching
their “Response Time SLA”, the time between registration of an incident and when
the incident is assigned to someone who starts working on it. In this post I’ll
describe a solution for reacting on a “Response Time Breaches” and using Incident
Event Workflows triggered by the breach to send notifications. The solution I describe
below will be configured to send a notification to the “primary owner” of an Incident
if the Incident has been registered for more than 30 minutes without being assigned
to anyone. The solution consists of the following components:

  • Response Time Breached Property: A class type extension of the
    Incident class type consisting of one property Name: RTBreached Type: Integer DefaultValue:
    0
  • Scheduled workflow: A re-occurring workflow created using the Authoring
    Tool that runs every other minute to locate all Incidents that has breached the
    “Response Time Threshold” using the following criteria: Status equal Active CreatedDate
    less than (“Now – 30 minutes”) AssignedTo equal Null RTBreached not equal 0
  • Notification Template: Defines how the email will look like when
    the notifications are sent regarding the breach.
  • Incident Event Workflow: A workflow created directly in the console
    that will be indirectly triggered for each Incident detected by the re-occurring
    workflow.

You’ll need the following PowerShell module to make things work:

http://smlets.codeplex.com

Extending the Incident Class Type

  1. Create a new strong name key using sn.exe (if you don’t already
    have a suitable one) In case you aren’t using the “Visual Studio Command Prompt”
    (Start >> Microsoft Visual Studio 2010 >> Visual Studio Tools >>
    Visual Studio Command Prompt (2010)) you can locate sn.exe at %ProgramFiles%\Microsoft
    SDKs\Windows\v7.0A\bin\sn.exe
  2. Start the Service Manager Authoring Tool
  3. Create new Management Pack called Litware.Demo.ResponseTimeSLA.Library
  4. In the new MP, right click “Classes” and select “Create
    other class…”
  5. Choose Incident as base class
  6. Name the new class Litware.Incident.ResponseTimeExtension
  7. Set class properties as follows: Extension: True
  8. In the list of class type properties, delete the automatically added property (called
    something like “Property26”).
  9. Add a new property as follows: Internal name: RTBreached Data type: Integer Default
    value: 0
  10. Save the MP
  11. Right click Litware.Demo.ResponseTimeSLA.Library in the
    Mangement Pack Explorer
    , select Seal Management Pack
  12. In the Seal Management Pack dialog
    1. Choose choose a suitable output directory
    2. Browse to and select your strong name key
    3. Enter your company name
    4. Click Seal then Close

Creating the re-occurring workflow

  1. Back in the Authoring Tool, create a new Management Pack called Litware.Demo.ResponseTimeSLA.Config
  2. In the new MP, right click Workflows, select Create
  3. Enter the following information then click Next Name: DetectResponseTimeBreaches
    Description: “Used to detect incidents breaching their Response Time SLA”
  4. Select “Run at a scheduled time or at scheduled intervals” then
    click Next
  5. Select “Other Interval”, specify the frequency “2 minutes”
    and click Next
  6. Verify the summary and click “Create” then “Close”
  7. Drag a Windows PowerShell Script activity onto
    the design surface from the Activities ToolBox
  8. Change the name property of the activity to “MarkRTBreachedIncidents”
  9. In the Script Body of the PowerShell Activity
    paste the following script:
  10. ?View Code POWERSHELL
        # Author: Patrik Sundqvist
    # Author blog: http://blogs.litware.se
    # Requires: SCSM PowerShell Cmdlets (Beta 1 or later)
    # Update 2012-02-15 - Major imp. on performance.
     
    Import-Module -Name smlets -Force
    # Get the type projection
    $IncidentProjection = Get-SCSMTypeProjection System.WorkItem.Incident.View.ProjectionType$
    # Get id for Incident Status "Active"
    $IncidentActive = (Get-SCSMEnumeration IncidentStatusEnum.Active$).Id
    # Set the target response time to 30 minutes
    $RTSLA = New-Object TimeSpan 0,0,30,0,0
     
     
    # Define the required ObjectProjectionCriteria
    $CTYPE = "Microsoft.EnterpriseManagement.Common.ObjectProjectionCriteria"
     
    # After defining the XML based criteria, I had to replace the following charcters, "<>$" with their ASCII representation 
    $lt = [char]60 
    $gt = [char]62 
    $dollar = [char]36 
     
    $C = @"
    $($lt)Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/"$($gt)
      $($lt)Reference Id="System.WorkItem.Incident.Library" PublicKeyToken="31bf3856ad364e35" Version="7.0.6555.0" Alias="CoreIncident" /$($gt)
      $($lt)Reference Id="System.WorkItem.Library" PublicKeyToken="31bf3856ad364e35" Version="7.0.6555.0" Alias="CoreWorkItem" /$($gt)
      $($lt)Expression$($gt)
        $($lt)And$($gt)
          $($lt)Expression$($gt)
            $($lt)SimpleExpression$($gt)
              $($lt)ValueExpressionLeft$($gt)
                $($lt)Property$($gt)$($dollar)Context/Property[Type='CoreIncident!System.WorkItem.Incident']/Status$($dollar)$($lt)/Property$($gt)
              $($lt)/ValueExpressionLeft$($gt)
              $($lt)Operator$($gt)Equal$($lt)/Operator$($gt)
              $($lt)ValueExpressionRight$($gt)
                $($lt)Value$($gt){0}$($lt)/Value$($gt)
              $($lt)/ValueExpressionRight$($gt)
            $($lt)/SimpleExpression$($gt)
          $($lt)/Expression$($gt)
          $($lt)Expression$($gt)
            $($lt)SimpleExpression$($gt)
              $($lt)ValueExpressionLeft$($gt)
                $($lt)Property$($gt)$($dollar)Context/Property[Type='CoreIncident!System.WorkItem.Incident']/CreatedDate$($dollar)$($lt)/Property$($gt)
              $($lt)/ValueExpressionLeft$($gt)
              $($lt)Operator$($gt)Less$($lt)/Operator$($gt)
              $($lt)ValueExpressionRight$($gt)
                $($lt)Value$($gt){1}$($lt)/Value$($gt)
              $($lt)/ValueExpressionRight$($gt)
            $($lt)/SimpleExpression$($gt)
          $($lt)/Expression$($gt)
          $($lt)Expression$($gt)
            $($lt)UnaryExpression$($gt)
              $($lt)ValueExpression$($gt)
                $($lt)GenericProperty Path="$($dollar)Context/Path[Relationship='CoreWorkItem!System.WorkItemAssignedToUser' SeedRole='Source']$($dollar)"$($gt)Id$($lt)/GenericProperty$($gt)
              $($lt)/ValueExpression$($gt)
              $($lt)Operator$($gt)IsNull$($lt)/Operator$($gt)
            $($lt)/UnaryExpression$($gt)
          $($lt)/Expression$($gt)
          $($lt)Expression$($gt)
            $($lt)SimpleExpression$($gt)
              $($lt)ValueExpressionLeft$($gt)
                $($lt)Property$($gt)$($dollar)Context/Property[Type='CoreIncident!System.WorkItem.Incident']/RTBreached$($dollar)$($lt)/Property$($gt)
              $($lt)/ValueExpressionLeft$($gt)
              $($lt)Operator$($gt)NotEqual$($lt)/Operator$($gt)
              $($lt)ValueExpressionRight$($gt)
                $($lt)Value$($gt)1$($lt)/Value$($gt)
              $($lt)/ValueExpressionRight$($gt)
            $($lt)/SimpleExpression$($gt)
          $($lt)/Expression$($gt)
        $($lt)/And$($gt)
      $($lt)/Expression$($gt)
    $($lt)/Criteria$($gt)
    "@
     
    $cc = [string]::Format($C, $IncidentActive, (Get-Date).Subtract($RTSLA).ToUniversalTime())
    $criteria = new-object $CTYPE $cc,$IncidentProjection.__Base,$IncidentProjection.managementgroup
     
    # Get incidents matching the criteria
    $BreachedIncidents = get-scsmobjectprojection -criteria $criteria|%{$_.Object}
     
    # Marking all located incidents to trigger notification workflow
    if ($BreachedIncidents -ne $null)
    {
        $BreachedIncidents|Set-SCSMObject -Property RTBreached -Value 1
    }
  11. Save the Management Pack
  12. Go to the directory where you created the Management Packs
  13. Copy the assembly (dll) called DetectResponseTimeBreaches.dll to
    %ProgramFiles%\Microsoft System Center\Service Manager 2010\
  14. In the Service Manager console, go to Administration – Management Packs
    and import the MPs in the following order

    1. Litware.Demo.ResponseTimeSLA.Library.mp
    2. Litware.Demo.ResponseTimeSLA.Config.xml

You might need to restart the Service Manager services after deploying the workflow
assemblies.

Creating the notification template

  1. In the console, go to Administration – Notifications – Templates
  2. Create a template with the following characteristics: Name: Response
    Time Breached Template Description: Notify the primary user of
    an unassigned incident when Response Time SLA is breached Target class:
    Incident Management pack: Litware.Demo.SLAResponseTime.Config
    Subject
    : Response Time SLA Breached Message body:
    The Response Time has breached for the incident described below.Name: $Context/Property[Type='System!System.Entity']/DisplayName$
    Priority: $Context/Property[Type='CustomSystem_WorkItem_Library!System.WorkItem.TroubleTicket']/Priority$
    Primary Owner: $Context/Path[Relationship='CustomSystem_WorkItem_Incident_Library!System.WorkItem.IncidentPrimaryOwner'
    TypeConstraint='System!System.User']$?$DisplayName$? Please act now!

Creating the Incident Event Workflow

  1. Go to Administration – Workflows – Configuration
  2. Create an Incident Event Workflow with the following characteristics : Name:
    Response Time Breached Notification Check for event: When an incident
    is updated Management pack: Litware.Demo.ResponseTimeSLA.Config
    Criteria – Changed From: “RTBreached equals 0” Criteria – Changed
    To
    : “RTBreached equals 1” Enable notification: “True”
    (Checked) Add a notification: User – Primary Owner, Message Template
    – Response Time Breached Template

To try things out – create an incident, assign yourself as the primary owner and
leave it for 30 minutes then check your mailbox :)

The PowerShell cmdlets for Service Manager has gone Beta 1. I would like to thank Jim Truher for joining the project and putting in a lot of effort in this release!

Release notes:
The snapin has been converted to a PowerShell version 2 module, so registration is not needed. To install extract the zip archive into the
C:\Windows\System32\WindowsPowerShell\v1.0\modules directory

and then run:

PS> Import-Module SMLets

This module now contains the following:

?View Code POWERSHELL
PS> get-command -module smlets |sort commandtype | ft commandtype,name -au
 
CommandType Name
----------- ----
      Alias load
      Alias new-mg
   Function New-ManagementGroup
   Function import-Assembly
   Function Get-SCSMClassProperty
   Function get-SCSMCommand
   Function get-SCSMproperty
     Cmdlet Get-SCSMTask
     Cmdlet Get-SCSMSubscription
     Cmdlet Get-SCSMTypeProjection
     Cmdlet Get-SCSMTaskResult
     Cmdlet Get-SCSMUserRole
     Cmdlet Get-SCSMTopLevelEnumeration
     Cmdlet Import-SCManagementPack
     Cmdlet Remove-SCSMSubscription
     Cmdlet Remove-SCSMObject
     Cmdlet Remove-SCManagementPack
     Cmdlet Set-SCSMObject
     Cmdlet Set-SCSMIncident
     Cmdlet Set-SCSMAnnouncement
     Cmdlet New-SCSealedManagementPack
     Cmdlet New-SCManagementPack
     Cmdlet Set-SCSMObjectProjection
     Cmdlet New-SCSMObject
     Cmdlet New-SCSMIncident
     Cmdlet New-SCSMAnnouncement
     Cmdlet Get-SCSMRunAsAccount
     Cmdlet Get-SCDWWarehouseModuleTypes
     Cmdlet Get-SCDWRelationshipFactTypes
     Cmdlet Get-SCManagementPack
     Cmdlet Get-SCSMAnnouncement
     Cmdlet Get-SCManagementPackElement
     Cmdlet Get-SCDWOutriggerTypes
     Cmdlet Get-DataWarehouseConfiguration
     Cmdlet Export-SCManagementPack
     Cmdlet Get-SCDWDimensionTypes
     Cmdlet Get-SCDWMeasureTypes
     Cmdlet Get-SCDWFactTypes
     Cmdlet Get-SCSMCategory
     Cmdlet Get-SCSMRelatedObject
     Cmdlet Get-SCSMObjectProjection
     Cmdlet Get-SCSMRelationshipClass
     Cmdlet Get-SCSMRule
     Cmdlet Get-SCSMResource
     Cmdlet Get-SCSMObject
     Cmdlet Get-SCSMClass
     Cmdlet Get-SCSMChildEnumeration
     Cmdlet Get-SCSMConfigItem
     Cmdlet Get-SCSMIncident
     Cmdlet Get-SCSMEnumeration

Grab the release over at codeplex!
http://smlets.codeplex.com

Jun
10

Custom console tasks: Part 2

Posted by: | Comments (6)

In the first part of the “Custom console tasks” series i described a very simple but powerful way to use the “Tasks” functionality in the Service Manager console to reboot an affected computer of an incident. This time I’m going to show you how we can use PowerShell scripting and some custom Cmdlets I’ve published on Codeplex to create a task that can re-activate (open) a closed incident, something that isn’t possible to do in the console out-of-the-box. Before we go on, please note that according to most process frameworks you shouldn’t re-open a closed incident.

Updated 2011-01-04 since there was a typo in step 6, parameters argument

  1. The first thing you need is the custom Service Manager Cmdlets i published on Codeplex (download here). You can follow the instructions here to install the Cmdlets. Using these cmdlets you’re able to update some incident properties, most importantly for this scenario the “Status” property.
  2. Secondly you need a powershell script that uses the Cmdlets to do all the magic. I’ve written a script that forces the status of an incident to “Active” (no matter previous Status). Since this is kind of hardcore, and should be considered as an override, I wanted to prompt the user for a “reason”. The script accomplishes this by running some C# code that renders a dialog where the user can provide a reason for running the task on the incident. If the user click the “Cancel” button I abort the action and leave the status “as is”. If the user click the “OK” button I go ahead and change status to “Active” (the reason is logged in the action log). 
  3. 2010-11-19 Updated script to work with SMLets Beta 1 or later

    ?View Code POWERSHELL
    param($ID)
    Import-module smlets -Force
     
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
     
    $objForm = New-Object System.Windows.Forms.Form
    $objForm.Text = "Force Incident Status"
    $objForm.Size = New-Object System.Drawing.Size(400,200)
    $objForm.StartPosition = "CenterScreen"
     
    $objForm.KeyPreview = $True
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
        {$x=$objTextBox.Text;$objForm.Close()}})
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
        {$objForm.Close()}})
     
    $OKButton = New-Object System.Windows.Forms.Button
    $OKButton.Location = New-Object System.Drawing.Size(125,120)
    $OKButton.Size = New-Object System.Drawing.Size(75,23)
    $OKButton.Text = "OK"
    $OKButton.Add_Click({$x=$objTextBox.Text;$objForm.Close();$result="OK"})
    $objForm.Controls.Add($OKButton)
     
    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Size(200,120)
    $CancelButton.Size = New-Object System.Drawing.Size(75,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Add_Click({$objForm.Close()})
    $objForm.Controls.Add($CancelButton)
     
    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(10,20)
    $objLabel.Size = New-Object System.Drawing.Size(360,20)
    $objLabel.Text = "Please enter a reason for forcing the incident status to Active:"
    $objForm.Controls.Add($objLabel) 
     
    $objTextBox = New-Object System.Windows.Forms.TextBox
    $objTextBox.Location = New-Object System.Drawing.Size(10,40)
    $objTextBox.Size = New-Object System.Drawing.Size(360,60)
    $objTextBox.Multiline = "True"
    $objForm.Controls.Add($objTextBox) 
     
    $objForm.Topmost = $True
     
    $objForm.Add_Shown({$objForm.Activate()})
    [void] $objForm.ShowDialog()
     
    if($result -eq "OK")
    {
        Set-SCSMIncident -ID $ID -Status Active
        Write-Host "Incident status forced to Active"
        if($x -gt 0)
        {
        	Write-Host "Reason for action: $x"
        }
        else
        {
        	Write-Host "No reason provided"
        }
    }
    else
    {
        Write-Host "Force to active status aborted"
    }
  4. Place the script in a local folder, I use “C:\PowerShell Scripts”
  5. Create a new task in the Service Manager console: Administration – Library – Tasks
    1. Name: Force Activate
    2. Description: Used to force status of incident to active, can be used on closed incidents!
    3. Target Class: Incident
    4. Category: Incident Management Folder Tasks
    5. Command Line: powershell.exe
    6. Parameters: &'.\ForceActivate.ps1' $Context/Property[Type='WorkItem!System.WorkItem']/Id$
    7. Working Directory: C:\PowerShell Scripts\
    8. Log in action log when this task is run: True
    9. Show output when this task is run: True

You’re done! You’re now able to break all the rules and open closed incidents…

This was an example of how powerful tasks can be built using PowerShell and the Service Manager SDK (used by the Cmdlets) to customize the console functionality in Service Manager. I plan to write a final part of this series where we go all the way and use managed code only to create a custom task including a custom WPF form.

May
31

SCSM PowerShell Cmdlets v0.1

Posted by: | Comments (5)

Inspired by Travis Wright over at the product team’s Service Manager blog, I’ve started a new Codeplex project that will develop a PowerShell snap-in containing useful Service Manager cmdlets. Today I’ve released the first preview version which will give you an idea of what’s to come!

Included is a set of cmdlets which gives you the power to create, update and search for Incidents in Service Manager 2010.
Go to http://smlets.codeplex.com/ and grab the download to start testing this out!

Included cmdlets are:

  • Get-SCSMIncident
    Ex. get all incidents that includes the word “network” in the title (wildcard by %) and has the status “Active”
  • Set-SCSMIncident
    Ex. update an incident’s description and attach a new file.
  • New-SCSMIncident
    Ex. register a new incident with title, description, impact, urgency, classification and source.

Using these we can now do stuff like closing all incidents which title starts with “Network”, has the status Resolved and hasn’t been touched for a given period of time
Get-SCSMIncident –Title “Network%” –Status Resolved –InactiveFor 5.00:00:00 | Set-SCSMIncident –Status Closed –Comment “Closed due to inactive period”

Don’t miss the solution built using these cmdlets by Anders Bengtsson over at contoso.se, where he solves the intensively discussed problem regarding “update incident by mail”.

Comments (5)