Monday, December 31, 2012

Description of Jannah (Paradise, Heaven) - Tawfique Chowdhury

The following information is based on my notes from a recent speech by Sheikh Tawfique Chowdhury at the Twins of Faith Conference (2012) in Malaysia.  The topic was "A Day in Paradise", and it was a brief description of Jannah based on a poem of Ibn Qayyim (based on relevant hadiths.  I believe the work is called Haadi al-Arwaah ilaa Bilaad il-Afraah).  I will do my best to accurately convey the message of the speech, though there may be some mistakes here and there.  Any mistakes are from me, and I ask forgiveness for them.

To begin, the Sheikh posed the question of what is our ultimate purpose of our creation.   The answer of the participants followed along the lines of what is outlined in the Quran where it is stated that Man and Jinn were created for the worship of Allah.  The Sheikh continued asking if this was the ultimate purpose of our creation?  If this was it, why is it that we are not required to fast, pray, give charity, etc when we are in Jannah?  He stated that his opinion on the matter is that we were created for the purpose of entering Jannah and that Allah loves his creation.

To illustrate the love of Allah for his servant, he quoted Sufian Ath-Athari (spelling?), who once said something to the effect of: He would rather have Allah as a judge over him than have his parents as a judge over him.  As the love of Allah for his servant exceeds that of the love of a parent for a child.

(random point of note) The purpose of ibaadah in this  world could be said that it is our showing thankfulness for the fact that Allah created Jannah for us and wants us to be in it.

In another narration related to Jannah, there was a companion by the name of Haritha who was on the way to the battle of badr, but died in route due to an accident.  As he did not die as a matyr in battle, his mother was distressed about what his state would be in the hereafter.  The prophet (saw) said to her (paraphrasing here), Woe to you oh mother of Haritha, do you think there is only one Jannah?  There are hundreds of Jannah's, and Haritha is in the highest.

---Side note here.  When the phrase "Woe to you" is used by the prophet (pbuh), it can be either a sign of anger, or a sign of happiness (depending on the circumstance)

In reference to the levels of jannah, the scholars are of two opinions.  One opinion is that there are 100 levels and this is based on an authentic hadith that states the specific number.  The other opinion stated that there was (5600?, didn't catch the exact number) levels, and this was based on the hadith where people are told to recite the quran as they recited it in the world and with each verse they would raise on level in the heavens.  So the number here relates to # of verses?  Allah knows best.

As for the levels themselves, the difference between one level and the next is like the heavens and the earth is in this world.  When one raises a level he will think he has gone so far that there couldn't be any more levels above them.

As to how to get to Jannah, the Sheikh referenced a hadith where the prophet stated 3 times, Is not the price of Jannah expensive?  In relation to this we need to understand it cannot be bought.  We need to perform actions to make us worth of it.  We can't just say we love Allah, because speaking such words does not guarantee Allah loves us in return.  The sheikh then referenced the verse of the Quran (3:31):

Sahih International: Say, [O Muhammad], "If you should love Allah , then follow me, [so] Allah will love you and forgive you your sins. And Allah is Forgiving and Merciful." 

 To emphasis how special Jannah is, the Sheikh related a hadith that mentions that Allah has created 3 things with his own hands:
1) The Taurat
2) Adam (As)
3) Jannah


In the description of Jannah, we start out on the big plain in front of Jannah.  There will be 120 rows of people; not old (our age is changed to 33 years old), we have the beauty of Yusuf, and we are 60ft tall/7ft wide which is the size of Adam (as).  Additionally there is no hijab and no beard (These are tests of this life.  And in relation to modesty of sisters, men will just not be able to see them).  Of the 120 rows, 80 will be from the ummah of Muhammad (saw), and the other 40 will be from people of the earlier nations.  In the rows will also be Jinn, though one point of note here is that in Jannah, we can see the Jinn and they cannot see us [reverse of this world].

Servants (in the form of children), who look like scattered pearls will come out.  They will give you the clothes of Jannah and the drinks of Jannah (to prepare you to be in an appropriate state for your entry).  The Clothes are made of the Tuba tree's bark.  You will also be given ornaments of gold and other precious materials.

The smell of Jannah is so great that we won't be able to hold ourselves back.  It can be smelled at the distance of 500 years travel.   (one note from myself to those reading this.  Think of this along with the hadiths that mention that the people that do certain actions will not even smell the fragrance of Jannah; may Allah protect us).

Jannah itself is held by 70,000 rings.  Each ring is held by 70,000 angels.

There are 8 gates to Jannah, and the distance between each gate is 500 years of travel.  The gates have huge signs with names carved in stone.  The list will be the names of those allowed to enter through this gate.  Each gate will have a spring that will contain the drinks of Jannah.  The people will rush between gates looking for where they will be allowed to enter.  Fragrance of Jannah will be blown down from the gates.  The sheikh did not mention all of the gates, but specifically pointed out four: 1) Jihad, 2) fasting, 3) goodness to parents?, 4) charity

When you find a gate that you are allowed to enter, an Angel will permit you to come in.  When you enter, you will look up and see rows of Angels of all types (2 wings, 4 wings, etc etc).  The angels will greet you with the greetings of peace.  You will see flashes of lightning behind the angles that are so severe that you can't look at them.  You will ask the servants of Jannah what they lightning is and they will just smile, turn away and talk to the other servants.  An angel will inform you that the lightning is caused by the smiles of your wives in Jannah.  

In relation to the wives, ibn Qayyim said that if it wasn't forbidden for us to die in Jannah, we would die from looking at our wives. 

From here, the Sheikh discussed some of the scenery and geography of Jannah.  The ground of Jannah is like saffron with pebbles made of rubites and other precious stones.  We will try to pick them up, but there will be too many.  There are rivers of Wine, milk and honey which will always stay fresh.  When you intend to take a drink from one of them, a spring will pop up for you to drink.  There are trees with all forms of fruit.  When we want to eat a piece of fruit, the tree will bend to bring it to us for us to bite.  When we desire to eat a bird of paradise, we just think it and it will come in front of us fully cooked.  We can eat as much as we want and we don't have to worry about weight and health.

On the horizon we will see the massive tree of Tuba, which gives shade to half of Jannah.  The distance of travel under it is 100 years travel.  It only grows in Jannah, and its bark is very silky and peels off easily in this layers like an onion.

As for our places in Jannah, the last person to enter jannah will see his kingdom, and it will be a size of 2000 years travel (in size) [my note: so imagine the size of those who are of higher degrees] 

In our kingdom's we will have a tent which is made of a hollowed out pearl.  This will be 600ft tall.  From the outside you will be able to see the inside, and from the inside you will be able to see outside.  In the tent you will meet your first wife. 

The women of jannah will have petite faces, translucent type of skin so that you can see the nerves in their body, full lips, full breasted, etc...very beautiful.  When they see you they will sing to you, and when you meet them it is as though you have been absent from them for a long time (as they have been waiting you...and watching you in this world).  When a mujahidin goes to battle, the women of Jannah put on marriage clothes in anticipation of the Mujahidin's martyrdom.  When we meet them in Jannah, they will ask us if we remember what day Allah married us to them.  This day can be any point in your life; perhaps you met a point of great difficulty in your life and you had patience and firm faith.

