Showing posts with label msoms. Show all posts
Showing posts with label msoms. Show all posts

Thursday, September 8, 2016

OpsMgr (SCOM) - NTP Deviation Monitor (VBScript)

OpsMgr by default doesn't evaluate the deviation between the NTP server time and the server/workstation time, and since there are some critical services that require this deviation to be almost 0, i've decided to create a three-state monitor for this evaluation.

First, create a "Timed Script Three State Monitor" on the Authoring pane (Create a Monitor) - select "Unit Monitor"
Associate it to a brand new MP.



Then, target it to Windows Computer. I've decided to select "Configuration" to it's parent monitor (this is up to you!).
Also decided to NOT ENABLE IT by default.



Next, select the most effective schedule for you, i've decided to leave it to 5 minutes.



Fill the file name also as the script area, with this script :
(As you might notice, part of this code is from Nagios check check_time.vbs - credits go to the Author : Dmitry Vayntrub (dvayntrub@yahoo.com) )

 Dim oAPI, oBag  
 Set oAPI = CreateObject("MOM.ScriptAPI")  
 Set oBag = oAPI.CreatePropertyBag()  
 Set objWMISvc = GetObject( "winmgmts:\\.\root\cimv2" )  
 Set colItems = objWMISvc.ExecQuery( "Select * from Win32_ComputerSystem" )  
 For Each objItem in colItems  
   strComputerDomain = objItem.Domain  
 Next  
 Set objShell = CreateObject("Wscript.Shell")  
 strCommand = "%SystemRoot%\System32\w32tm.exe /monitor /nowarn /computers:" & strComputerDomain  
 set objProc = objShell.Exec(strCommand)  
 warn = "5"  
 crit = "10"  
 input = ""  
 strOutput = ""  
 Do While Not objProc.StdOut.AtEndOfStream  
     input = objProc.StdOut.ReadLine  
     If InStr(input, "NTP") Then  
         strOutput = strOutput & input  
     End If  
 Loop  
 Set myRegExp = New RegExp  
 myRegExp.IgnoreCase = True  
 myRegExp.Global = True  
 myRegExp.Pattern = " NTP: ([+-][0-9]+\.[0-9]+)s"  
 Set myMatches = myRegExp.Execute(strOutput)  
 result = ""  
 If myMatches(0).SubMatches(0) <> "" Then  
     result = myMatches(0).SubMatches(0)  
 End If  
 For Each myMatch in myMatches  
     If myMatch.SubMatches(0) <> "" Then  
             If abs(result) > Abs(myMatch.SubMatches(0)) Then  
                 result = myMatch.SubMatches(0)  
             End If  
     End If  
 Next  
 If result = "" Then  
     Err = 3  
     Status = "UNKNOWN"  
  ElseIf result > crit Then  
     Err = 2  
     status = "CRITICAL"  
  ElseIf result > warn Then  
     Err = 1  
     status = "WARNING"  
  Else  
     Err = 0  
     status = "OK"  
 End If  
 Call oBag.AddValue("NTPStatus",status)  
 Call oAPI.Return(oBag)  



Next, we'll set the expressions (Unhealthy, Degraded and Healthy).
As you might notice i populated the Property Bag with parameter name as "NTPStatus" so you need to name your Parameter Name as follow :







Let the configure health step as it is.

For last, configure the alert rising as follows :



Now you need to override the monitor to a group or class as you prefer, and this is what it would like:



And that's it!

Hope you enjoy!

Monday, August 8, 2016

Powershell - SCOM (OpsMgr) Distributed Application to SCCM (ConfigMgr) Collection

For reporting purposes i had to create equal SCCM collections with the same members that i had in SCOM Distributed Applications.
Now that i have same DA as Collections, and respective members, i can match alerts (DA from SCOM), as well i can have a list of required updates (Collection from SCCM) in the same PowerBI report.
It can be really useful for Application Owners or Sys Admin teams.

