Quantcast
Channel: Matthew Dowst – Catapult Systems
Viewing all 39 articles
Browse latest View live

SCSM Customize the Business Services Form

$
0
0

I recently added some custom fields to the Business Service class, and I wanted to add those fields to the Business Service’s form. However, it turns out that the Business Service’s form is really tied to the Service class, so I was unable to see my extensions in the Authoring Console. This can be a real big problem because the Service class is an abstract class, which means that you cannot extend it. So the only real option to be able to add the Business Service extensions to the form is to create a custom form. Which you should know can take a long time and a lot of development. However, I was able to avoid this by making a copy of the Service form from the Service Maps Library, and placing it in the management pack attached to the end of this article.

In order for this form to work I made a few changes to the xml. First, it must reference the assembly file Microsoft.EnterpriseManagement.ServiceManager.ServiceMaps.Forms.dll. Instead of extracting this dll from the Service Maps Library management pack bundle, I simple referenced the assembly from the Service Maps Library management pack. This way, if the assembly file is ever updated you will not have to update this custom form management pack. It will automatically point to be updated assembly file.

The second changed I made was a little simpler, but essential to getting this form to work. To get the form to point to the Business Service class instead of the Service class, I updated the type projection to point to Business Service class.

So now to customize the Business Service’s form, all you need to do is download the Custom Business Service Forms, open it in the Author Console, make you customization, seal it (if you prefer), and import into your Service Manager environment.

Customizable Business Services Form – TechNet Gallery

Please note that this form was made using System Center 2012 R2. If you are on a previous version of Service Manager, this form should still work, but you would just need to update the versions numbers in the References section of the management pack.


Start Azure Automation runbook on Hybrid Worker with PowerShell

$
0
0

It appears that the Azure Automation cmdlet Start-AzureAutomationRunbook, in the Azure PowerShell module v0.9.3, has been updated to include a new parameter named RunOn. This parameter can be used to execute a runbook on a Hybrid Worker. The parameter is not yet listed on TechNet, or with the Get-Help command. However, by running Get-Command against the cmdlet you can see the RunOn parameter. The help message for this parameter states, “Optional name of the hybrid agent which should execute the runbook”. When using this parameter you just need to specify a string value with the name of the Hybrid Worker you want the runbook to execute on.

Start-AzureAutomationRunbook -AutomationAccountName "AutoLab" -name "Clear-Logs" -RunOn "LabHybrid"

 

SCSM PowerShell: Series Overview

$
0
0

Unless you’ve been living under a rock for the last few years, you know that PowerShell is the future for Microsoft. With products like Service Manager Automation and Azure Automation, a solid PowerShell foundation will be required for a lot of tasks. When System Center 2012 was released the Service Manager PowerShell modules received a huge over haul. There is almost nothing you can’t do using the provided Cmdlets. However, aside from the few examples on TechNet, there is not much documentation on working with the Service Manager Cmdlets. The purpose of this blog series will be to get you familiar with working with the Service Manager Cmdlets, and show you some of the great things you can accomplish using them.

All articles in their series are linked below. Be sure to read the overview below and check back often for new content and updates.

  1. Getting Started with Service Manager PowerShell
  2. Get Work Item Information
  3. Get Configuration Item Information
  4. Get Relationships
  5. Modifying Work/Configuration Items
  6. Working with Relationships
  7. Get List Items
  8. Creating Work/Configuration Items (coming soon)
  9. Creating PowerShell Workflows (coming soon)
  10. Creating Subscriptions (coming soon)
  11. Data Warehouse Cmdlets (coming soon)
  12. Performing Administrative Tasks (coming soon)

Series Overview

This series is not designed to be an introduction to PowerShell or Service Manager, just the Service Manager PowerShell modules. I assumes that you are familiar with Service Manager, and have a basic working knowledge of PowerShell. I will explain what the commands are doing, and I will provide links to additional resources where needed. This series will assume that you are familiar with running basic PowerShell commands and using PowerShell ISE. Some of the common PowerShell concepts we will use are variables, arrays, hash tables, foreach loops, and if else statements. Don’t worry if you are not experienced in all of these areas, you should still be able to follow along. I will explain what the commands are doing and provide links to additional information where needed.

I also recommend that you learn how to use the Get-Help and Get-Member cmdlets. They are not unique to Service Manager, and can be helpful with any PowerShell  project.

Get-Help will return the help information for a Cmdlet.

Get-Member will list the properties and methods of a command or object.

Terminology – For those of you who are new to Service Manager, I suggest reviewing the Service Manager Glossary. One item that is not in the glossary that you will often see is Work Item. In Service Manager this can refer to Change Management, Incident Management, Problem Management, Release Management, Service Request fulfillment, and all Activity types. Basically everything under the Work Items workspace, in the console.

 

All code examples used in the series can also be found at https://gist.github.com/mdowst

SCSM PowerShell: Getting Started with Service Manager PowerShell

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

Service Manager comes with two different PowerShell modules, the Administrator module and the Data Warehouse module. The Administrator module contains the cmdlets for working with the different work items and configuration items, and for performing administrative tasks within Service Manager. The Data Warehouse module is used for administering the data warehouse. Unless otherwise specified, the posts in this series will be referring to the Administrator module, and its cmdlets.

Loading the Service Manager Modules

There are a few ways that you can access the Service Manager module. First, in the Service Manager console, if you are in the Administration work space, you can click on Start PowerShell Session. Or on a machine with the Service Manager console installed you can go to Start > Microsoft System Center then click on Service Manager Shell. Either one of these will open a PowerShell prompt with both of the Service Manager modules loaded. This is fine for running short commands, but for more advanced scripts I prefer using the PowerShell Integrated Scripting Environment (ISE).

*PowerShell Tip – Execution Policy
The Windows PowerShell execution policy determines whether or not you can run scripts. By default this is set to “Restricted” which prevents all scripts from running. You can use the Set-ExecutionPolicy cmdlet to change this level. Typically you want to keep this set to the highest level possible that still allows you to perform your work. I typically recommend setting it to RemoteSigned. This allows you to execute all scripts you created locally, but requires a digital signature from a trusted publisher for any downloaded scripts. For more information on execution policies see about_Execution_Policies on TechNet.

Using PowerShell ISE

When using PowerShell ISE the Service Manager PowerShell modules will not be automatically loaded. You must load them manually. Typically to load a PowerShell module you enter Import-Module and the module name. However, this only works if the module is installed in the path listed in the environment variable PSModulePath. Unfortunately, the Service Manager modules are not installed using this path, so you must specify the full path to the modules psd1 file. The Service Manager PowerShell modules are installed in the Service Manager install folder. The default directory is “C:\Program Files\Microsoft System Center 2012\Service Manager” or “C:\Program Files\Microsoft System Center 2012 R2\Service Manager” depending on your version. To make things even more interesting the the Data Warehouse module is located at the top level of this folder, but the Administrator module is located in the subfolder PowerShell. The commands to load the modules using the default R2 paths are listed below.

# Service Manager Administrator Module
import-module 'C:\Program Files\Microsoft System Center 2012 R2\Service Manager\Powershell\System.Center.Service.Manager.psd1'

# Service Manager Data Warehouse Module
import-module 'C:\Program Files\Microsoft System Center 2012 R2\Service Manager\Microsoft.EnterpriseManagement.Warehouse.Cmdlets.psd1'

As you can see this can cause problems because the default path changes between versions, and you don’t always install programs to the C drive or the default directory. Luckily, Service Manger stores its install path in the registry key InstallDirectory located under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup. So to make a script more transportable you can write the value this key to a string variable and concatenate the psd1 file to the end of it.

# Service Manager Administrator Module
$InstallationConfigKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
$AdminModule = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Powershell\System.Center.Service.Manager.psd1"
Import-Module -Name $AdminModule

# Service Manager Data Warehouse Module
$InstallationConfigKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
$DWModule = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Microsoft.EnterpriseManagement.Warehouse.Cmdlets.psd1"
Import-Module -Name $DWModule

To confirm that the modules have loaded you can enter the command Get-Module. You should see an entry named System.Center.Service.Manager for the Administrator module and Microsoft.EnterpriseManagement.Warehouse.Cmdlets for the Data Warehouse module.

In some cases if you run the same script in the same session it can cause an error message because it is trying to load a module that is already loaded. To prevent this you can use an If statement to check and see if the module is loaded. If it is not, then it will load it. If it is, then it will just continue on.

if(@(get-module | where-object {$_.Name -eq 'System.Center.Service.Manager'}  ).count -eq 0)
{
    $InstallationConfigKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
    $InstallPath = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Powershell\System.Center.Service.Manager.psd1"
    Import-Module -Name $InstallPath -Global
}

*PowerShell Tip – Automatically Loading Modules
PowerShell and PowerShell ISE both let you create profiles. A profile is a script that runs automatically when you open the console. You can create a profile to automatically load the modules for you every time you open PowerShell.  Scripting Guy, Ed Wilson, has a great article on detailing the ins and outs of creating a PowerShell ISE profile, so I won’t go into much detail here. But below is an example of a script you can run to create a profile that will load the Service Manager modules for you. It uses the same commands as you just saw, but with some additional checks rolled in, just incase. All you need to do is open PowerShell ISE, copy and paste the code below into the script window, and execute. Once your command completes, close and reopen PowerShell ISE. Then enter Get-Module and confirm that the Service Manager modules are loaded.

# add command to load Service Manager modules to profile
@'
$InstallationConfigKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
if(Test-Path $InstallationConfigKey)
{
$AdminModule = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Powershell\System.Center.Service.Manager.psd1"
Import-Module -Name $AdminModule -Global
$DWModule = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Microsoft.EnterpriseManagement.Warehouse.Cmdlets.psd1"
Import-Module -Name $DWModule -Global
}
else
{
throw "ERROR: Could not locate Service Manager PowerShell module on cur"
}
'@ | out-file $profile -Append

 

Connecting to Service Manager Management Server

Unless you are on the Service Manager Management Server, the Service Manager cmdlets will not know which server to run against. To let them know which server to connect to, you can do one of two things. You can add the –ComputerName parameter to end every command you enter specifying your manager server. As you can imagine, this would get old pretty quick. Your other option is to create a Management Group Connection, using the New-SCManagementGroupConnection cmdlet. When you use this command, it will maintain the connection to your Service Manager environment as long as you keep the session open. This means you just run this once, and you don’t have to specify the computer name for any of the other commands in your script.

New-SCManagementGroupConnection -ComputerName "SCSM01"

Like with the Import-Module you can also use a registry key to quickly return the Service Manager server. However, keep in mind that this key is user specific, and they must have connected to the Service Manager server in order for this key to be populated.

$ServerConfigKey = 'HKCU:\Software\Microsoft\System Center\2010\Service Manager\Console\User Settings'
$SvcMgmtSrv = (Get-ItemProperty -Path $ServerConfigKey).SDKServiceMachine
New-SCManagementGroupConnection -ComputerName $SvcMgmtSrv

To confirm that you have an active connection you can use the Get-SCManagementGroupConnection cmdlet.

mggroup

 

Now that you have loaded the Service Manager modules and connected it to your Service Manager environment, you are ready to start using them. The next post will cover how to query and return information related to the different work items. And be sure to check the Overview Post for more content in this series.

SCSM PowerShell: Get Work Item Information

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

