So, it has been a while.

SO, the requirement for this project was as follow.

1. Use Service Manager

2. Provide a portal for logging a VM Creation request.

3. Build the computername based on a predetermined pattern.

4. Determine first available name

5. Perform a capacity check.

6. Deploy the set number of VMs.

I am going to skip over the first 2 Points, there are numerous topics covering this. So, onto point 3, I created a Service Request Extension, if anyone would like some info on this, also plenty of articles, please let me know and I can do a blog post on the Extension, or I can make the extension available for you to seal yourselves. Anyways, I mapped all the prompts required within the Request Offering and published it to SharePoint Service Manager Portal.

So, now it is onto the Computername Pattern, for the sake of my customer’s privacy, I will not use there exact naming pattern, but rather use XX-YY-Z type of naming, to give you the idea. Of course, Loving PowerShell the way I do, I turned to old faithful.

I will provide the basic PowerShell code in snippets and allow you to expand onto it as you see fit, I built in a fair amount of specific error checking for my customer which will NOT be included in this post.

The Quest Commandlets will be needed for these scripts. The code is being re-formatted due to the blog hoster. Please change the required values.

#================================================================================
#build computer name

$businessApplication = <"value"> #now from ORC as part of SR
$function = <"value">  #now from ORC as part of SR
$location = <"value"> #now from ORC as part of SR
$affinity = <"value">  #now from ORC as part of SR
$role = <"value">  #now from ORC as part of SR
$businessUnit = <"value">  #now from ORC as part of SR

#naming convention
#1 letter - BusinessUnit
$businessUnit = $businessUnit.Substring(0,1)
$businessApplication
#3 letters - location
$location = $location.Substring(0,3)
$location

#placecholder (-)

#1 letter - role
$role = $role.Substring(0,1)
$role

#1 letter - Function
$function = $function.Substring(0,1)
$function

#tiebreaker (XXX)

#3 Letters - Business Application
$businessApplication = $businessApplication.Substring(0,3)
$businessApplication

#1 letter - Affinity
$affinity = $affinity.Substring(0,1)
$affinity

$computernamepattern = $businessUnit+$location+"-"+$role+$function+"XXX"+$businessApplication+$affinity
$computernamepattern

$computernametocheck = $computernamepattern.Replace("XXX&","001")
$computernametocheck

$part1 = $computernamepattern.Substring(0,7)
$part3 = $computernamepattern.Substring(10,4)
$part2 = $computernamepattern.Substring(7,3)

#==============================================================
#Check if computer name exists

Add-PSSnapin quest.ActiveRoles.ADManagement
$i = 1
do
{
  $computername = $part1 + "{0:D3}" -f $i + $part3
  "testing $computername"
  $test = get-qadcomputer $computername
  if ($test)
  {"Computer Exists"}
  Else
  {$computerstartname = $computername}
  $i++
}
until ($computerstartname -ne $null)

#==========================================================
#create computers

$part1 = $computerstartname.Substring(0,7)
$part3 = $computerstartname.Substring(10,4)
$part2 = $computerstartname.Substring(7,3)
[int]$partnumber = $part2.Substring(2,1)
$computerstobeCreated = ''
$computersRequested = "<Value_from_Orc>";

$computersCreated = 0
$filepath = 'C:\Tools\testing.txt'
New-Item -Path $filepath -ItemType file

do
{
  $newcomputer = $part1 + "{0:D3}" -f $partnumber + $part3
  #New-QADComputer -Name $newcomputer -ParentContainer "OU=Test Computer Accounts,OU=System Accounts,DC=bui,DC=co,DC=za"
  write-host $newcomputer
  write-host $computersCreated
  Add-Content -Path $filepath -Value $newcomputer
  $computersCreated++
  $partnumber++
  $computerstobeCreated = $computerstobeCreated + "" + $newcomputer
}
while ($computersCreated -le ($computersRequested-1))

$computerstobeCreated = $computerstobeCreated.Substring(1)

#======================================================================================= 

I  check the nodes within the Cluster and then find the node with the least amount of Virtual Machines on it. Then use this node information for the creation of the VM. Now time for the free space, since this was a Cluster, I simply used the free space attribute within PowerShell and Virtual Machine Commandlets. I get all the CSV information and then sort by the free space and select the first object as this one would have the most amount of free space.

This is a basic capacity check as per the customer’s requirement, as part of this a field within Service Manager is updated to allow some more information to be shared within the members of the team working on this request.

#==========================================================
#Check for placement issues

$VMCluster = <"value">

$VMMServer = <"value">

$numberOfVMS = <"value">

$hostmachinedomain = (gwmi WIN32_ComputerSystem -ComputerName $VMMServer).Domain

$ClusterFQDN = $VMCluster +"."+$hostmachinedomain

$nodes = (get-scvmhostcluster -Name $VMCluster).nodes 

$colPlacementInfo = @()

#$placementInfo = New-Object system.object

