Thursday, March 31, 2016

OpsMgr (SCOM) - Powershell Event Views Dashboard

I don't like the idea to have lot's of console connecting to my Management Servers, so i give my clients the webconsole link.
But, as you migh know, there's a bunch of limitiations, like "Event Views" don't show up.
So, i had the need to overcome this issue.

Solution was to put Powershell in a SCOM Dashboard.

First, create a new Powershell Grid Layout "Dashboard View" with one cell.
Configure it and paste this code :

 # This example is for a Rule i have for unexpected restart/shutdowns (EventID = 1074)  
 # You can change as you want!  
 $a = Get-SCOMManagementGroup  
 $b = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringEventCriteria "RuleId='e7c857e6-7654-5f89-ecdf-8f93325c83ee'"  
 $Events = $a.GetMonitoringEvents($b)  
 $i = 0  
 foreach ($Event in $Events) {  
   $EventDescription = 'User : ' + [string]$Event.Parameters[6] + ' || Type : ' + [string]$Event.Parameters[4] + ' || Reason : ' + [string]$Event.Parameters[5]  
   $TimeAdded = $Event.TimeAdded  
   $LoggingComputer = [string]$Event.LoggingComputer  
   $dataObject = $ScriptContext.CreateInstance("xsd://foo!bar/baz")  
   $dataObject["Id"]=$i.toString()  
   $dataObject["TimeAdded"]=$TimeAdded  
   $dataObject["LoggingComputer"]=$LoggingComputer  
   $dataObject["Description"]=$EventDescription  
   $ScriptContext.ReturnCollection.Add($dataObject)  
   $i++  
 }  

:) Enjoy!

Wednesday, March 30, 2016

OpsMgr (SCOM) - Unix/Linux Agents Requisites and Troubleshooting

UNIX/Linux Monitoring/Discovery in OpsMgr can be very hard to troubleshoot sometimes.
You can have several discovery issues, like:
- SSH connection erros;
- Certificate Issues;
- Network port issues;
- Bad SUDO permissions;
- And so on.

Since there's a lot of information spreaded in several blogs, but i've never founded a centralized source of troubleshooting steps about this thread, i've decided to create this post/thread to continually update it with found errors and related resolution.

First, check if you have any of this errors documented on TechNet :
http://social.technet.microsoft.com/wiki/contents/articles/4966.scom-2012-troubleshooting-unixlinux-agent-discovery.aspx

If not, you can read further :)

In first place i'll leave all the pre-requisites you'll need to have on your environment to make it work perfectly.

First, you need a user!
root or with SUDO permissions ?
If your UNIX/Linux SysAdmin wants to limit your 'sudoers' file, this is what you need :
(root) NOPASSWD: /bin/sh -c cp /tmp/scx-monuser/scx.pem /etc/opt/microsoft/scx/ssl/scx.pem; rm -rf /tmp/scx-monuser; /opt/microsoft/scx/bin/tools/scxadmin -restart  
(root) NOPASSWD: /bin/sh -c sh /tmp/scx-monuser/GetOSVersion.sh; EC\=$?; rm -rf /tmp/scx-monuser; exit $EC  
(root) NOPASSWD: /bin/sh -c cat /etc/opt/microsoft/scx/ssl/scx.pem  
(root) NOPASSWD: /bin/sh -c rpm -e scx  
(root) NOPASSWD: /bin/sh -c /bin/rpm -F --force /tmp/scx-monuser/scx-*.rpm; EC\=$?; cd /tmp; rm -rf /tmp/scx-monuser; exit $EC  
(root) NOPASSWD: /bin/sh -c /bin/rpm -U --force /tmp/scx-monuser/scx-*.rpm; EC\=$?; cd /tmp; rm -rf /tmp/scx-monuser; exit $EC  
(root) NOPASSWD: /opt/microsoft/scx/bin/scxlogfilereader -p  
# I've added this so you can re-generate certificates if you need to  
(root) NOPASSWD: /opt/microsoft/scx/bin/tools/scxsslconfig *  