If you look at the Service Manager cmdlets, you will notice that there are no cmdlets along the lines of Get-Incident or Get-ServiceRequest. So you may ask yourself, how do I find this information? And the answer is, by using the Get-SCSMClassInstance cmdlet. The Get-SCSMClassInstance cmdlet can be used to return any work item or configuration using either the item’s ID or Class. The ID parameter is not referring the ID you see in the console, like SR1234 or IR429. It is referring to the internal GUID of the item. Since I don’t commonly make a habit of memorizing 128-bit integers, I tend to find the Class parameter much easier to use. You can use the Class parameter to query against all items in a specific class. Then you can use filters to get the items you want. In our examples here we will mainly be using the Class parameter.

Before diving into the Get-SCSMClassInstance cmdlet I want to give a quick recap on how Service Manager classifies things. All work items and configuration items inside of Service Manager are members of a class. Incident is a class, Service Request is a class, Computers is a class, and on. Also, many classes inherit from parent classes. For example, Incident and Service Request are sub classes under the Work Items class. Configuration items work the same way, but we will cover those in a later section.

Finding the Class

In order to return a class instance, you must first take a step back and find what class you want. When dealing with Work Items this is usually pretty easy to do. They will typically fall into one of the major ITIL roles; Incident, Service Request, Change Request, Problem, Release Records, and Activities. To get the class we’ll use the Get-SCSMClass cmdlet. For example to get the Incident class we use the command.

Get-SCSMClass -Name System.WorkItem.Incident

So how did I know to use the name “System.WorkItem.Incident”? To tell you the truth, I’ve been working with these enough, that I have it memorized. However, there are hundreds of different classes, and I admit I don’t have all of them committed to memory. To find the class you want, you could enter Get-SCSMClass, without any parameters, and dig through all of the results until you find the one you want. Another option is, since you must likely know the name of the class you want, you can use the DisplayName parameter with wildcards. This will greatly reduce the number of results. For example to find the Incident class I used the command:

Get-SCSMClass -DisplayName *incident* | FT DisplayName, Name

This returned a short list of all classes with the word “incident” in the display name. From here I can easily find the incident work item class’s name and use that in my command. You may have noticed that I used the Name parameter with System.WorkItem.Incident instead of the DisplayName parameter with just Incident. This is because the display name is not a unique field. You can have two classes with the same display name. For this reason, always use the Name parameter in your final scripts.

Querying Work Items

Now that you have your class you can use the Get-SCSMClassInstance cmdlet. When using the Class parameter you cannot just enter the class name. You must use the class object returned from the Get-SCSMClass cmdlet. You can either do this directly in the command line by using parenthesis, or by saving the class to a variable. I suggest the latter when you will be using the same class multiple times in a script.

Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident)

$class = Get-SCSMClass -Name System.WorkItem.Incident
Get-SCSMClassInstance -Class $class

When you run this command it will return every incident in your Service Manager environment. You can narrow down your results by using filters. There are two ways that you can filter the results. You can use the Filter parameter, or you can use the where-object cmdlet. Which one you choose is really up to your preference and the requirements of your script. There are a few things to consider when choosing between Filter and Where-Object. When you use the Filter parameter it will only return results that make the filter. When using the Where-Object all objects are returned, but those matching the where-object are displayed. The filter parameter will typically run quicker and use less memory, but it is very finicky. It requires very specific formatting and is case sensitive with the properties. The where-clause is much more powerful than the filter. With the where-clause you can string together multiple criteria much easier, and work with sub properties and arrays. As I stated previously it is up to you which you choose to use. My personal preference is to use the where-object, unless I’m returning so many objects the filter causes a significant performance improvement. Below are examples using both methods to return incident ID IR3570.

Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | Where-Object {$_.ID -eq "IR3570"}

Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) -Filter "Id -eq IR3570"

*PowerShell Tip – You can use a simple question mark “?” in place of the Where-Object command. Be aware if you see a ? in future examples it is the same as typing Where-Object. This is known as an alias. When PowerShell was first created, they included aliases for well know commands. For example if you type “dir” into a PowerShell window, it will actually run the “Get-ChildItem” cmdlet. Over the years this has been expanded upon, and you can even create your own if you like.

Formatting Your Output

PowerShell allows you to control the output to the screen in multiple ways. The two we will work with here are Format-List and Format-Table, or FL and FT respectively. Format-List displays all results in a list form, with each property displayed on a separate line. Format-Table displays the properties across the top and the values below. Each of these can be run with or without the names of the properties you want to display.

Format-List
image

Format-Table
image

To find a list of all properties a cmdlet will return, you can use the Get-Member cmdlet. By adding a pipe and Get-Member to the end of a command, it will display all of the properties and methods associated with that command, but not their actual value.

image

When you run the command you’ll notice multiple member types. I will not go into detail here on what all the different member types are, but there are a few I would like to cover. You see there are multiple different type of properties (NoteProperty, Property, ScriptProperty). For now all you need to know is these properties are used to return values. For example, ID is a ScriptProperty and it returns the actual ID of the item. The other type we will be working with later on are the Methods. Methods allow you to examine, compare, and format many properties of a PowerShell object. We will cover methods more in later sections. For more information in the different MemberTypes refer to MSDN.

Saving Your Results

Often when you are using PowerShell you want to perform multiple things with the items you returned. To do this you can save your output into a variable. In the example below, I am writing the results to the variable $IR. Now, anytime I want to reference these results I just need to enter $IR.

$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.ID -eq "IR3570"}

Once you have your results loaded into the variable you can work with individual properties by using the variable name dot the property name.

$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) |  ?{$_.ID -eq "IR3570"}
Write-Host "ID: " $IR.ID
Write-Host "Title: " $IR.Title

Filtering Your Results

As you saw in the examples above I filtered the results on the ID of the Incident, using the logic ID –eq “IR3570”. So what if I want to return all incidents with a status of Active? I could try Status –eq “Active”. However, if I ran that I would receive zero results. This is because Active is a display name, and not the internal property name. A good rule of thumb to help you remember this is, if it is a list item (e.g. a dropdown on the form in console), then it will have an internal name and a display name. This is similar to what was covered earlier with classes. So in order to return Active incidents, you need to query off of the sub properties. Examples of these are shown below.

Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.Status.Name -eq "IncidentStatusEnum.Active"} | FT ID, Status
 
Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.Status.DisplayName -eq "Active"} | FT ID, Status

The same logic here applies to all list items like category, urgency, impact, etc. We will cover ways to determine the name values of list items in a later post, but a quick way to find the internal name is to run the command against an item you know has the property set, and find the internal name in the output.

Working with Results

One of the best things you can learn to work with in PowerShell is the Foreach-Object cmdlet. It allows you to step through, and perform actions on each item individually. It can be run straight from the command line using a pipe or on its own. For simplicity we will be running it as a stand alone in the examples below.

Let’s go back to returning all Active incidents. I’m going to save the results to the variable $IR and I want to output the ID, Status, and Classification Category for each of these. If I simply did a FT and listed the properties out, it would return them, but it would return the internal names of the status and classification categories. To get the display name property we need to specify the sub property of display name. This can be done with the $IR.Status.DisplayName.

$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.Status.Name -eq "IncidentStatusEnum.Active"}
 
Foreach($item in $IR)
{
    Write-Host $item.ID "`t" $item.Classification.DisplayName "`t" $item.Status.DisplayName
}

*PowerShell Tip – PowerShell ISE has a horrible habit of crashing the first time you try to enter information inside of the foreach brackets. It appears to be an issue with the auto-complete feature. To prevent this I always create the foreach with nothing inside the brackets, then execute the script. After doing this the auto-complete works, and ISE does not crash.

In this post you have seen how to determine the class of a work item and use that to return information about specific work items. The next few sections are going to cover creating and updating work items, and working with relationships. And also be sure to check the Overview Post for more content in this series.

SCSM PowerShell: Get Configuration Item Information

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In the previous post we looked at getting work item information using the Get-SCSMClassInstance. This post will expand upon that to include configuration items. Like work items, configuration items are classes in Service Manager, so they also use the Get-SCSMClassInstance cmdlet.

Tips to Find the Class

Just like with the work items you need to start out by determining the name of the class for the item you want to return. However, unlike work items configuration item classes can be a little more difficult to find. This is especially true if you have synced your Operations Manager management packs into Service Manager. For example, in a standard Service Manager environment if I run the Get-SCSMClass cmdlet for any classes that have a display name that contains “computer”, 22 different classes are returned. If that is not bad enough, I ran this same command in a Service Manager environment where I had imported the Operations Manager management packs, and it returned 79 different classes. To be completely honest, sometimes it just takes some trial and error to find the correct class. However, there are a few tricks you can use to help find the correct class.

One way to find the class name is by looking at the views in Service Manager. If you right click on a view and click edit view, it will open the view editing form. If you scroll down to the Criteria section, you will see the display name of the class in the Search for objects of a specific class box.

edit_view

Another option is to search for classes based on the management pack. For example, if I search for classes that contain “SQL” in their display name, 96 different classes are returned. But if you look at the data returned you’ll notice the column ManagementPackName. By filtering down my results to only include results in the “Microsoft.SQLServer.Library” management pack, I can reduce the number I have to look through to 20.

sql1 sql2

One last thing you can do to try and determine the configuration item class, is search the default configuration item class (System.ConfigItem) for the item. For example, say you want to find the class for the computer, and you know that there is a computer with the display name of DC01. I can run the Get-SCSMClassInstance to return all configuration items with a display name of DC01. Then, in the output there will be property called “#FullName”. This property is composed of the class name, then a colon followed by the items internal name. Depending on your connectors and how many items you have in your CMDB, you still may receive multiple items returned, but you will be much closer to finding your class.

# Find a CI's Class
$CIclass = Get-SCSMClass -Name System.ConfigItem
Get-SCSMClassInstance -Class $CIclass | ?{$_.DisplayName -eq "DC01"} | FT DisplayName, "#FullName" -AutoSize

*PowerShell Tip – You may have noticed in the command above, that I put #FullName in quotes. This is because the hash (#) symbol marks the start of a line comment. Therefore anything after the hash would be ignored. By placing it in between quotes, PowerShell sees it as a string and treats like any other string.

*PowerShell Tip – Sometimes you will notice that PowerShell cuts off the end of the output. This can happen when you return long strings like the #FullName property often is. To prevent this, you have use the AutoSize parameter with the FT cmdlet. The AutoSize parameter will calculate the column sizes and adjust them to best fit your screen. If you are still unable to fit the full output on your screen, you can also use the Wrap parameter to allow word wrapping of the results. For more information on formatting output see the TechNet article Using Format Commands to Change Output View.

Bonus Script

I often find it useful to know what classes a class inherits. To find this, I have written a Function that will parse backwards to find all inherited classes, using the Base property for a class. In the example below, I am using it find all of the classes that the class “Microsoft.Windows.Computer” inherits.

Function GetClassBase($classId)
{
    $class = Get-SCSMClass -Id $classId
    $collection += New-Object PSObject -Property @{Name=$class.Name; DisplayName=$class.DisplayName}
    If($class.Base)
    {
        GetClassBase $class.Base.Id.Guid
    }
    return $collection
}

$class = Get-SCSMClass -Name Microsoft.Windows.Computer
GetClassBase $class.Id | FT DisplayName, Name -au

This post demonstrated some tips on how to determine configuration item classes. Be sure to check the Overview Post for more content in this series.

 

