File System Audit and the WABAC machine – Now with set-acl

‘Twas a long time ago, longer now than it seems
In a place that perhaps you’ve seen in your dreams.
For the story that you are about to be told
Began with the technicians of old.

A very long time ago, as a junior tech I got a seemingly simple request.  “Tell me what user X has access to”
Quickly I looked up the groups and and provided the list, and got the response you already know. “We know that but what do those groups have access to?”

Of course this is a completely different question, so I set forth to write some code that would go through a file server and would tell me what the permissions where and at what level they changed. I purchased books and tried to call the API directly but was not able to create what I wanted and since nobody else could do it either the project just dropped off the radar.

Very recently I got another request. “We need to look through a directory structure to see where the “Domain Admins” group does not have full access. Users have been creating folders and adjusting permissions that we will need to fix without resetting everything in the tree.”

This time I started writing in PowerShell, I didn’t think I would say this but I wish PowerShell existed in the NT days. I didn’t need to fix anything just report, and within a couple of hours I had it.

 

The following script will go through directories and output 1 line for each folder or file it finds where the requested group does not have the access level defined UNLESS the file or folder is inheriting permissions from a higher level. The intent is that you should only need to make 1 change per line of output to correct the permissions, and even if there are thousands of files with incorrect permissions that inherit permission from a  parent.

You could have this fix things if you want using set-acl but I didn’t need to.

 

cls
# The intent of this script
# Loop recursively through files and folders starting at $target provided below)
# Look for the existance of an account or group in the ACL
# Check the access level for that user \ groupin the ACL
# Generate a report showing ONLY the places you would need to make a change
# to make sure the required account has the correct access on the entire tree.
# you could easily use set.acl to actually correct for issues (see note below) but I only needed a report.
#  File System Audit and the WABAC machine - www.blackops.ca - blog - for more info.
$target="\\Servername\sharename"
$GroupToCheck="Domain\User"
$AccessExpected="FullControl"
Function CheckAll {
# This function starts at the folder provided above
param([string]$rootfolder)
CheckACL $rootfolder
#Check the current folder ACL's
$items = Get-ChildItem -Path $rootfolder
If ($items)
# The above IF traps for a condition of a folder with no files in it.
{
foreach ($item in $items)
{
if ($item.Attributes -ne "Directory")
{
# for each file (note the -NE) check the ACL for the file
checkacl $item.fullName
}
}
foreach ($item in $items)
{
if ($item.Attributes -eq "Directory")
{
# for each directory (note the -EQ this time) Recurse back into this function with the directory as the new root folder
CheckAll $item.fullName
}
}
}
}
Function CheckACL {
param([string]$target)
$Check=$false
$inheritON=$false
$acl=Get-Acl $target # this reads the ACL
if ($acl.AreAccessRulesProtected)
# if AreAccessRulesProtected is TRUE then that means that a folder is NOT inheriting it's permissions
# if a folder is inheriting then it will NOT appear in the output
{
foreach ($ACLDetail in $acl.Access)
{
#This loops through each user \ access level and checks to see if access levels are explicit or inherited.
if ($acldetail.IsInherited) {$inheritON=$true} #inheritance check if inhereted file \ folder will NOT appear in report
if ($acldetail.IdentityReference -eq $GroupToCheck) #This checks each user for the one we are looking for (provided at the top of the script
{
if ($acldetail.FileSystemRights -eq $AccessExpected) #This checks to see if the permissions are correct
{
$Check = $true
}
else
{
#If the permissions are not correct output the full path\name and the current access level
Write-Host -NoNewline $Target
Write-Host -NoNewline "`t" #tab
Write-Host $acldetail.FileSystemRights
# If you want to fix the issue you would need a set_acl here and once more below
}
}
}
if (!$Check -and !$inheritON) #if the expected user is not found AND there is no inheritance set then log the file\folder
{
Write-Host -NoNewline $target
write-host -NoNewline "`t" #tab
write-host -NoNewline $GroupToCheck
write-host " Missing"
# If you want to fix the issue you would need a set_acl here and once more above
}
}
}
"begin"
checkall $target
"end"

And yes I am old enough to remember the WABAC Machine although not quite old enough to see the episodes as they aired for the first time.

 

 

Well it didn’t take long to go from report only to please fix it, so I have another script. If you want the script to add the required access for the defined group use the script below. I actually found set-acl harder to use than I expected and remember you see different statements for each condition

ADD to ACL for folder
Modify ACL for folder
ADD to ACL for file
Modify ACL for file

 