If not, you'll just need :
monuser ALL=(ALL) NOPASSWD: ALL
Remind that you need to comment out the line :
#Defaults !requiretty
so it needs to be as :
Defaults !requiretty

Next, you need to ensure you've TCP/IP port connection from your UNIX/Linux Resource Pool Servers to your UNIX/Linux servers on ports 22 and 1270.

Other thing you might need to is to re-generate your SCXAgent certificate.
Some companies have 2 different FQDN's for the same server so it can respond in a different network device (management network device instead of the service network device), so if you're discovering your server by that particular management FQDN, the certificate needs to be generated with the FQDN you're discovering the server with.
Eg.
You have server myserver.mydomain.com to discover
The management FQDN is myserver.mymngtdomain.com
You'll be discoverying your server by myserver.mymngtdomain.com
So, you need to ensure that the certificate is generated to it.
Login by SSH into myserver.mymngtdomain.com and run :
openssl x509 -noout -in /etc/opt/microsoft/scx/ssl/scx.pem -subject -issuer -dates  
If it's not the FQDN you want, run :
sudo /opt/microsoft/scx/bin/tools/scxsslconfig -f -h myserver -d mymngtdomain.com
So, the errors you can come across with are :

1) WinRM cannot complete the operation.
    Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enable and allows access from this computer.
   By default, the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet.
2) Agent verification failed. Error detail: The server certificate on the destination computer (SERVER_FQDN:1270) has the following errors
3) DNS Configuration error:
    The provided hostname SERVER_FQDN resolved to the IP address of x.x.x.x.
    The hostname SERVER_FQDN returned by reverse lookup of the IP address x.x.x.x did not match the provided hostname.
    Verify the DNS configuration and try the request again.
4) sudo: no tty present and no askpass program specified
5) The agent responded to the request but the WSMan connection failed due to: Access is Denied.”

# 1) WinRM cannot complete the operation.

Verify that your FQND is correct;
You have TCP/IP connection with your server in 22 and 1270 ports;
You might use this PS1 script from one of your UNIX/Linux Resource Pool servers to check :
$list = Get-Content -path 'Path_to_ServerList'
Foreach ($server in $list) {
    $SSHStatus = (new-object System.Net.Sockets.TcpClient("$server","22")).connected
    $MNGTStatus = (new-object System.Net.Sockets.TcpClient("$server","1270")).connected
    "$server | $SSHStatus | $MNGTStatus"
}
# 2) Agent verification failed. Error detail: The server certificate on the destination computer (SERVER_FQDN:1270) has the following errors

The certificate is not compliant with the FQDN you're discoverying the server with.
For example :
You have server myserver.mydomain.com to discover
The management FQDN is myserver.mymngtdomain.com
You'll be discoverying your server by myserver.mymngtdomain.com
So, you need to ensure that the certificate is generated to it.
Login by SSH into myserver.mymngtdomain.com and run :
openssl x509 -noout -in /etc/opt/microsoft/scx/ssl/scx.pem -subject -issuer -dates
If it's not the FQDN you want, run :
sudo /opt/microsoft/scx/bin/tools/scxsslconfig -f -h myserver -d mymngtdomain.com
If you've several UNIX/Linux servers in this condition, use this ShellScript i've made to correct it :
(I personal use MobaXTerm to do such things)
#!/bin/sh
for i in `cat list`
do
    a=`echo $i | cut -d"." -f1`
    ssh monuser@$i "sudo /opt/microsoft/scx/bin/tools/scxsslconfig -f -h $a -d mymngtdomain.com"
done
# 3) DNS Configuration error.

You might need to correct name resolution configuration for forward and reverse lookup on your DNS server.

# 4) sudo: no tty present and no askpass program specified