Instead of creating by hand every DA I've in SCCM as a collection, and since i've got around 60 DA's, i came up with this script!
(Sorry for the variable names, and for some bad code - not having the time i need to get it better!)
(PS: Read the comments before you run the script! :) )

 Import-Module OperationsManager  
 # Your SCCM Server  
 $SCCMServer = 'Your SCCM Server'  
 $Class = Get-SCOMClass -DisplayName 'User Created Distributed Application'  
 $DistrApps = Get-SCOMClassInstance -Class $Class  
 $DARelationList = ''  
 $ListaDAandHosts = @()  
 Foreach ($DA in $DistrApps) {  
   $DAHosts = ($DA | % {$_.GetRelatedMonitoringObjects()} | % {$_.GetRelatedMonitoringObjects()}).DisplayName  
   Foreach ($Hostz in $DAHosts) {  
     $ListaDAandHosts += $DA.DisplayName + ';' + ($Hostz -split '\.')[0]  
   }  
 }  
 #This is because i only want DA with valid hostnames!  
 $RegexQuery=[regex]"^[0-9A-Za-z].*;(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$"  
 $ListaDA = @()  
 $ListaDA += 'DA;ServerFQDN'  
 Foreach ($line in $ListaDAandHosts) {  
   If ($RegexQuery.Match($line).Success -eq $true) {      
       $ListaDA += $RegexQuery.Match($line).Groups[0].Value  
   }  
 }  
 # Set the out-file as you like!  
 $ListaDA | Out-File "\\\$SCCMServer\c$\FOLDER\ListaDA.csv"  
 $SCCMScriptBlock = {  
   Import-Module "D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1"  
   $DACSV = Import-csv 'C:\GDC\ListaDA.csv' -Delimiter ';'  
   $DAList = ($DACSV | Group {$_.DA.Substring(0)}).Name  
   # Set Location to Site Name #  
   Set-Location YOUR_SITE_NAME:  
   # Your SCCM Limit Collection #  
   $Limitingcollections = "All Systems"  
   # Let the show begin #  
   Foreach ($DA in $DAList) {  
     $DAHostList = @()  
     $DAHosts = (($DACSV | ? { $_.DA -eq $DA } | Select ServerFQDN).ServerFQDN)  
     # Create DA If not Exists #    
     If (Get-CMDeviceCollection -Name $DA) {  
       # Does Nothing #  
       $DA + ' | Already exists!'  
     } Else {  
       $DANewCollection = New-CMDeviceCollection -Name "$DA" -LimitingCollectionName $Limitingcollections  
     }  
     # Add each host to Collection #  
     Foreach ( $SCCMAgent in $DAHosts ){  
       Try {  
         Add-CMDeviceCollectionDirectMembershipRule -CollectionName "$DA" -ResourceID $(get-cmdevice -name "$SCCMAgent").ResourceID  
       } Catch {  
         $SCCMAgent + ' | Already in Collection or not found'  
       }  
     }  
       # Move your collection to specific location - if you want to #  
     Move-CMObject -FolderPath 'SITE_NAME:\DeviceCollection\YOUR_SPECIFIC_FOLDER' -InputObject $DANewCollection  
   }  
 }  
 $SCCMSession = New-PSSession -ComputerName $SCCMServer  
 Invoke-Command -Session $SCCMSession -scriptblock $SCCMScriptBlock  

And, that's it!

Thursday, July 14, 2016

C# - First steps (Orchestrator Web-Service!)

First of all, i'm no developer - at all - and i thought that it would be easier to adapt since i (thought) have some powershell skills! (ahahahah)

So, for my first C# project i thought that i could play a little bit with the Orchestrator web-service and manage, list, start (and so on...) some runbooks - well, we all hate the silverlight thing!

This is what i achieved :



The icons with the start,pause,stop and restart functions, are not working (yet!) - that's the next fase!

I'll share the project soon on GitHUB and let you all know.

Cheers,

SCCM (ConfigMgr) - IIS Inventor with VBScript and WMI Classes

Recently we had this need for a customer of ours - Make an IIS Inventory report in SCCM with all the sites, related application pools, bindings and IIS versions.

It seems easy, but in a few moments it turned into a great nightmare, still a great challange - that i accepted gladly!

The first thing i've banged into was the fact of having multiple IIS versions and Operating Systems (IIS 6, 7, 8 - and 2003, 2008, 2012).
Why ?
Because for IIS version 6 you get information from "ROOT\MicrosoftIISv2" namespace, and for IIS>7 you have "ROOT\WebAdministration" namespace.
The problem wasn't having multiple data sources from where you could collect data from - i'll explain it further!
There's a solution, a really easy one to overcome this issue - install IIS WMI 6 Compatability role on your IIS>7 - this will make/create the "ROOT\MicrosoftIISv2" namespace even on IIS>7 machines - this way you'll only have a datasource to 'drink' data from.
But, there're some security issues, that some sysadmins don't like about this role, so i couldn't go there!