SCSM PowerShell: Get Relationships

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In the previous posts, we looked at returning work item and configuration item information with the Get-SCSMClassInstance cmdlet. In one of the examples, we returned all of the properties related to an Incident. You may have noticed, when running this command, that certain items were not returned. Most notably are the assigned to user and the affected user. It also did not return any related configuration items, work items, or activities. The reason these were not returned, is because they are not properties. They are related items. In order to find this you must first find the relationship, then you can return the details of the object.

Determining the Relationship

Just like with class instances, if you want to return a related object, you need to know what the relationship is. As with most things in Service Manager, there are multiple ways that you can determine the relationship. The examples below use the Get-SCSMRelationship cmdlet to determine the relationship. Example 1 is using the Display Name of the relationship. In this case I knew the name of the relationship, and was able to return it in my command. If you are unsure of the relationship you can use a wildcard like in example 2. You can also search by using the Source of Target class. However, you need to be careful with this. If, for example, you ran this command against the Incident class (System.WorkItem.Incident), all that would be returned is the Primary Owner relationship. This is because it is the only one unique to the Incident class. To find the affect user you would need to search on the base System.WorkItem class. For this reason, I generally stick to searching by name.

# Example 1: Using the Display Name
Get-SCSMRelationship -DisplayName "Affected User"
 
# Example 2: Using a Wildcard
Get-SCSMRelationship -DisplayName "*User*" 
 
# Example 3: Using the Source 
Get-SCSMRelationship -Source (Get-SCSMClass -Name System.WorkItem)

One way to determine the relationship name is to look at the history of an item in the console. If you look at the item history you will see all relationship changes. The display name of the relationship will be present under the relationship class column.

rel1

For more information in determining classes, I suggest checking out this post by Marcel Zehner. He created an excel sheet with all of the different relationship types in Service Manager.

There is one more thing to keep in mind with the Get-SCSMRelationship cmdlet. As with other things in SCSM, multiple relationships can have the same display name. So after finding the relationship using the methods above, it is best to use the Name parameter since this is a unique value. However, for some reason the Get-SCSMRelationship cmdlet does not output the name by default, so you should add “| FT DisplayName, Name” to the end of your command to determine the name.

# Step 1: Determine the relationship
Get-SCSMRelationship -DisplayName "Affected User"
 
# Step 2: Find the name
Get-SCSMRelationship -DisplayName "Affected User" | FT DisplayName, Name
 
# Step 3: Using the name parameter save the relationship to a variable
$rel = Get-SCSMRelationship -Name "System.WorkItemAffectedUser"

Finding the Related Objects

After determining the relationship, you can use the methods in Get-SCSMClassInstance cmdlet to return the related objects. There are two different methods to find these, GetRelatedObjectsWhereSource and GetRelatedObjectsWhereTarget. You need to use the one where the class of the object you are returning from Get-SCSMClassInstance matches either the source or the target. The example below show how to find the affected user of the incident. Since Get-SCSMRelationship showed that the source is System.WorkItem, we’ll use the GetRelatedObjectsWhereSource method.

# Get the relationship
$rel = Get-SCSMRelationship -Name "System.WorkItemAffectedUser"
 
# Get the incident
$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ? {$_.ID -eq "IR3570"} 
 
# Use the methods to return the object
$IR.GetRelatedObjectsWhereSource($rel.ID)

When you run the command above it will output the EnterpriseManagementObject, which is typically the display name of the object. In this case it would be the name of the Affected User. However, the EnterpriseManagementObject is not just a string value. It contains multiple sub properties of the related object. As with everything else, you never want to go strictly off the display name. So if you add “.EnterpriseManagementObject.id.GUID” to the end of the command it will return the unique GUID of the object. If you save that in a variable, you can then use it with the Get-SCSMClassInstance to return all of the information about the object.

# Get the relationship
$rel = Get-SCSMRelationship -Name "System.WorkItemAffectedUser"
 
# Get the incident
$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ? {$_.ID -eq "IR3570"} 
 
# Use the methods to return the object GUID and save it to a variable
$AffctUsr = $IR.GetRelatedObjectsWhereSource($rel.ID).EnterpriseManagementObject.id.GUID
 
# Return the details of the related object
Get-SCSMClassInstance -ID $AffctUsr

This same logic can be applied to the assigned to user, created by user, related work items, affected configuration items, etc. You just need to know the relationship you want to find.

Finding the Related Object by Target

Using the same logic as the previous example you can easily find the activities contained within a Change Request.

# Get the relationship
$rel = Get-SCSMRelationship -Name "System.WorkItemContainsActivity"
 
# Get the change request
$CR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.ChangeRequest) | ? {$_.ID -eq "CR823"} 
 
# Use the methods to return the object GUID and save it to a variable
$Act = $CR.GetRelatedObjectsWhereSource($rel.ID).EnterpriseManagementObject.id.GUID
 
# Return the details of the related object
Get-SCSMClassInstance -ID $Act

The example above returns the activities contained within the change request CR823. But what if you have the review activity, and you want to return the change request it belongs to? In that case all you would need to do is to change your script to use the GetRelatedObjectsWhereTarget method.

# Get the relationship
$rel = Get-SCSMRelationship -Name "System.WorkItemContainsActivity"
 
# Get the review activity
$RA = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Activity.ReviewActivity) | ? {$_.ID -eq "RA825"} 
 
# Use the methods to return the object GUID and save it to a variable
$WI = $RA.GetRelatedObjectsWhereTarget($rel.ID).EnterpriseManagementObject.id.GUID
 
# Return the details of the related object
Get-SCSMClassInstance -ID $WI

This same logic would apply if you wanted to find all of the work items a user is assigned to, or any other type of relationship.

This section showed how to find different related objects. The next few sections will cover how to update, create, and remove relationships and work items. And be sure to check the Overview Post for more content in this series.

SCSM PowerShell: Modifying Work/Configuration Items

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In the previous posts we saw how to return Work and Configuration Item information. Now in this post, I am going to demonstrate how you can update Work and Configuration Items using the Service Manager PowerShell cmdlets. Just like in the previous posts, Service Manager uses a single cmdlet to update work and configuration items. The cmdlet we will use is Update-SCSMClassInstance.

Using Update-SCSMClassInstance
Unlike the Get-SCSMClassInstance cmdlet, the Update-SCSMClassInstance cmdlet only has a few parameters to choose from. They are Instance, PassThru, Confirm, and WhatIf. Instance allows you to specify an instance of a class to update. This would be an instance that you returned using the Get-SCSMClassInstance. As you’ll see shortly, this parameter is not often used. PassThru, tells the command to pass your results through to a variable or output. Again more on this shortly.

The other two parameters are useful for development and testing. Confirm will pause your script and prompt you to confirm prior to each change being made. WhatIf will output what would happen during execution, but does not actually commit any changes.

If you look at the documentation for the Update-SCSMClassInstance cmdlet, you’ll see that the Instance parameter accepts pipeline inputs. This means that we can use the Get-SCSMClassInstance cmdlet to get the instance to change, then pipe in the changes in the same line. As you’ll see in the example below, the syntax for this will be “Your SCSM Class Instance” | “values to change” | Update-SCSMClassInstance.

$SR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest) | ? {$_.ID -eq "SR4"}
$SR | %{$_.title="SCSM PowerShell Demo";$_} | Update-SCSMClassInstance

The example above demonstrates how to update the title of a service request. This could be done on one line, but to make things more readable I’ve broken it down. The first line gets the class instance of the service request, and saves it to the variable $SR. Then $SR is loaded, then a pipe, followed by a foreach statement, which is represented here by a percent sign. This foreach statement is where you specify what value to change, and what to change it to. You can specify multiple values to change at once, by separating them with a semi-colon. At the end a simple $_ is added to tell it to keep everything else the same. Then finally another pipe followed by the Update-SCSMClassInstance cmdlet.

Updating Multiple Items
The previous example showed how to update the title of a single request, but what do you do if you want to change multiple requests at once? The answer to that is simple. You do the same exact thing. In the example below we are returning every incident with the word Test in its title. Then it will set it status to resolved, and add “resolved by script” to the description.

$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.title -like "*Test*"} 
$IR | %{$_.Status="IncidentStatusEnum.Resolved";$_.Description=$($_.Description + " resolved by script");$_} | Update-SCSMClassInstance

There are a few things you will want to make note of in the example above. First, it uses the internal name for setting the status. As we covered in the previous posts, you always want to use the internal name of a list item because display names are not always unique. You will also want to note the variable setting for the description. In this case we wanted to append “resolved by script” to the existing description. If you just set it to “resolved by script”, it would override the existing description with just that. However, the example above takes the existing description, adds the new text to the end, and sets that as the new description value in the incident.

Complex Updates
The great thing about the Update-SCSMClassInstance cmdlet is, no matter what you are updating the underlying syntax stays the same. The format for updating an Incident, is the same as it would be when updating a computer or user object. With a little PowerShell know how you can perform some really time consuming tasks in a matter of seconds.

Take for instance the next example. It shows how you can use the Update-SCSMClassInstance cmdlet in conjunction with some foreach and if statements to perform different updates based on the item. In this example, all user account objects are returned. Then using a foreach statement each object is parsed individually. If the user’s username ends with “_svc”, then write Service to the first name, and Account to the last name. Else if the account does not have a last name, then write the username to the first name and Unknown to the last name field. Also, this example is using the WhatIf parameter so that I can test and see what objects will get updated, before actually performing the update.

$Users = Get-SCSMClassInstance -Class (Get-SCSMClass -name Microsoft.AD.User)

Foreach($User in $Users)
{
    if($User.UserName -like "*_svc")
    {
        $User | %{$_.FirstName="Service";$_.LastName="Account";$_} | Update-SCSMClassInstance -WhatIf
    }
    elseif(!$User.LastName)
    {
        $User | %{$_.FirstName=[string]$_.username;$_.LastName="Unknown";$_} | Update-SCSMClassInstance -WhatIf
    }
}

*PowerShell Tip – You may have noticed in the script above that I put [string] prior to the username variable. This is to force the variable to be a string type, which is what the FirstName field will accept. It is a good habit to ensure that you are inputting the correct type. If not, you may receive a message stating “Simple object value was not the proper type”. When you see this, you need to check to make sure that the value you are updating is using the correct type. You can determine the type of value required, by examining the property collection of the class and its base classes. I’ve included a script below that you can run against a class to return all of its properties, and the type of value they require.

$class = Get-SCSMClass -name Microsoft.AD.User
$properties = @()
foreach ($item in @($Class) + @($Class.GetBaseTypes())) 
{
    $properties += $item.PropertyCollection
}
$properties | FT Name, Type -au

In this post you have seen how to update objects in SCSM using the Update-SCSMClassInstance cmdlet. Be sure to check the Overview Post for more content in this series.


SCSM PowerShell: Working with Relationships

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In a previous post we looked at how to display relationships information using the Service Manager PowerShell cmdlets. In this post we will cover how to create, update, and remove relationships using these cmdlets.

Creating Relationships

Every time you create a relationship in Service Manager, a relationship instance is created. You can think of the relationship instance as the object that joins the two items together. The diagram below shows an example of this. It shows the relationship between an incident and the person assigned to it.

relationship1

Each box, in the diagram above, represents an object in Service Manager, and has its own unique GUID. Therefore to relate two object together a relationship instance must be created. In order to do this you will need to use the New-SCRelationshipInstance cmdlet.