Well, this is a sudo problem.
Check if you have either this in (/etc/sudoers) :
monuser ALL=(ALL) NOPASSWD: ALL
or this in your sudoers file:
(root) NOPASSWD: /bin/sh -c cp /tmp/scx-monuser/scx.pem /etc/opt/microsoft/scx/ssl/scx.pem; rm -rf /tmp/scx-monuser; /opt/microsoft/scx/bin/tools/scxadmin -restart
(root) NOPASSWD: /bin/sh -c sh /tmp/scx-monuser/GetOSVersion.sh; EC\=$?; rm -rf /tmp/scx-monuser; exit $EC
(root) NOPASSWD: /bin/sh -c cat /etc/opt/microsoft/scx/ssl/scx.pem
(root) NOPASSWD: /bin/sh -c rpm -e scx
(root) NOPASSWD: /bin/sh -c /bin/rpm -F --force /tmp/scx-monuser/scx-*.rpm; EC\=$?; cd /tmp; rm -rf /tmp/scx-monuser; exit $EC
(root) NOPASSWD: /bin/sh -c /bin/rpm -U --force /tmp/scx-monuser/scx-*.rpm; EC\=$?; cd /tmp; rm -rf /tmp/scx-monuser; exit $EC
(root) NOPASSWD: /opt/microsoft/scx/bin/scxlogfilereader -p
# I've added this so you can re-generate certificates if you need to
(root) NOPASSWD: /opt/microsoft/scx/bin/tools/scxsslconfig *
and if you've this line commented out in (/etc/sudoers) :
#Defaults !requiretty
# 5) The agent responded to the request but the WSMan connection failed due to: Access is Denied.”

  If you get the "The agent responded to the request but the WSMan connection failed due to: Access is Denied.”" error, first, from one of your UNIX/Linux Resource Pool, run :
Test-WSMan -Port 1270 -ComputerName “ServerName” -Authentication Basic -Credential (Get-Credential) -UseSSL
If you got an error, you might need to edit your pam file (/etc/pam.d/omi) and leave it like this :
omi auth sufficient pam_vas3.so create_homedir get_nonvas_pass store_creds try_first_pass
omi auth requisite pam_vas3.so echo_return
omi auth required /usr/lib/security/pam_aix use_new_state use_first_pass
omi account required /usr/lib/security/pam_seos.o
omi account sufficient pam_vas3.so
omi account requisite pam_vas3.so echo_return
omi account required /usr/lib/security/pam_aix
Re-discover your agent, and you'll get it working!

Tuesday, March 29, 2016

OpsMgr (SCOM) - Network Resource Pool Members "Not Monitored"

Recently i disabled OMS on my environment.
But soon i had an issue that came up.
My network resource pool and my discovery servers (the same that are RP members) went grey.

I realized it was a wrong server configuration because i got lots of 1202 (New Management Pack with id:"ZXYZ", version:"A.B.C.D" conflicts with cached Management Pack. Condition indicates wrong server configuration) events on my servers.
I tried to follow this solution :
http://thoughtsonopsmgr.blogspot.pt/2013/07/getting-rid-of-eventid-1202-condition.html
More cores - No luck.
But then it got me thinking ... What if it's a pool/networking discovery problem caused by OMS de-integration ?
I've deleted the discovery rule, i deleted the RP, and created new ones, exactly the same as before, but new ones!
And, for my suprise, my management servers and RP went green again!

So, and for the record, i've got about 500 network devices to be discovered, and i don't know if it was a OMS thing or not that perhaps messed up with some networking configuration or not - But, it worked for me!

Perhpas no one is going to have this exact problem (i think...) but if someone do, hope this could help you out!

Friday, March 4, 2016

OpsMgr (SCOM) - Grey Agents

It's a problem of us all - true story.

But how can i easily get a list of them ?

Like this :

 New-SCOMManagementGroupConnection -ComputerName "localhost"  
 $Agents = Get-SCOMClass -name “Microsoft.SystemCenter.Agent"  
 $objects = Get-SCOMMonitoringObject -class:$Agents | where {$_.IsAvailable –eq $false}  
 ForEach ($object in $objects) {  
     $object.DisplayName  
 }  