Also in your place of Jannah you will have magnificent palaces on top of rivers.  In this palace will be the rest of your wives of Jannah, and you can also have your wife from dunia as well.  The female believers in jannah will be so much better in beauty than the women of Jannah.  The sheikh made a statement (possibly a quote that I didn't catch the author of): Does the king think of King think of the servant when the Queen is there? Not at all.

It is possible to have children in Jannah.  Pregnancy only lasts 1 hour, and you can decided what ages the children are.  So you could have children that are a specific age forever so they you can always enjoy them at whatever age you like best.

At the end of your first week in Jannah, on Friday, a beautiful northern wind will blow, which makes a beautiful sound from the leaves of the Tuba tree.  There will be a rain with a beautiful smell of perfume that will cleanse you.  Your Buraq will come to take you to a gathering of all of the residents of Jannah at one level of Jannah.  Here you can see friends, prophets, companions, etc.  You will be seated in a circle on thrones of light.  You can drink together and talk.  After a while, and Angel will come to bring you away to an hour long face to face session with Allah.  

Of all of the pleasures of paradise, nothing is better than being able to see Allah.   The created tragedy is to not be able to experience this.

After we leave the gathering, we pass through the markets of Jannah which is hosted by the Angels.  In it are all sorts of amazing creations of Allah.  When we want something, we will realized we don't have any money, but we will be given the items on the basis of our good deeds done in this life.

The Sheikh mentioned another hadith:  Whoever says: O Allah, enter me into jannah (3 times), Jannah will ask Allah to enter him into Jannah.

This is the extent of what I have in my notes.  A few additional points to consider, although the description above may be wonderful, we need to know that even with these descriptions, we are absolutely incapable of imagining how great Jannah is really like.  In some other narrations mentioned in other talks, 1) Allah has only given one part of his mercy in this dunia, and saved 99 for the hereafter.   2)  Everyone will enter Jannah, except for the one that refuses to enter.  Do not be the one that refuses to enter Jannah by the disobedience of Allah.  Strive as hard as you can in this life, and have sincere intentions in all that you do (Ikhlaas, inshallah will cover some notes about that from AdbulRahman Green's talk that followed this one).

   

Thursday, December 27, 2012

"System detected a possible attempt to compromise security" Enter network password popup comes up on 2008R2

I came across an odd issue recently where a system was getting this error whenever you tried to access network resources. Additionally domain connectivity was not working very well. Some symptoms included:


1) Dns registration failed (secure DDNS)
2) Group policy processing failed: Event 1053 "Could not resolve the user name"
3) LSASRV 40960 events with authentication errors to various kerberos services such as domain controllers LDAP/ SPNS, and cifs/ for the DFS namespace.
4) TerminalServices error 1067, Cannot registery TERMSRV Service Principal Name
5) When joining the domain and changing primary dns suffix "Changing the primary domain DNS name of this computer failed." "A Directory service error has occurred"
6) Klist shows no kerberos tickets


When I looked at this in netmon, all of the Kerberos transactions for TGT requests would receive a preauthentication required error from the KDC (domain controller) and it wasn't following up on that. After trying to dig around for information related to that, and any possible Kerberos settings that might impact this, I could find nothing. I looked in the registry for LSA settings and found LMCompatibilityLevel at 1. After changing this to a 2, everything started to work fine. From the Microsoft description of NT Compatibility levels, I don't see how this would impact Kerberos transactions, but apparently there may be some correlation. This fix worked for a short while, however everything broke again soon after. Later investigation found the Kerberos encryption types had been restricted to AES only, which was not compatible with the domain. After enabling RC4-HMAC, the problems went away. I have seen related issues on other machines where neither of these two should have been the problem. So perhaps there are many causes.

Tuesday, December 18, 2012

POSH What do I need to do to get a ping? (test-connection out of resources)

I frequently write powershell scripts that run against a large number of machines. It has been put out in many places that for best practices, and error handling you should check connectivity to the remote machine first. This make sense. Another best practice typically thrown around is to use existing cmdlets. This of course saves the time of writing your own code, however I noticed with test-connection, it seems to have problems when you're running fast code on a large number of machines. If we use the existing Test-Connection in an IF statement for true/false checks of the machine being accessible or not, you may end up at a point where your machine is unable to process the command due to resource issues.

Error: Test-Connection : Testing connection to computer '10.0.0.1' failed: Error due to lack of resources

This is a problem as one of your attempts to handle errors becomes an error by itself. You can get around this with a few other methods. There is the standard ping.exe command which will return different error codes for status. You could use that and read the return to see if it evaluates successfully. Another way is through .NET's Net.NetworkInformation.Ping class. This can be used quickly and with few options, or if you want to control the ping you can look at Net.NetworkInformation.PingOptions.

As an example, here is an old piece of code I put together in the early Powershell v1 days.


function ping-host([string]$server) {
 #This function will perform a simple, small size 
        #single packet ping of a machine and return 
        #true/false for the result
 if ([string]::IsNullOrEmpty($server) ) {return $false}
 #ping first for reachability check
 $po = New-Object net.NetworkInformation.PingOptions
 $po.set_ttl(64)
 $po.set_dontfragment($true)
 [Byte[]] $pingbytes = (65,72,79,89)
 $ping = new-object Net.NetworkInformation.Ping
 $savedEA = $Erroractionpreference
 $ErrorActionPreference = "silentlycontinue"
 $pingres = $ping.send($server, 1000, $pingbytes, $po)
 if (-not $?) {return $false}
 $ErrorActionPreference = $savedEA
 if ($pingres.status -eq "Success") { return $true } else {return $false}
}

This will send a very small ping packet with a 1 sec timeout, hiding any .NET error messages and returning a true or false for availability.



Thursday, November 22, 2012

LDAP AD: Finding members of a group who have it as their primary group

An interesting problem came up today, where a developer was having problems pulling members of a domain group. The group shows hundreds of users when looking at it in Active Directory Users and Computers, however any LDAP connection to it only results in 50 users. Based on the name, I realized this group was used as a "Primary Group" for a group of special case users. Typically the "Primary Group" for a user is the Domain Users builtin group for the domain. If you look at this forum post, it shows a way to query for this group. On each user account is an attribute called PrimaryGroupID, which is a numeric value. In the article you can see that Domain Users is value 513 which is derived from its well known SID, ending in -513. So if you have another group that you want to look at, you will need to pull its SID, strip off the last number and query for all users that have that PrimaryGroupID number. This may not yield a complete list, so you can also search memberOf for that same group, or go with the member's attribute of the group itself. The first option allows you to build a single query with an | (or) statement in it, the latter would require some combining.

As an example, lets say our group was called SecondaryUsers.

In powershell we can get the object SID
$id = new-object System.Security.Principal.NTAccount(CONTOSO\SecondaryUsers)
$sid = $id.translate([system.security.principal.securityidentifier]).tostring()
$sidval = $sid.substring($sid.lastindexof('-')+1)

Now that we have our group's number, lets do an ldap search

$de = new-object directoryservices.directoryentry("LDAP://dc=contoso,dc=com")
$ds = new-object directoryservices.directorysearcher($de)
$ds.filter = "(&(objectclass=user)(|(primarygroupid=$($sidval))(memberof:=cn=SecondaryUsers,ou=MyGroups,dc=Contoso,Dc=com)))"
$ds.propertiestoload.add("samaccountname") |out-null
$users = $ds.findall()

Now we have all of the users under that group, whether they are memberof or member by PrimaryGroup.

Thursday, October 11, 2012

DES encryption and 2008R2 (does it really work?)

For everyone that finds themselves with DES dependencies and are having issues with 2008R2 and the DES turned off by default settings that it has, I have found a few things you can use to help check your environment.  First off, if you have systems that are 2008R2 pre SP1, there is a hotfix that is needed.  Otherwise SP1 should fix it (though there may be some exceptions).  If you have this in place, you can edit group policy to allow all encryption types.  After that, if you see see any intermittent issues, you can check individual domain controllers for DES support with java.  Get a copy of a java JRE, and there are some built in utility classes that you can use for quick tests of kerberos.

Java.exe "-Djava.security.krb5.kdc=MyDomainController1" "-Djava.security.krb5.conf=c:\windows\krb5.ini" "-Djava.security.krb5.realm=CONTOSO.COM" "-Djava.security.krb5.internal.crypto=DesCbcCRCEType" "sun.security.krb5.internal.tools.Kinit"  someuseraccount@CONTOSO.COM UserPassword