foreach ($node in $nodes)
{
  $placementInfo = New-Object system.object

  $placement = (Get-SCVMHost $node).AvailableForPlacement

  switch($placement)
  {
    "True"
    {
      $placementInfo | Add-Member NoteProperty -name Placement -value "True";
    }
    "False"
    {
      $placementInfo | Add-Member NoteProperty -name Placement -value "False"
    }
  }
  $colPlacementInfo += $placementInfo
} 

If ($colPlacementInfo.placement -ccontains "False")
{$Proceed="No"}
else
{$Proceed="Yes"}

#=========================================================================

#capacity check

$VMCluster = "<value_from_orc>"

$VMMServer = "<value_from_orc>"

$numberOfVMS = "<value_from_orc>"

$hostmachinedomain = (gwmi WIN32_ComputerSystem -ComputerName $VMMServer).Domain

$ClusterFQDN = $VMCluster +"."+$hostmachinedomain

$nodes = (get-scvmhostcluster -Name $VMCluster).nodes 

foreach ($node in $nodes)
  {
    #get Memory Information
    $memoryAvailable = (Get-SCVMHost $node.name).AvailableMemory # MB
    $memoryAvailableCluster = $memoryAvailableCluster + $memoryAvailable

    [int]$memoryTotal = ((Get-SCVMHost $node.name).TotalMemory) /1mb #bytes "{0:D3}" -f
    $memoryTotalCluster = $memoryTotalCluster + $memoryTotal

    #get CPU Information
    $CpuCoreCount = (Get-SCVMHost $node.name).CoresPerCpu
    $CpuCoreTotalCluster = $CpuCoreTotalCluster + $CPUCoreCount
    $CpuCoreTotalCluster

    $CpuCount = (Get-SCVMHost -ComputerName $node.name).PhysicalCPUCount
    $CpuTotalClusterCount = $CpuTotalClusterCount + $CpuCount
    $CpuTotalClusterCount

}
#$memoryAvailableCluster = ($memoryAvailableCluster *1024*1024) /1GB
#$memoryTotalCluster = ($memoryTotalCluster *1024*1024) /1GB
$memoryAvailableClusterdisplay = $memoryAvailableCluster *1024 *1024 /1GB
#$memoryAvailableCluster = ($memoryAvailableCluster) /1GB
$memoryTotalClusterdisplay = $memoryTotalCluster *1024 *1024 /1GB
#$memoryTotalCluster = ($memoryTotalCluster) /1GB

  $VMTemplate = Get-SCVMTemplate -Name '<Name>'

  $TemplateMemory =  $vmtemplate.Memory * $numberOfVMS

  $TemplateCPU =  $vmtemplate.CPUCount * $numberOfVMS

  #Memory Calculation

  $memoryAvailbleAfterVm = $memoryAvailableCluster - $TemplateMemory
  $memoryAvailbleAfterVmdisplay = $memoryAvailbleAfterVm *1024 *1024 /1GB
  #$memoryAvailbleAfterVm = $memoryAvailbleAfterVm /1GB
  $memoryAvailbleAfterVm
  $memoryAvailbleAfterVmdisplay
  #$memoryAvailbleAfterVm = $memoryAvailbleAfterVm.tostring() + "GB" 

$Storage = (get-scvmhostcluster $VMCluster).SharedVolumes
$StorageCapacity = ($Storage | Measure-Object -Property Capacity -Sum).sum
$StorageFreeSpace = ($Storage | Measure-Object -Property FreeSpace -Sum).sum
$StorageUsed = $StorageCapacity - $StorageFreeSpace

$VMVHDUsedSpace = $VMTemplate.TotalVHDCapacity *$numberOfVMS
$freeSpaceAfterVMS = $StorageFreeSpace - $VMVHDUsedSpace

#=============================================

So, I have been using bits and pieces of Orchestrator to string these activities together. However the real power comes in now after using all the pieces from other steps to now, the ACTUAL Deployment. For the sake of simplicity, I am attaching a screenshot of the runbook. Please feel free to contact me (details below) should you want any additional information.

Most of the PowerShell within the Runbook is as a result of unique constraints within this environment, and most cannot be shared. However, like I said, should you want specifics, feel free to contact me and will provide what I can.

The runbook is attached as well, please use at your own discretion, PROVIDED AS IS. DeployVMRunbook

 

2014-07-31_14-11-46

(E-Mail me)

BlogEmailIcon

Follow me,

Twitter (Personal & System Centre)

 Twitter (System Centre Focused)

So, Cireson have been investing into System Centre Service Manager (SCSM) very heavily and the end results are WELL worth it.

The Cireson Outlook Console has been out a little while, however it just keeps getting better and better. The Console is super easy to install. The Install instructions are provided by Cireson. There are a few steps to follow as well as the installation of an Outlook Plugin.