My next step is to give you the reason! :)

SCCM - Security and Critical Updates | Datazen Dashboard

Recently i needed to provide customer a dashboard on the missing patches for every machines in the park - and because managers like the fancy datazen dashboards, and i also like the easy way we can build some dashboards - why not ?

First of all, created this datasource in Datazen CP :


 SELECT    dbo.v_R_System.Name0 AS 'Computername', dbo.v_UpdateInfo.Title AS 'Updatename', dbo.v_StateNames.StateName, dbo.v_UpdateInfo.InfoURL,  
         dbo.v_Update_ComplianceStatusAll.LastStatusCheckTime, dbo.v_UpdateInfo.DateLastModified, dbo.v_UpdateInfo.IsDeployed, dbo.v_UpdateInfo.IsSuperseded,   
         dbo.v_UpdateInfo.IsExpired, dbo.v_UpdateInfo.BulletinID, dbo.v_UpdateInfo.ArticleID, dbo.v_UpdateInfo.DateRevised,   
         catinfo.CategoryInstanceName as 'Vendor',  
     catinfo2.CategoryInstanceName as 'UpdateClassification',  
         COUNT(case when catinfo2.CategoryInstanceName like 'Security%' then '1' else NULL end ) IsSecurity,  
         COUNT(case when catinfo2.CategoryInstanceName like 'Critical%' then '1' else NULL end ) IsCritical  
 FROM    dbo.v_StateNames  
         INNER JOIN dbo.v_Update_ComplianceStatusAll  
         INNER JOIN dbo.v_R_System ON dbo.v_R_System.ResourceID = dbo.v_Update_ComplianceStatusAll.ResourceID  
         INNER JOIN dbo.v_UpdateInfo ON dbo.v_UpdateInfo.CI_ID = dbo.v_Update_ComplianceStatusAll.CI_ID ON dbo.v_StateNames.StateID = dbo.v_Update_ComplianceStatusAll.Status  
         INNER JOIN v_CICategories_All catall on catall.CI_ID = dbo.v_UpdateInfo.CI_ID  
         INNER JOIN v_CategoryInfo catinfo on catall.CategoryInstance_UniqueID = catinfo.CategoryInstance_UniqueID and catinfo.CategoryTypeName='Company'  
         INNER JOIN v_CICategories_All catall2 on catall2.CI_ID=dbo.v_UpdateInfo.CI_ID  
         INNER JOIN v_CategoryInfo catinfo2 on catall2.CategoryInstance_UniqueID = catinfo2.CategoryInstance_UniqueID and catinfo2.CategoryTypeName='UpdateClassification'  
 WHERE    (dbo.v_StateNames.TopicType = 500)  
 AND        (dbo.v_StateNames.StateName = 'Update is required')  
 AND        (dbo.v_R_System.Name0 IN   
           (SELECT TOP (100) PERCENT SD.Name0 AS 'Machine Name'  
             FROM    dbo.v_R_System AS SD INNER JOIN  
                     dbo.v_FullCollectionMembership AS FCM ON SD.ResourceID = FCM.ResourceID INNER JOIN  
                     dbo.v_Collection AS COL ON FCM.CollectionID = COL.CollectionID LEFT OUTER JOIN  
                     dbo.v_R_User AS USR ON SD.User_Name0 = USR.User_Name0 INNER JOIN  
                     dbo.v_GS_PC_BIOS AS PCB ON SD.ResourceID = PCB.ResourceID INNER JOIN  
                     dbo.v_GS_COMPUTER_SYSTEM AS CS ON SD.ResourceID = CS.ResourceID INNER JOIN  
                     dbo.v_RA_System_SMSAssignedSites AS SAS ON SD.ResourceID = SAS.ResourceID  
             ))  
 AND        ((catinfo2.CategoryInstanceName like 'Critical%' ) OR (catinfo2.CategoryInstanceName like 'Security%' ))  
 GROUP BY dbo.v_R_System.Name0 , dbo.v_UpdateInfo.Title, dbo.v_StateNames.StateName,   
         dbo.v_Update_ComplianceStatusAll.LastStatusCheckTime, dbo.v_UpdateInfo.DateLastModified, dbo.v_UpdateInfo.IsDeployed, dbo.v_UpdateInfo.IsSuperseded,   
         dbo.v_UpdateInfo.IsExpired, dbo.v_UpdateInfo.BulletinID, dbo.v_UpdateInfo.ArticleID, dbo.v_UpdateInfo.DateRevised,   
         catinfo.CategoryInstanceName, catinfo2.CategoryInstanceName, dbo.v_UpdateInfo.InfoURL  