If you look at the example above (the quotes are in there for use within powershell), the first -D option points to your KDC, which is the domain controller you want to test.  The second points to a krb5.ini file (might not be required, you can search around to find the format of that if needed).  The third option: Realm, is the dns name of your domain.  Our fourth option is the encryption type you want to test.  You can do DesCbcCRCEtype or DesCbcMd5Etype (among others), to test different types of DES encryption ticket requests.  The last part of the java class paths is the Kinit class utility, and the two arguments after that are the username and password.  These can be any account in the domain, and doesn't require the DES checkbox on the user account to be checked.  Run this command and check output.  If there are errors, it will give you further details.  If DES is not supported on a domain controller, you will get an error 14 talking about encryption type no supported.  Otherwise, you will get a kerberos ticket that will be stored in a file. 

If you want verbose debugging details for the whole connection, you can add these two options prior to the kinit:

-Dsun.security.krb5.debug=true
-Dsun.security.krb5.krb5debug=all

If you want to test a large number of domain controllers, you can wrap this into a powershell function similar to this:

function check-dessupport {
    param(
        [string]$server,
        [string]$realm,
        [string]$user,
        [string]$password
    )
    echo $server >>c:\temp\kerbout.txt
    $res = new-object PSObject
    add-member -inputobject $res NoteProperty Server $server
    Java.exe "-Djava.security.krb5.kdc=$($server)" "-Djava.security.krb5.conf=c:\windows\krb5.ini" "-Djava.security.krb5.realm=$($realm)" "-Djava.security.krb5.internal.crypto=DesCbcCRCEType" "sun.security.krb5.internal.tools.Kinit"  $user $password 2>>c:\temp\kerbout.txt
    add-member -inputobject $res NoteProperty DesCbcCrc $?
    Java.exe "-Djava.security.krb5.kdc=$($server)" "-Djava.security.krb5.conf=c:\windows\krb5.ini" "-Djava.security.krb5.realm=$($realm)" "-Djava.security.krb5.internal.crypto=DesCbcMd5EType" "sun.security.krb5.internal.tools.Kinit"  $user $password 2>>c:\temp\kerbout.txt
    add-member -inputobject $res NoteProperty DesCbcMd5 $?
    return $res
}

This will return the server name and true/false for each time.  Just note that the failure of a test can just be the server is not available or is slow to respond.  This example logs the full details into c:\temp\kerbout.txt

To check the allowed encryption types that are set by policy you can look at the registry key

function check-enctypeRegEntry {
    param(
        [string]$server
    )
    $key = "Software\Microsoft\Windows\CurrentVersion\Policies\System\kerberos\parameters"
    $type = [Microsoft.Win32.RegistryHive]::LocalMachine

    $regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$server)
    $regkey = $regkey.opensubkey($key)
    $enctypes = $regkey.getvalue("supportedencryptiontypes")
    $ret = New-Object psobject
    Add-Member -InputObject $ret NoteProperty Server $server
    Add-Member -InputObject $ret NoteProperty EncType $enctypes
    return $ret
}

And you will need to decode the numeric value from there.  A decimal value of 31 is the value for all check boxes being checked except future encryption types.

For further details on dealing with DES, you can look at my previous article on the subject. If you do have everything enabled and you are at SP1 but still have DES issues, please comment on the post.

Thursday, June 28, 2012

Can you trust the man in the red underpants?

The man in the red underpants.  A great short story on how to trust ideas and beliefs.   The book starts by pointing out that you're not going to like it and it will talk about things you don't want to think about.  But, its a quick read, so why would you let that stop you? 

Click on the man to start exercising your mind.

Wednesday, June 27, 2012

Getting AD object metadata via powershell

Occasionally I receive requests in my organization to see when was a user changed, when was someone added removed from a group, etc etc.  I thought it would be nice to get away from repadmin with this and come up with something that can provide enough information, be somewhat easy to use, and not require any special rights or tools.  So I put together this script to pull metadata (either attribute changes, or multivalue changes) on computers, users, or group objects.  In the script, you provide the common name of the object and the type of object.  From here it searches the global catalog for it, pulls the metadata, cuts down the attributes and outputs it in pre-sorted columns.  From there it can be searched or further formated using typical powershell commandlets to manipulate objects.  For those that want to take this further, you can follow the example of how to pull the metadata via ldap and manipulate the XML to include the attributes you want.  Some caveats with this which you won't find in repadmin /showobjmeta is that the times are in Z time, and recording domain controller data is using the domain controller GUID.  So there are some opportunities for expansion and improvement of this script for a more tech oriented audience.




#requires -version 2

param(
 [parameter(mandatory=$true)][alias('samaccountname','cn','username','groupname')]$name,
 [parameter(mandatory=$true)][ValidateSet("Group","Computer","User")]
  [alias('object','objecttype')]$type,
 [switch][alias("members","member")]$valuemeta
)

#Set up connection to forest
$de = New-Object directoryservices.DirectoryEntry("GC://dc=contoso,dc=com")
$ds = new-object directoryservices.directorysearcher($de)

#set an appropriate search filter for each type of object
switch ($type) {
  "group" {$ds.filter = "(&(objectclass=group)(|(samaccountname=$name)(cn=$name)))" }
  "computer" { $ds.filter = "(&(objectclass=computer)(cn=$name))" }
  "user" { $ds.filter = "(&(objectclass=user)(samaccountname=$name))" }
} 

#load up metadata attribs and search
$ds.propertiestoload.add("distinguishedname") > $null
$fu = $ds.findone()
if ($fu -ne $null) {
 $de = New-Object directoryservices.DirectoryEntry("LDAP://" + $fu.properties.distinguishedname[0])
} else {
 Write-Error "Object not found in AD"
 exit 1
}
$ds.searchroot = $de
$ds.propertiestoload.add("msDS-ReplAttributeMetaData") > $null
$ds.propertiestoload.add("msDS-ReplValueMetaData") > $Null
$fu = $ds.findone()

#display the requested type of data
if ($valuemeta) {
 $xml = "<root>" + $fu.properties."msds-replvaluemetadata" + "</root>"
 $xml = [xml]$xml
 $xml.root.DS_REPL_VALUE_META_DATA | 
  Select-Object @{name="Attribute"; expression={$_.pszAttributeName}},@{name="objectDN";expression={$_.pszObjectDN}},ftimeDeleted,ftimeCreated |
  Sort-Object attribute
} else {
 $xml = "" + $fu.properties."msds-replattributemetadata" + ""
 $xml = [xml]$xml
 $xml.root.DS_REPL_ATTR_META_DATA |
  select-object @{name="Attribute";expression={$_.pszAttributeName}},@{name="ChangeTime";expression={$_.ftimeLastOriginatingChange}}|
  Sort-Object attribute
}

<#
.DESCRIPTION
 Show-ADObjMeta will show the attributes and last time written on the object.
 
.PARAMETER Name
 The name of the user/computer/group that you want to pull the metadata for.  This should be the logon
 id if it is a user, the computer name for computers, and the groupname (cn or samaccountname) for groups.

.PARAMETER ValueMeta
 Use this parameter if you want to view multivalue item metadata, like Group Member add/remove details
 
.PARAMETER Type
 Specify the type of object: User, Group, Computer
 
.EXAMPLE
 Show-ADObjMeta -type user -name myuser

 Get the metadata for attributes of myuser
 
#>

Tuesday, June 26, 2012

Bulk adding users to a domain group based on their email address

This is a quick script I threw together to help out someone with adding two thousand users to a group based on an email list. As you may notice, the format of the email and the domain DN is specified here, so if you want to use it in your environment, change the "contoso" parts to whatever is valid for your forest. This script assumes that the group that you want to add users to is a uniquely named group, as I didn't write anything to handle multiple groups in different domains with the same CN value. The script will take the email addresses from a text file (one per line) and add them to the group. If the email can't be found, or group can't be modified it will provide error details on a per email basis.