*PowerShell Tip – As we noted in this Get Work Items post, PowerShell often uses aliases. In this case you may have noticed that the cmdlet is New-SCRelationshipInstance and not New-SCSMRelationshipInstance. This is because many of the Service Manager cmdlets simply use the SC and have an alias to the SCSM name. From what I have witnesses this is the case with many of the cmdlets that are universal between Service Manager and Operations Manager. In this case, you can actually run either version of the cmdlet and get the same results.

For the first example, I am going to use one of the most common tasks associated with creating new relationship instances, setting the assigned to user of incident. In order to do this you will need three things. First, you will need the work item, in this case the incident. Then you will need the user object you want to set as the assigned to user. And finally, you will need the relationship you want to create. As shown in previous posts, you can get the incident and the user object with the Get-SCSMClassInstance cmdlet, and you can get the relationship using the Get-SCSMRelationship cmdlet. Then you simply need to put the three items together using the New-SCRelationshipInstance cmdlet.

# Get the Incident
$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.Id -eq "IR1820"}

# Get User Object
$User = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.User) | ?{$_.UserName -eq "adamb"}

# Get the Relationship
$AssignedToUser = Get-SCSMRelationship -name System.WorkItemAssignedToUser | FL

# Create Relationship Instance
New-SCRelationshipInstance -RelationshipClass $AssignedToUser -Source $IR -Target $User

Just like in the Get Relationships post, you will need to examine the output of the Get-SCSMRelationship to determine the appropriate source and target objects. In this case you may noticed that the source class for the “Assigned To User” relationship is System.Workitem, and we used an incident for the source. This works because the Incident class is a subclass of Work Item, and therefore inherits all of its properties and relationships.

Updating Relationships

If you look at the Service Manager PowerShell cmdlets you will notice there is not an Update-SCRelationshipInstance cmdlet. This is because you do not update relationships in the same way that you update objects. Depending on how the relationship is created in Service Manager, you can have 1, or as many as 2,147,483,647 instances of a relationship. In the previous example we added an “Assigned To User” relationship to an incident. Since you can only have 1 person assigned to an incident at a time, if there was a previous relationship, the new one would override it. However, if we added the user to the relationship, that allows more than one instance, for example “Is Related to Configuration Item”, then it would add the new relationship to the list of existing relationships. In order to determine the total number of allowed relationship instances you can use the Get-SCSMRelationship cmdlet. The total number of relationship instances is listed the MaxCardinality of the Target property. The example below shows you how to output this information for a couple of relationship types.

# Display the number of relationship instances for Assigned To User
Get-SCSMRelationship -name System.WorkItemAssignedToUser | %{Write-Host $_.Name "-" $_.Target.MaxCardinality}

# Display the number of relationship instances for Is Related to Configuration Item
Get-SCSMRelationship -name System.WorkItemRelatesToConfigItem | %{Write-Host $_.Name "-" $_.Target.MaxCardinality}

If you run the commands above you will see “Assigned To User” can have 1 instance, and “Is Related to Configuration Item” can have over 2.1 billion. Remember if you add a new instance and it exceeded the number allowed, then the previous instances will be overwritten. So in the case of updating the “Assigned To User”, or any other relationship that has only 1 instance, you just need to run the New- SCRelationshipInstance cmdlet again, and you will overwrite any previous relationships with your new one. However, if you want to change a relationship where there are multiple instances allowed, then you will need to remove the old instances and then add the new ones.

Removing Relationships

The Remove-SCRelationshipInstance cmdlet is used when removing a relationship instance in Service Manager. Just like with the Update-SCSMClassInstance cmdlet, the easiest way to use the Remove-SCRelationshipInstance cmdlet is by piping it into another command that returns the instance you want to remove. To do this, you need to use the Get-SCRelationshipInstance cmdlet. Personally, I’m not a big fan of this cmdlet, since the parameters do not work like you would expect them to. For example, it has a parameter for the Source instance and a parameter for Target instance. So you would think that if you entered an object for the source and an object for the target, it would tell you the relationship instances between the two, but it does not. I will display all of the relationship for the source and all of the relationship for the target into one big list. Therefore, when using this cmdlet it is best to only list a source or a target, then use a where clause to narrow down to the specific relationship type. In the example below you can see how it is used to return all instances of the “Is Related to Configuration Item” relationship class for a specific incident, then it removes them all.

# Get the Incident
$IR = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem.Incident) | ?{$_.Id -eq "IR1820"}

# Get the relationship
$rel = Get-SCSMRelationship -Name "System.WorkItemRelatesToConfigItem"

# Return and remove the relationship instances
Get-SCRelationshipInstance -Source $IR | ?{$_.RelationshipId -eq $rel.Id} | Remove-SCRelationshipInstance

Review Activity Relationships

One of the most confusing tasks in Service Manager automation can be working with Reviewers in a Review Activity. This is because the user listed as the reviewer does not have a direct relationship to the review activity.  There is a class named Reviewer that gets created and related to the review activity as the Reviewers relationship. Then a user or group can be related to that Reviewer as the “Is User” or “Voted By User”. The “Is User” is the person who is assigned as the reviewer and the “Voted By User” is the person who actually approved or rejected the request. So remember, when dealing with Review Activities there are three different relationships to take into consideration. I have listed them in the table below for your reference.

Display Internal Name Source Class Target Class
Reviewers
System.ReviewActivityHasReviewer
System.WorkItem.Activity
System.Reviewer
Is User
System.ReviewerIsUser
System.Reviewer
System.User
Voted By User
System.ReviewerVotedByUser
System.Reviewer
System.User

In order to add a reviewer to a review activity you must first create the Reviewer, then relate the Reviewer to the Review Activity, then relate the user to the Reviewer as the Is User. The diagram below shows a visual representation of this, with the rectangles being Service Manager objects and the ovals being relationships.

relationship2

Now there is one more thing to mention about adding a reviewer to a Review Activity. You cannot add an existing Reviewer to a Review Activity. If you try you will receive a message stating, “A discovery data item was rejected because the item is already bound to another Membership relationship.” In order to avoid this you need to create the Reviewer object at the same time you create the Reviewers relationship instance to the Review Activity. As shown in the example below, you do this by using the TargetClass and TargetProperty parameters in the New-SCRelationshipInstance cmdlet.

# Get the Review Activity
$RAId = "RA1873"
$RA = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.WorkItem) | ?{$_.ID -eq $RAId}

# Get the Reviewers relationship
$ReviewerRel = Get-SCRelationship -name System.ReviewActivityHasReviewer

# Set the properties for the Reviewer object (Set MustVote and Veto according to your needs)
$reviewer = @{
Decision = "Not Yet Voted"
MustVote = $False
Veto = $False
}

# Create the Reviewer and Relate it to the Review Activity
$ReviewerRelInstance = New-SCRelationshipInstance -RelationshipClass $ReviewerRel -Source $RA -TargetClass (Get-SCSMClass -name System.Reviewer ) -TargetProperty $reviewer -PassThru

# Get the Reviewer object that was just created
$ReviewerInstance = Get-SCSMClassInstance -Id $ReviewerRelInstance.TargetObject.Id.Guid

# Get User Object to relate to the Reviewer
$Approver = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.User) | ?{$_.UserName -eq "adamb"}

# Get the Is User Relationship
$ReviewerIsUser = Get-SCRelationship -name System.ReviewerIsUser

# Create the Is User relationship between the Reviewer and the User
New-SCRelationshipInstance -RelationshipClass $ReviewerIsUser -Source $ReviewerInstance -Target $Approver

There are a few things you will want to make note of in the script above. First, when setting the properties of the Reviewer object, we created a hash table. This allows you to set the all of the values required for the object and pass them as one variable in the command. You will also what to note the settings in this hash table. The decision should always be set to “Not Yet Voted”, but the MustVote and Veto can be set to True or False depending on your needs. Finally, you may have noticed that we used the PassThru parameter when creating the Reviewers relationship. The PassThru parameter allows you to save the newly created object to a variable, in this case the $ReviewerRelInstance variable. We then used this variable to get the unique GUID of the Reviewer object that was created. This is then used that to save the Reviewer object to the variable $ReviewerInstance, which is used to related the User to the Reviewer.

In this post you have seen how to create, update, and remove Service Manager relationships. Be sure to check the Overview Post for more content in this series.

SCSM PowerShell: Get List Items

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In the overview article for this series I stated, with regards to the Service Manager PowerShell Module, “There is almost nothing you can’t do using the provided Cmdlets.” Working with enumeration lists is a reason I said “almost nothing” in that post. This is because, there are no cmdlets in the Service Manager PowerShell Module that are equivalent to the SMLet cmdlets Get-SCSMEnumeration or Get-SCSMChildEnumeration. However, that does not mean you cannot return list item using the built-in PowerShell cmdlets, it just means we have to go about it a different way.

PowerShell is designed to allow you load .NET Framework and COM interfaces, so that you are not limited to tasks that can be performed strictly by cmdlets. Therefore, to return list items you will need to use the Service Manager .NET Framework. To do this you will need to use the PowerShell cmdlet New-Object. Using the New-Object cmdlet, you can load the Service Manager .NET Framework, which will then give you full access to the Service Manager API.

To do this you need to load the Microsoft.EnterpriseManagement.EnterpriseManagementGroup class library. From there you can load all of the enumeration lists into a single variable. Then I will show, how you can then use the standard PowerShell filters and functions to get the exact data you want to return.

In the first example below I am using the New-Object cmdlet to load the Enterprise Management Group class and saving it to the variable EMG. Then using the GetEnumerations method of the EntityTypes property, and saving it to the Enums variables. This will load all of the enumeration items into the Enums variable.

$EMG = New-Object Microsoft.EnterpriseManagement.EnterpriseManagementGroup "localhost"
$Enums = $EMG.EntityTypes.GetEnumerations()

If you notice in the example above localhost is listed at the end of the New-Object cmdlet. This is because the Enterprise Management Group class needs to know the name of your Service Manager Management server. If you are running the above command from the management server, then you can enter localhost. If you are running it from a remote machine, then you need to specify the management server name.

*Service Manager Tip – Refer to MSDN, for more information on Enterprise Management Group class properties and methods.

Now that you have the enumeration items loaded into the Enums variable you can use it to find any list value you want by using a standard PowerShell where-object statements. Using the first example, below, you can display all list items with the word “Closed” in its display name.  You can use the second example to return all top level lists.

# Get all list items with Closed in the Display Name
$Enums | ?{$_.DisplayName -like "*Closed*"} 

# Get all enumeration lists
$Enums | ?{$_.Parent -eq $null} | sort DisplayName | FT DisplayName, Name -au

Now that you have all of the top level list items, you can use a function to parse through the list and display all items. The function in the example below will do this, and output the results to the screen. It will also include all child items if they exist.

$EMG = New-Object Microsoft.EnterpriseManagement.EnterpriseManagementGroup "localhost"
$Enums = $EMG.EntityTypes.GetEnumerations()

function getChildren([Microsoft.EnterpriseManagement.Configuration.ManagementPackEnumeration]$parent)
{
    $outPut = "$outPut - " + $parent.DisplayName
    Write-host $outPut
    Foreach ($child in $EMG.EntityTypes.GetEnumerations() |?{$_.Parent -eq $parent})
    {
        getChildren -parent $child -child $true
    }
}

$list = 'IncidentClassificationEnum'
$parent = $Enums |?{$_.Name -eq $list}


