After some googling i didn't find any solution to put ADR Collection Members into Maintenance in OpsMgr, i started to code some powershell.
If you might remember my later post about "OpsMgr (SCOM) - Schedule Maintenance Mode" i used the same idea to put collection members into maintenance mode.
So before the powershell script that gives me all the ADR Collection Members i had to made some changes into my OpsMgr_MM database.
I've added a new collumn "ADR_ID".
To add it just run the following SQL query :
ALTER TABLE Scheduling
ADD ADR_ID varchar(100);
Also changed the runbook powershell script that puts the agents into maintenance to this :
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 { "" }
$script:SQLServer = "" # Your SQL Server
$script:SQLDBName = "OpsMgr_MM" # Your Database
$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 = ""
$script:ClassInstance = ""
$script:table = ""
# Connection to OpsMgr Management Group
try{
$MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings('Your_SCOM_SERVER')
$MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)
} Catch { ' ' }
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
The changes are :
- Function "SqlDataManagement" now accepts 'update_not_found' parameter for not found agents in OpsMgr
- Function "Get-SCOMObjectbyClass" now makes the criteria LIKE instead of = (SCCM agents are listed as HOSTNAME instead of the FQDN)
- If the agent is not found it also let you know sending you an e-mail.
So, since we've got it all done before running our new PS1 script, the script it self :
Import-Module "D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"
cd SITE_CODE:
$logFile = 'Your Path to SCCM_MM.log'
$MaintenanceWindowMode = 'Collection'
$SQLServer = "" #Your Server
$SQLDBName = "OpsMgr_MM" #YourDBName
$LimitDate = Get-Date
$AutoDeployRules = @()
Foreach ( $ADR in (Get-CMAutoDeploymentRule -Fast )) {
$ADRSchedule = (Convert-CMSchedule ($ADR.Schedule) | Select StartTime).StartTime
If ( $ADRSchedule -ge $LimitDate -and $ADR.LastRunTime -le $LimitDate) {
$AutoDeployRules += $ADR
}
Else { $ADR.Name + ' not reliable to Maintenance - Maintenance Window is in the past!' >> $logFile }
}
Foreach ( $ValidADR in $AutoDeployRules ) {
#ADR Info ----> SELECT NAME, Schedule
$ADRID = ($ValidADR.UniqueIdentifier).Guid
$ADRMaintenanceStart = (Convert-CMSchedule $ValidADR.Schedule).StartTime
$ADRName = $ValidADR.Name
# Check if ADR is already in Maintenance Mode Database #
$connString = "Data Source=$SQLServer;Initial Catalog=$SQLDBName;Integrated Security = True"
$connection = New-Object System.Data.SqlClient.SqlConnection($connString)
$connection.Open()
$sqlcmd = $connection.CreateCommand()
$SqlQuery = "set dateformat dmy ; SELECT * FROM Scheduling WHERE ADR_ID = '$ADRID' AND [StartTime] != '$(Get-Date $ADRMaintenanceStart -Format g)' ;"
$sqlcmd.CommandText = $SqlQuery
$result = $sqlcmd.ExecuteReader()
If ( $result.HasRows -eq $False ) {
If ( $MaintenanceWindowMode = "ADR" ) {
# <Duration>1</Duration><DurationUnits>Hours</DurationUnits>
[xml]$ADRDeploymentTemplate = $ValidADR.DeploymentTemplate
[Int32]$ADRDuration = $ADRDeploymentTemplate.DeploymentCreationActionXML.Duration
$ADRDurationUnits = $ADRDeploymentTemplate.DeploymentCreationActionXML.DurationUnits
# ADR Stop Maintenance Calculation
Switch ($ADRDurationUnits){
Hours { $ADRMaintenanceStop = (Get-Date $ADRMaintenanceStart).AddHours($ADRDuration) ; break }
Days { $ADRMaintenanceStop = (Get-Date $ADRMaintenanceStart).AddDays($ADRDuration) ; break}
Weeks { $ADRMaintenanceStop = (Get-Date $ADRMaintenanceStart).AddDays( $ADRDuration * 7 ) ; break}
Months { $ADRMaintenanceStop = (Get-Date $ADRMaintenanceStart).AddMonths($ADRDuration) ; break}
}
}
If ( $MaintenanceWindowMode = "Collection" ) {
$ADRMaintenanceStop = (Get-Date $ADRMaintenanceStart).AddMinutes(((Get-CMCollectionSetting -CollectionId $ValidADR.CollectionID | select -ExpandProperty ServiceWindows | select Duration).Duration))
}
# ADR Collection Members Info
$ADRCollectionName = (Get-CMCollection -Id $ValidADR.CollectionID).Name
$ADRCollectionMembers = Get-CMCollectionMember -CollectionId $ValidADR.CollectionID
Foreach ( $CMDeviceMember in $ADRCollectionMembers ) {
$CMDevice = $CMDeviceMember.Name
$connString = "Data Source=$SQLServer;Initial Catalog=$SQLDBName;Integrated Security = True"
$connection = New-Object System.Data.SqlClient.SqlConnection($connString)
$connection.Open()
$sqlcmd = $connection.CreateCommand()
$SqlQuery = "set dateformat dmy ; INSERT INTO Scheduling (ci,type,Team,StartTime,EndTime,Comment,Status,ADR_ID) VALUES ( '$CMDevice', 'Computer', 'SCCM', '$(Get-Date $ADRMaintenanceStart -Format g)', '$(Get-Date $ADRMaintenanceStop -Format g)', 'SCCM MaintenanceMode for : $ADRCollectionName | $ADRName' , 'NOT PROCESSED', '$ADRID');"
$sqlcmd.CommandText = $SqlQuery
$result = $sqlcmd.ExecuteNonQuery()
}
} Else { $ADRName + ' already in MM Database' >> $logFile }
}
Now, you just need to create a scheduled task in your SCCM server to run whenever you might like, and ... :
# The Scheduled Task :
# OpsMgr Console Maintenance Mode window :
And that's it!
If you bump into some error or bug, please let me know, this is just too fresh and made just some few tests.
Cheers,
No comments:
Post a Comment