#requires -version 2

#Bulk add by email address value

param(
 [parameter(mandatory=$true)][ValidateScript({Test-path $_ -PathType Leaf})]$userlist,
 
 [parameter(mandatory=$true)][ValidateScript( {
  #This validator will check AD to see if the group exists, and it will set $script:tgtgrp to the object that
  #will be modified
  $validatesearcher = New-Object directoryservices.DirectorySearcher("GC://dc=contoso,dc=com")
  $validatesearcher.filter = "(&(objectclass=group)(cn=$_))"
  $script:tgtgrp = $validatesearcher.findone()
  return $tgtgrp
 })]$groupname
)

$script:tgtgrp = [adsi]$script:tgtgrp.path
$users = get-Content $userlist
$de = New-Object directoryservices.DirectoryEntry("GC://dc=contoso,dc=com")
$searcher = New-Object directoryservices.Directorysearcher($de)
foreach ($user in $users) {
 try {
  if ($user -notmatch "@contoso.com") {
   throw "Entry: $user  is not a valid email address"
  }
  
  $searcher.filter = "(&(objectclass=user)(proxyaddresses=SMTP:$user))"
  $userobj = $searcher.findone()
  if ($userobj -eq $null) {
   throw "Entry: $user  could not be found in active directory"
  }
  
   try {
    $script:tgtgrp.properties["member"].add($userobj.properties.distinguishedname[0]) |out-null
    $script:tgtgrp.setinfo()
   } catch {
    throw "Error occurred when trying to add member:$($userobj.properties.distinguishedname[0]) to group"
   }
  
 } catch {
  Out-Default -InputObject $_.exception
 }
}

<#
.DESCRIPTION
 Add-BulkUserByEmail will add users to a specified group (Used quotes around groups with spaces in them)
 from a text file list of user email addresses (one user per line)
 
.PARAMETER Userlist
 The full path to the userlist text file (ex: c:\temp\userlist.txt)
 
.PARAMETER Groupname
 The name of the group object in active directory (CN attribute value).  This name must be unique in the 
 forest, or the wrong group may be selected

.EXAMPLE
 Add-BulkUserByEmail -userlist c:\temp\userlist.txt -groupname "My Distro List group"
 
#>

Monday, June 4, 2012

Trinity - summed up in one verse

 
I have seen a lot of attempts to wrap the idea of trinity (God being 3 in 1), by projecting this philosophy on to various verses in ways that make the verse mean other than what it obviously means. In other cases, there were fabricated verses inserted into the bible. And in more we have people attempting to explain away glaring contradictions by saying Jesus didn't know he was God, or was only human at that specific time...basically grasping at straws with no knowledge of the incident in question. I thought I would sum up the trinity in one verse using basic logic. The verse:

Mark 10:18       (Comparitive translations)

"Why do you call me good?" Jesus answered. "No one is good--except God alone."

This is in response to a man just calling Jesus "Good teacher" and asking for guidance on how to get eternal life. Since Jesus was a teacher of goodness and called people to the truth, this would be an appropriate title to address him with. But Jesus responds that only God is good. Lets look at this from the perspective of Jesus and God being part of a trinity.

In Trinity: Jesus = God
In Logic: Jesus says only God is good, thus stating that he is not, by his rejection of the man's
               assertion Jesus = Good, he implies Jesus != Good 
               If Jesus = God, Jesus would be the same entity and (Jesus = Good) must also be true.

Since he is rejecting this statement, then this must be true:
               Jesus != God 

Additionally, in the verse, the word used is "God", not "The Father".  So there is no means that anyone could try to say that they are still the same essence, with one being better than the other in some way.

Besides the logical argument, look how Jesus reacts to the statement.  He could have ignored it, or assumed it was a simple compliment, but he instead immediately redirected the focus to the One that is worthy of all praise...OUR LORD.  Jesus stated that the most important commandment is the first commandment; the oneness of God.  This is a perfect demonstation of giving God what is due to Him, as Jesus said, I of myself can do nothing.


There are many obvious verses that point to this truth. Even verses used to "prove" the trinity, do more to disprove it than they do to support it. I hope the Christians of the world contemplate deeply about the nature of their faith and the source of their book. Look deep for truth.  Ask yourself why none of the messengers talked about trinity or God being multiple parts.  When asked what is the most important of commandments, Jesus stated in Mark 12:29 that is the first: "Hear, O Israel: The Lord our God, the Lord is one."  Jesus points out that he is an example to follow, yet none follow his ways of doing things. For the sincere of faith, and the one that truly wants success in thereafter, pray as Jesus prayed (Matthew 26:39) and ask our Lord (the father) for guidance to see truth and to know the way to return to him with success.  It would be a tragedy if the good works you do are in vain because you haven't applied the first commandment.

Start looking for answers with sincerity.
A good place to start, The true message of Jesus


For some more points to consider, please view some points from my next post.

Thursday, April 26, 2012

Decoding AD ACL's (Powershell)

Today I was looking at a deep dive video on creating modules and found out about the BSonPosh module.  This contains some interesting commandlets, one of which is similar to some of the work I have been doing lately related to managing AD object ACL's.  The commandlets for get-adacl in this module are similar to what you can get with the Microsoft AD module using get-acl pointing to the AD PSDrive.  The thing that makes BSOnPosh great in comparison is that it works great on a multi-domain environment, and you don't need to go out of your way to point to 2008 servers if you have a mixed environment.  In any case, if you look at my previous post related to decoding GUID's in ACL's by converting GUID's into an ldap searchable format, we can use this to extend the work done by BSonPosh.  First, lets look again at what the acl entries look like:

ActiveDirectoryRights : CreateChild, DeleteChild
InheritanceType : None
ObjectType : bf967aa8-0de6-11d0-a285-00aa003049e2
InheritedObjectType : 00000000-0000-0000-0000-000000000000
ObjectFlags : ObjectAceTypePresent
AccessControlType : Allow
IdentityReference : S-1-5-32-550
IsInherited : False
InheritanceFlags : None
PropagationFlags : None

Here, you can see many of the enumerations are decoded into text, but we have guid's and SID's that may show up without translation.  With BSonPosh, there is a function for converting SID's already, though in this case I'm modifying it to handle SID's that do not translate with that code.  Combining that with another helper function to decode the GUID's to the ldap display attribute listed in the schema,  you can get a result that is more readable like this:

Get-ADACL -distinguishedname "dc=contoso,dc=com" | convert-adacl

ActiveDirectoryRights : ReadProperty, WriteProperty, Delete
InheritanceType          : Descendents
ObjectType                : msRTCSIP-UserPolicies
InheritedObjectType   : user
ObjectFlags                : ObjectAceTypePresent, InheritedObjectAceTypePresent
AccessControlType    : Allow
IdentityReference        : CONTOSO\RTCDomainUserAdmins
IsInherited                   : False
InheritanceFlags          : ContainerInherit
PropagationFlags        : InheritOnly

Below is the code.  One function was changed (ConvertTO-Name).  The rest is added to BSActiveDirectory.psm1.  It is important to note, that this conversion function creates its own custom PSobject, so it is not longer an ACL that you can play with and try to write back.  This was more designed for auditing purposes.



#region Convert-ADACL 