getChildren $parent

The function above is good for outputting your list items to the screen for reviewing, but does not serve much purpose for other things. So using the same technique as above, the function below will load all of the list items into an array with the Name, Display Name, Ordinal (order), and the Parent item.

Function GetEnum($parent)
{
    $collection = @()
    $enum = $EMG.EntityTypes.GetEnumerations() |?{$_.Parent -eq $parent}
    Foreach($item in $enum)
    {
        $collection += New-Object PSObject -Property @{Name=$item.Name; DisplayName=$item.DisplayName; Ordinal=$item.Ordinal; Parent=$parent.Name}
        GetEnum $item
    }
    return $collection
}



$IncClass = GetEnum $($EMG.EntityTypes.GetEnumerations() |?{$_.Name -eq 'IncidentClassificationEnum'})
$IncClass

You can then use this information to get the internal names for list items to use when creating or updating work and configuration items. You will see an example of this in the upcoming post on creating work items. Be sure to check the Overview Post for more content in this series.

SCSM PowerShell: Create Configuration Items

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In the previous posts we looked at updating items and relationships using the Service Manager PowerShell cmdlets. Now in this post we are going use the PowerShell cmdlets to actually create new items in Service Manager. We are going to do this using the cmdlet New-SCSMClassInstance. This cmdlet is used to create new Work Items and Configuration Items in Service Manager. In this post we are just going to cover creating Configuration Items. Work Items will be covered in a separate post.

Prior to creating a new object in Service Manager you need to determine what values are available and required for the object type. I have included a script below that will allow you to query a class type and return all of the properties, the property type, and whether or not it is a Key. If the key value of a property is True then the property is required. All other properties are optional.

$class = Get-SCSMClass –name Microsoft.SystemCenter.ConfigurationManager.MobileDevice 

$properties = @()
foreach ($item in @($Class) + @($Class.GetBaseTypes())) 
{
    $properties += $item.PropertyCollection
}
$properties | sort -Descending Key | FT Name, Type, Key -au

After running the script above for the Mobile Device class you should see an output similar to the one below.

ci1

This output tells me that the ID is a required field and all other are optional. Also you’ll notice that AssetStatus and ObjectStatus are enum types. As with updating objects you will want to use the internal name and not the display name for the enums. You can find these using the techniques shown in the post Get List Items.

So now that we have our properties we can use the New-SCSMClassInstance cmdlet to create our configuration items. If you look at the help for the New-SCSMClassInstance cmdlet you will see that the Property parameter is a Hashtable. This means that we will need to include all of the properties we want in a hash table and pass that to the Property parameter.

*PowerShell Tip – Hash Tables