Then created this dashboard :



The challange now is to make this possible to any group of collection we might want to.

I made a post about it ... give it a try:

http://itopstuff.blogspot.pt/2015/11/sccm-missing-updates-per-collection.html

:)

OpsMgr (SCOM) - Schedule Maintenance Mode

Scenario :
- On our company third-party CMDB Management Portal, people could register interventions on their servers, scheduling downtimes on their servers in SCOM, so it won't impact on KPI or Uptime.

Solution :
- Created a small database with a simple table.
- Created a PS1 that reads data from the above table and for every record that is not 'Processed' it schedules its maintenance on SCOM.

1) Create the database on your MSSQL Server and the table with the following schema :

 CREATE TABLE Scheduling (  
       ID int IDENTITY(1,1) PRIMARY KEY,  
       CI varchar(255),  
       Instance varchar(255),  
       Type varchar(255) NOT NULL,  
       Team varchar(255) NOT NULL,  
       StartTime SMALLDATETIME NOT NULL,  
       EndTime SMALLDATETIME NOT NULL,  
       Comment varchar(255) NOT NULL,  
       Status varchar(255) NOT NULL,  
 );  

2) Create two Runbooks like this :

2.1 ) InsertIntoDB
- Basicaly used for the Third-Party software to consume it as web-service so it can make new inserts in your MSSQL table.

 $SQLServer = "" #Your Server   
 $SQLDBName = "" #YourDBName  
 $connString = "Data Source=$SQLServer;Initial Catalog=$SQLDBName;Integrated Security = True"  
 $connection = New-Object System.Data.SqlClient.SqlConnection($connString)   
 $connection.Open()  
 $sqlcmd = $connection.CreateCommand()  
 # This Strange Characters will disapear when pasting it into Orchestrator :)  
 $SqlQuery = "INSERT INTO Scheduling (ci,citype,Team,StartTime,EndTime,Comment,Status) VALUES ('\`d.T.~Ed/{B65C878B-BF92-4D90-AEA1-E8E03B2813D6}.{27788CF1-0A35-47D4-AA0B-B06325A5C5D4}\`d.T.~Ed/','\`d.T.~Ed/{B65C878B-BF92-4D90-AEA1-E8E03B2813D6}.{B0B8E6F4-2245-4FA6-BB99-D626CCA011E7}\`d.T.~Ed/','\`d.T.~Ed/{B65C878B-BF92-4D90-AEA1-E8E03B2813D6}.{7E4E2E7D-6070-472F-AB9A-7331E13CD6CF}\`d.T.~Ed/','\`d.T.~Ed/{B65C878B-BF92-4D90-AEA1-E8E03B2813D6}.{35128BA4-9FB4-417F-BC9C-FE4F43EC884C}\`d.T.~Ed/','\`d.T.~Ed/{B65C878B-BF92-4D90-AEA1-E8E03B2813D6}.{6B2977E6-609B-4139-A3FD-CF7646C27C10}\`d.T.~Ed/','\`d.T.~Ed/{B65C878B-BF92-4D90-AEA1-E8E03B2813D6}.{B7CD14CD-6990-4ECD-9CAA-A5EDF9C6D73D}\`d.T.~Ed/','NOT PROCESSED');"  
 $sqlcmd.CommandText = $SqlQuery  
 $sqlcmd.CommandText = $SqlQuery  
 $results = $sqlcmd.ExecuteNonQuery()  

        2.2 ) OpsMgr MM