function Convert-ADACL
{
        
    <#
        .Synopsis 
            Translates the AD Object ACL to a more readable format by converting SID and GUID values to text
            
        .Description
            Translates the AD Object ACL to a more readable format by converting SID and GUID values to tex
            
        .Parameter ACL
            ACL Object to Apply
            
        .OUTPUTS
            Object
            
    #>
        [cmdletbinding()]
        Param(
    
            [Parameter(ValueFromPipeline=$true)]
            [System.DirectoryServices.ActiveDirectoryAccessRule]$ACL
            
        )

  Begin {
   $results = @()
  }
  
  process {
   if ($_.ActiveDirectoryRights -eq "ExtendedRight") {
    $myresult = New-Object PSobject -Property @{
     ActiveDirectoryRights = $_.ActiveDirectoryRights
     InheritanceType = $_.InheritanceType
     ObjectType = Convert-GUIDToName -guid $_.objecttype -extended
     InheritedObjectType = Convert-GUIDToName -guid $_.inheritedobjecttype
     ObjectFlags = $_.ObjectFlags
     AccessControlType = $_.accesscontroltype
     IdentityReference = ConvertTo-Name -sid $_.identityReference
     IsInherited = $_.isinherited
     InheritanceFlags = $_.InheritanceFlags
     PropagationFlags = $_.PropagationFlags
    }
   } else {
          $myresult = New-Object PSobject -Property @{
     ActiveDirectoryRights = $_.ActiveDirectoryRights
     InheritanceType = $_.InheritanceType
     ObjectType = Convert-GUIDToName -guid $_.objecttype
     InheritedObjectType = Convert-GUIDToName -guid $_.inheritedobjecttype
     ObjectFlags = $_.ObjectFlags
     AccessControlType = $_.accesscontroltype
     IdentityReference = ConvertTo-Name -sid $_.identityReference
     IsInherited = $_.isinherited
     InheritanceFlags = $_.InheritanceFlags
     PropagationFlags = $_.PropagationFlags
    }  
   }
   $results += $Myresult

  }
  end {
   $results |Select-Object ActiveDirectoryRights,InheritanceType,ObjectType,InheritedObjectType,ObjectFlags,`
    AccessControlType,IdentityReference,IsInherited,InheritanceFlags,PropagationFlags
  }
}
    
#endregion 


#region ConvertTo-Name 

function ConvertTo-Name
{
    param($sid)
 Write-Verbose $sid
 try {
     $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
     $User = $ID.Translate( [System.Security.Principal.NTAccount])
     $User.Value
 } catch {
  switch($sid) {
   #Reference http://support.microsoft.com/kb/243330
   "S-1-0" { "Null Authority" }
   "S-1-0-0" { "Nobody" }
   "S-1-1" {"World Authority" }
   "S-1-1-0" { "Everyone" }
   "S-1-2" { "Local Authority" }
   "S-1-2-0" { "Local" }
   "S-1-2-1" { "Console Logon" }
   "S-1-3" { "Creator Authority" }
   "S-1-3-0" { "Creator Owner" }
   "S-1-3-1" { "Creator Group" }
   "S-1-3-4" { "Owner Rights" }
   "S-1-5-80-0" {"All Services" }
   "S-1-4" { "Non Unique Authority" }
   "S-1-5" { "NT Authority" }
   "S-1-5-1" { "Dialup" }
   "S-1-5-2" { "Network" }
   "S-1-5-3" { "Batch" }
   "S-1-5-4" { "Interactive" }
   "S-1-5-6" { "Service" }
   "S-1-5-7" { "Anonymous" }
   "S-1-5-9" { "Enterprise Domain Controllers"}
   "S-1-5-10" { "Self" }
   "S-1-5-11" { "Authenticated Users" }
   "S-1-5-12" { "Restricted Code" }
   "S-1-5-13" { "Terminal Server Users" }
   "S-1-5-14" { "Remote Interactive Logon" }
   "S-1-5-15" { "This Organization" }
   "S-1-5-17" { "This Organization" }
   "S-1-5-18" { "Local System" }
   "S-1-5-19" { "NT Authority Local Service" }
   "S-1-5-20" { "NT Authority Network Service" }
   "S-1-5-32-544" { "Administrators" }
   "S-1-5-32-545" { "Users"}
   "S-1-5-32-546" { "Guests" }
   "S-1-5-32-547" { "Power Users" }
   "S-1-5-32-548" { "Account Operators" }
   "S-1-5-32-549" { "Server Operators" }
   "S-1-5-32-550" { "Print Operators" }
   "S-1-5-32-551" { "Backup Operators" }
   "S-1-5-32-552" { "Replicators" }
   "S-1-5-32-554" { "Pre-Windows 2000 Compatibility Access"}
   "S-1-5-32-555" { "Remote Desktop Users"}
   "S-1-5-32-556" { "Network Configuration Operators"}
   "S-1-5-32-557" { "Incoming forest trust builders"}
   "S-1-5-32-558" { "Performance Monitor Users"}
   "S-1-5-32-559" { "Performance Log Users" }
   "S-1-5-32-560" { "Windows Authorization Access Group"}
   "S-1-5-32-561" { "Terminal Server License Servers"}
   "S-1-5-32-561" { "Distributed COM Users"}
   "S-1-5-32-569" { "Cryptographic Operators" }
   "S-1-5-32-573" { "Event Log Readers" }
   "S-1-5-32-574" { "Certificate Services DCOM Access" }
   "S-1-5-32-575" { "RDS Remote Access Servers" }
   "S-1-5-32-576" { "RDS Endpoint Servers" }
   "S-1-5-32-577" { "RDS Management Servers" }
   "S-1-5-32-575" { "Hyper-V Administrators" }
   "S-1-5-32-579" { "Access Control Assistance Operators" }
   "S-1-5-32-580" { "Remote Management Users" }
   
   default {$sid}
  }
 }
}
    
#endregion 

#region Convert-GUIDToName

 #helper module to convert schema GUID's to readable names

function Convert-GUIDToName
{
 param(
  [parameter(mandatory=$true)][string]$guid,
  [switch]$extended
 )
 
 $guidval = [Guid]$guid
 $bytearr = $guidval.tobytearray()
    $bytestr = ""
    
 foreach ($byte in $bytearr) {
          $str = "\" + "{0:x}" -f $byte
          $bytestr += $str
    }
 
 if ($extended) {
  #for extended rights, we can check in the configuration container
  $de = new-object directoryservices.directoryentry("LDAP://" + ([adsi]"LDAP://rootdse").psbase.properties.configurationnamingcontext)
  $ds = new-object directoryservices.directorysearcher($de)
  $ds.propertiestoload.add("displayname")|Out-Null
  $ds.filter = "(rightsguid=$guid)"
  $result = $ds.findone()
 } else {
  #Search schema for possible matches for this GUID
  $de = new-object directoryservices.directoryentry("LDAP://" + ([adsi]"LDAP://rootdse").psbase.properties.schemanamingcontext)
  $ds = new-object directoryservices.directorysearcher($de)
  $ds.filter = "(|(schemaidguid=$bytestr)(attributesecurityguid=$bytestr))"
  $ds.propertiestoload.add("ldapdisplayname")|Out-Null
  $result = $ds.findone()
 } 
 if ($result -eq $null) {
  $guid
 } else {
  if ($extended) {
   $result.properties.displayname
  } else {
   $result.properties.ldapdisplayname 
  }
 }
 
}
#endregion

Update: 1/29/2013

Just an additional note. Since you need distinguishednames for the commandlets, this can be a pain at times. If you like to use dsquery for quick and short results you can pipe these in, however you need to deal with the quotations that are put around the dsquery output. Example:

dsquery group -name Mygroup | foreach {$_.replace("`"","")} | 
get-adacl | where {$_.isinhertied -eq $false} |convert-adacl