Yes, as you may have guessed from the name, this allows you to connect to Service Manager with almost a full experience directly from OUTLOOK, how great is that. It simply sits in a corner and connects automatically or it can be set not to, if you are like me and always traveling and do not always have connection to your SCSM Environment. I have tested with extensively, it works VERY WELL over Mobile Broadband (please note that I am in South Africa, the broadband here is NOT great) with a VPN Connection.

Yes, you read that correctly, it is configured in such a way to be light enough to use or a mobile connection with a VPN connection to your environment. Like I said, well worth the wait.

So, once the installation is complete and the Management Packs have been imported and the Outlook Plugin has been installed, the fun can start.

Ok, so onto configuring the Outlook Client,

Click Settings

2014-06-17_07-04-42

Configure as needed, you will notice that I have configured some of the features, like “When Outlook starts, disable features and don’t connect”, i have done purely because i am NOT domain joined and do not always want the Outlook Console to always try and connect especially when I am NOT connected to my corporate environment in any way, this can be easily set and configured to meet your needs. I also enable the the “Prompt for Credentials when connecting”, if I do not do this, it will try to connect using NON DOMAIN credentials. Cireson have clearly thought of this scenario. :)

 

2014-06-17_07-11-13

So, now we can connect to the Environment.

2014-06-17_07-13-00

Provide the required username, password and domain.

2014-06-17_07-14-00

The connection is usually made quite quickly and you can easily see when the connection has been made as a few more icons will now appear and allow you to start using the real power of the Outlook Connector.

2014-06-17_07-16-44

Very quickly, you can see that you can easily Create, Edit and see you Active Work Items. The connector is very intuitive, and easy to use. Simply click one the buttons above or right click on an e-mail, this allows you to create or update a Work Item (yes, you can do this for Problems, Incidents, Changes and Service Requests). This outlook connector really is a ONE-STOP shop for everything Service Manager related.

Creation is easy, and the subsequent menu are easy to understand

2014-06-17_07-20-49

As is editing an incident,

2014-06-17_07-22-08

With the Edit, it allows to search for the Work Item and then modify the required Work item.

2014-06-17_07-23-42

The Outlook Console has a very familiar look and feel to it, allowing pretty much all the same functionality as the Native SCSM Console. When it comes to tasks and the like, such as resolving, you will notice that there is NO context pane for these tasks. However, this is solved by Cireson, with the use of a “Tasks drop down”, allowing for the simplest tasks do to completed as efficiently as possible.

2014-06-17_07-24-37

Notice the tasks,

2014-06-17_07-27-58

All tasks like re-assignment work in much the same way as the Native SCSM Console, also allowing for searching as you type.

2014-06-17_07-29-19

Once all the required updates and/or tasks have been completed, simply click apply or OK, whichever you normal use.

More Functionality

You can also use email received by you to log calls.

So, here is the scenario, you normally allow people to send email and then the call gets logged, or you get an e-mail directly from a user. This should be a new call or perhaps an update to an existing call, the Cireson Outlook Console handles this with ease. The Cireson Outlook Console allows you to update or log Work Items directly from Outlook.

What is also very nice and handy is that the linking of the Affected user happens automatically, the “From” is used for the “Affected User”, in the same way that the Exchange Connector for SCSM works.

2014-06-17_07-36-44

2014-06-17_07-37-38

2014-06-17_07-38-33

All required Fields completed, OK button is now available.

2014-06-17_07-42-10

Updates to work Items are handled in pretty much the same way, select the e-mail, Right Click -> Service Manager -> Choose to Update or add to Work Item. Search for Work Item and and Click OK.

2014-06-17_07-47-56

2014-06-17_07-51-47

OK, so you clicked “OK” and t disappeared, how do I check?

Very easy, you can use the “Edit” option and search for Incident and you can check it was added as a log.

2014-06-17_07-55-47

2014-06-17_07-56-30

2014-06-17_07-57-18

A lot of though has gone into this product, I ALWAYS recommend this product to my customers, as most of us spend the majority of our time within Outlook as it is, this simply adds the immense power of System Centre Service Manager into Outlook as well, allowing you to further empower your IT Staff.

(E-Mail me)

 

Follow me,

Twitter (Personal & System Centre)

 Twitter (System Centre Focused)

Hi All,

It has been a little while, i have been quite involved with an Integration Project. With this project there was a requirement to add comments to a Work Item. The information is passed on from “System A” to “System B” via XML. Using PowerShell, extracting the XML contents is not too difficult, if anyone would like to dive into that a little bit, please let me know with Comments.

I decided not to re-invent the wheel, so some searching was in order, and I found the following post, this post gave the basics. However every time I tried to add additional comments, the comments were over-written and NOT replaced. Not quite the desired effect. So after some more checking and research and tinkering within the Service Manager, I discovered the following about comments.

When adding a Comment, there is adding of a relationship class, already mentioned in this post, however it also uses a GUID.

2014-04-03_07-51-37

There is also another trick to adding comments within Orchestrator, this is discovered by trying to add a comment and then pulling all the available fields and looking at them. It is a small component which can be easily missed. The “Display Name” must also be the same GUID. So you might be asking how do we get the GUID?