But, right before i set the classes i wanted, i created a simple collection with this WQL (All devices with IIS installed) :

 select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SERVICE on SMS_G_System_SERVICE.ResourceID = SMS_R_System.ResourceId where SMS_G_System_SERVICE.Name = 'W3SVC'  

And right after, a custom agent setting where only there i would enable the further classes and the inventory of "C:\Windows\System32\InetSrv" folder so i could inventory inetmgr.exe file so i could've exact IIS version foreach machine.

Now, WMI Classes setup!
Ok! So i checked which classes i was going to set up and read information from, and came up with this :

IIS6 or with IIS 6 WMI Compatability :

 Namespace : MicrosoftIISv2  
 Class : IIsWebVirtualDirSetting  
 Query : SELECT * FROM IIsWebVirtualDirSetting


 Namespace : MicrosoftIISv2  
 Class : IIsWebServerSetting  
 Query : SELECT * FROM IIsWebServerSetting  

For IIS>7 :

 Namespace : WebAdministration  
 Class : Application  
 Query : SELECT * FROM Application

 Namespace : WebAdministration  
 Class : Site  
 Query : SELECT * FROM Site      

But right there, feeling really lucky about how it was going, and banged into my first issue!
At (ROOT\WebAdministration) Application class! You can't enable it because there's already a SCCM built-in class with this name.
So, after some googling i've learned that i could make a UNION class that "mirrors" all the information from a source into this new class in a namespace i wanted - and came up with this code :

 #pragma namespace("\\\\.\\root\\cimv2")  
 [Union,ViewSources{"select ApplicationPool,EnabledProtocols,ServiceAutoStartEnabled,ServiceAutoStartProvider,Path,PreloadEnabled,SiteName from Application"},ViewSpaces{"\\\\.\\root\\webadministration"},dynamic,Provider("MS_VIEW_INSTANCE_PROVIDER")]  
 class IIS_Application  
 {  
     [PropertySources{"ApplicationPool"}] string ApplicationPool;  
     [PropertySources{"EnabledProtocols"}] string EnabledProtocols;  
     [PropertySources{"ServiceAutoStartEnabled"}] boolean ServiceAutoStartEnabled;  
     [PropertySources{"ServiceAutoStartProvider"}] string ServiceAutoStartProvider;  
     [PropertySources{"Path"},key] string Path;  
     [PropertySources{"PreloadEnabled"}] boolean PreloadEnabled;  
     [PropertySources{"SiteName"},key] string SiteName;  
 };  

A Free-Tip : You must have all the KEY propreties mapped in your new class, meaning that if your source class has 3 key properties, your new custom class must also have those 3 key properties.

Basically it's kind of a view of "ROOT\WebAdministration\Application" class that i created in "ROOT\CIMV2" and named it "IIS_Application".

There was another problem i faced - SCCM hardware inventory can't read WMI Classes proprieties when they are objects, meaning that i've tried to set "ROOT\WebAdministration\Site" for having sites information with binding association and i couldn't.
So, again, i needed to make a brand new class :

 #pragma namespace ("\\\\.\\root\\cimv2")  
 class IIS_Bindings  
 {  
 [key]  
 STRING SiteName;   
 STRING Bindings;  
 UInt32 SiteId;  
 };  

But there's a problem - this is not a view! This is a simple new class - with no data, just empty! And the only way (i know) to populate this, was with this script :

 strComputer = "."  
     Set objWMIService = GetObject("winmgmts:{authenticationLevel=pktPrivacy}\\" & strComputer & "\root\cimv2")  
     Set colBindingsItems = objWMIService.ExecQuery("Select * from IIS_Bindings")  
     For Each objItem in colBindingsItems  
             objItem.Delete_()  
     Next  
     Set objWMIService = GetObject("winmgmts:{authenticationLevel=pktPrivacy}\\" & strComputer & "\root\WebAdministration")  
     Set colItems = objWMIService.ExecQuery("Select * from Site")  
     Set oWMI = GetObject("winmgmts:root\cimv2")  
     Set oData = oWMI.Get("IIS_Bindings")  
     Set oInstance = oData.SpawnInstance_  
     For Each objItem in colItems  
             oInstance.SiteName = objItem.Name  
             oInstance.SiteId = objItem.Id  
             Bindings = ""  
             For i = 0 to Ubound(objItem.Bindings)  
         Bindings = Bindings + objItem.Bindings(i).BindingInformation + "|"  
             Next  
             Bindings = LEFT(Bindings, (Len(Bindings) -1))  
             oInstance.Bindings = Bindings  
             oInstance.Put_()  
     Next  