Update: Sept 2020
It looks like all the links to the BSOnPosh module are broken now. Below is the functions that were referenced from there.


 function Get-ADACL
{
        
    <#
        .Synopsis 
            Gets ACL object or SDDL for AD Object
            
        .Description
            Gets ACL object or SDDL for AD Object
            
        .Parameter DistinguishedName
            DistinguishedName of the Object to Get the ACL from
            
        .Parameter SDDL [switch]
            If passed it will return the SDDL string
            
        .Example
            Get ACL for ‘cn=users,dc=corp,dc=lab’
                Get-ADACL ‘cn=users,dc=corp,dc=lab’
            Get SDDL for ‘cn=users,dc=corp,dc=lab’
                Get-ADACL ‘cn=users,dc=corp,dc=lab’ -sddl
                
        .Outputs
            Object
            
        .Link
            N/A
            
        .Notes
            NAME:      Get-ADACL
            AUTHOR:    YetiCentral\bshell
            Website:   www.bsonposh.com
            #Requires -Version 2.0
    #>
    
    [Cmdletbinding()]
    Param(
    
        [Alias('dn')]
        [ValidatePattern('^((CN|OU)=.*)*(DC=.*)*$')]
        [Parameter(ValueFromPipeline=$true,Mandatory=$True)]
        [string]$DistinguishedName,
        
        [Parameter()]
        [switch]$SDDL
    )
    Write-Verbose " + Processing Object [$DistinguishedName]"
    $DE = [ADSI]"LDAP://$DistinguishedName"
    
    Write-Verbose "   - Getting ACL"
    $acl = $DE.psbase.ObjectSecurity
    if($SDDL)
    {
        Write-Verbose "   - Returning SDDL"
        $acl.GetSecurityDescriptorSddlForm([System.Security.AccessControl.AccessControlSections]::All)
    }
    else
    {
        Write-Verbose "   - Returning ACL Object [System.DirectoryServices.ActiveDirectoryAccessRule]"
        $acl.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier])
    }
}

Thursday, April 19, 2012

Powershell overlapping pipeline problem

During the recent Microsoft 2012 scripting games, I encountered an interesting problem which really threw me off for a few hours. When trying to link up WMI results with registry entries in the Advanced 8 event, I ended up with a lot of Get-ItemProperty failures, where the values being read in my registry access where actually results from my WMI lookups. Here is a sample of the code

function get-physicalphysical {
     param(  [parameter(position=0)][string]$nicGUID   )
    $vals = Get-ChildItem hklm:"\System\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}" |
          get-itemproperty -name $_.name | where {$_.netcfginstanceid -match $nicGuid}
    if ($vals.characteristics -eq 0x84) { return $true } else { return $false }
}

$nics = Get-WmiObject -Class Win32_NetworkAdapter -Filter "PhysicalAdapter = True" |
where {get-physicalphysical $($_.guid)}

So we have GWMI for network adapters piped to a function called get-physicalphysical.  The guid of the WMI network adapter result is searched in the registry for lower level details that are not available via WMI.  The error we get is something like this (repeating many times and for different adapter names)

Get-ItemProperty : Property Intel(R) 82567LM Gigabit Network Connection does not exist at path HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\0000.
At line:11 char:25
+         get-itemproperty <<<<  -name $_.name | where {$_.netcfginstanceid -ma
tch $nicGuid}
    + CategoryInfo          : InvalidArgument: (Intel(R) 82567L...work Connect
   ion:String) [Get-ItemProperty], PSArgumentException
    + FullyQualifiedErrorId : System.Management.Automation.PSArgumentException
   ,Microsoft.PowerShell.Commands.GetItemPropertyCommand

In this case, the WMI results have a property called Name, and the entries in the registry that we were looking at also have a value for Name.  What we want is the name of the registry key that contains the configurations for the adapter.  But with the use of two pipelines, it appears that the $_ referenced in the pipeline within the function is accessing the pipeline $_ from outside of the function.  I'm not expert in low level powershell details and how the pipeline works, but after playing around a bit with trying to scope the $_ variable, and eventually reading the about_functions help, I found that you can scope a function.  By scoping the function and the $_ variable within it (using the same scope), I was able to separate the two pipelines and accomplish what I was hoping for.  You can see the full code at the entry script.

Friday, March 23, 2012

AD Powershell module and managing multiple domains

One of the things I have noticed every time I try using the Microsoft AD powershell commandlets is that they are not very multi-domain friendly. Most of the commandlets have a -server option where you can point to a server. To do this dynamically, you need to discover a DC with Get-ADDomainController first. However, today I was thinking it would be nice to update permissions cross domain with get-acl/set-acl. Unfortunately, there is no -server option here. From the examples I have seen for managing AD permissions with these two, it uses the AD: PSdrive that gets created when the ActiveDirectory Module is loaded. This default drive points to the domain that your machine is a member of, so this may not be helpful. If you try to use get-acl with this drive and point to a distinguishedName in another domain you will see an error like this:

Get-Acl : A referral was returned from the server

Since AD: is a PSDrive, there is no reason you can't add some more for other domains. Lets say you have two domains, contoso.com and child.contoso.com

New-PSDrive -Name "Child" -Root "" -PsProvider ActiveDirectory -server (Get-ADDomainController -domain child.contoso.com -discover -writable).name

This will create a PSDrive called Child:, which will reference this domain. So to work with get-acl you can reference child:"objectdn" to get the acl. For other commandlets, set-location child: and try using these to access objects in it.

If you wanted to auto-create a drive for every domain in your forest you could do this easily (though a bit slowly). PSDrive names are limited in that they cannot contain '.', so in this example I'm just stripping a domain's first portion of its name out:

(get-adforest).domains|foreach {$temp = $_.substring(0,$_.indexof('.')); New-PSDrive -Name $temp -Root "" -PsProvider ActiveDirectory -server (Get-AD
DomainController -domain $_ -discover -writable).name}


Update:

After trying to work with the Microsoft AD powershell module for this, and having a lot of problems in a mixed 2008/2003 multidomain environment, I ran into the BSonPosh module. This has a much simpler way of reading and writing ACL's in AD which looks like it will run very easily in a multidomain environement, or non-2008 AD domain. I have a extension to the module to Decode AD ACL.

Wednesday, March 21, 2012

Checking NIC for collisions (speed/duplex mismatch) Windows Powershell

This is an old powershell v1 script that I came up with quite a long time ago. I'm sure it can be modified for easier and more robust usage. Since network card settings in the registry can be difficult to decode between vendors, the best way to look for speed/duplex issues is looking at collisions. Microsoft systems are not very details on networking errors, but they do keep track of this type of problem and it is available WMI. The script below pieces together two WMI classes to get the speed setting and looks for collisions.

Results in PSObject array of all NIC's
Computername        NicName                       LinkSpeed          Collisions
------------        -------                       ---------          ----------
MyComputer          Intel(R) PRO/100...                 100                   0


function get-NicError ([string]$server) {
 #This function uses WMI queries against the target machine to get link speed information and
 #check for any recorded network collissions.  It combines the two queries to print out collisions
 #per adapter with link speed.

 if ([string]::IsNullOrEmpty($server)) {
  write-host -foregroundcolor "yellow"  "Usage: get-NicError servername"
  write-host -foregroundcolor "yellow"  "   Get speed and collision details for network adapters"
  return
 }

 $result = @()

 $a = gwmi -namespace root\wmi -class msndis_ethernetmoretransmitcollisions -computername $server |select-object instancename,ndisethernetmoretransmitcollisions
 $b = gwmi -namespace root\wmi -class msndis_linkspeed -computername $server | select-object instancename,ndislinkspeed 

 if (($a -eq $null) -or ($b -eq $null)) {
  write-error "WMI connection error"
  return 
 }

 foreach ($link in $b) {
  $link.ndislinkspeed = $link.ndislinkspeed / 10000
 }

 foreach ($item in $a) {
   foreach ($seconditem in $b) {
    if ($item.instancename -eq $seconditem.instancename) {
      $myres = New-Object psobject
     Add-Member -InputObject $myres NoteProperty Computername $server
     Add-Member -InputObject $myres Noteproperty NicName $item.instancename
     Add-Member -InputObject $myres Noteproperty LinkSpeed $seconditem.ndislinkspeed
     Add-Member -InputObject $myres Noteproperty Collisions $item.ndisethernetmoretransmitcollisions
     $result += $myres
    }
   }
 }

 return $result
   
}