Well, PowerShell can easily handle this for you.

$guid = [System.Guid]::NewGuid().ToString()

You simply use this a pre-cursor step and publish this data from “Run .Net Script”, so a sample would look like this.

2014-04-03_07-58-27

Get-Object Properties

I pass on the ID Value I want to modify from another Runbook

2014-04-03_07-58-58

Create Related Object Properties

I use an “Extended” class of comments needed for this project, principal remains the same.

2014-04-03_08-01-08

I hope this saves  you some time and add some value.

 

(E-Mail me)

 

Follow me,

 

Twitter (Personal & System Centre)

 Twitter (System Centre Focused)

So recently, I had a customer ask me for the following as their previous incident management system had this functionality.

The customer wanted to be able to send an “Update Email” to a 3rd party from the SCSM Console with automatically placing the reference number into said email. They needed the functionality of sending to any email address. This was a requirement for the type of business they are in.

Please remember that I am by no means a developer. I was thinking of using System Centre Orchestrator, however for this simple requirement, I thought that it might be overkill. I am sure that in time, as the requirements grows and/or changes, System Centre Orchestrator will become a better fit. For this solution, I turned to my friend, PowerShell

I simply wanted to provide a clean interface for the customer to use. I follow a strict rule of KISS (Keep It Straight and Simple). With the PowerShell script, I provide two input fields.

  1. Email address of user for update to go to
  2. Update to be sent to customer.

The screenshot of what the window looks like

All of this was built in PowerShell with some from Sapien PrimalForms Community Edition to assist with the GUI part. The GUI is fully functional and tested. “Send Update” is responsible for sending the update to the specified person and the “Cancel” simply closes the form nicely and allows you to continue as normal.

PowerShell Code Below (SendEmail – Technet Gallery Download)


#region Script Settings
#<ScriptSettings xmlns="http://tempuri.org/ScriptSettings.xsd">
#  <ScriptPackager>
#    <process>powershell.exe</process>
#    <arguments />
#    <extractdir>%TEMP%</extractdir>
#    <files />
#    <usedefaulticon>true</usedefaulticon>
#    <showinsystray>false</showinsystray>
#    <altcreds>false</altcreds>
#    <efs>true</efs>
#    <ntfs>true</ntfs>
#    <local>false</local>
#    <abortonfail>true</abortonfail>
#    <product />
#    <version>1.0.0.1</version>
#    <versionstring />
#    <comments />
#    <company />
#    <includeinterpreter>false</includeinterpreter>
#    <forcecomregistration>false</forcecomregistration>
#    <consolemode>false</consolemode>
#    <EnableChangelog>false</EnableChangelog>
#    <AutoBackup>false</AutoBackup>
#    <snapinforce>false</snapinforce>
#    <snapinshowprogress>false</snapinshowprogress>
#    <snapinautoadd>2</snapinautoadd>
#    <snapinpermanentpath />
#    <cpumode>1</cpumode>
#    <hidepsconsole>false</hidepsconsole>
#  </ScriptPackager>
#</ScriptSettings>
#endregion

 param($ID)
Import-Module 'C:\Program Files\Common Files\SMLets\SMLets.psd1' -Force

 #Generated Form Function