cls
# The intent of this script
# Loop recursively through files and folders starting at $target provided below)
# Look for the existance of an account or group in the ACL
# Check the access level for that user \ groupin the ACL
# Generate a report showing ONLY the places you would need to make a change
# to make sure the required account has the correct access on the entire tree.
# for more info s@blackops.ca
# THIS VERSION WILL CORRECT ISSUES. 
$target="C:\Temp"
$GroupToCheck="BUILTIN\Administrators"
$AccessExpected="FullControl"
#$DesiredPermission = $GroupToCheck,$AccessExpected,"ContainerInherit, ObjectInherit","NoPropagateInherit","Allow"
$person = [System.Security.Principal.NTAccount]"$GroupToCheck"
$access = [System.Security.AccessControl.FileSystemRights]"$AccessExpected"
$Folderinheritance = [System.Security.AccessControl.InheritanceFlags]“ContainerInherit, ObjectInherit”
$Fileinheritance = [System.Security.AccessControl.InheritanceFlags]“None”
$propagation = [System.Security.AccessControl.PropagationFlags]“NoPropagateInherit”
$type = [System.Security.AccessControl.AccessControlType]“Allow”
$FolderaccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($person,$access,$Folderinheritance,$propagation,$type)
$FileaccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($person,$access,$type)
$AccessModification = New-Object system.security.AccessControl.AccessControlModification
$AccessModification.value__ = 2
$Modification = $False
Function CheckAll {
# This function starts at the folder provided above
param([string]$rootfolder)
CheckACL $rootfolder
#Check the current folder ACL's
$items = Get-ChildItem -Path $rootfolder
	If ($items)
	# The above IF traps for a condition of a folder with no files in it.
	{
			foreach ($item in $items)
			{
			if ($item.Attributes -ne "Directory")
		       {
			   		 # for each file (note the -NE) check the ACL for the file
		             checkacl $item.fullName
		       }
		 	}
			foreach ($item in $items)
			{
			if ($item.Attributes -eq "Directory")
		       {
			   		# for each directory (note the -EQ this time) Recurse back into this function with the directory as the new root folder
		             CheckAll $item.fullName
		       }
		 	}
		}
}
Function CheckACL {
param([string]$target)
$Check=$false
$inheritON=$false
$acl=Get-Acl $target # this reads the ACL
if ($acl.AreAccessRulesProtected)
# if AreAccessRulesProtected is TRUE then that means that a folder is NOT inheriting it's permissions
# if a folder is inheriting then it will NOT appear in the output
	{
	foreach ($ACLDetail in $acl.Access)
		{
		#This loops through each user \ access level and checks to see if access levels are explicit or inherited.
		if ($acldetail.IsInherited) {$inheritON=$true} #inheritance check if inhereted file \ folder will NOT appear in report
		if ($acldetail.IdentityReference -eq $GroupToCheck) #This checks each user for the one we are looking for (provided at the top of the script
			{
			if ($acldetail.FileSystemRights -eq $AccessExpected) #This checks to see if the permissions are correct
				{
				$Check = $true
				}
			else
				{
				#If the permissions are not correct output the full path\name and the current access level
				Write-Host -NoNewline $Target
				Write-Host -NoNewline "`t" #tab
				Write-Host  $acldetail.FileSystemRights
				If (Test-Path $target -pathtype container)
					{
					# If you want to fix the issue you would need a set_acl here and once more below
					$acl.ModifyAccessRule($AccessModification, $FolderaccessRule, [ref]$Modification) |Out-Null
					Set-Acl $target -AclObject $acl
					$Check = $true
					}
				Else
					{
					$acl.ModifyAccessRule($AccessModification, $FileaccessRule, [ref]$Modification) |Out-Null
					Set-Acl $target -AclObject $acl
					$Check = $true 
					}
				}
			}
		}
		if (!$Check -and !$inheritON) #if the expected user is not found AND there is no inheritance set then log the file\folder
			{
			Write-Host -NoNewline $target
			write-host -NoNewline "`t" #tab
			write-host -NoNewline $GroupToCheck
			write-host  " Missing"
			if (Test-Path $target -pathtype container)
				{
				$acl.AddAccessRule($FolderaccessRule)
				Set-Acl $target -AclObject $acl
				# If you want to fix the issue you would need a set_acl here and once more above
				}
			Else
				{
				$acl.AddAccessRule($FileaccessRule)
				Set-Acl $target -AclObject $acl
				}
			}
	}
}
"begin"
checkall $target
"end"