Thursday, February 23, 2012

Crazy uptime (windows 2000 domain controller)

C:\>uptime
\\SRVNAMECHANGED has been up for: 2111 day(s), 10 hour(s), 31 minute(s), 52 second(s)

Anyone got a datacenter and server that stable?  This is a Dell PE2650 with windows 2000, which also has 0 hardware problems. 

Tuesday, February 21, 2012

Converting guid to escaped bytes for LDAP lookups (powershell, AD ACL)

If you have looked at access lists in active directory using get-acl, you may be familiar with this type of entry

ActiveDirectoryRights : CreateChild, DeleteChild
InheritanceType : None
ObjectType : bf967aa8-0de6-11d0-a285-00aa003049e2
InheritedObjectType : 00000000-0000-0000-0000-000000000000
ObjectFlags : ObjectAceTypePresent
AccessControlType : Allow
IdentityReference : S-1-5-32-550
IsInherited : False
InheritanceFlags : None
PropagationFlags : None

Given that the ObjectType and occsionally the InheritedObjectType are guid's, it may be difficult to determine what this entry is referring to. You can do an ldap lookup into the schema partition to find what object or attribute this guid is referring to, however the ldap filter for the search needs the guid in binary. .Net comes to the rescue for easy conversion to binary, but this is not sufficient for an ldap search, as it returns a byte array.

You can create a GUID object in powershell by casting $myguid = [guid]"bf967aa8-0de6-11d0-a285-00aa003049e2"

In this object, there is a byte conversion method tobytearray().

For ldap searches, we need the byte array escaped with forward slashes on each byte. To do this you can run the guid through this simple function


function guid-toescapedbyte($guid) {
     $bytearr = $guid.tobytearray()
     $bytestr = ""
     foreach ($byte in $bytearr) {
          $str = "\" + "{0:x}" -f $byte
          $bytestr += $str
     }
     return $bytestr
}

In our example we will get this:

guid-toescapedbyte([guid]"bf967aa8-0de6-11d0-a285-00aa003049e2")
\a8\7a\96\bf\e6\d\d0\11\a2\85\0\aa\0\30\49\e2

We can add this to a filter like this:

$de = new-object directoryservices.directoryentry("LDAP://cn=schema,cn=configuration,dc=contoso,dc=com")
$ds = new-object directoryservices.directorysearcher($de)
$bytestr =  guid-toescapedbyte [guid]"bf967aa8-0de6-11d0-a285-00aa003049e2"
$ds.filter = "(|(schemaidguid=$bytestr)(attributesecurityguid=$bytestr))"

$ds.findone().properties