function GenerateForm {
########################################################################
# Code Generated By: SAPIEN Technologies PrimalForms (Community Edition) v1.0.10.0
# Generated On: 2013-11-14 09:08 AM
# Generated By: Fletcher
########################################################################

 #region Import the Assemblies
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
#endregion

 #region Generated Form Objects
$SendUpdateForm = New-Object System.Windows.Forms.Form
$CancelButton = New-Object System.Windows.Forms.Button
$SendUpdateButton = New-Object System.Windows.Forms.Button
$UpdateTextBox = New-Object System.Windows.Forms.TextBox
$UpdateLabel = New-Object System.Windows.Forms.Label
$EmailToTextBox = New-Object System.Windows.Forms.TextBox
$EMailToLabel = New-Object System.Windows.Forms.Label
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
#endregion Generated Form Objects

 #----------------------------------------------
#Generated Event Script Blocks
#----------------------------------------------
#Provide Custom Code for events specified in PrimalForms.
$SendUpdateButton_OnClick=
{
        Write-Host "Send Update"
        $to = $EmailToTextBox.Text
        Write-Host $to
        $from = "Servicedesk@bui.co.za"
        $emailbody = $UpdateTextBox.Text
$subject = @"
Update [ID]
"@
$body = @"
$emailbody
"@
        #$subject = "Update [ID]"
        #Write-Host $subject
        #Write-Host $body
        $smtpserver = "buiexc20.bui.co.za"
        #[string]$body = $UpdateTextBox.Text
        #$testcommand = "Send-MailMessage -To $to -From $from -Subject $messagencontent -Body '$body' -SmtpServer $smtpserver"
        Send-MailMessage -To $to -From $from -Subject $subject -Body $body -SmtpServer $smtpserver
        #Write-Host $testcommand
        $SendUpdateForm.Close()

 }

 $CancelButton_OnClick=
{
#TODO: Place custom script here
$SendUpdateForm.Close()
}

 $OnLoadForm_StateCorrection=
{#Correct the initial state of the form to prevent the .Net maximized form issue
    $SendUpdateForm.WindowState = $InitialFormWindowState
}

 #----------------------------------------------
#region Generated Form Code
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 323
$System_Drawing_Size.Width = 409
$SendUpdateForm.ClientSize = $System_Drawing_Size
$SendUpdateForm.DataBindings.DefaultDataSourceUpdateMode = 0
$SendUpdateForm.Name = "SendUpdateForm"
$SendUpdateForm.Text = "Send Update"

 
 $CancelButton.DataBindings.DefaultDataSourceUpdateMode = 0

 $System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 182
$System_Drawing_Point.Y = 264
$CancelButton.Location = $System_Drawing_Point
$CancelButton.Name = "CancelButton"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 124
$CancelButton.Size = $System_Drawing_Size
$CancelButton.TabIndex = 5
$CancelButton.Text = "Cancel"
$CancelButton.UseVisualStyleBackColor = $True
$CancelButton.add_Click($CancelButton_OnClick)

 $SendUpdateForm.Controls.Add($CancelButton)

 
 $SendUpdateButton.DataBindings.DefaultDataSourceUpdateMode = 0

 $System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 35
$System_Drawing_Point.Y = 264
$SendUpdateButton.Location = $System_Drawing_Point
$SendUpdateButton.Name = "SendUpdateButton"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 23
$System_Drawing_Size.Width = 116
$SendUpdateButton.Size = $System_Drawing_Size
$SendUpdateButton.TabIndex = 4
$SendUpdateButton.Text = "Send Update"
$SendUpdateButton.UseVisualStyleBackColor = $True
$SendUpdateButton.add_Click($SendUpdateButton_OnClick)

 $SendUpdateForm.Controls.Add($SendUpdateButton)

 $UpdateTextBox.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 35
$System_Drawing_Point.Y = 135
$UpdateTextBox.Location = $System_Drawing_Point
$UpdateTextBox.Multiline = $True
$UpdateTextBox.Name = "UpdateTextBox"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 103
$System_Drawing_Size.Width = 290
$UpdateTextBox.Size = $System_Drawing_Size
$UpdateTextBox.TabIndex = 3

 $SendUpdateForm.Controls.Add($UpdateTextBox)

 $UpdateLabel.DataBindings.DefaultDataSourceUpdateMode = 0

 $System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 35
$System_Drawing_Point.Y = 110
$UpdateLabel.Location = $System_Drawing_Point
$UpdateLabel.Name = "UpdateLabel"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 22
$System_Drawing_Size.Width = 359
$UpdateLabel.Size = $System_Drawing_Size
$UpdateLabel.TabIndex = 2
$UpdateLabel.Text = "Please enter the information to be sent to intended recipient:"

 $SendUpdateForm.Controls.Add($UpdateLabel)

 $EmailToTextBox.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 35
$System_Drawing_Point.Y = 54
$EmailToTextBox.Location = $System_Drawing_Point
$EmailToTextBox.Name = "EmailToTextBox"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 20
$System_Drawing_Size.Width = 290
$EmailToTextBox.Size = $System_Drawing_Size
$EmailToTextBox.TabIndex = 1

 $SendUpdateForm.Controls.Add($EmailToTextBox)

 $EMailToLabel.DataBindings.DefaultDataSourceUpdateMode = 0

$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 35
$System_Drawing_Point.Y = 27
$EMailToLabel.Location = $System_Drawing_Point
$EMailToLabel.Name = "EMailToLabel"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Height = 24
$System_Drawing_Size.Width = 439
$EMailToLabel.Size = $System_Drawing_Size
$EMailToLabel.TabIndex = 0
$EMailToLabel.Text = "Please enter the email address of the intended recipient:"

 $SendUpdateForm.Controls.Add($EMailToLabel)

 #endregion Generated Form Code

 #Save the initial state of the form
$InitialFormWindowState = $SendUpdateForm.WindowState
#Init the OnLoad event to correct the initial state of the form
$SendUpdateForm.add_Load($OnLoadForm_StateCorrection)
#Show the Form
$SendUpdateForm.ShowDialog()| Out-Null

 } #End Function

 #Call the Function
GenerateForm

[/code]

Ok, so that is the PowerShell side done. Now, it is time to create the console task. This is a fairly straight forward exercise. There are only a few tips I would like to share. I used a shared folder for the scripts and then mapped the console task to the shared folder. Netivia Consulting has a great idea as well, I will be changing to PowerShell Remoting soon, great idea!!!!!

I have configured the PowerShell script to use a parameter, the ID of the Work Item, this is very easy to configure and ensure that the parameter is passed on. See below.