Powershell – Rename Local Admin Account OR Ecuritysay Oughthray Obscurityway

This PowerShell script looks for the local Administrator account (as a member of the local Administrators group) and renames it. Then it creates a bogus local administrator account with a 32 character password that next expires and is not a member of any groups.

Likely this will only catch your local admins who have not had their morning coffee yet, but you can audit and know that any attempt to logon to Administrator is bad.

This script renames the Administrator to Admin (not very creative yes, if you don’t like that change $NewAdminName to something better. I suggest Ted.

The script takes input from a file c:\servers.csv that has a header of “ComputerName” and then one per line the list of server names you want to check and adjust.

If someone really wants more explanation post a comment.

Thanks to the creator of the password generator (Adam Bell) but I wish I noticed the comment about the break sooner. I have they adjusted code in my script.

function create-complexpassword
 {
 #  *** Unable to generate complex password less then 5 chars ***
 #  ASCII data taken from http://msdn2.microsoft.com/en-us/library/60ecse8t(VS.80).aspx
  
 Param (
   [int]$PassLength
   )
  
 #  Let's work out where our 3 complex characters will be inserted in the password...
   [int]$mark = ($PassLength/3)
   $ComplexChar = @("marker") 
  $ComplexChar[0] = $mark
   $ComplexChar = $ComplexChar+($ComplexChar[0] + $mark)
   $ComplexChar = $ComplexChar+(($ComplexChar[1] + $mark) -1)
  
   $Password = $null
   $rnd = new-object random
  
 #  "i" is our counter while we make the password, one char at a time.
   $i = $Password.length
   do 
    {
       switch ($Password.length)
         {
           $ComplexChar[0]
             {
               # Make this character a Numeric
               $password = $password+([char]($rnd.next(48,57)))
             
             }
           
           $ComplexChar[1]
             {
               # Make this character a LowerAlpha
               $password = $password+([char]($rnd.next(65,90)))
             }
           
           $ComplexChar[2]
             {
               # Make this character a Upper Alpha
               $password = $password+([char]($rnd.next(97,122)))
             }
             
           default
             {
               # In case this is used in a DCPromo answer files, theres a few chars to 
              # avoid: Ampersand, Less than, double quote and back slash
               $NextChar = $rnd.next(33,123)
               
               switch ($nextChar)
                 {
                   34 {break}
                   38 {break}
                   60 {break}
                   92 {break}
                   default 
                    {
                       $Password = $Password+([char]$nextChar)
                   
                     }                
                 }
             }
         }
       $i++
	    } 
  Until ($Password.length -eq $PassLength)
   return $Password
   }
cls
$NewAdminName="Admin"
$InputServers = Import-Csv “c:\servers.csv”
$Servercount = 1
$ServerCountTotal = $InputServers.Count
$InputServers | ForEach-Object {
			$_.computername
			$computer=$null
			$Group=$null
			$LocalAdministrators=$null
			$Administrator=$null
			$Admin=$null
			$PasswordString=$null
			$computer = [ADSI]("WinNT://" + $_.computername + ",computer")
			$Group = $computer.psbase.children.find("Administrators")
			$LocalAdministrators= $Group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
			$Admin = $computer.psbase.children.find($NewAdminName) 
			$Administrator = $computer.psbase.children.find("Administrator") 
			$PasswordString=create-complexpassword(32)
			 If ($Administrator) 
			 {
			  if (($LocalAdministrators -contains "Administrator" ) -and (!$Admin))
			  	{
				$Administrator.Rename($NewAdminName)
				Start-Sleep -Seconds 1
				$computer=$null
				$Group=$null
				$LocalAdministrators=$null
				$Administrator=$null
				$Admin=$null			
				$computer = [ADSI]("WinNT://" + $_.computername + ",computer")
				$Group = $computer.psbase.children.find("Administrators")
				$LocalAdministrators=$null
				$LocalAdministrators= $Group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
				$Admin = $computer.psbase.children.find($NewAdminName)
				$Administrator=$null
				$Administrator = $computer.psbase.children.find("Administrator") 
				}
			 }
			 If (!$Administrator)
			 	{
				$objUser = $Computer.Create("User","Administrator")
			 	$objUser.setpassword($passwordstring)
				$objUser.SetInfo()
				$objUser.userflags = $objUser.userflags + 65536 # flag - never expires 
			 	$objUser.SetInfo()
				$objUser.Description="Built-in account for administering the computer/domain"
			 	$objUser.SetInfo()
				$objUser.FullName=""
			 	$objUser.SetInfo()
				}
 }

Powershell – Add users to local groups OR Sounds like a job for group policies

Today I needed to add a couple of AD users to the local administrators group on a number of servers….. and I am not allowed to use a GPO to do it.

Not wanting to manually connect to every machine I used the following powershell script

cls

$InputServers = Import-Csv “c:\servers.csv”

$InputUsers = Import-Csv “c:\users.csv”

$Servercount = 1

$ServerCountTotal = $InputServers.Count

$InputServers | ForEach-Object {

$ServerTemp=$_.ComputerName

“Starting ” + $Servercount + ” of ” + $Servercounttotal + ” : ” + $ServerTemp

$InputUsers | ForEach-Object {

$objUser = [ADSI](“WinNT://” + $_.Username )

$objGroup = [ADSI](“WinNT://” + $ServerTemp +”/Administrators”)

$objGroup.PSBase.Invoke(“Add”,$objUser.PSBase.Path)

}

“Complete ” + $Servercount + ” of ” + $Servercounttotal + ” : ” + $ServerTemp

$Servercount++

}

“Script Complete”

 

The script looks for 2 CSV files in the root of c:\

Servers.csv has a header of “ComputerName” and then a list of servers you want to affect one per line.

Users.csv has a header of “UserName” and then a list of user names (OLD FORMAT) Domain/User one per line.

 

Enjoy

 

A SQL Function : Split or Reinventing the wheel

It has been far too long since my last post.

Lately I have been working on some operational tasks that include a log of data gathering. I am using powershell and sql for this and thus the next few posts are likely to move in this direction.

One of the first few things I found was a complete lack of a Split function in SQL. I have instances where I receive a delimited string from an external process and need to handle it for use in SQL.

I found the following function using XML and am reposting it here for future reference.  Thanks to http://blog.codelab.co.nz/2009/07/29/split-function-for-t-sql-using-xml/ for the code.

You simply call the function with 2 strings, the seperator and the string you want to split, and out pops a one column table with your results in each row.

select * from split (‘~’,’Strange things are~a foot at the circle-K‘)

RESULTS

val
Strange things are
a foot at the circle-K

CREATE FUNCTION [dbo].[Split](@sep VARCHAR(32), @s VARCHAR(MAX))

RETURNS @t  TABLE

(val VARCHAR(MAX))

AS

BEGIN

DECLARE @xml XML

SET @XML =N'<root><r>’+REPLACE(@s, @sep,'</r><r>’)+'</r></root>’

INSERTINTO @t(val)

SELECT r.value(‘.’,’VARCHAR(MAX)’)as Item

FROM @xml.nodes(‘//root/r’)AS RECORDS(r)

RETURN

END

This e-mail address already exists in this organization OR How to find a needle in AD

Problem :

You go to create an email address and get the error “This e-mail address already exists in this organization”

so you search and find http://support.microsoft.com/kb/280765 and it does not help at all.

The Solution :

Active Directory Users and Computers utility

Right click on the domain, choose Find.

 In the Find dropdown list, choose “Custom Search” and  “Entire Directory”.

 Click the Advanced property page and enter the following LDAP query

proxyaddresses=smtp:smtp@domain.com

Click Find and you should find the object that already has that SMTP address.

Now you know what object and probably don’t have a problem if you are really in a bind right click, exchange tasks “Remove Exchange Attributes”


SCOM, SNMP and TRAPS or The Good, the Bad and the Ugly : Part 2

If you have made your way through Part 1 then you have written your management pack complete with your own custom discovery and imported it into SCOM.  Once you have ensured that it is discovering only the devices you wish to manage in this pack it is time to begin writing the monitors and rules that will apply to the detected devices.   As was mentioned in Part 1a program such as MIB Browser can be very handy in assisting with sorting through all of the OID’s and the healthy values which correspond with each individual OID. 

Creating an SNMP Get Monitor in SCOM 2007 R2

I find the easiest way to create a new monitor or rule is to start with the System Center Operations Manager Console. I will admit it’s not the best and does not give you many of the options you probably want but I find it’s the easiest way to get the XML started, and then edit it to get exactly what we want after the fact.

We will start in the management console, Authoring tab, expand Management Pack Objects and right click on Monitors, Select Create a Monitor \ Unit Monitor.

From within SCOM click on the Authoring tab and then right click on Monitors which is listed beneath Management Pack Objects.  At this point we would like to choose Create a Monitor – Unit Monitor, once this has been picked you will see the following screen:

 First we will create a simple expression Get Monitor and later we will deal with TRAPS, so we pick SNMP – Probe Based Detection – Simple Event Detection – Event Monitor……

Be sure to create this in the management pack you created for the discovery of the object.

Now we have to name our monitor, Select a target (You are looking for the device type you defined in part one and you may have to click the “View all targets” radio button for it to appear) and add a parent monitor (this defines where in the health view tree your new monitor will appear)

Personally I always use the discovery community string but you could use something custom if you want. The frequency is how often you want the monitor to poll the device and the object identifier or OID. This is the bit this will be used in the SNMP get call I find it works most reliably if you don’t have a leading period.

We need to create an expression what causes an alarm. I will keep the expressions simple so you can get a feel for one that works. Click the +Insert at the top and you are presented with 3 fields.  The first field that appears parameter name is the magic field.

/DataItem/SnmpVarBinds/SnmpVarBind[1]/Value

This is the value you are going to compare it is based on the First SnmpProbe from the step before. I have read that if you have more than one SnmpProbe that the number in this case [1] is in reverse order so [1] is at the bottom and [2] would be just above it in the list. Personally I have only one OID providers right now so I don’t know. Let me know if you figure it out for sure.  The operator gives you a drop down of choices. I will get into it more below but thing about this one carefully. If you can use a simple equals or does not equal you can make things much easier. Think of it like this if a UPS battery charge of anything less than 100% is bad then use an expression like “/DataItem/SnmpVarBinds/SnmpVarBind[1]/Value – does not equal – 100” instead of  “/DataItem/SnmpVarBinds/SnmpVarBind[1]/Value – less than – 100” it will just save you a bunch of extra steps even if it is not quite as flexible.

 

Second SnmpProbe lets you pick an OID just like for the first SnmpProbe personally all the monitor I have so far use the same OID as in the first provider as I am watching for a single value to be either good or bad. The second expression is exactly the same as the first. If you want a monitor that will not recover (you have to manually reset the health state  I use something like “/DataItem/SnmpVarBinds/SnmpVarBind[1]/Value – does not match wildcard – *” since any GET will have some result it will never not match and this will never recover.

Configure health lets you decide how the device health will change when the monitor gets tripped. I use second event raised as healthy and first event raised as warning or critical depending on whats going on.

The last option is if you want to create an alert or not, up to you.

Not so simple expressions

So lets say you don’t want a simple equals or does not equal kind of expression. It’s there in the drop down so whats the big deal you say? Well the SCOM Console make what I consider a bad assumption when creating rules and monitors. All the datatypes are strings. so although “100” does not equal “10” produces a true result “100” is greater than “10” when the values are strings has no meaning. Fixing this is actually not so hard and you have 2 choices. If the next bit is clear to use go for manual xml editing, if that makes you nervous then hold on for the second option.

Option 1 : Advanced

Export your MP to XML and open it in your favorite xml editor.

Way at the bottom you will find an ElementID linked to the text label you assigned to the monitor. Use this ElementID to find your monitor or rule and alter as follows.  I have highlighted the 4 places you must change “String” to “Integer”. Save the file and re-import it into SCOM and your monitor should be working.

<UnitMonitor ID=”UIGeneratedMonitor38d2a38d163b4c1f971885f7ea686f16″ Accessibility=”Public” Enabled=”true” Target=”GEUPS.Single.Phase.Management.Pack.SNMPDevice” ParentMonitorID=”Health!System.Health.AvailabilityState” Remotable=”true” Priority=”Normal” TypeID=”Snmp!System.SnmpProbe.2SingleEvent2StateMonitorType” ConfirmDelivery=”false”>
        <Category>Custom</Category>
        <AlertSettings AlertMessage=”UIGeneratedMonitor38d2a38d163b4c1f971885f7ea686f16_AlertMessageResourceID”>
          <AlertOnState>Error</AlertOnState>
          <AutoResolve>true</AutoResolve>
          <AlertPriority>Normal</AlertPriority>
          <AlertSeverity>Error</AlertSeverity>
          <AlertParameters>
            <AlertParameter1>$Data/Context/SnmpVarBinds/SnmpVarBind[1]/Value$</AlertParameter1>
          </AlertParameters>
        </AlertSettings>
        <OperationalStates>
          <OperationalState ID=”UIGeneratedOpStateId8a4572649aec48b58c336b84182c464b” MonitorTypeStateID=”SecondEventRaised” HealthState=”Success” />
          <OperationalState ID=”UIGeneratedOpStateId2bcf307948d14f4a892a99088603714a” MonitorTypeStateID=”FirstEventRaised” HealthState=”Error” />
        </OperationalStates>
        <Configuration>
          <FirstInterval>60</FirstInterval>
          <FirstIsWriteAction>false</FirstIsWriteAction>
          <FirstIP>$Target/Property[Type=”NetLib!Microsoft.SystemCenter.NetworkDevice”]/IPAddress$</FirstIP>
          <FirstCommunityString>$Target/Property[Type=”NetLib!Microsoft.SystemCenter.NetworkDevice”]/CommunityString$</FirstCommunityString>
          <FirstVersion>$Target/Property[Type=”NetLib!Microsoft.SystemCenter.NetworkDevice”]/Version$</FirstVersion>
          <FirstSnmpVarBinds>
            <SnmpVarBind>
              <OID>.1.3.6.1.2.1.33.1.2.4.0</OID>
              <Syntax>0</Syntax>
              <Value VariantType=”8″ />
            </SnmpVarBind>
          </FirstSnmpVarBinds>
          <FirstExpression>
            <SimpleExpression>
              <ValueExpression>
                <XPathQuery Type=”Integer“>/DataItem/SnmpVarBinds/SnmpVarBind[1]/Value</XPathQuery>
              </ValueExpression>
              <Operator>Less</Operator>
              <ValueExpression>
                <Value Type=”Integer“>96</Value>
              </ValueExpression>
            </SimpleExpression>
          </FirstExpression>
          <SecondInterval>60</SecondInterval>
          <SecondIsWriteAction>false</SecondIsWriteAction>
          <SecondIP>$Target/Property[Type=”NetLib!Microsoft.SystemCenter.NetworkDevice”]/IPAddress$</SecondIP>
          <SecondCommunityString>$Target/Property[Type=”NetLib!Microsoft.SystemCenter.NetworkDevice”]/CommunityString$</SecondCommunityString>
          <SecondVersion>$Target/Property[Type=”NetLib!Microsoft.SystemCenter.NetworkDevice”]/Version$</SecondVersion>
          <SecondSnmpVarBinds>
            <SnmpVarBind>
              <OID>.1.3.6.1.2.1.33.1.2.4.0</OID>
              <Syntax>0</Syntax>
              <Value VariantType=”8″ />
            </SnmpVarBind>
          </SecondSnmpVarBinds>
          <SecondExpression>
            <SimpleExpression>
              <ValueExpression>
                <XPathQuery Type=”Integer“>/DataItem/SnmpVarBinds/SnmpVarBind[1]/Value</XPathQuery>
              </ValueExpression>
              <Operator>GreaterEqual</Operator>
              <ValueExpression>
                <Value Type=”Integer“>96</Value>
              </ValueExpression>
            </SimpleExpression>
          </SecondExpression>
        </Configuration>
      </UnitMonitor>

Option 2 : Easy

Export your management pack to XML then Using System Center Operations Manager 2007 R2 Authoring Console open it.  You may be asked for dependencies that are usually found in “C:\Program Files\System Center Operations Manager 2007” but you can easily enough find *.mp

Once you find your monitor or rule of choice right click, properties. Configuration Tab.  Under each >>>XPathQuery and >>>Value you will see >>>@Type you need to change 4 Types to Integer.  You can see examples of the last two changed in the image below.

Then once you are finished just save the management pack and re-import it into SCOM and it should work.

 

Note: I am writing this in a somewhat sleep deprived state. I have not talked about rules at all but they are simpler than monitors so I hope it’s clear where the magic is. I will also thank David Allen for some blog posts that helped be although I can’t find them right now.  If things here are not clear or more detail is needed please comment or contact me and I will see what I can do.  

Part 1

Part 2

Part 3

SCOM 2007 R2 Automatic Alert Closing or Death by Brackets

Issue:

You have informational alerts, or any other alerts in the SCOM console that you want to have, but not stack up forever.

Solution:

Power shell, again I am far from a power shell expert, in fact this might be the first script I have created that is more than just calling an existing command-let.

for those of you who don’t care, here is a line that will resolve informational alerts more than 12 hours old. (run it from Operations Manager Shell typically  C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe -PSConsoleFile Microsoft.EnterpriseManagement.OperationsManager.ClientShell.Console.psc1 -NoExit .\Microsoft.EnterpriseManagement.OperationsManager.ClientShell.Startup.ps1)

get-alert -criteria “Severity = ‘0’ AND ResolutionState = ‘0’ AND LastModified <= ‘$((((Get-Date).ToUniversalTime())).addhours(-12))'”|resolve-alert| out-null

For those who want to know how it works , or for me once I forgot

 Get-Alert is a SCOM command-let (try get-help get-alert) for all the details.

-criteria allows us to filter based on whatever we want.

Severity = ‘0’  This is a zero just in case you are wondering and

Severity 0 = informational

Severity 1 = Warning

Severity 2 = Error

ResolutionState = ‘0’ again a zero and means

ResolutionState = ‘0’ is New

ResolutionState = ‘255’ Closed

Anything in the middle would be things you configured as custom resolution states


and now  the one that took all the effort

LastModified is when the alert was last modified, well duh you say and I agree but now for the hard part. This is logged in UTC so it won’t match with what you see in the console so we need to feed it a UTC time 12 hours in the past and for that we need more brackets that I ever figured.

$(Get-Date) processes the get-date command-let and passes a date that looks like this “Sunday, February 07, 2010 1:30:00 PM”

$((Get-Date).ToUniversalTime()) Takes the date from above and converts it to UTC based on your time zone offset, resulting in “Sunday, February 07, 2010 9:30:00 PM”

$((((Get-Date).ToUniversalTime())).addhours(-12)) takes the date from above and subtracts 12 hours giving “Sunday, February 07, 2010 9:30:00 AM”

By the magic of power shell this is changed into something more like ‘2/7/2010 9:30: AM’ and for that magic I am eternally grateful as I always hated date format issues in scripting (yea powershell)

Now power shell has gathered a lost of all the alerts we want to clear and we simply pipe that to resolve-alert

and pipe the output from they whole line to out-null so we don’t get any output.


Scheduling the Task

I never imagined that it would take more time and lines of code to schedule this script than it did to create.

Normally you could just run

The script as saved on the local drive as ClearInfo.ps1

$RMSFQDN=”FQDN of your RMS
Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client
New-PSDrive -Name: Monitoring -PSProvider OperationsManagerMonitoring -Root: \
cd monitoring:\
New-ManagementGroupConnection $RMSFQDN
cd $RMSFQDN
$pf = (gc Env:\ProgramFiles)
cd “$pf\System Center Operations Manager 2007”
.\Microsoft.EnterpriseManagement.OperationsManager.ClientShell.Functions.ps1;
Start-OperationsManagerClientShell -ManagementServerName: $RMSFQDN -PersistConnection: $true -Interactive: $true;
get-alert -criteria “Severity = ‘0’ AND ResolutionState = ‘0’ AND LastModified >= ‘$((((Get-Date).ToUniversalTime())).addhours(-12))'”|resolve-alert| out-null

Then simply call something like C:\WINDOWS\system32\WINDOW~2\v1.0\powershell.exe C:\scripts\ClearInfo.ps1 from task manager.

You may want to have a look at http://technet.microsoft.com/en-us/library/ee176949.aspx

SCOM 2007 R2 Console Command Line

Microsoft.MOM.UI.Console.Exe

Typically installed in C:\Program Files\System Center Operations Manager 2007

Microsoft.MOM.UI.Console.Exe /?

Command Line Syntax:

Microsoft.MOM.UI.Console.Exe {/Option[:Value]}

Option Description
/? Shows this help window
/ClearCache Clear the UI cache (this is the one that made me look for this)
/Server:<ServerName> Connect to the specified server
/ViewName:<ViewName> Display a view
/TaskName:<TaskName> Run a task
/TaskTarget:<ObjectId> Use in conjunction with /Task
/ManagementPack:<MpName> Use in conjunction with /TaskName and ViewName options

You can see it says you can find a complete list int he help but I have not found anything yet. Anyone else?

FYI – Don’t use /Clearcache when connected via RDP unless you know there are no others using the console at the time.

Also remember if you are trying to use viewname etc you should be using the internal name not the display name, something like this :

Microsoft.Mom.UI.Console.exe /viewname:System.Views.AlertView

SCOM 2007 Limited Access – The Shiny Red Button.

Issue:

So you want to have some people have access to see details in the SCOM console but you don’t trust them after having a conversation that goes something like this:

Now, listen. I’ve got a JOB for you. See this button?  DON’T TOUCH IT!

So… what’ll happen?

That’s just IT! You don’t KNOW! Maayyyybeeee something bad?… Mayyyybeeee something good! I guess we’ll never know! ‘Cause you’re not going to touch it! You won’t TOUCH it, will you?

Solution:

Actually this is quite simple and very effective.

Administration Tab: User Roles

SCOM 2007 R2 comes with built in roles, you may have seen them Administration, Security, User Roles. Whats that you say? You can’t use these because the users you want to grant access should only get specific server like SQL only and there give access to everything? Well not to worry I’ll get to that in a minute.

Default Roles

Profile typeProfile descriptionRole scope

Administrator

Has full privileges to Operations Manager; no scoping of the Administrator profile is supported.

Full access to all Operations Manager data, services, administrative, and authoring tools.

Advanced Operator

Has limited change access to Operations Manager configuration; ability to create overrides to rules; monitors for targets or groups of targets within the configured scope. Advanced Operator also inherits Operator privileges.

Can be scoped against any groups, views, and tasks currently present and those imported in the future.

Author

Has ability to create, edit, and delete tasks, rules, monitors, and views within configured scope. Author also inherits Advanced Operator privileges.

Can be scoped against any target, groups, views, and tasks currently present and those imported in the future. The Author role is unique in that this is the only profile type that can be scoped against the targets.

Operator

Has ability to edit or delete alerts, run tasks, and access views according to configured scope. Operator also inherits Read-Only Operator privileges.

Can be scoped against any groups, views, and tasks currently present and those imported in the future.

Read-Only Operator

Has ability to view alerts and access views according to configured scope.

Can be scoped against any groups and views currently present and those imported in the future.

Report Operator

Has ability to view reports according to configured scope.

Globally scoped.

Report Security Administrator

Enables integration of SQL Reporting Services security with Operations Manager roles.

No scope.

Pick a type that has the level of access you are looking for and then right click on Users Roles and create a new role.

General Properties : Here you can give your role a name, description and add members to it. Personally I suggest adding AD groups and not individual users but hey, it’s your environment so your call.

Group Scope : This is the half the magic but I talked about earlier. Here you define what groups of objects you want the user to be able to affect.

Tasks: You can approve all or only specific tasks you want this rile to be able to run.

Views:  The second half of the magic. Here you can pick specific branches of your monitoring tree and that’s all this rile will be able to see.

Now your console may look something like this for a UPS operator…

 

All right now, wasn’t that fun? Let’s try something else…..


Notification subscription ID generating this message: GUID’s. Very dangerous. You go first….

Issue:


Lately I have started getting multiple emails for a specific alert.


They all end something like this “Notification subscription ID generating this message: {5EAC6B2E-CA6B-8E0C-25C2-FD1C08AE7E47}” and I think to myself, would it have been so hard to put the friendly name in the email?


So I start looking for information on this GUID. I find that subscriptions are stored in a special management pack called  Microsoft.SystemCenter.Notifications.Internal, so I find and export this management pack and start having a look. Sure enough near the bopttom I can see friendly names of my subscriptions and the subscribers near the top but strangely a search for the GUID has no hits.  Before I really dig into this further I find another option.


Typically I prefer SQL queries becasue this is what I have done most over the years,


select * from managementpack where mpname=’Microsoft.SystemCenter.Notifications.Internal’


this will find me the management pack and in the column MPXML we find the whole XML of the management pack, but it’s not in a datatype that easily provides for xml queries so again I am off in another direction.  As a side note this appears to be an easy way to get the XML without having to export and unseal. I can’t stress enough I would never alter the XML here but it’s a easy way to get a quick look.


The Solution :


Powershell has been on my list to learn better now for a very long time, that really needs to change because of things like this.


Get-NotificationSubscription -id 5EAC6B2E-CA6B-8E0C-25C2-FD1C08AE7E47


Results


Configuration     : Microsoft.EnterpriseManagement.Administration.AlertChangedSubscriptionConfiguration
Name              : Subscription94b48623_87c2_4d90_b55e_b9b242d1b0be
DisplayName       : GE UPS Sungle Phase
Description       :
Actions           : {DefaultSmtpAction}
ToRecipients      : {Distribution List }
CcRecipients      : {}
BccRecipients     : {}
Enabled           : True
Id                : 5eac6b2e-ca6b-8e0c-25c2-fd1c08ae7e47
ManagementGroup   : A01
ManagementGroupId : 1d07457d-e935-7145-628e-22efb9d7fe5e

Now we have the friendly name of the subscriptions.


You can also use powershell to enable, disable or delete subscriptions as well but I didn’t look into that too much.



Now that I know what subscriptions are sending the alerts I can start working on removing the duplicates.