Name Value
---- -----
systemmustcontain {versionNumber, uNCName, shortServerName, ser...
admindisplayname {Print-Queue}
name {Print-Queue}
objectguid {184 69 64 30 219 21 163 78 168 107 59 106 18...
systemonly {False}
whencreated {10/21/1630 4:21:11 PM}
defaultobjectcategory {CN=Print-Queue,CN=Schema,CN=Configuration,DC...
systemflags {16}
ldapdisplayname {printQueue}
usnchanged {4748}
objectcategory {CN=Class-Schema,CN=Schema,CN=Configuration,D...
systemposssuperiors {organizationalUnit, domainDNS, container, co...
showinadvancedviewonly {True}
defaultsecuritydescriptor {D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RP...
instancetype {4}
distinguishedname {CN=Print-Queue,CN=Schema,CN=Configuration,DC...
cn {Print-Queue}
dscorepropagationdata {1/1/1601 12:00:00 AM}
objectclass {top, classSchema}
defaulthidingvalue {False}
usncreated {4748}
rdnattid {cn}
objectclasscategory {1}
systemmaycontain {priority, printStatus, printStartTime, print...
schemaidguid {168 122 150 191 230 13 208 17 162 133 0 170 ...
subclassof {connectionPoint}
whenchanged {6/11/2010 5:23:32 PM}
governsid {1.2.840.113556.1.5.23}
admindescription {Print-Queue}
adspath {LDAP://CN=Print-Queue,CN=Schema,CN=Configura...


And we can see from our results that our ACL is related to print queue child objects. If we translate the SID, we will see this is the print operators builtin group.

Wednesday, February 1, 2012

Dell components check for updates via powershell (remote machine SUU)

Yesterday I posted on my XPath learning that I was going through while playing around with the Dell SUU catalog.xml file.  After further coding today, I threw together a good functional prototype of a powershell script to run SUU checks on remote machine.  It will find the upgradable components for the target machine, and return details like this:

Component      : FlashBIOS Updates
path           : PE2850_BIOS_WIN_A07.EXE
vendorVersion  : A07
currentversion : A05
releaseDate    : May 23, 2008
Criticality    : Optional
AtCurrent      : False

Component      : Embedded Server Management
path           : BMC_FRMW_WIN_R223079.EXE
vendorVersion  : 1.83
currentversion : 1.52
releaseDate    : June 30, 2009
Criticality    : Optional
AtCurrent      : False

for all firmware, bios, and drivers (probably OMSA too).  The AtCurrent value is a true/false test to show if you are at the current level (current to your version of SUU that you are using).  The script takes a server name and path (to your catalog.xml file in the suu\repository folder), and remotely checks the machine.  This works for windows machines only, since it uses WMI.  I'm sure some method of scripting using omreport data could be used to due similiar work for Linux.  Some ideas for using this information for updating, take the file name returned in each component, copy the file from the repository to the remote machine and run the installs with the silent run switch.



#Requires -version 2
#Author: Nathan Linley
#Script: Computer-DellUpdates
#Date: 2/9/2012

param(
 [parameter(mandatory=$true)][ValidateScript({test-path $_ -pathtype 'leaf'})][string]$catalogpath,
 [parameter(mandatory=$true,ValueFromPipeline=$true)][string]$server
)

function changedatacase([string]$str) {
 #we need to change things like this:  subDeviceID="1f17" to subDeviceID="1F17"
 #without changing case of the portion before the =
 if ($str -match "`=`"") {
  $myparts = $str.split("=")
  $result = $myparts[0] + "=" + $myparts[1].toupper()
  return $result
 } else { return $str}
}

$catalog = [xml](get-Content $catalogpath)
$oscodeid = &{
 $caption = (Get-WmiObject win32_operatingsystem -ComputerName $server).caption
 if ($caption -match "2003") {
  if ($caption -match "x64") { "WX64E" } else { "WNET2"}
 } elseif ($caption -match "2008 R2") { 
  "W8R2" 
 } elseif ($caption -match "2008" ) {
   if ($caption -match "x64") { 
    "WSSP2" 
   } else {
    "LHS86"
   } 
 }
}
write-debug $oscodeid

$systemID = (Get-WmiObject -Namespace "root\cimv2\dell" -query "Select Systemid from Dell_CMInventory" -ComputerName $server).systemid
$model = (Get-WmiObject -Namespace "root\cimv2\dell" -query "select Model from Dell_chassis" -ComputerName $server).Model
$model = $model.replace("PowerEdge","PE").replace("PowerVault","PV").split(" ")   #model[0] = Brand Prefix  #model[1] = Model #

$devices = Get-WmiObject -Namespace "root\cimv2\dell" -Class dell_cmdeviceapplication -ComputerName $server
foreach ($dev in $devices) {
 $xpathstr = $parts = $version = ""
 if ($dev.Dependent -match "(version=`")([A-Z\d.-]+)`"") { $version = $matches[2] } else { $version = "unknown" }
 $parts = $dev.Antecedent.split(",")
 for ($i = 2; $i -lt 6; $i++) {
  $parts[$i] = &changedatacase $parts[$i]
 }
 $depparts = $dev.dependent.split(",")
 $componentType = $depparts[0].substring($depparts[0].indexof('"'))
 Write-Debug $parts[1]
 if ($dev.Antecedent -match 'componentID=""') {
  $xpathstr = "//SoftwareComponent[@packageType='LWXP']/SupportedDevices/Device/PCIInfo"
  if ($componentType -match "DRVR") {
   $xpathstr += "[@" + $parts[2] + " and @" + $parts[3] + "]/../../.."
   $xpathstr += "/SupportedOperatingSystems/OperatingSystem[@osVendor=`'Microsoft`' and @osCode=`'" + $osCodeID + "`']/../.."
  } else {
   $xpathstr += "[@" + $parts[2] + " and @" + $parts[3] + " and @" + $parts[4] + " and @" + $parts[5] + "]/../../.."
   #$xpathstr += "/SupportedSystems/Brand[@prefix=`'" + $model[0] + "`']/Model[@systemID=`'" + $systemID + "`']/../../.."
   $xpathstr += "/ComponentType[@value='FRMW']/.."
   
  }
  $xpathstr += "/ComponentType[@value=" + $componentType + "]/.."
 } else {
  $xpathstr = "//SoftwareComponent[@packageType='LWXP']/SupportedDevices/Device[@" 
  $xpathstr += $parts[0].substring($parts[0].indexof("componentID"))
  $xpathstr += "]/../../SupportedSystems/Brand[@prefix=`'" + $model[0] + "`']/Model[@systemID=`'"
  $xpathstr += $systemID + "`']/../../.."
 }
 Write-Debug $xpathstr
 
 $result = Select-Xml $catalog -XPath $xpathstr |Select-Object -ExpandProperty Node
 $result |Select-Object @{Name="Component";Expression = {$_.category.display."#cdata-section"}},path,vendorversion,@{Name="currentversion"; Expression = {$version}},releasedate,@{Name="Criticality"; Expression={($_.Criticality.display."#cdata-section").substring(0,$_.Criticality.display."#cdata-section".indexof("-"))}},@{Name="AtCurrent";Expression = {$_.vendorVersion -le $version}}
}

Tuesday, January 31, 2012

Powershell XML XPATH search example (Dell SUU)

Over the last few years, I have taken a few shots at using powershell's XML features to do some automating of the Dell SUU update system.  In the past, I had some vaguely working prototypes that worked on one system and didn't work on another.  After taking another look at it, I discovered the use of Xpath to help match up available updates for a system.  In the SUU CD/DVD sets, you will find a Catalog.xml file in the Repository folder.  This file contains SoftwareComponents and SoftwareBundle branches.  The Bundle nodes are collections of OS + Device Model information to provide a full list of all updates.  The SoftwareComponents get into the individual updates themselves + OS + SupportedDevices.  Using Dell's WMI extensions (installed with OMSA 4.? and higher); namespace: root\CIMV2\Dell and class Dell_CMDevice to get information about the on board devices.  Each entry in this Class has a deviceID, vendorID, subDeviceID and subVendorID which can be used as an XPath search in the catalog file for the latest update for that component.

Here's an example of getting started with this:

$catalog = [xml](get-content c:\temp\suu\repository\catalog.xml)
$devices = Get-wmiobject -namespace "root\cimv2\dell" -class Dell_cmdevice
$mydevice = $devices[0]
$xpathstr = "//SoftwareComponent[@packageType='LWXP']/SupportedDevices/Device/PCIInfo"
$xpathstr += "[@deviceID=`'" + $mydevice.deviceID + "`' and @vendorID=`'" + $mydevice.vendorID +"`' and @subDeviceID=`'" + $mydevice.subDeviceID + "`' and @subVendorID=`'" + $mydevice.subvendorID + "`']/../../.."
select-xml $catalog -xpath $xpathstr |select-object -expandproperty Node

This will give you something like this:

schemaVersion        : 1.0
packageID              : R222914
releaseID                : R222914
hashMD5                : 6944D638979E5CE651234D216AFDC3C7
path                         : RAC_FRMW_WIN_R222914.EXE
dateTime                  : 2009-06-30T14:33:14-05:00
releaseDate              : June 30, 2009
vendorVersion          : 1.75
dellVersion               : A01
packageType           : LWXP
Name                      : Name
ComponentType      : ComponentType
Description              : Description
LUCategory            : LUCategory
Category                 : Category
SupportedDevices   : SupportedDevices
ImportantInfo           : ImportantInfo
Criticality                 : Criticality

As you can see, the device that is being check is a RAC card.  This is the latest update package for it.  You can use other classes in the Dell WMI namespace to pull the current version details of your devices to compare it to what is available in the SUU repository.  This part its a bit hackish though as you need to look at Dell_CMDeviceApplication and pull data out of the middle of strings, or combine Dell_CMDeviceApplication with Dell_CMApplication class results to get an easier version number.  The Dell_CMDeviceApplication class seems to just bridge between two different classes giving data like this:

Antecedent       : //Server/root/cimv2/dell:Dell_CMDevice.componentID="
                   ",name="Dell Remote Access Controller 4/I",vendorID="1028",d
                   eviceID="0012",subDeviceID="0012",subVendorID="1028",bus="",
                   device="",function=""
Dependent        : //Server/root/cimv2/dell:Dell_CMApplication.componen
                   tType="FRMW",subComponentID="",version="1.35",name="Dell Rem
                   ote Access Controller 4/I Firmware",deviceKey=":Dell Remote
                   Access Controller 4/I:1028:0012:0012:1028:::"

In the future, I will pull this together a bit more and try to provide a working SUU check for all devices on a Dell system along with update options.  Update: Solution sample

Thursday, January 26, 2012

Finding a domaincontroller in powershell (without add ons and AD module)

I was looking for a good way to find the nearest 2008 domain controller in relation to a new server that is being prepared to be promoted.  Since the Active Directory Powershell module had not been installed yet (and was failing to install due to some other pending install), I needed to find an alternative to:

(get-addomaincontroller -mimumdirectoryserviceversion Windows2008 -discover -avoidself -nextclosestsite).hostname

I considered .NET's active directory collection, but the methods to find domain controllers with that, have really limited locator options.  I didn't see any method for finding only 2008 domain controllers and higher, so I fell back to command line tools.

This can be done with the nltest.exe:

(nltest /dsgetdc:yourdomain.com /DS_6 /avoidself)[0].substring(17)

It is a bit messy, and hackish with the substring, but it works just fine as long as you have a 2008 dc in that domain.  Otherwise, it returns a single string instead of an array, and you can see the "Cannot index into a null array" exception.

PSRemoting out of memory (NTDSUTIL)

I'm in the process of creating a domain controller build automation script.  One of the steps I had hoped to achieve was to connect to the nearest 2008R2 domain controller and run the ntdsutil scripted command:

ntdsutil ifm "activate instance ntds" ifm "create sysvol full d:\ifm"

on the remote system.  This can be done through invoke-command with this command as the scriptblock, however it was failing with a JET out of memory exception.

     Target Database: d:\ifm\Active Directory\ntds.dit

                  Defragmentation  Status (% complete)

          0    10   20   30   40   50   60   70   80   90  100
          |----|----|----|----|----|----|----|----|----|----|
          ........Operation terminated with error -1011( JET_errOutOfMemory, Out of Memory ).
 error 0x800720d9(A database error has occurred.)

Searching around a bit, led to finding the WSMAN limitations of per shell memory.  Configured as:


Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 1024

Configuring this on the local machine (the non-domain controller) didn't help.  Trying to configure it on the remote machine with invoke-command gives access denied.  To work on the remote machine, I found this helpful article which shows the Connect-wsman commandlet connecting to a remote machine:

connect-wsman remotemachine
set-item wsman:\remotemachine\Shell\MaxMemoryPerShellMB 1024

This setting chance was enough to complete the IFM collection on the remote machine.

Wednesday, January 25, 2012

For NYMB christian visitors

For my brothers in humanity, seek knowledge for your success.





For further details, check out this recent post.