You will see that I used the “Log in action log when this task is run”. That is where the Write-host in the PowerShell script becomes handy, the write-host lines are added to the Action Log.

Please feel free to use and modify to meet your needs.

(E-Mail me)

Follow me.

Facebook (Personal)

Twitter (Personal & System Centre)

Twitter (System Centre Focused)

So, I am quite a keen user of PowerShell and System Centre Orchestrator. Ok, so I recently had a request from a customer that went as follows “I need DPM to alert me when tapes are ready to  be taken off-site.”

DPM does not seen to have this functionality out of the box within the Graphical User Interface (GUI). All is NOT lost, Data Protection Manager has a PowerShell component and i was sure i could figure out a way to get this to work within PowerShell. Sure enough a solution in the form of PowerShell was found, so using this as a base, I modified the PowerShell script to suit my needs. It was also slightly modified to use with System Centre Orchestrator. The custoemr also wanted some colour and wanted the result formatted in HTML. primarily for reading purposes and auditing purposes. PowerShell code below and link here.

Import-Module DataProtectionManager
$date = Get-Date -Format "M-d-yyyy"
$path = "c:\DPMOffisteTapes\"
$filepath = $path + $date + ".html"
#creates the folder if it does not exist
New-Item -itemType file -Path $filepath
$AllLibraries = get-dpmlibrary 
$AllTapes = Get-Tape -DPMLibrary $AllLibraries
$offistereadytape = $AllTapes | Where-Object {$_.isoffsiteready -eq $true}
$a = "<style>"
$a = $a + "BODY{background-color:Green;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:GreenYellow}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:palegoldenrod}"
$a = $a + "</style>"
$offistereadytape | Select-object location, barcode , isoffsiteready|ConvertTo-Html -Head $a -Body "<H2>Offsite Ready Tapes</H2>" | out-file -FilePath $filepath

OK, so now we have the data in decent format.

image

So, with the more tapes that are ready, the more rows of data will be within the HTML.

Ok, so now we need to schedule the task. I know i could have used Task Scheduler and the like, however we have System Centre Orchestrator and with Service Management Automation available, more and more tasks will be automated. With this example, i decided to use Orchestrator.

So, I simply use a Monitor/Date Time to run the required activity at set intervals

image

I then run a DPM Script from the Data Protection Manager Integration Pack.

image

image

The filepath Variable is where the html (the full path to the html file) is written, this is especially helpful when sending the Email Message. (I know this can be done with send-mailmessage directly within the PowerShell code)

image

So there you have it, Some Orchestrator Magic with PowerShell and DPM.

(E-Mail me)

Follow me.

Facebook (Personal)

Twitter (Personal & System Centre)

Twitter (System Centre Focused)

So, one of the consultants in the company I work at came to me with a strange request.

The request from the customer he was helping was the following. They wanted a way to connect to all the switches within their environment and then run some commands against the switch after connecting to the switch on port 23. Once the connection has been made, then some commands are run to get the current running configuration. Then export this to a text file.

So I found a basic script here. I have since taken this and modified and extended the script. I have added an array for the IPs to be checked. I have also added some checking prior to running any commands. I have added some testing.

First, we test the connection and see if the device is responding to pings. If the device is responding to pings, then test and see if the device is configured for telnet. So basically I am looking to see if port 23 is open. If port 23 is not opened, a log file is updated with the IP Address. If the device is not responding to pings, a log file is updated stating the IP of the “non-responding” device.

As an addition to the script, the files are then added to an e-mail which is sent to whomever you want to add. As part of the email process, a check is done and if an error is thrown, there is more information presented in the console to point you in the correct direction.


## PowerShell: Script To Telnet To Remote Hosts And Run Commands Against Them With Output To A File ##
## Overview: Useful for Telnet connections to Cisco Switches and other devices. Can add additional command strings
## Usage Examples: Add your environment specific details into the parameters below, or include when calling the script:
## ./PowerShellTelnetRemoteSession.ps1 "127.0.0.1" "23" "admin" "password" "term len 0" "en" "enablepassword" "show interface"
 
param(
    #[string] $remoteHost = "172.31.0.111", 
    #[int] $port = 23,
    [string] $username = "admin",
    [string] $password = "",
    #[string] $termlength = "term len 0", #Useful for older consoles that have line display limitations
    [string] $enable = "en", #Useful for appliances like Cisco switches that have an 'enable' command mode
    [string] $enablepassword = "",
    [string] $command1 = "show run", #You can add additional commands below here with additonal strings if you want
    [string] $command2 = " ",
    [string] $command3 = " ",
    [string] $command4 = " ",
    [string] $command5 = " ",
    [int] $commandDelay = 1000
   )
 
[string] $output = ""
 