Yes, you need to do a new based script application and deploy it into your collection - it will take a wile so you get data back into your CM database.

So now, that we've done all the setup we need to have all IIS information - in my case "Machine;Site;ApplicationPool;Path;Bindings;IISVersion" - it's time to query!
For every class you enable, SCCM will create a table like "MY_CLASSNAME_DATA", for example : IIS_Application_DATA
And it's here where all the magic can be done.

So, finally, i came up with this query (It needs to be modified to return better results, specially when it comes to the bindings ... i'll do it later someday!)

 SELECT DISTINCT  
     CSYS.Name0 as [Server Name],  
     IISApp.ApplicationPool00 as [AppPools],    
     IISApp.SiteName00 as [Site Name],  
     IISBind.Bindings00 as [Bindings],  
     SUBSTRING(SF.FileVersion, 1,14) AS [IIS Version]  
 FROM   
     [V_R_system] SYS with (nolock)  
     JOIN [v_GS_COMPUTER_SYSTEM] CSYS on CSYS.ResourceID = sys.ResourceID  
     JOIN [v_FullCollectionMembership] FCM on FCM.ResourceID = CSYS.ResourceID  
     JOIN [v_GS_SoftwareFile] SF on SF.ResourceID = SYS.ResourceID  
     FULL JOIN [IIS_Application_DATA] IISApp on IISApp.MachineID = SYS.ResourceID  
     FULL JOIN [IIS_Bindings_DATA] IISBind on IISBind.MachineID = SYS.ResourceID  
 WHERE  
     FCM.CollectionID like 'YOU_Collection_ID_Goes_Here'  
 AND   
     IISApp.SiteName00 = IISBind.SiteName00  
 AND   
     SF.FileName like '%inetmgr%'  

NOTE: This is the first version of the project, a POC if you want, just to show you how to get data, perhpaps it has some flaws - but it's a way of showing how to get data specially if you don't have it in the first place.



Hope this might be useful to you in someway.

Cheers,

Tuesday, July 5, 2016

OpsMgr (SCOM) - Exchange 2013 Management Pack | How to

Finally i got my hands on this MP.
This new version is pretty better than the old one - the old one used to be 'heavy' and 'noisy' for some people, and since a my friend PFE told me that this new version is way better than the older version, and the fact that we're needing to monitor two Exchange Servers for a specific client, i gave it a shot.

So - download the MP, and install it!

After you installed it, the *.mp files will be located at
"C:\Program Files (x86)\System Center Management Packs\SC Management Pack for Exchange 2013"

So, open up Operations Manager Console and import the MP files.


Then, you need to create a domain user account that will take care of some of the management pack workflows, and create a Windows Run As Account in Operations Manager Console:



For best practices propose, choose to manually select the computers that will receive this account.


After you import the management pack, it'll create a Run As profile named "Microsoft Exchange 2013 SDK Monitoring Run As Profile" that you'll need to associate the previously created Run As account - you might noticed that i created a SCOM group to have (in my case) two Exchange Servers, and i pointed the profile to that group.



After that, you'll notice that you'll be receiving data - this is the Summary Dashboard :


These are your views:



That's it!
Hope it could be useful :)

Cheers,

Friday, May 27, 2016

OpsMgr (SCOM) - Unix/Linux Agent Powershell DSC

Who doesn't have any Unix/Linux agent deployment issue ?

Remebering this earlier post i made about "OpsMgr (SCOM) - Unix/Linux Agents Requisites and Troubleshooting"
I came up with the idea to make a script that made this validations for ourselves.

Basically it'll login your unix/linux servers with your own credentials and will make a bunch of configurations tests.
But, please remember that this is my own scenario oriented, so, read and edit the code for your own scenario.

So, in first place, you'll need this library :
http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library