A Hash Table in PowerShell is simply a collection of names and their values. A hash table is a type of array, but instead of storing data using a numeric reference, you are creating unique names and assigning them values. For more information on hash table refer to the TechNet article. (https://technet.microsoft.com/en-us/library/Ee692803.aspx)

To create a hash table you can either list each property on a separate line or put them all on one line separating them with a semi-colon. For ease of reading and clarity I will be putting each on a separate line. You start a hash table by using the at symbol followed by curly brackets. The script below show an example of how this could look.

$Properties = @{
Id = [string]$([guid]::NewGUID())
AssetStatus = "System.ConfigItem.AssetStatusEnum.Deployed"
DeviceManufacturer = "Samsung"
DeviceModel  = "Note 4"
DevicePhoneNumber = "555-555-5555"
DeviceImei = "12903810923348230948234098"
}

The first line declares the variable Properties then starts the hash table with the @{. Then each following line contains a property name and is value. One thing you may notice is the NewGUID command for the value of the Id property. This is because the Id is a key so it must be a unique value each time. You can make this any value that you want, but to ensure that I will not have a duplicate Id, I’m generating a GUID. The chance of having two GUID with the same value is 1 in 2^128 or 1 in 340,282,366,920,938,463,463,374,607,431,768,211,456.

Service Manager Note
When dealing with Id properties with Work Items you will often see the value set to “{0}”. This tells Service Manager to use the next available number available. For example if the last incident was IR362 and you ran the command to create a new one using “{0}” then the next one would be IR363. We will cover this in more detail in the next post on creating work items.

Now that we have the properties set we can pass it to the New-SCSMClassInstance cmdlet to create our configuration item. To use the New- SCSMClassInstance you just need to provide the class and the properties, as you can see in the example below.

$Properties = @{
Id = [string]$([guid]::NewGUID())
AssetStatus = "System.ConfigItem.AssetStatusEnum.Deployed"
DeviceManufacturer = "Samsung"
DeviceModel  = "Note 4"
DevicePhoneNumber = "555-555-5555"
DeviceImei = "12903810923348230948234098"
}

$Class = get-scsmclass –name 'Microsoft.SystemCenter.ConfigurationManager.MobileDevice'
New-SCSMClassInstance -Class $Class -Property $Properties

When you run the above command it will create the configuration item, and unless there is an error you will not see any output. If you want to see the output you can add the PassThru parameter to the New- SCSMClassInstance command. The PassThru parameter can also be used to save the newly created configuration item to a variable to use later in the script. For example after you create the configuration item you can then create a new relationship instance to set the owner of the item. This is shown in the example below.

$Properties = @{
Id = [string]$([guid]::NewGUID())
AssetStatus = "System.ConfigItem.AssetStatusEnum.Deployed"
DeviceManufacturer = "Samsung"
DeviceModel  = "Note 4"
DevicePhoneNumber = "555-555-5555"
DeviceImei = "12903810923348230948234098"
}

$Class = get-scsmclass –name 'Microsoft.SystemCenter.ConfigurationManager.MobileDevice'

# Create the config item and save it to the Phone variable
$Phone = New-SCSMClassInstance -Class $Class -Property $Properties -PassThru

# Get the Owned By User Relationship
$OwnerRel = Get-SCSMRelationship -Name System.ConfigItemOwnedByUser

# Get the user to set as the owner
$Owner = Get-SCSMClassInstance -Class (Get-SCSMClass -Name System.User) | ?{$_.username -eq "TWarren"}

# Set the user and the owner of the phone
New-SCRelationshipInstance -RelationshipClass $OwnerRel -Source $Phone -Target $Owner -PassThru

Bulk Import

You can perform bulk imports utilizing the Import-CSV cmdlet in PowerShell. This cmdlet will let you import the data from a CSV to a custom PowerShell object. Each column in the CSV file becomes a property of the custom object and the items in rows become the property values. In the following example I am going to populate a custom class I created for Microsoft Surfaces. To get started I run the script below get the properties.

$class = Get-SCSMClass –name CAT.Surface

$properties = @()
foreach ($item in @($Class) + @($Class.GetBaseTypes())) 
{
    $properties += $item.PropertyCollection
}
$properties | sort -Descending Key | FT Name, Type, Key -au

ci2

From here I can create my CSV file that contains the information I want to import. To make things easier on myself I give the columns the same name as the properties in the class.

ci3

Now I can import the data to PowerShell using the Import-Csv cmdlet. Then I’ll use a foreach command to parse individually through each line of the CSV, creating the Surface configuration item based on the values of that line, and assigning the owner.

# Import the CSV file
$CSV = Import-Csv "C:\Scripts\surfaces.csv"

# Parse through each line of the CSV
Foreach($item in $CSV)
{
    # Create the new Surface CI
    $Surface = New-SCSMClassInstance -PassThru (get-scsmclass –name 'CAT.Surface') @{
        Model = [string]$item.model
        OS = [string]$item.os
        Capacity = [string]$item.capacity
        Memory = [string]$item.memory
        PurchaseDate = $item.PurchaseDate
        SerialNbr = [string]$item.SerialNbr
        CostCenter = [string]$item.CostCenter
        }

    # Get the user to set as the owner
    $Owner = Get-SCSMClassInstance -Class (Get-SCSMClass -Name Microsoft.AD.User) | ?{$_.UserName -eq $item.Owner}
    
    # Set the user and the owner of the Surface
    New-SCRelationshipInstance -RelationshipClass (Get-SCSMRelationship -Name System.ConfigItemOwnedByUser) -Source $Surface -Target $Owner -PassThru 

}

As with any bulk importing automation, I highly recommend only a few items to the CSV to test your script with. Then if everything works go ahead and add the rest.

In the next post I will cover creating Work Items using the PowerShell cmdlets. Also, be sure to check the Overview Post for more content in this series.

 

SCSM PowerShell: Create Work Items

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

In the previous post, Creating Configuration Items, we saw how to create configuration items using the Service Manager PowerShell Cmdlets. Now, in this post, we are going to look into creating work items with these Cmdlets.

Creating Work Items using the Service Manager PowerShell Cmdlets is very similar to creating configuration items, with one major difference. When creating work items you have to deal with the work item Id. Since creating configurations items and work items are so similar, I am not going to cover the detailed steps of determining the properties and creating the hash table to input the values you want to use. If you need a refresher on these items please refer to the Creating Configuration Items post.

As I mentioned earlier, when creating work items there are certain considerations you need to take regarding the work item Id. If you look at the properties of any of the work item classes, you’ll see that Id is a key field. This means that it is a required field and cannot be changed after creation. So how do you know what the next available Id is? Luckily, you don’t have to. You can use the value of “{0}” which tells the system to use the next available number.

*Service Manager Note
If you look at your Incidents in the console, you may have noticed that the Ids are not always sequential. This is because the Ids are set on the Work Item level, and not the inherited classes, such as Incident and Service Request. Therefore, every time any work item is created the work item Id is incremented. For example, if you create an incident and the Id is IR120, next you create a Service Request its Id would be SR121, then you create another incident, it would be IR122.

So using what we know we can create an Incident by setting the Id field equal to {0}, but there is a catch. If you actually run this, it will create the incident, however it will not contain the incident class prefix. So instead of creating incident IR125 it will create incident 125. To prevent this you can simply add the IR prefix in front of the {0}. So your id property would be set to “IR{0}”. You can see this in the example below.

# Get the Incident class
$IRClass = get-scsmclass -name system.workitem.incident

# Set the properties
$Property = @{Id="IR{0}"
                Title="Test Incident"
                Description="Test please ignore"
                Urgency="System.WorkItem.TroubleTicket.UrgencyEnum.Medium"
                Source="IncidentSourceEnum.Email"
                TierQueue="IncidentTierQueuesEnum.Tier3"
                Classification="IncidentClassificationEnum.CIOverride"
                Impact="System.WorkItem.TroubleTicket.ImpactEnum.Medium"
                Status="IncidentStatusEnum.Active"
            }

# Create the Incident
New-SCSMClassInstance -Class $IRClass -Property $Property -PassThru

As you know it is never good idea to hard code settings, like we just did with the Incident prefix. This is because different environments can have different prefixes. So to make your script truly portable and future proof, you should query the system to get the work item class’s prefix. To get the prefix for a specific class you need to get the class instances of the settings class. For the incident class the settings are in the class System.WorkItem.Incident.GeneralSetting. In the script below you can see the PrefixForId property being saved to the variable $prefix. The prefix variable is then added to the value for the Id property.

# Get the Incident class
$IRClass = get-scsmclass -name system.workitem.incident

# Get the Incident class prefix
$prefix = (Get-SCClassInstance -class (Get-SCClass -Name System.WorkItem.Incident.GeneralSetting)).PrefixForId

# Set the properties
$Property = @{Id="$prefix{0}"
                Title="Test Incident"
                Description="Test please ignore"
                Urgency="System.WorkItem.TroubleTicket.UrgencyEnum.Medium"
                Source="IncidentSourceEnum.Email"
                TierQueue="IncidentTierQueuesEnum.Tier3"
                Classification="IncidentClassificationEnum.CIOverride"
                Impact="System.WorkItem.TroubleTicket.ImpactEnum.Medium"
                Status="IncidentStatusEnum.Active"
            }

# Create the Incident
New-SCSMClassInstance -Class $IRClass -Property $Property -PassThru

For your convenience, I have listed all the out-of-box work item class’s prefix settings below.

# Activity Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityIdPrefix

# Dependent Activity Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityDependentActivityIdPrefix

# Manaual Acitivty Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityManualActivityIdPrefix

# Parallel Activity Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityParallelActivityIdPrefix

# Review Activity Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityReviewActivityIdPrefix

# Squential Activity Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivitySequentialActivityIdPrefix

# Incident Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.WorkItem.Incident.GeneralSetting)).PrefixForId

# Change Request Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ChangeSettings)).SystemWorkItemChangeRequestIdPrefix

# Problem Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ProblemSettings)).ProblemIdPrefix

# Release Record Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ReleaseSettings)).SystemWorkItemReleaseRecordIdPrefix

# Service Request Prefix
(Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ServiceRequestSettings)).ServiceRequestPrefix

Now we can take this, and what we learned in the Working with Relationships post, and add different relationships to the work items we created. In the example below we are taking the same incident creation script that we used above, but this time we are saving it to a variable, then adding an Affected User relationship to it.

# Get the Incident class
$IRClass = get-scsmclass -name system.workitem.incident

# Get the Incident class prefix
$prefix = (Get-SCClassInstance -class (Get-SCClass -Name System.WorkItem.Incident.GeneralSetting)).PrefixForId

# Set the properties
$Property = @{Id="$prefix{0}"
                Title="Test Incident"
                Description="Test please ignore"
                Urgency="System.WorkItem.TroubleTicket.UrgencyEnum.Medium"
                Source="IncidentSourceEnum.Email"
                TierQueue="IncidentTierQueuesEnum.Tier3"
                Classification="IncidentClassificationEnum.CIOverride"
                Impact="System.WorkItem.TroubleTicket.ImpactEnum.Medium"
                Status="IncidentStatusEnum.Active"
            }

# Create the Incident
$IR = New-SCSMClassInstance -Class $IRClass -Property $Property -PassThru

# Get the Affected User Relationship
$AffectedRel = get-scsmrelationship -Name 'System.WorkItemAffectedUser'

# Get the Affected User
$AffectedUser = Get-SCSMClassInstance -class (Get-SCSMClass -name System.User) | ?{$_.UserName -eq "Your User name or variable"}

# Created the relationship between the Incident and the Affected User
New-SCRelationshipInstance -RelationshipClass $AffectedRel -Source $IR -Target $AffectedUser

In this next example we are getting an existing Service Request. Then creating and relating a Manual Activity to it.

# Get the required classes
$SRClass = Get-SCSMClass -name System.WorkItem.ServiceRequest
$MAClass = Get-SCSMClass -name System.WorkItem.Activity.ManualActivity

# Manaual Acitivty Prefix
$MAPrefix = (Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityManualActivityIdPrefix

# Get the Service Request
$SR = Get-SCSMClassInstance -Class $SRClass | ?{$_.ID -eq "SR29"}

# Get the Contains Activity relationship
$ActivityRelationship = Get-SCRelationship -name System.WorkItemContainsActivity

# Set the properties for the Manual Activity
$MAProperty = @{
    Id = "$MAPrefix{0}"
    SequenceId = 2
    Title = "Test MA"
    }

# Create the Manaual Activity and Relate it to the Service Request
New-SCRelationshipInstance -RelationshipClass $ActivityRelationship -Source $SR -TargetClass $MAClass -TargetProperty $MAProperty -PassThru

*Service Manager Note
You may have noticed the SequenceId property in the example above. The Sequence Id determines the order in which the activities are set in the parent work item, starting with 0. You will want to make sure that you are adding your activities in the appropriate order using this property. Therefore, if we want to add the new activity we need to know where to place it. To help with this I have written a function that can be used to add an activity anywhere in a parent work item.

All you need to do is pass the work item’s Id, then specify if you want your activity to be first, last, or in a specific position. If you use the First switch, it will increment every activity SequenceId, then return 0 for you to use as your SequenceId. If you use the Last switch, then it will simply return the next available SequenceId. If you use the SequenceId switch you will need to specify what position you want. Remember the first one starts at 0. The function will increment every activity greater than and equal to the one you specified, and return the SequenceId you entered.  Then all you need to do is set this returned variable as the SequenceId property in your script. The example below uses the same script we used to earlier to create the manual activity, but this time we are going to specify that it goes in SequenceId 3.

$ID = "SR29"

Function Sequence-Activities
{
    param (
        [Parameter(Mandatory=$True,Position=1)] [string]$Id,
        [parameter(Mandatory=$True,ParameterSetName="set1")] [int]$SequenceId,
        [parameter(Mandatory=$True,ParameterSetName="set2")] [System.Management.Automation.SwitchParameter]$First,
        [parameter(Mandatory=$True,ParameterSetName="set3")] [System.Management.Automation.SwitchParameter]$Last
    )

    # Get the Work Item 
    $WI = Get-SCSMClassInstance -Class $SRClass | ?{$_.ID -eq $Id}

    # Get the Contains Activity relationship
    $ActivityRelationship = Get-SCRelationship -name System.WorkItemContainsActivity

    # Get Related Activities
    $Activities = $WI.GetRelatedObjectsWhereSource($ActivityRelationship.ID).EnterpriseManagementObject.Id.Guid

    if($Activities)
    {
        If($First)
        {
            # Increment all existing activities by 1 and return 0
            Get-SCClassInstance -id $Activities | %{$_.SequenceId=$($_.SequenceId+1);$_} | Update-SCClassInstance
            Return 0
        }
        elseif($Last)
        {
            # Get the last SequenceId and increment it
            $SequenceId = (Get-SCClassInstance -id $Activities | sort SequenceId | select -Last 1).SequenceId
            $SequenceId++
            Return $SequenceId
        }
        else
        {
            Get-SCClassInstance -id $Activities | ?{$_.SequenceId -ge $SequenceId} | %{$_.SequenceId=$($_.SequenceId+1);$_} | Update-SCClassInstance
            Return $SequenceId
        }

    }
    else
    {
        Return 0
    }
}

# Get the Sequence Id
$SequenceId = Sequence-Activities -Id $Id -SequenceId 3

# Get the required classes
$SRClass = Get-SCSMClass -name System.WorkItem.ServiceRequest
$MAClass = Get-SCSMClass -name System.WorkItem.Activity.ManualActivity

# Manaual Acitivty Prefix
$MAPrefix = (Get-SCClassInstance -class (Get-SCClass -Name System.GlobalSetting.ActivitySettings)).SystemWorkItemActivityManualActivityIdPrefix

# Get the Service Request
$SR = Get-SCSMClassInstance -Class $SRClass | ?{$_.ID -eq $ID}

# Get the Contains Activity relationship
$ActivityRelationship = Get-SCRelationship -name System.WorkItemContainsActivity

# Set the properties for the Manual Activity
$MAProperty = @{
    Id = "$MAPrefix{0}"
    SequenceId = $SequenceId
    Title = "Test MA with Function postion 3"
    }

# Create the Manaual Activity and Relate it to the Service Request
New-SCRelationshipInstance -RelationshipClass $ActivityRelationship -Source $SR -TargetClass $MAClass -TargetProperty $MAProperty -PassThru

In this post you have seen how to create work items using the Service Manager PowerShell. Be sure to check the Overview Post for more content in this series.

SCSM PowerShell: Creating PowerShell Workflows

$
0
0

This post is part of a series on Service Manager PowerShell. Be sure to check out the Overview Page for all posts in this series.

Now that we have covered creating and editing items using the Service Manager PowerShell cmdlets, I would like to touch on one of the best uses of this, which is creating PowerShell workflows. As you will see, utilizing PowerShell can greatly increase the flexibility you have with regards to Service Manager workflows.

First things first, you need to create a management pack in the Authoring Console. Once you’ve done this right click on Workflows and choose Create.

wf01

Now you just need to give your workflow a name and select your trigger conditions. In this example I am going to create a workflow to automatically set the support group to Teir 1 if no support group is selected when the incident is created.

wf02

Next, you just need to drag the Windows PowerShell Script activity into your workflow and open the Script Body properties. Here you can enter your script and properties, but before we do this let’s take a look at a few best practices.

Workflow Script Best Practices

When you create a script to be used with a workflow, there are a few things you need to take into consideration. The first and foremost is performance. If you write a PowerShell script to run as a workflow, you are going to want it to run as quickly as possible. This is not only for speed and ease of use, but also to prevent taxing the resources of your system. Second, you want to make your script is as portable as possible. As you should know, all workflows run on the primary management server, so it may be tempting to hardcode things like the module path. However, what happens if your primary server crashes and you need to promote one of your secondary servers? If Service Manager was not installed to the same path, then your workflows will start failing as well. And the last thing you want in a situation like this is more work. On top of these you will want to add error checking and catching, if necessary to your script.

Script Performance   

Trying to make your scripts run as efficiently as possible can take some trial and error, as well as experience. However, Microsoft has provided some tools to help you out. One of these tools is the Measure-Command cmdlet. The Measure-Command cmdlet enables you to measure the running time of a command or script down to the millisecond.

In the post Modifying Work/Configuration Items, we saw how to use the Update-SCSMClassInstance cmdlet to update a work item. So let’s take a look at our example of setting the support group for an Incident.

In the example below, I am getting the Incident, then I am setting the support group to Tier 1.

$IR = Get-SCSMClassInstance -Class (Get-SCClass -Name System.WorkItem) | ?{$_.Id -eq "IR27"}
$IR | %{$_.TierQueue="IncidentTierQueuesEnum.Tier1";$_} | Update-SCClassInstance

There are a few things to note in the first line. First, I am using the class System.WorkItem instead of System.WorkItem.Incident. Technically this will work because the Incident class is derived from the Work Item class, but this could give us a larger amount of returned data to filter through. To test, I will use the Measure-Command cmdlet to see how long it takes that first line to run.

Measure-Command {
$IR = Get-SCSMClassInstance -Class (Get-SCClass -Name System.WorkItem) | ?{$_.Id -eq “IR27″}
}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 140
Ticks             : 1400774
TotalDays         : 1.6212662037037E-06
TotalHours        : 3.89103888888889E-05
TotalMinutes      : 0.00233462333333333
TotalSeconds      : 0.1400774
TotalMilliseconds : 140.0774

As you can see from the output above the command took 140 milliseconds. This is no too bad, but I need to take into consideration that this is my test environment that I just rebuilt, and it has less than 30 work items in it. So I ran it again in an older environment, with a lot of ticket data.

Measure-Command {
$IR = Get-SCSMClassInstance -Class (Get-SCClass -Name System.WorkItem) | ?{$_.Id -eq “IR31806″}
}  | FL TotalSeconds, TotalMilliseconds

TotalSeconds      : 13.6384204
TotalMilliseconds : 13638.4204

This time it took 13 seconds to run, as compared to the 140 milliseconds like before. I ran the Measure-Command several more times to ensure that this was not a one off result. On average the command took 12 seconds to run. Having a script run for 12+ seconds every time this workflow is initiated, can start causing a backlog on your system, depending on how often it runs. So let’s see if we can improve the execution time. The first thing we can try is to narrow our command down to the just the Incident class.

Measure-Command {
$IR = Get-SCSMClassInstance -Class (Get-SCClass -Name System.WorkItem.Incident) | ?{$_.Id -eq “IR31806″}
} | FL TotalSeconds, TotalMilliseconds

TotalSeconds      : 10.270151
TotalMilliseconds : 10270.151

Now the command is completing with an average of around 10 seconds. You can see that we saved some time, but not a lot. So let’s take a look at another way we can improve the execution time.

You may have noticed that the command is using a Where clause to get the specific Incident. However, since the Where clause is a piped command PowerShell will run the first half of the command completely before applying the Where clause after the pipe. This means that the first part of the command will return all items in the Incident class, then it will filter down to the one we want. So instead of using the Where clause, I’m going to test using the Filter parameter that is part of the Get-SCSMClassInstance cmdlet. This cause the filtering to happen during the execution of the Get-SCSMClassInstance cmdlet.

Measure-Command {
$IR = Get-SCSMClassInstance -Class (Get-SCClass -Name System.WorkItem.Incident) -Filter ‘Id -eq “IR31806″‘
} | FL TotalSeconds, TotalMilliseconds

TotalSeconds      : 0.1083781
TotalMilliseconds : 108.3781

So now our command is running at an average of around 100 milliseconds. This means the command is now running faster than the original one did in the brand new clean environment, but I still think we can make it faster. One way to do that is by using the Incidents Guid instead of the Work Item Id. The Guid can be passed directly to the Get-SCSMClassInstance without the need for any filters or Where clauses.

Measure-Command {
$IR = Get-SCSMClassInstance -Id 4dcab5b1-8b26-e0bd-74ae-005699366a48
} | FL TotalSeconds, TotalMilliseconds

TotalSeconds      : 0.0352629
TotalMilliseconds : 35.2629

As you can see by using the Guid, the command now runs in 35 milliseconds. So we have now taken the execution time from over 10 seconds to under 40 milliseconds. Now you may be asking where I got the Guid from. In this case I got the Guid by saving the Incident to the variable $IR then outputting the value of $IR.EnterpriseManagementObject.Id.Guid. Of course I had to get the Incident before I could get the Guid, so this example may not be the best to use in the scripts you run manually. However, as you will see, when you are creating a workflow you can have the system pass this variable straight to the script.

Now I have one last thing to check when it comes to the script. In the example I am saving the Incident to the variable $IR then on the next line I am updating the support group. I ran both lines together using the Measure-Command. Then I tested it, putting it all on one line to together. I found in either case the run time was about the same. So, for ease of readability I’m going to leave it at two lines.

Script Portability

Now that our script is running as efficient as possible, we need to make it as portable as possible. The best way to do this is to never hardcode any names or paths. In the case of a Service Manager workflow we are going to need to import the Service Manager PowerShell Module. As you know from previous posts this module is not always properly registered in PowerShell, so it is best to point it directly to the Service Manager install directory. Luckily, when Service Manager is installed it writes the install directory to a registry key. So we can query that key to get the path, then import the module. Also to make things even faster we can check to see if the module is already loaded, and if so,  just go ahead and skip importing it. The example below shows how to do this, and is the start of every Service Manager workflow I create.

if(@(get-module | where-object {$_.Name -eq 'System.Center.Service.Manager'}  ).count -eq 0)
{
    $InstallationConfigKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
    $InstallPath = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Powershell\System.Center.Service.Manager.psd1"
    Import-Module -Name $InstallPath -Global
}

Error Checking

I’m not going to go into too much detail on error checking, because the techniques for it are not unique to Service Manager. But I would recommend thoroughly testing your scripts and learning about try/catch/final commands in PowerShell.

Putting it all Together

Now that we have our full script, as shown below, it is time to go back to the Authoring Tool.

if(@(get-module | where-object {$_.Name -eq 'System.Center.Service.Manager'}  ).count -eq 0)
{
    $InstallationConfigKey = 'HKLM:\SOFTWARE\Microsoft\System Center\2010\Service Manager\Setup'
    $InstallPath = (Get-ItemProperty -Path $InstallationConfigKey -Name InstallDirectory).InstallDirectory + "Powershell\System.Center.Service.Manager.psd1"
    Import-Module -Name $InstallPath -Global
}

$IR = Get-SCSMClassInstance -Id $id
$IR | %{$_.TierQueue="IncidentTierQueuesEnum.Tier1";$_} | Update-SCClassInstance

In the Authoring Tool open the Script Body properties. There are two sections in this window. The Script Body and the Script Properties. In the Script Body go ahead and click the down arrow in the View or Edit Script section, and paste in your script.

wf03

You’ll notice  in the script we are using the variable $Id in the Get-SCSMClassInstance cmdlet to return the Incident. You’ll also notice that we are not setting that anywhere in the script. This is because we are going to have Service Manager pass the value to the script at runtime. To do this go to the Script Properties section and click New to create a parameter for your script. Set the name field to the same name as your PowerShell variable, minus the dollar sign. In this case our name will be Id.

wf04

Next click on the grey box next to the Value field. Here is where we will tell Service Manager what value to pass to the script. In our case we want the Guid, so on the left select, Use a class property. Then scroll down and look for the property ID (Internal). This is the Guid of the item. The field that is just ID will be the work item Id, for example IR1234. After you select the ID (Internal) property click OK.

wf05

Click OK again to close the Script Properties. Then save your management pack, and import it into Service Manager. Also, remember when you create a workflow it will also create a dll with the same name as the workflow in the management pack’s folder. You will need to copy this dll into the Service Manager program folder on the primary management server.

To Remove or Not to Remove

There is often the debate as to whether or not you need to add the Remove-Module to the end of your PowerShell scripts. Personally, it is not something I bother with, for a couple of reasons. First, once your workflow stops running the session is closed, so any loaded modules will be closed. Second, if for some reason the module is not closed, we have placed a check at the beginning of our script to go ahead and skip the load. Finally, this seems to be a hold over from SMLets. I have seen SMLets throw error messages if the module is not removed. However, I have never seen this problem with the Service Manager cmdlets.

If you need one more reason, not to remove, there here is a quote from Ed Wilson on the Remove-Module cmdlet, from the post How to Remove a Loaded Module.

Typically, the only people who remove modules are those who are developing the module in question or those who are working in an application environment that’s encapsulating various stages in the process as modules. A typical user rarely needs to remove a module. The Windows PowerShell team almost cut this feature because it turns out to be quite hard to do in a sensible way.

In this post we covered creating a PowerShell workflow and some best practices around them. Stay tuned for the next blog in this series where I’ll go into reporting on and troubleshooting workflows with PowerShell. Be sure to check the Overview Post for more content in this series.

 

 

HTML5 Self-Service Portal Roles and Features PowerShell

$
0
0

Here is a quick one line command to install all the required roles and features for the new Service Manager HTML5 Self-Service Portal.

Install-WindowsFeature –Name WEB-Server,WEB-WebServer,WEB-Common-Http,WEB-Default-Doc,WEB-Dir-Browsing,WEB-Http-Errors,WEB-Static-Content,WEB-Health,WEB-Http-Logging,WEB-Performance,WEB-Stat-Compression,WEB-Security,WEB-Filtering,WEB-Basic-Auth,WEB-Windows-Auth,WEB-App-Dev,WEB-NET-Ext45,WEB-ASP,WEB-Asp-Net45,WEB-ISAPI-Ext,WEB-ISAPI-Filter,WEB-Mgmt-Tools,WEB-Mgmt-Console,NET-Framework-Features,NET-Framework-Core,NET-Framework-45-Features,NET-Framework-45-Core,NET-Framework-45-ASPNET,NET-WCF-Services45,NET-WCF-HTTP-Activation45,NET-WCF-TCP-PortSharing45

Remember to launch PowerShell as an administrator before executing this command.

Cluster Shared Volumes Cleanup Script

$
0
0

I was recently tasked with cleaning up a client’s Hyper-V environment. This particular environment had experienced some pretty bad VM sprawl. Even worse when VMs were migrated to cluster, or removed, the files were not always cleaned up properly. The environment has over 200 VMs, so it would have taken forever to go through and check each VM and file individually. So to make my life much easier, I created a PowerShell script to help me out.

This script will parse all of the Cluster Shared Volumes in a cluster, and create a PowerShell object with all the files in them. It will then create another PowerShell object for all the Virtual Hard Disks, ISOs, and Snapshot files associated to the Virtual Machines. Then it will compare the two objects, and output the results to a CSV file.

The script must be ran from a server that is a member of the cluster, so that it can access the Cluster Shared Volumes. To run the script simply supply the cluster name, and optionally a location for the CSV file to be exported to.

csv01

Depending on the size of your environment this script can take some time to run, so I created a progress bar to let you know that it is still running.

csv02

Once the script completes it will output the CSV file path.

csv05

The CSV will contain a status for each file. The status of “Attached” is for ones that are associated with a virtual machine, and “Not Attached” for those not associated to a virtual machine. If it is attached it will also list the specific machine name. It will exclude any of the Hyper-V Virtual Machine configuration files. These are files with the extension types of xml, bin, vsv, and exp.

csv06

Here you can see I have several files sitting on the Cluster Shared Volumes that don’t need to be there. I can now go move these to a more appropriate storage location or delete them as needed. 

You can download the script from the TechNet Gallery –  Cluster Shared Volumes Cleanup Script 

This script is provided “AS-IS” with no warranties of any kind. Although tested and found as error-free, no responsibility is taken for any misuse of the script, or damage caused by using it. It is recommended that you making full backups of your environment prior to making any major changes.


Creating SCSM Work Items from Template in Orchestrator (with Prefixes!)

$
0
0

One of the most common complaints I’ve heard when creating work items from a template in Orchestrator is the lack of prefixes on the activities. This same problem exists when using SMLets to create a work item from a template. Since the Id is a key value there is no way to go back after it has been created and add the prefix. However, using Orchestrator, the SDK, and a little C#, you can work around this problem.

To do this you need to copy the Service Manager SDK binaries to your runbook server, then use the Run .Net Script activity in Orchestrator. In this example I will be creating a Service Request, but you could easily update the code for other classes. Also, due to the limitations of the Run .Net Script activity this only works for top level activities. So if you have parallel or sequential activities this will not update the activities inside of it. Although I am working on a work around for this, and will post the solution as soon as it is ready.

Update: I have released a stand alone executable that you can use to get around the problem of parallel and sequential activities. Click here for more details.

First start by copying the SDK Binaries folder from the Service Manager management server to your runbook server. If you have installed the Service Manager console, or SMLets on your runbook server, these may already be on your server. Once you have these on your runbook server you are ready to create your runbook.

In the code example here I am creating a Service Request by passing the template’s Guid to the Run .Net Script activity. This little bit of code will create the Service Request, then set the prefixes, and save. It will then output the SC Object Guid of the Service Request it created, so that you can use the standard SCSM integration pack activities to make any other required changes to the request.

  1. Start by adding a new Run .Net Script activity to you runbook.
  2. Then set the Type to C#.
  3. Next on the Advanced tab add the following Namespaces.
    System
    Microsoft.EnterpriseManagement
    Microsoft.EnterpriseManagement.Common
    Microsoft.EnterpriseManagement.Configuration
  4. Then add the following references
    C:\Microsoft.EnterpriseManagement.Core.dll (your path may be different based on where you copied the SDK binaries to)
    C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll
    C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.XML.dll
    prefix2
  5. On the Published Data tab, create a variable named strSRGuid
    prefix1
  6. Now you are ready to enter the C# code. On the details tab paste in the code below.
    Guid TemplateId = new Guid("<guid passed in>");
    EnterpriseManagementGroup emg = new EnterpriseManagementGroup("<SM Mgmt Srv>");
    
    //Create Service Request
    ManagementPackObjectTemplate mpt = emg.Templates.GetObjectTemplate(TemplateId);  
    EnterpriseManagementObjectProjection emop = new EnterpriseManagementObjectProjection(emg, mpt);
    
    //Get the service request prefix settings
    //Get the settings class
    ManagementPackClass mpc = emg.EntityTypes.GetClass(new Guid("fa662352-1660-33ae-6316-7fe1c9fecc6d"));
    //Get the emo for the settings
    EnterpriseManagementObject emo = emg.EntityObjects.GetObject<EnterpriseManagementObject>(mpc.Id, ObjectQueryOptions.Default);
    //Return prefix
    string prefix = emo[mpc, "ServiceRequestPrefix"].Value.ToString();
    //Add prefix to ID
    string strSRID = prefix + emop.Object[null, "Id"].ToString();
    emop.Object[null, "Id"].Value = strSRID;
    
    //Set Created Date
    emop.Object[null, "CreatedDate"].Value = DateTime.Now.ToUniversalTime();
    
    ManagementPackRelationship relContainsActivity = emg.EntityTypes.GetRelationshipClass(new Guid("2da498be-0485-b2b2-d520-6ebd1698e61b"));
    foreach (IComposableProjection objCurComponent in emop[relContainsActivity.Target])
    {
    	// check all prefixes for activity
    	EnterpriseManagementObject ActEmo = objCurComponent.Object;
    	EnterpriseManagementObject objSettings = emg.EntityObjects.GetObject<EnterpriseManagementObject>(new Guid("5e04a50d-01d1-6fce-7946-15580aa8681d"), ObjectQueryOptions.Default);
    	// set default prefix
    	string Actprefix = objSettings[null, "SystemWorkItemActivityIdPrefix"].Value.ToString();
    	// check more specified activities
    	// Get Activity Types
    	ManagementPackClass MAc = emg.EntityTypes.GetClass(new Guid("7ac62bd4-8fce-a150-3b40-16a39a61383d"));
    	ManagementPackClass RBc = emg.EntityTypes.GetClass(new Guid("5fe5d511-efb9-54a1-4be9-811f60e186c4"));
    	ManagementPackClass RAc = emg.EntityTypes.GetClass(new Guid("bfd90aaa-80dd-0fbb-6eaf-65d92c1d8e36"));
    	ManagementPackClass PAc = emg.EntityTypes.GetClass(new Guid("568c49f2-d7d6-d7d7-89dc-dfb5b39fded7"));
    	ManagementPackClass SAc = emg.EntityTypes.GetClass(new Guid("0ad0812b-f267-52bf-9f11-c56587786791"));
    	ManagementPackClass DAc = emg.EntityTypes.GetClass(new Guid("e786e1c7-b1fe-5b8b-ef8f-9e2dc346c44f"));
    	if (ActEmo.IsInstanceOf(MAc))
    	{
    		Actprefix = objSettings[null, "SystemWorkItemActivityManualActivityIdPrefix"].Value.ToString();
    	} else if (ActEmo.IsInstanceOf(RBc))
    	{
    		Actprefix = objSettings[null, "MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix"].Value.ToString();
    	} else if (ActEmo.IsInstanceOf(RAc))
    	{
    		Actprefix = objSettings[null, "SystemWorkItemActivityReviewActivityIdPrefix"].Value.ToString();
    	} else if (ActEmo.IsInstanceOf(PAc))
    	{
    		Actprefix = objSettings[null, "SystemWorkItemActivityParallelActivityIdPrefix"].Value.ToString();
    	} else if (ActEmo.IsInstanceOf(SAc))
    	{
    		Actprefix = objSettings[null, "SystemWorkItemActivitySequentialActivityIdPrefix"].Value.ToString();
    	} else if (ActEmo.IsInstanceOf(DAc))
    	{
    		Actprefix = objSettings[null, "SystemWorkItemActivityDependentActivityIdPrefix"].Value.ToString();
    	}
    	//else if // check other types with same way
    	string strActID = Actprefix + ActEmo[null, "Id"].Value.ToString();
    	ActEmo[null, "Id"].Value = strActID;
    }
    
    // Save SR
    emop.Overwrite();
    strSRGuid = emop.Object.Id.ToString();
  7. You will need to provide the Guid of the template you want to use and the name of your Service Manager management server. On lines 1 and 2. Other than that you can leave the code as is.

The easiest way to get the Guid of the template is to use the Get-SCSMObjectTemplate PowerShell cmdlet. You could easily put this in another Run .Net Script prior to this one and pass in the Guid that way.

Creating SCSM Work Items from Template with Prefixes!

$
0
0

In a previous post I showed how you can create a Service Manager Work Item from a template in Orchestrator by using the SDK. The reason you would want to do this, is so you can include the prefixes on the Work Item and its child activities. This is something the Service Manager integration pack does not handle well, especially with regard to the child activities. However, there was one major drawback to the method I showed. It does not work for activities that are inside of parallel or sequential activities. This is due to way that the Run .Net Script activity behaves in Orchestrator.

So to get around this, I have written a light weight executable that you can pass a template ID to. It will create a new Work Item, and assign prefixes and proper display names to all child activities. The reason I made it an executable, is to allow you to run it in Orchestrator using the Run Program activity, call it from a Scheduled Task, run it from a custom Task in the console, or even call it from a PowerShell script. This executable will work for Change, Incident, Service Request, Release, and Problem templates.

To run the executable, you need to provide the template’s Guid and optionally the name of your Service Manager management server. The executable will output the Guid of the Work Item it created, so that you can perform other tasks on it afterwards.

You can download the executable or the entire source code on HERE on GitHub.

Usage Examples

As I mentioned earlier in the post, you can run this executable using the Run Program activity in Orchestrator. To do this, select “Command execution” for the mode, and add the path to the executable along with the parameters.

template1

After the activity runs you can select the Pure Output from the Published Data returned. This will be the Guid Id of the Work Item it just created. You can use this with other activities like the Update Object activity to make additional changes.

template2

Keep in mind this executable is a work in progress, and has only been tested in the handful of environments I have. Please feel free to let me know of any issues or recommendations you may have.

Install PowerShell in CentOS 7 Core

$
0
0

Installing PowerShell for Linux is a pretty straight forward process, but can be kind of a pain if you are using a core install. Below are tips and directions for installing PowerShell in CentOS 7 core.

In order to download the PowerShell install files you will need wget. If you don’t have it installed you can install it using the following command.

sudo yum install wget

Once wget is installed you are ready to download PowerShell. In order to download from wget you just need to supply the URL of the file to download. However, the URL to download PowerShell is https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.9/powershell-6.0.0_alpha.9-1.el7.centos.x86_64.rpm. After attempting to type in this URL multiple times, I decided to find a better way. That better way is to use a Tiny URL. I created the URL http://tinyurl.com/hua9mtm that redirects to the full download link. So all you need to do is use wget to download the install file, rename it to an RPM file, then install it. You may want to make your own Tiny URL if you want to install a different version. The commands to do this are below.

wget http://tinyurl.com/hua9mtm
mv hua9mtm powershell.rpm
sudo yum install powershell.rpm

 

linuxps3

Once the installer completes you can type in powershell to launch it. From there you are in the PowerShell environment. I typed in Get-Command to confirm everything installed correctly, and to get a list of the commands available.

linuxps5

Create SLA Specific Subscriptions in Service Manager

$
0
0

Often times you need to use different email templates for SLA notifications, or send notifications to different recipients. Unfortunately, with the way that Service Manager handles SLAs, this can be difficult to achieve. With all SLA beingmembers of the “Service Level Instance Time Information” class, how can you send different notifications for different work item types, or even for different SLAs? The answer is by using the Id of the Service Level Configuration.

The Id field in the Service Level Configuration is unique to the particular SLA and is listed as a related class when you create subscriptions using the “Service Level Instance Time Information” class. This allows you to create individual subscriptions for each SLA. The only tricky part can be finding the Id of the SLA, but one simple line of PowerShell can get that for you.

If you open PowerShell from your Service Console and enter the command below it will return all of your SLAs.

Get-SCClassInstance (Get-SCClass -Name System.SLA.Configuration)

In my case I want to make a notification for the “IR Resolve” SLA, so I need to make note of its Id, which is show below to be SLAConfiguration_317f2a80-5418-4289-89cd-464581c96005.

sla1

Now all I need to do it create a subscription and in the Changed To criteria add the Service Level Configuration Id field and add the Id I returned from PowerShell.

sla2 sla3

And there you have it. This particular SLA can now have its own template and recipients.

Spell Check Your Service Manager Environment

$
0
0

Good spelling is an important, and sometimes overlooked, aspect of providing your users and customers a professional IT Service Management experience. Nothing is more embarrassing than going back to look at the Request Offering or Email Template, sometimes months after it has been in production, and finding a misspelled word. So to help avoid situations like this, I have created a script that uses Microsoft Word’s spell checker to check the spelling on multiple different items inside Service Manager.

The script can be run to check the spelling on Email Templates, Subscriptions, Work Item Templates, Enumeration Lists, Request Offerings, and Service Offerings. It also takes into consideration HTML based Email Templates. It extracts just the text from the HTML so you don’t receive a bunch of false positives from the HTML elements. You also have the option to output the results to a CSV file. As well as the ability to specify words that you would like to exclude from being checked.

The script requires that you have the Service Manager PowerShell module and Microsoft Word installed on the computer executing it. Since I’m sure you don’t want to install Word on your management server, you can specify the Service Manager instance to connect to by using the “MgmtSrv” parameter.

You can choose to run it against all the item types by using the “-All” switch or you can choose the individual types you want to check by listing each switch you would like. For example, adding “-RequestOfferings” and “–ServiceOfferings” would only check the Request Offerings and Service Offerings.

You can exclude certain words from being checked by adding them to the “Exclusions” parameter as an array. In the example below the words “WeirdCompany” and “Runbook” are excluded from being checked.

$exclude  = "WeirdCompany","Runbook"
.\SMSpellCheck.ps1 -All -Exclusions $exclude

The script will automatically select the language to check based on the default language of your operating system. To check what that is set to on you computer you can run the Get-Culture cmdlet in PowerShell.

The script will output five properties for each spelling error it finds. These are detailed below.

Type Object type (ex. Request Offering, Email Templates, etc)
DisplayName The display name of the object
Name The internal name of the object
Text The misspelled word
Element The element of the object with the misspelling (ex. DisplayName, Title, Body, etc.)

For a full list of the switches, parameters, and examples you can refer to the help section of the script.

Download the Service Manager Environment Spellchecker from TechNet Gallery.

Viewing all 39 articles
Browse latest View live