## Read output from a remote host
function GetOutput
{
  ## Create a buffer to receive the response
  $buffer = New-Object System.Byte[] 4096
  $encoding = New-Object System.Text.AsciiEncoding
 
  $outputBuffer = ""
  $foundMore = $false
 
  ## Read all the data available from the stream, writing it to the
  ## output buffer when done.
  do
  {
    ## Allow data to buffer for a bit
    Start-Sleep -m 1000
 
    ## Read what data is available
    $foundmore = $false
    $stream.ReadTimeout = 1000
 
    do
    {
      try
      {
        $read = $stream.Read($buffer, 0, 1024)
 
        if($read -gt 0)
        {
          $foundmore = $true
          $outputBuffer += ($encoding.GetString($buffer, 0, $read))
        }
      } catch { $foundMore = $false; $read = 0 }
    } while($read -gt 0)
  } while($foundmore)
 
  $outputBuffer
}
 
function Main
{
    param ($ip,$port)
  ## Open the socket, and connect to the computer on the specified port
 
  Write-Host "Connecting to $ip on port $port"


  $socket = New-Object System.Net.Sockets.TcpClient($ip, $port)
  
 
  Write-Host "Connected. Press ^D followed by [ENTER] to exit.`n"
 
  $stream = $socket.GetStream()
 
  $writer = New-Object System.IO.StreamWriter $stream
 
    ## Receive the output that has buffered so far
    $SCRIPT:output += GetOutput
 
        $writer.WriteLine($username)
        $writer.Flush()
        Start-Sleep -m $commandDelay
                $writer.WriteLine($password)
        $writer.Flush()
        #Start-Sleep -m $commandDelay
                #$writer.WriteLine($termlength)
        #$writer.Flush()
        Start-Sleep -m $commandDelay
                $writer.WriteLine($enable)
        $writer.Flush()
        Start-Sleep -m $commandDelay
                $writer.WriteLine($enablepassword)
        $writer.Flush()
        Start-Sleep -m $commandDelay
                $writer.WriteLine($command1) #Add additional entries below here for additional 'strings' you created above
        $writer.Flush()
        Start-Sleep -m $commandDelay
                $writer.WriteLine($command2) #Add additional entries below here for additional 'strings' you created above
        $writer.Flush()
                Start-Sleep -m $commandDelay
                $writer.WriteLine($command3) #Add additional entries below here for additional 'strings' you created above
        $writer.Flush()
                Start-Sleep -m $commandDelay
                $writer.WriteLine($command4) #Add additional entries below here for additional 'strings' you created above
        $writer.Flush()
                Start-Sleep -m $commandDelay
                $writer.WriteLine($command5) #Add additional entries below here for additional 'strings' you created above
        $writer.Flush()
        Start-Sleep -m $commandDelay
        $SCRIPT:output += GetOutput
                
 
 
  ## Close the streams
  $writer.Close()
  $stream.Close()

  #building file name
  $filename = "c:\switch\$ip - " + $timestamp + ".txt"
 
  $output | Out-File $filename  #Change this to suit your environment
}

#build the list of ips to be interegated 
$ips = "192.168.1.2","169.254.2.2","192.168.1.101"

#building a file path
$timestamp = get-date -Format g | foreach {$_ -replace ":","."}

#building different file paths for different functions
$errortimestamp = Get-Date -Format o | foreach {$_ -replace ":", "."}
$errorfilename = "c:\switch\" + $timestamp + " - ERROR.txt"

#building different file paths for different functions
$blockedtimestamp = Get-Date -Format o | foreach {$_ -replace ":", "."}
$blockedfilename = "c:\switch\" + $timestamp + " - blocked.txt"

#looping through each ip in ip list
foreach ($ip in $ips)
{
    Write-host "Testing $ip"
    #test to see if device is responding to pings
	if (Test-Connection $ip -Quiet)
    {
        #creating connection on port 23
		$t = New-Object Net.Sockets.TcpClient
		$t.Connect($ip, 23)

        #if it connects, runs the required function with the ip
		if ($t.Connected)
		{
			. Main $ip "23"
		}
        #script block for device responding to ping, but port 23 is NOT open
		else
		{
			$portblocked = "Port 23 on $ip is closed , You may need to contact your IT team to open it. "
            Add-Content $blockedfilename "`n$portblocked"
		}
    }
    #script block for NO RESPONSE
	else 
	    {
    	$errorfilename = "c:\switch\" + $timestamp + " - ERROR.txt" 
        Add-Content $errorfilename "`nCould not ping $ip" 
	    }
}

#displaying information on console
Write-Host "Build file list" -NoNewline
#getting file list to be emailed
$files = Get-ChildItem C:\Switch\ | Where {-NOT $_.PSIsContainer} | foreach {$_.fullname}
#pausing script
Start-Sleep 3
Write-Host "`t" [File List Built] -ForegroundColor "Green"

Write-Host "Sending Email" -NoNewline

#building checks for sending emails

#no error

#replace as needed
$to = "<to>"
$from = "<from>"
$smtpserver = "<smtpserver>"