- Used to periodically put objects into maintenance.
- It selects unprocessed items with specific conditions.
- When processed it updates table with processed status so it wont process anymore.
         (Note) - I've got this small PS1 script before i start doing things - it's simple test connectivity to the database from the runbook server :

 If ( (new-object System.Net.Sockets.TcpClient("\`d.T.~Vb/{4D50CD67-31C0-48AA-A37E-2E017F6DC9F9}\`d.T.~Vb/","1433")).connected -eq $true ) {  
   $LOG = "DATABASE CONNECTION OK" } Else { $LOG = "DATABASE CONNECTION NOT OK" }  

This is the one that does the magic!

 try{
    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager.Common") | Out-Null
    [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.EnterpriseManagement.Core') | Out-Null
    [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.EnterpriseManagement.OperationsManager') | Out-Null
    [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.EnterpriseManagement.Runtime') | Out-Null
} Catch { "" }  

 try{
     $script:MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings('YOUR_OPS_MGR_SERVER')
     $script:MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)
 } Catch {  
     $Script:log += "Cannot connect to OpsMgr "  
     exit
 }
   
 $script:SQLServer = "MyMSSQLServer"  
 $script:SQLDBName = "DatabaseName"  
 $script:connString = "Data Source=$SQLServer;Initial Catalog=$SQLDBName;Integrated Security = True"  
 $script:connection = New-Object System.Data.SqlClient.SqlConnection($connString)  
 $script:Reason = [Microsoft.EnterpriseManagement.Monitoring.MaintenanceModeReason]::PlannedOther  
 $script:Transversal = [Microsoft.EnterpriseManagement.Common.TraversalDepth]::Recursive  
 $Script:log = "--------"  
 $script:class = ""  
 $ClassInstance = ""  
 $table = ""  
   
 Function SqlDataManagement {
    param($QueryType)
    Try { 
        $connection.Open()
    } Catch {
     $Script:log += "Cannot Open DB Connection"
     exit
    } # Open Database Connection 
    $sqlcmd = $connection.CreateCommand()
    If ($QueryType -eq 'update') {
        $SqlQuery = "UPDATE Scheduling SET Status = 'Processed' WHERE ID = $ID"
        $sqlcmd.CommandText = $SqlQuery
        $results = $sqlcmd.ExecuteNonQuery()
    } # Update Database
    If ($QueryType -eq 'update_not_found') {
        $SqlQuery = "UPDATE Scheduling SET Status = 'CI NOT FOUND' WHERE ID = $ID"
        $sqlcmd.CommandText = $SqlQuery
        $results = $sqlcmd.ExecuteNonQuery()
    } # Update Database
    If ($QueryType -eq 'select') {
        $SqlQuery = "SELECT * FROM Scheduling WHERE Status = 'Not Processed' AND DATEDIFF(MINUTE,GETDATE(),[StartTime]) BETWEEN -1 AND 0 AND DATEDIFF(MINUTE,[StartTime],[EndTime]) > 5"
        $sqlcmd.CommandText = $SqlQuery
        $results = $sqlcmd.ExecuteReader()
        $script:table = new-object “System.Data.DataTable”
        $script:table.Load($results)
    } # Select rows to manage
    $connection.Close()
}

Function Get-SCOMObjectbyClass([string]$ClassDisplayName,[string]$CI) {
    $script:ClassCriteria = New-Object Microsoft.EnterpriseManagement.Configuration.MonitoringClassCriteria("Name = '$ClassDisplayName'")
    $script:MonitoringClass = $MG.GetMonitoringClasses($ClassCriteria)
    $script:MOCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGenericCriteria("DisplayName LIKE '$CI%'")
    Try {
        $script:ClassInstance = ($MG.GetMonitoringObjects($MOCriteria, $MonitoringClass[0]))[0]
    } Catch { 
        $Script:log += "$CI not found or not belonging to $ClassDisplayName"
      }
}

Function Send-Email([string]$Status) { #Mail & HTML Stuff
    $Head = ""
    $Image = "C:\OpsMgr\MM\images\logo_detail.png"
    $att1 = new-object Net.Mail.Attachment($Image)
    $att1.ContentType.MediaType = “image/png”
    $att1.ContentId = “Attachment”
    $att1.ContentDisposition.Inline = $true
    $att1.ContentDisposition.DispositionType = “Inline”
    $body = "<img src='cid:Attachment' height='12%' width='12%'/><br/>"  
    $body += "<center><h5 style=color:#999999>SCOM - Schedule Maintenance Mode</center></h5>"
    If ( $Status -eq "OK" ) {
        $body += "CI       - $CI 
 "
        $body += "Inicio   - $StartTime 
"
        $body += "Fim      - $EndTime 
"
        $body += "Razão    - $Comment 
"
    } ElseIf ( $Status -eq "NOT OK" ) {
        $body += "CI       - $CI 
 "
        $body += ""
        $body += "Putting $CI in MM failed"
        $body += "Reason:
"
        $body += ""
        $body += "$LOG"
    }
    $smtpServer = "SMTP.SERVER"
    $smtpFrom = "FROM@ADDRESS.COM"
    $smtpTo = "TO@ADDRESS.COM"
    $messageSubject = "SCOM-ScheduleMaintenanceMode - $CI"
    $message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
    $message.Subject = $messageSubject
    $message.IsBodyHTML = $true
 $message.Attachments.Add($att1)
    $message.Body = ConvertTo-Html -Body $body -Head $head
    $smtp = New-Object Net.Mail.SmtpClient($smtpServer)
    $smtp.Send($message)
}
SqlDataManagement -QueryType select

foreach ( $i in $table ) {
    $script:StartTime = (Get-Date -date ($i.StartTime).ToString()).ToUniversalTime()
    $script:EndTime = ($StartTime.AddMinutes(($i.EndTime - $StartTime).TotalMinutes)).ToUniversalTime()   
    $script:Comment = $i.Comment
    $script:ID = $i.ID
    $script:Team = $i.Team
    $script:Type = $i.Type
    $script:CI = $i.CI
    switch ( $Type ) {
        "NetworkDevice" { $script:Class = 'System.NetworkManagement.Node' }
        "Computer"      { $script:Class = 'System.Computer' }
    } # Switch to check which object class type it is | Add many as you may like or need.
    Get-SCOMObjectbyClass -ClassDisplayName "$script:Class" -CI $script:CI
    If ( $ClassInstance -ne $null -and ($ClassInstance.InMaintenanceMode) -ne $true ) {
        try {
            $ClassInstance.ScheduleMaintenanceMode($StartTime,$EndTime,$Reason,$Comment,$Transversal)
   $ClassInstance = ($MG.GetMonitoringObjects($MOCriteria, $MonitoringClass[0]))[0]
            If ( $ClassInstance.InMaintenanceMode -eq $true ) {
                SqlDataManagement -QueryType update
                Send-Email -Status "OK"
            }
            Else { $Script:log +=  = "Failed to put $CI in MM."
                   Send-Email -Status "NOT OK"           
            }
        } # Object in Maintenance Mode
        Catch { 
            $Script:log += "Exception while putting $CI in MM :" + "$_.Exception.Message"
            Send-Email -Status "NOT OK"
        } 
    } Else { 
        $Script:log += "ClassInstance ($ClassInstance) Not Found or already in Maintenance" 
        SqlDataManagement -QueryType update_not_found
        Send-Email -Status "NOT OK"
      }
}

$SCOrchLog = $script:log
Runbook Design (Insert) :



Runbook Design (Process) :



Later i'll post on how to tell the third-party could "consume" the Insert into DB runbook as webservice.

:)

OpsMgr (SCOM) - Alert Views without any console ?

Recently i got the need to put "Alert Views" on 4 different Teams TV's.

My first though was ... "WebConsole can't do the job ..."
So i remembered that PS1 could save my day!

Cons:

- SCOM Web Console too slow;
- You need IE;
- ... and silverlight;

Solution :

- Created a PS1 that for every different group i want gets latest 24h alerts (Warn/Crit);
- Foreach group i create an HTML file and put it on my favourite Web-Server;
- Created a Runbook that for a 90 seconds schedule runs the PS1;
      - You can also have a Scheduled Task for the Job;
- HTML has a meta tag that makes HTML refresh every 30 seconds;


 Import-Module OperationsManager  
 New-SCOMManagementGroupConnection -ComputerName "SCOMSERVER_GOES_HERE"  
 $MyGroups = @()  
 Foreach ($item in Get-Content C:\OpsMgr\WebAlertViews\conf\Groups.conf ) {  # Dont Forget to change this!
     $MyGroups += Get-SCOMGroup -DisplayName $item  
 }  
 $newTime = (Get-Date).AddHours(-24)  
 $Criteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringAlertCriteria("ResolutionState = 0 AND Severity >= 1 AND TimeRaised > `'$newTime`'")  
 $TransversalDepth = [Microsoft.EnterpriseManagement.Common.TraversalDepth]::Recursive  
 Foreach ( $Group in $MyGroups ) {  
     $Head = "<meta http-equiv='refresh' content='30'>"  
     $Head +="<style>"  
     $Head +="BODY{background-color:White;font-family:Verdana,sans-serif; font-size: x-small;}"  
     $Head +="TABLE{font-family: verdana,arial,sans-serif; font-size:12px; color:#333333; border-width: 1px; border-color: #666666; border-collapse: collapse;}"  
     $Head +="TH{border-width: 1px; padding: 8px; border-style: solid; border-color: #666666; background-color: #dedede;}"  
     $Head +="TD{border-width: 1px; padding: 8px; border-style: solid; width:auto;}"  
     $Head +="</style>"  
     $Body = "<br><br>"  
     $Body += "<img src='.\images\nos_logo_detail.png' height='12%' width='12%'>"  
     $Body += "<center><h1 style=color:#999999>.: Relatório SCOM - Alert View | $(($Group).DisplayName) :.</center>"  
     $Body += "<center><table>"  
     $Body += "<tr>"  
     $Body += "<td>Severity</td>"  
     $Body += "<td>Time Raised</td>"  
     $Body += "<td>Path</td>"  
     $Body += "<td>Name</td>"  
     $Body += "<td>DisplayName</td>"  
     $Body += "<td>Description</td>"  
     $Body += "</tr>"  
     $Alerts = $Group.GetMonitoringAlerts( $Criteria, $TransversalDepth )  
     Foreach ($Alert in $Alerts ) {  
         If ($Alert.Severity -eq 2) { $image = 'critical.png' }  # You need this files
         If ($Alert.Severity -eq 1) { $image = 'warning.png' }  
         $Body += "<tr>"  
         $Body += "<center><td><img src='.\images\$Image' height='25px' width='25px'></td></center>"  
         $Body += "<td>$(($alert).TimeRaised)</td>"  
         $Body += "<td>$(($alert).MonitoringObjectPath)</td>"    
         $Body += "<td>$(($alert).Name)</td>"  
         $Body += "<td>$(($alert).MonitoringObjectDisplayName)</td>"  
         $Body += "<td>$(($alert).Description)</td>"  
         $Body += "</tr>"  
     }      
     $Body += "</table></center>"  
     # I got the above replace because of "Unix/Linux Group!"
     $HTMLFileName = (($Group.DisplayName -replace '/','_') -replace ' ','') + '.html'  
     $HTML = ConvertTo-Html -Head $head -Body $Body  
     $HTML > \\MyWebServer\SiteName\AlertViews\AlertView_$HTMLFileName    
 }