You can put it on your Modules favourite folder (eg. C:\Program Files\WindowsPowerShell\Modules)

Finally!
You can execute this script from your Unix/Linux Resource Pool gateway or MS:

 $ServerList = 'C:\Powershell\SCXAgentDSC\list.txt'  
 $SCXAgents = Get-Content -Path $ServerList  
   
 # Change values for your own  
 $user = 'Your_Run_AsAccountGoesHere!'   
 $pass = ConvertTo-SecureString 'YourPassword' -AsPlainText -Force   
 $creds = New-Object System.Management.Automation.PsCredential($user,$pass)  
   
 try { Import-Module SSH-Sessions }  
 Catch { 'No SSH Modules Found' ; Exit }  
 foreach ( $scxagent in $SCXAgents ) {  
   $scxdomain = ($scxagent -split "\.")[-2..-1] -join '.'  
     # Change values for your own here as well   
   if( (New-SshSession -ComputerName $scxagent -Username Your_Run_AsAccountGoesHere -Password "YourPassword") -notmatch “successfully”) {  
     $scxagent + ' Could not SSH (bad user / password ? | Or no route ? )'  
     $SSHStatus = "1"  
   } Else { $SSHStatus = "0" }  
   If ($SSHStatus -eq "0" ) {  
     Invoke-SshCommand -Quiet -ComputerName $scxagent -Command "sudo -l" | Out-File C:\Powershell\sudo.txt  
     Invoke-SshCommand -Quiet -ComputerName $scxagent -Command "cat /etc/issue" | Out-File C:\Powershell\issue.txt  
     Invoke-SshCommand -Quiet -ComputerName $scxagent -Command "openssl x509 -noout -in /etc/opt/microsoft/scx/ssl/scx.pem -subject -issuer -dates" | Out-File C:\Powershell\certconfig.txt  
         # This is only applied if you have limited sudo configuration   
         # This line will check if the sudo config escapes the EC (error code) variable   
     $ECCount = (Get-Content C:\Powershell\sudo.txt | select-string -SimpleMatch "EC\=0" | measure).Count  
         # This will check if you have enought perms for RPM install and uninstall  
     $RPMLines = Get-Content C:\Powershell\sudo.txt | select-string -SimpleMatch "--force /tmp/scx-monuser/scx"  
         # This will check if you have root permissions (and no further sudo config is needed - so comment the lines that does not match your scenario)  
         $SUDOALL = Get-Content C:\Powershell\sudo.txt | select-string -SimpleMatch "(root) NOPASSWD: ALL"  
         # This will check if you can re-generate certificates if needed  
     $SSLConfig = Get-Content C:\Powershell\sudo.txt | select-string -SimpleMatch "/opt/microsoft/scx/bin/tools/scxsslconfig"  
         # This will check if you have a certificate and for the correct FQDN  
     $CertConfig = Get-Content C:\Powershell\certconfig.txt | select-string -SimpleMatch "$scxagent"  
     $SCXSSLDomain = ((Get-Content C:\Powershell\certconfig.txt | Select-String -SimpleMatch "subject") -split "=")[-1]  
         # Port testing (22 and 1270)  
     Try { If ((new-object System.Net.Sockets.TcpClient("$scxagent","1270")).connected -eq $true ) { $AgentPortStatus = "OK" } Else { $AgentPortStatus = "NOT OK" } } Catch { $AgentPortStatus = "NOT OK"}  
     Try { If ((new-object System.Net.Sockets.TcpClient("$scxagent","22")).connected ) { $sshstatus = "OK"} Else { $sshstatus = "NOT OK" } } Catch { $sshstatus = "NOT OK" }  
         # WSMan Testing   
     If ( Test-WSMan -Port 1270 -ComputerName $scxagent -Authentication Basic -Credential $creds -UseSSL -ErrorAction SilentlyContinue ) { $wsmanstatus = 'OK' } Else { $wsmanstatus = 'NOT OK' }  
     $scxagenturi = "https://"+"$scxagent"+":1270/wsman"  
         # WinRM validation  
     Try { If ( winrm enumerate http://schemas.microsoft.com/wbem/wscim/1/cim-schema/2/SCX_Agent?__cimnamespace=root/scx -username:'YOUR_USERNAME_HERE' -password:'YOUR_PASSWORD_HERE' -r:$scxagenturi -auth:basic -skipCACheck -skipCNCheck -skiprevocationcheck -encoding:utf-8 ) { $winrmstatus = "OK" } Else { $winrmstatus = "NOT OK" } } Catch { $winrmstatus = "NOT OK" }  
     If ( $ECCount -gt "0" )        { $ecstatus = "NOT OK" } Else { $ecstatus = "OK" }  
         If ( $SUDOALL )            { $sudoallstatus = "OK" } Else { $sudoallstatus = "NOT OK" }  
     If ( $RPMLines -match "[0-9]" )    { $rpmstatus = "NOT OK" } Else { $rpmstatus = "OK" }  
     If ( $SSLConfig -match "scxsslconfig" ) { $SSLConfigStatus = "OK" } Else { $SSLConfigStatus = "NOT OK" }  
     If ( $CertConfig -match "$scxagent" -and $CertConfig -match $scxdomain) { $CertificateStatus = "OK" } Else { $CertificateStatus = "NOT OK" }  
         # Remove the ones that not match your cenario (For sudo config)  
     Write-Output "$scxagent | WSMAN : $wsmanstatus | SSH : $sshstatus | AgentPort : $AgentPortStatus | EC SUDOConfig : $ecstatus | RPM SUDOConfig : $rpmstatus | SUDOAll : $sudoallstatus | SCXConfig SUDOConfig : $SSLConfigStatus | Certificate : $CertificateStatus | WinRM : $winrmstatus"  
   }  
 }  
 Remove-SshSession -RemoveAll | Out-Null  


OpsMgr (SCOM) - (Bulk) Set Failover Management Server Powershell Script

I believe every SCOM Admin needs this.

This a script that for every agent you have, if it doesn't have a failover Management Server, it'll set it one.

NOTE : Please read comments inline before you run this

Enjoy!

 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 { '[OpsMgr] - DLL could not be loaded' }  
 try{  
   $MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings("$env:computername")  
   $MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)  
 } Catch { '[OpsMgr] - Could not connect to Management Group' }  
 # Let's say .. you want to have a list of MS Servers you dont want to work as failover (in my case i don't want Network RP MS servers ... - It's up to you!) :)  
 $IgnoredMS = @('Server1','Server2')  
 # Criteria for every MS you got  
 $MSCriteria = New-Object Microsoft.EnterpriseManagement.Administration.ManagementServerCriteria("Name LIKE '%'")  
 # Let's say you only want to work on specific criteria  
 $MSCriteria = New-Object Microsoft.EnterpriseManagement.Administration.ManagementServerCriteria("Name LIKE 'OpsMgrServer%'")  
 $ManagementServers = ($MG.Administration.GetManagementServers($MSCriteria)).DisplayName | ? { $_ -notin $IgnoredMS }  
 $AgentCriteria = New-Object Microsoft.EnterpriseManagement.Administration.AgentManagedComputerCriteria("Name LIKE '%'")  
 $Agents = $MG.Administration.GetAgentManagedComputers($AgentCriteria) | ? { $_.PrimaryManagementServerName -in @($ManagementServers) }  
 # My logic is :  
 #     - Specific agent will only have a failover MS with the same domain  
 #    So you may need to edit code before running this.  
 Foreach ( $agent in $Agents ) {  
   If ( !($agent.GetFailoverManagementServers()) ) {  
     $PrimaryMS = $agent.PrimaryManagementServerName  
     $Domain = ($PrimaryMS -split "\.")[-2..-1] -join '.'  
     $FailoverMS = ($ManagementServers | Select-String -Pattern "$Domain" | ? { $_ -notin $PrimaryMS })[0]  
     $PrimaryMSCriteria = New-Object Microsoft.EnterpriseManagement.Administration.ManagementServerCriteria("Name = '$PrimaryMS'")  
     $PrimaryManagementServerID = $MG.Administration.GetManagementServers($PrimaryMSCriteria).ID  
     $PrimaryManagementServer = $MG.Administration.GetManagementServer($PrimaryManagementServerID)  
     $FailoverMSCriteria = New-Object Microsoft.EnterpriseManagement.Administration.ManagementServerCriteria("Name = '$FailoverMS'")  
     $FailoverMSIList = New-Object 'Collections.Generic.List[Microsoft.EnterpriseManagement.Administration.ManagementServer]'  
     $MG.Administration.GetManagementServers($FailoverMSCriteria) | % { $FailoverMSIList.Add($_) }  
     $agent.SetManagementServers($PrimaryManagementServer, $FailoverMSIList)  
   } Else { "$(($agent).DisplayName)" + ' has already a Failover MS configured' + "$($agent.GetFailoverManagementServers().DisplayName)" }  
 }  

Cheers!

Monday, May 23, 2016

OpsMgr (SCOM) - Solve the "An Item With The Same Key Has Already Been Added" error


For some reasons, like, having a bunch of procedures that include Unix/Linux servers into SCOM, you might get the 'An Item With The Same Key Has Already Been Added' error when you go to Administration - Unix/Linux Agents.

That means that you've duplicate unix/linux servers in your SCOM installation.

The only way to solve this is to find the duplicate entry, and delete them both.

To do that, you can't use the Get-SCXAgent, this will output unique values only - the only way to solve this is going to your OpsDB execute a querry (bellow), and foreach value you get, you need to run the 'get-scxagent "duplicate_value" | Remove-ScxAgent' cmdlet.

So, first things first.

The query :

 DECLARE @ClassName NVARCHAR(256)   
 DECLARE @CManagedTypeId UNIQUEIDENTIFIER   
 SET @ClassName = 'Microsoft.Unix.OperatingSystem'  
 SET @CManagedTypeId = (   
     SELECT ManagedTypeId  
     FROM ManagedType   
     WHERE TypeName = @ClassName )   
 SELECT  
     [ManagedEntityGenericView].[Id],   
     [ManagedEntityGenericView].[Name],   
     [ManagedEntityGenericView].[Path],   
     [ManagedEntityGenericView].[FullName],   
     [ManagedEntityGenericView].[LastModified],   
     [ManagedEntityGenericView].[TypedManagedEntityId],   
     NULL AS SourceEntityId   
 FROM  
     dbo.ManagedEntityGenericView   
 INNER JOIN (      
     SELECT DISTINCT [BaseManagedEntityId]   
     FROM dbo.[TypedManagedEntity] TME WITH(NOLOCK)   
     JOIN [dbo].[DerivedManagedTypes] DT ON DT.[DerivedTypeId] = TME.[ManagedTypeId]   
     WHERE  
         DT.[BaseTypeId] = @CManagedTypeId  
         AND TME.IsDeleted = 0 )  
 AS ManagedTypeIdForManagedEntitiesByManagedTypeAndDerived   
 ON ManagedTypeIdForManagedEntitiesByManagedTypeAndDerived.[BaseManagedEntityId] = [Id]   
 WHERE  
     [IsDeleted] = 0 AND  
     [TypedMonitoringObjectIsDeleted] = 0 AND  
     [ManagedEntityGenericView].[Path] IN (   
                                             SELECT [BaseManagedEntity].[Path]   
                                             FROM [BaseManagedEntity]   
                                             GROUP BY [BaseManagedEntity].[Path]   
                                             HAVING COUNT([BaseManagedEntity].[Path]) > 1   
                                          )  
 GROUP BY [ManagedEntityGenericView].[Id],   
     [ManagedEntityGenericView].[Name],   
     [ManagedEntityGenericView].[Path],   
     [ManagedEntityGenericView].[FullName],   
     [ManagedEntityGenericView].[LastModified],   
     [ManagedEntityGenericView].[TypedManagedEntityId]  
 HAVING COUNT([ManagedEntityGenericView].[Path]) > 1  

Now that you've the duplicate values to delete, just open a powershell prompt and run the follow cmdlet foreach duplicate value you've.

 get-scxagent "Your_Server" | Remove-ScxAgent  

And, your problem is solved.

Cheers,

OpsMgr (SCOM) - BUILTIN\Administrators

Well, imagine that for some reason you delete the "BUILTIN\Administrators" group in OpsMgr Administrator Role, you might have some issues if you don't have all the profiles and roles correctly assinged.

There's a workaround, not supported by MSFT, but, well ... :)

Into OpsDB, run this query.

 insert into AzMan_Role_SIDMember ([RoleID],[MemberSID])  
 VALUES (1, 0x01020000000000052000000020020000) -- This is the hex value for BUILTIN\Administrators  

And off you go!

Cheers,