try
{
    Send-MailMessage -Attachments $files -to $to -from $from -Subject "Network Config backup - $timestamp" -SmtpServer $smtpserver -ErrorAction Stop
    Write-Host "`t [Email Sent]" -ForegroundColor "Green"
}
#error
catch
{
    $ErrorMessage = $_.Exception.Message
    #$ErrorMessage
    if ($ErrorMessage -ne $null)
    {
        Write-Host "`t [Unable to send Mail]" -ForegroundColor "Red"
        Write-Host "There was an error: $ErrorMessage" -ForegroundColor "Yellow"
    }
}

Start-Sleep 3
Write-Host "Removing Files" -NoNewline
$files | Remove-Item
Write-Host "`t [Files removed]" -ForegroundColor "Green"


(E-Mail me)

Follow me.

Facebook (Personal)

Twitter (Personal & System Centre)

Twitter (System Centre Focused)

I have found a great for load testing SCSM with Incidents. The script uses SMLets to generate Incidents and you can then gauge how your system would handle the load you are planning within your Production Environment.

I will not go into the detail of the script as this blog post does a great job of explaining it. SO this got me to thinking about other Load Testing you could perform. The most common Load Test is with Incidents. However, i decided that another good load test would be Service Requests. What is nice about this load test, is that it uses a Management Pack which I have included in this post. The Management Pack contains a Service Request Template with a Manual Activity added as the first Activity. This will allow you to test Service Requests and well as Activities, which would in turn, test any workflows or runbooks that you have configured.

For my environment, i have additional runbooks which are also triggered when Manual Activities are completed or when Review Activities are completed. So with this Load Test script it also test my runbooks and other workflows.

Remember, to set a different Implementer for the Manual Activity to allow the runbooks or workflows to be triggered off correctly. Remember, no Manual Activity Implementer or if the implementer has no email address, your emails will not be triggered correctly. Below, is a breakdown of the script I am using.

Script

#SMLets is needed for the script to work
Import-Module SMlets

#the number of Service requests to be created
$SRCount = 10

#the delay between attempts
$delay = 10

# Display Start Time
$StartTime = get-date
Write-Host "Started"
Write-Host $StartTime
Write-Host "-------------------`n"
Write-Host "Creating Service Request"
Write-Host "-------------------"
$i =1

while ($i -le $SRCount)
{

#Variable / Class Setup
$srClass = Get-SCSMClass -name System.WorkItem.ServiceRequest$
$srprior = Get-SCSMEnumeration -Name ServiceRequestPriorityEnum.Medium
$srurg = Get-SCSMEnumeration -Name ServiceRequestUrgencyEnum.Medium
$ararea = get-SCSMEnumeration -Name ServiceRequestAreaEnum.Other

#building your own SR Title
$title = "Fletcher Load Testing $i - " + (get-date)

#Service Request Arguements
$srargs = @{
Title = $title;
Urgency = $srurg;
Priority = $srprior;
ID = “SR{0}”;
Area = $ararea;
Description = “Created by Script”
}

#Create Service Request
New-SCSMOBject -Class $srClass -PropertyHashtable $srargs

#Get Projection &amp; Object for Created Service Request
$srTypeProjection = Get-SCSMTypeProjection -name System.WorkItem.ServiceRequestProjection$
$SRProj = Get-scsmobjectprojection -ProjectionName $srTypeProjection.Name -filter “title -eq $title”

#Get Template
$template = Get-SCSMObjectTemplate -DisplayName "Load Test SR Template"

#Set Template
Set-SCSMObjectTemplate -Projection $SRProj -Template $template
$SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -filter "Title -eq $title"
$SRName = $SR.name
$SRTitle = $SR.Title
Write-Host $SRName " - " $SRTitle
#$SRName

# Pause before creating next SR
Start-Sleep -s 5

#getting the above created Service Request
$ServiceRequestToUpdate = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "Id -eq $SRName"

#get the Realted Child Manual Activity
$ChildActivities = (Get-SCSMRelationshipObject -BySource $ServiceRequestToUpdate | ?{$_.RelationshipID -eq "2DA498BE-0485-B2B2-D520-6EBD1698E61B"})

$get the ID Value
$MAIDToUpdate = $ChildActivities.targetobject.values[9].value

#getting the MA Class
$Class = Get-SCSMClass -Name System.WorkItem.Activity.ManualActivity

#getting the "Active" Status
$StatusActive = Get-SCSMEnumeration -name ActivityStatusEnum.Active

$Activity = Get-SCSMObject -Class $Class | ? {$_.ID -eq $MAIDToUpdate}

#setting MA to Active
Set-SCSMObject -SMObject $Activity -Property "Status" -Value $StatusActive

#showing current status
Write-host $MAIDToUpdate " is now " $StatusActive
$i +=1
Start-Sleep -s $delay
}

# Display End Time
Write-Host "_______________________`n"
$EndTime = get-date
Write-Host = "Finished"
Write-Host $EndTime
#End of Script
#

The script can also be downloaded from the Technet Gallery here.

(E-Mail me)

Follow me.

Facebook (Personal)

Twitter (Personal & System Centre)

Twitter (System Centre Focused)