Slipsec

July 9, 2010

AD Schema, Indexed Attributes

Filed under: AD,LDAP,cmdlet,powershell — admin @ 5:34 pm

I needed to optimize a query and make sure that I was only hitting indexed attributes.

So I’d better find out what’s being indexed. Each piece of the schema is stored as an LDAP object, and each of those has a bitwise property named SearchFlags where 1=Index.


#Connect to the rootDSE
$dse = [adsi]"LDAP://rootDSE"
#Look up the schema naming context
$schema = [adsi]"LDAP://$($dse.schemaNamingContext)"
#Build a directory searcher
$ds = new-ojbect DirectoryServices.DirectorySearcher
#LDAP queries can have a built-in bitwise AND
$bAND1 = "1.2.840.113556.1.4.803:=1"
$ds.SearchRoot = $schema
$ds.Filter = "(&(objectclass=attributeSchema)(searchFlags:$bAND1))"
$indexed = $ds.FindAll()
$indexed | ft {$_.properties.cn}

 

Of note here is the handy bitwise flags I can put in my filter. It looks like “attributename:ruleOID:=value” where ruleOID for Bitwise AND is 1.2.840.113556.1.4.803. The last line I’m not dealing with a DirectoryServices.DirectoryEntry object, but with a DirectoryServices.SearchResult, so I have to drill through the properties attribute to get the cn.

When I’m working with scripts interactively like this, I often chop up an array on the fly using a range operator so I don’t send thousands of objects down the pipeline like so: $array[0..4] | %{#do stuff}. This is fine most of the time but does get a bit funky when dealing with children. Initially, I had been working with $schema.children. Trying to specify an index or range objects with “Unable to index into an object of type System.DirectoryServices.DirectoryEntries”. Not something that inherits the full collection interface, but no matter- it’s easy enough to just force it to an array. Found this gem from /\/\o\/\/.


$children = $schema.children

#fails:
$children[0..4]
#forcing to an array: win
(@($children))[0..4] | Format-List

 

April 27, 2010

AD password expiration, and powershell

Filed under: AD,Every Day Powershell,LDAP,Quick Tip,cmdlet,code — admin @ 10:53 pm

Yes, I’m a bit spoiled by having exchange cmdlets on the majority of servers I run. A client wanted a password expiration date check, and ANR is a way for me to be lazy.


get-mailbox *@example.com | %{
  $ds = New-Object DirectoryServices.DirectorySearcher
  $ds.Filter = "(UserPrincipalName=$($_.UserPrincipalName))"
  $res = $ds.findall()
  "$($_.UserPrincipalName)`n"  + [datetime]::FromFileTime( $($res[0].Properties.pwdlastset))
}

 

This being a very fast answer to the problem has a few touches needed if I want to consider this ready to reuse:

  • I should not be using Exchange cmdlets when I’m building a new DirectorySearcher anyway.
  • I need to add checks for the bitwise parameter to catch “password never expires” where set.

But it’s the week of 2010 scripting games. I’ll add these to the list of things I need to revisit.

April 26, 2010

Take some Powershell Exchange inbox!

Filed under: Exchange2007,cmdlet,code,powershell — admin @ 10:48 pm

Raw snippet, but very cool. Will need to do a lot of cleanup and turn into a module, but just playing around I’m pretty excited about being able to get my email from Powershell… and without Outlook. More to come.

Install EWS managed API from here http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1


#default install path:
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services.0\Microsoft.Exchange.WebServices.dll"
[Reflection.Assembly]::LoadFile($dllpath)
$s = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)
$s.Credentials = Net.NetworkCredential('myUserName', 'MyPassw0rd', 'netBIOSdomainName')
$s.AutodiscoverUrl("myEmail@example.com")

$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($s,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$folderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(25)

#show folders in inbox, with some interesting properties
$inbox.FindFolders($folderView) |ft DisplayName,TotalCount,ChildFolderCount -AutoSize

#load the rest of the items to use for the MAPI propertySet
$pSubject = [Microsoft.Exchange.WebServices.Data.ItemSchema]::subject
$pAttachments = [Microsoft.Exchange.WebServices.Data.ItemSchema]::HasAttachments
$pReceived = [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived

#make a view (25 deep, no offset)
$ItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(25)
$ItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
$ItemView.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly, $pSubject, $pAttachments, $pBody, $pReceived)

#find, show!
$res = $inbox.FindItems($itemView)
$res | ft DateTimeReceived,HasAttachments,Subject -AutoSize

 

And that’s all there was to it. Can’t wait to do more.

April 23, 2010

Exchange mailbox move/export progress tracking.

Filed under: Exchange2007,Quick Tip,cmdlet,powershell — admin @ 7:09 pm

While an export is running I have found that I often wish I knew how much or what mailboxes have not successfully processed so I can get a head start on repairing corruption or other problems. This poses a few problems: 1 the xml report file does not yet have the correct closing tags. This can be fixed by using he following:


$foo = [xml]((gc 'C:\Program Files\Microsoft\Exchange Server\Logging\MigrationLogs\export-Mailbox20100423-160215-9382781.xml') + "  </TaskDetails></export-Mailbox>")

 

This is from a quick copy-past of the host display seen as individual mailboxes are exported. I could have set the export-mailbox or move-mailbox to an explicit file, but this is DIRTY scripting, so I don’t care that much and just copied the name from the output in another shell window after using the default.

The two tags that are left open vary a tiny bit between and , but no matter- sub the correct closers. Some quick powershell XML hacking gets the unsuccessfull mailboxes, and I’ll revisit this post in a bit to add some details for success.


 $foo.'export-Mailbox'.TaskDetails.Item | ?{$_.result.ErrorCode -ne 0} | %{write-host -for Cyan "$($_.source.Identity)"; $_.result.'#text'}

 

February 21, 2010

Logon Scripting with .vbs, Deleting the Outlook Profile, Registry and AD with .vbs.

Filed under: AD,Exchange,LDAP,vbs — admin @ 4:24 pm

I can’t wait for the day that every client in my domain has powershell installed on it. Then everyone can be set to the following .vbs


set objShell = CreateObject( “Wscript.Shell)
objShell.run( “powershell.exe -noexit c:\scripts\logon.ps1)

 

So I have no choice, I must do something I’ve avoided for a long time. I must wade into the quirks, oddities, and idiosyncrasies of VB Script.  Among them I had to brush up on:

  • The use of ” to escape ” as in: “look a “”quotation”"!” – good luck trying to figure the right keywords to google this.
  • The syntactical overloading of parentheses for FOUR different language elements, and the fuzzy rules on how this is decided.
  • Foreign (to me) structuring of If, Else, ElseIf, and End If
  • Error Handling (example) (MSDN).  On Error goto 0?  How dirty I feel! goto #fail.

But while I wait for the ubiquity of windows 7 (thus powershell 2.0) being installed on all client machines; migrations must be done so I’m faced the following requirements:

  1. Allow for “click-style admins” – those who  to control the execution of a logon script
  2. Script must execute once, and only once
  3. Script must delete the MAPI profile so that users may use Autodiscover to setup their new Outlook Anywhere profiles on our multitenancy system.
  4. (bonus) Lock out user access to the user’s current Exchange mailbox on their own system

First: allowing for execution (or not) of the script though an easy to administer way to control the execution of the script. I chose to use group membership. This lets the helpdesk personnel to simply add the users to be migrated to an AD group. Easy schmeezy for them. On the backend, it means that the logon script must check for membership in the group so:

  1. groupDN = "CN=lescript,OU=lescript,OU=Department,DC=FABRICAM,DC=DOM"
  2.   Set objSysInfo = CreateObject("ADSystemInfo")
  3.   Set user = getObject("LDAP://" & objSysInfo.UserName)
  4.   on error resume next
  5.   memberOf = user.getex("memberOf")
  6.   If (Err.Number <> 0) then
  7.     'you are here if the user is a member of no groups.
  8.     'msgbox("you are member of no groups")
  9.     On Error GoTo 0
  10.   else
  11.     For Each objGroup in memberOf
  12.       'msgbox("objgroup,groupDN is: " & vbcr & objGroup & vbcr &groupDN)
  13.       If objGroup = groupDN Then
  14.         'Checks completed.  "Work" code goes here.
  15.       End If
  16.     Next
  17.   End If
  18.  

On line 0 I hard code the DN of the group I want to test membership in.  Lines 1-2 create the LDAP object for connecting to AD.  Line 3 changes the error handling so I can capture errors and handle them.  This is needed because if the user is a member of no groups, an error will be thrown and needs to be handled.  Because I’m only enabling error handling here, any _other_ ADSI problems should cause execution to stop due to the default error handling.  The use of getex() over get() is important here because it is guaranteed to return an array, which can then be iterated through with For Each.  Finally on line 12 we pass the last If statement and have completed this requirement.

Assigning a logon script to a user or group.
Using AD groups from VBScript.
Getting the current AD user information from a logon script.
Enumerating AD Group membership from vbs.
A more familiar way to .NET programmers using DirectoryServices.DirectoryEntry (I found this after I had a working script :( )

Next: Execution should be once, and only once.  We don’t want to be deleting the Outlook profile every time a user logs on.

Set wmiLocator = CreateObject("WbemScripting.SWbemLocator") 'to get to stdRegProv
Set wshNetwork = CreateObject("WScript.Network") 'used to determine local machine name
'msgbox("name: " & WshNetwork.ComputerName"
set wmiNS = wmiLocator.ConnectServer(wshNetwork.ComputerName,"root\default") 'WMI namespace
set objReg = wmiNS.Get("StdRegProv")

 

In this snippet, I go through the steps required to get a connection to the local registry. More on working with the registry and keys from vbs. The comments are pretty self-explanatory, and the good news is that because I’m only looking at the HKCU hive, I’m do not need to use impersonation.

  1. if objGroup = groupDN then
  2.   set wsh = createobject("wscript.shell")
  3.   on error resume next
  4.   key = wsh.RegRead("HKCU\ProfileDeleted")
  5.   'when err = (hex)80070002, key does not exist
  6.   if err.number <> 0 then
  7.     if hex(err.number) = 80070002 then
  8.       'msgBox("Script set to run: deleting profile and adding reg. key")
  9.       'wsh.sleep 14000
  10.       'old way: lRC = DeleteRegEntry(HKEY_CURRENT_USER, profile)
  11.       wsh.RegWrite"HKCU\ProfileDeleted", "True", "REG_SZ"
  12.       wsh.run("regDelete.bat")
  13.     End If
  14.   Else
  15.     if key = "True" then
  16.       'msgBox("Profile has already been deleted, nothing will be done.")
  17.     ElseIf key = "False" then     
  18.       'external editing of the key is the only way to get here.
  19.       'Either the key does not exist, or it's set to true by the script.
  20.     msgBox("Please report this message to support" & vbcr & _
  21.       """Profile deletion key was set to False, no action was taken.""" & vbcr & _
  22.       "HKCU\ProfileDeleted")
  23.     Else    
  24.       msgbox("Please report this error to support" & vbcr & _
  25.         """Key set to something other than True or False""" & _
  26.         vbcr & "HKCU\ProfileDeleted" & vbcr & "Key value: " & key)
  27.     End If
  28.   End If
  29.  

In this section (executed after the group membership tests above) I check for registry key named “HKCU\ProfileDeleted”. If it does NOT exist (Line 6 checks for this read error), then I add it, set it to “True”, and run the batch file “regDelete.bat”. The rest of the snippet contains a do-nothing check if they key has already been set (lines 14-15) and error handling should the key be set to “False” (16-21) or some other value (22-25). Since these states should never happen, I’m only kicking an error to a message box, and asking the user to report it.

Now the “meat”: Deleting the MAPI profile. Because the profile is nothing more than a stack of registry keys, it should be simple to delete right? Wrong. VBS apparently has no way to recursively delete a registry key without writing the recursion yourself. If that wasn’t enough, for some reason I’ve been unable to completely isolate, when I tried to recursively delete keys (line 9) with the sample function (recursion handled via vbs logic) found here kb279847, some keys remain. I tried the standard programmer “wait 10″ solution in case something was accessing the MAPI profile and adding keys as I tried to delete them (line 8 ) but that yielded only slightly better results. Less subkeys remained, but full recursive deletion still failed.

So what to do? Well there’s Wscript.shell.Run(foo.exe) and that gives us the ability to run a file. So why not take the easy way out and just do a “reg /delete”?

  1. rem reg delete /va /f "HKCU\Software\Microsoft\WIndows NT\CurrentVersion\Windows Messaging Subsystem\"
  2. regedit.exe /s \FABRICAM.DOM\SYSVOL\LCS.CORP\scripts\profileDelete.reg
  3.  

Line 0 fails. So I’m just moving on, I’m really tired of jumping through hoops at this point, but trial and error proves that line 1 (using regedit to load a .reg file) works out just fine. I saved the “profileDelete.reg” to the System Volume and tested successfully.

Yes, it seems like this should have been easier, but soon enough it won’t matter and I’ll be able to use powershell

remove-item -path "HKCU\Software\Microsoft\WIndows NT\CurrentVersion\Windows Messaging Subsystem\"
 

Until then the .reg file looks like this:

Windows Registry Editor Version 5.00
[-HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem]
 

Forget it, I’ll use a .reg file
Removing the Outlook profile registry keys using regedit
More recursive registry vbs (.reg too)

The “bonus” requirement of locking users out of their mailboxes I’ll handle in my next post. Until then, you need an updated token– any ACL changes won’t take effect until you log out/in. Remember mailbox ACLs are held in the LDAP property “msExchMailboxSecurityDescriptor”, and you’d need to go through the painful process of decrypting that.

October 7, 2009

Powershell text processing

Filed under: Every Day Powershell,cmdlet,powershell — admin @ 8:58 pm

How I used powershell today. I’ve seen some of these “real life examples” of how people have dealt with real-world applications. Here is mine. I received a list of users I needed to migrate on my Exchange platform, but it was in the wrong form because it was just a paste of the junky output of another script. Long story short, I had a lot of crap around what I wanted from this file. Sure, I could just paste this into excel & figure out how to import it so they line up in a column & I could just copy them out but… that wouldn’t be usefull in the future if I wanted to automate it so I wound up finding a few ways of doing it for next time.

Load up a “here-string” named $foo with some of the text I received (you can see the formatting & junk I don’t want). Since I’m doing this interactive, the >> prompts are there when I paste.


[PS] $foo = @"
>>                                      18                                      11 an@example.com                Third Storage Group
>>                                     144                                      67 hr@example.com                      Third Storage Group
>>                                     348                                     204 ce@example.com              Third Storage Group
>>                                     415                                      27 sr@example.com                     Fourth Storage Group
>>                                     439                                       7 uc@example.com                 Third Storage Group
>>                                    1305                                     237 al@example.com              Fourth Storage Group
--REMOVED FOR READABILITY--
@”


Check to see what element 1 is (I just find it easier to type than the first element [0]), it’s not a whole line so split the string on the newline character & save it back into the same variable. Strange that the here-string didn’t understand the newlines when I pasted the text in, but I’ll look more into that later.


[PS] $foo[1]

[PS] $foo = $foo.split("`n")
[PS] $foo[1]
                                    144                                      67 hr@example.com                      Third Storage Group

 

Now it’s a line, but I don’t want anything but the email address… so I came up with this mess:


[PS] $foo[1].split(" ", [StringSplitOptions]::RemoveEmptyEntries)
144
67
hr@example.com
Third
Storage
Group

 

Right, so that’s a PITA to use the messy ENUM from some namespace google told me about called [stringsplitoptions]. So let’s um… do it simpler. Just splitting on a space (“ “) and using the “?” (Where-Object) to filter what comes down the pipeline.


[PS] $foo[1].split(" ") | ?{$_ -like "*example.com"}
hr@example.com

 

Since that’s what I wanted (the email address only), I don’t need to just run it on one element of the array of strings $foo, I can do it on the whole thing by wrapping it inside it’s own scriptblock:


[PS] $foo | %{$_.split(" ") | ?{$_ -like "*example.com"}}

 

So it’s a pipeline inside of another pipeline. I’d dare bash to pull this one off… but it’s just string processing so it’s pretty much what it was made for.

And with that, it’s on to my migration script:


[PS] $foo | %{$_.split(" ") | ?{$_ -like "*example.com"}} | Out-File example.com.1
[PS] ./init.ps1 -source example.com.1

 

September 29, 2009

Move-Mailbox -AllowMerge selects the wrong target

Filed under: AD,Exchange2007,LDAP,cmdlet,powershell — admin @ 3:12 pm

Never a good situation to find yourself in.

Fist things first, I’m looking for clues on how the lookup is done on the target account.  Loading up the move-mailbox log & casting it to XML makes it easy to work with.


$log = [xml](gc C:\logdir\logfile)

 

With that I can select the 2 mailboxes with the failed targets and see if there’s anything suspicious.  Looking at the “Target” node, it has little of interest, basically target forest GC and some other migration settings that have little if anything to do with the target account lookup.


$log."move-Mailbox".TaskDetails.Item | ?{$_.MailboxName -like "*foo*" -or $_.MailboxName -like "*bar*"} | %{$_.Source}
SourceDatabase
Identity
IsResourceMailbox
SourceDomainController
SourceDirectReports
SourcePublicDelegatesBL
SourceDeliverAndRedirect
SIDUsedInMatch
MatchedContactsDNList
Alias
SourceGlobalCatalog
SourceServer
SourceAltRecipientBL
SourcePublicDelegates
IsMatchedNTAccountMailboxEnabled
DistinguishName
DisplayName
PrimarySmtpAddress
LegacyExchangeDN
SourceManager
SMTPProxies
MatchedTargetNTAccountDN
SourceAltRecipient

 

Of interest, MatchedTargetNTAccountDN is incorrect, proving that the cmdlet lookup had the wrong account selected.  But why?  Looking at the SIDUsedInMatch I figured I should check the SID.


get-mailbox foo | %{[adsi]"LDAP://$($_.DistinguishedName)"} | %{
  $sid = New-Object System.Security.Principal.SecurityIdentifier $($_.objectSid),0
  $sid
  $sid.Translate([System.Security.Principal.NTAccount])
}

 

Good news, nothing matched.  So the confusion must be coming from somewhere else.  I know that the ‘sn’ is used in ANR so this could be a possibility, but I doubt it since it would cause problems with all the “Smith” surnames on the system.

After checking all this… I’m waiting on a response from PSS, I’ll update when I have it :)

September 14, 2009

Exchange 2003 mailbox permissions.

Filed under: AD,Exchange,cmdlet,powershell — admin @ 4:53 pm

WARNING!!! This is a 1/2 baked script that among other evil things:

  • Doesn’t return objects
  • Uses `t tab formatting as standard output
  • Uses Write-Host

BUT… it works.  Exchange 2007 provides different methods for these tasks, so I’m mainly preserving this for postarity as I close in on decomissioning my remaining 03 servers.  At some point I might need some of the functions or casts in here so I’m publishing something I’m not proud of.  If you like feel free to fix the issues, it’s bare minimum functionality at the moment, but like I said– it gets the job done.  After running the script as an audit, it was clear that the number of permissions I had to deal with in this migration made it not worth my time to automate the migration of them across forests.

So here is my pile of hackery:


param(
  [string]$OU = $(throw "Sorry, you must supply an OU.  Bye!")
)

#TODO don't pass the OU in a asinine way.
$SearchRoot = [adsi]"LDAP://OU=$OU"
$filter = "(&(&(&(&(mailnickname=*)(objectCategory=person)(objectClass=user)(msExchMailboxSecurityDescriptor=*)))))"
#$filter = "(&(&(&(&(mailnickname=cs)(objectCategory=person)(objectClass=user)(msExchMailboxSecurityDescriptor=*)))))"
$ds = New-Object DirectoryServices.DirectorySearcher($SearchRoot,$filter)
$ds.PageSize = 5000
$ds.PropertiesToLoad.Addrange(@("msExchMailboxSecurityDescriptor","userPrincipalName","objectSid"))


$ds.FindAll() | %{
  $user = $_.GetDirectoryEntry()
  Write-host -for Red "$($user.userPrincipalName)"

  #here the DaclByte is a byte[] that is the secuirtydescriptor
  [byte[]]$byteDACL = ($_.properties)["msexchmailboxsecuritydescriptor"][0]

  ###########################################################################
  #using an activeDirectorySecurity to manipulate the securityDescriptor
  #GetAccessRule is actually inherited from System.Security.AccessControl.DirectoryObjectSecurity, what's the point of using the child class?
  #http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.directoryobjectsecurity.getaccessrules.aspx
  #AuthorizationRuleCollection GetAccessRules(bool includeExplicit,bool includeInherited,Type targetType)
  ###########################################################################

  $adDACL = new-object System.DirectoryServices.ActiveDirectorySecurity
    #setSecurity...() takes a byte[] and will let us translate.
  $adDACL.SetSecurityDescriptorBinaryForm($byteDACL)
  $ACLs = $adDACL.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])
  $ACLs | %{ 
    if(($_.IdentityReference).IsAccountSid()){
      #translate will barf if the account does not exist (say from an old Domain trust).  So I'm checking $? and not bothering to list permissions for missing accounts.
      #and hiding errors by changing EAP
      $oldEAP  = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
      $name = ([System.Security.Principal.SecurityIdentifier]$_.IdentityReference).translate([System.Security.Principal.NTAccount])
      $exist = $?
      $ErrorActionPreference = $oldEAP

      if($exist){
        $permissions = @()
       write-host -for CYAN "`t$name"
        If ($_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::CreateChild){
          $ar = "FullAccess"
     "`t`t$ar"
          $permissions += ,$ar
        }
        If ($_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteOwner -ne 0){
     $ar = "ChangeOwner"
     "`t`t$ar"
          $permissions += ,$ar
        }
        If ($_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteDacl){
          $ar = "Modify User Attributes"
     "`t`t$ar"
          $permissions += ,$ar
        }
        If ($_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ListChildren){
          $ar = "Is mailbox primary owner of this object"
     "`t`t$ar"
          $permissions += ,$ar
        }
        If ($_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::Delete){
          $ar = "DeleteItem"
     "`t`t$ar"
          $permissions += ,$ar
        }
        If ($_.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ReadControl){
          $ar = "ReadPermission"
     "`t`t$ar"
          $permissions += ,$ar
        }
      }
    }
  }
  #TODO fix checks on invalid SIDs
  $sendAs = $user.psbase.get_objectSecurity().getAccessRules($true,$false, [System.Security.Principal.SecurityIdentifier]) | ?{$_.ObjectType -eq "ab721a54-1e2f-11d0-9819-00aa0040529b"}
  $sendAs | %{
    if(($_.IdentityReference).IsAccountSid()){
     
      #translate will barf if the account does not exist (say from an old Domain trust).  So I'm checking $? and not bothering to list permissions for missing accounts.
      #$oldEAP  = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
      $name = ([System.Security.Principal.SecurityIdentifier]$_.IdentityReference).translate([System.Security.Principal.NTAccount])
      #$ErrorActionPreference =$oldEAP
      "`t`tSEND AS: $name"
      $permissions += ,$ar
    }
  }
  $recvAs = $user.psbase.get_objectSecurity().getAccessRules($true,$false, [System.Security.Principal.SecurityIdentifier])| ?{$_.ObjectType -eq "ab721a56-1e2f-11d0-9819-00aa0040529b"}
  $recvAs | %{
    if(($_.IdentityReference).IsAccountSid()){
      #translate will barf if the account does not exist (say from an old Domain trust).  So I check $? and not bothering to list permissions for missing accounts.
      #$oldEAP  = $ErrorActionPreference; $ErrorActionPreference = "SilentlyContinue"
      ([System.Security.Principal.SecurityIdentifier]$_.IdentityReference).translate([System.Security.Principal.NTAccount])
      #$ErrorActionPreference = $oldEAP
      $permissions += ,$ar
      "`t`tRECEIVE AS: $name"
    }
  }
  #TODO return objects instead of tab formatted strings.
  #Write-host -for Red "$($user.userPrincipalName)"
  #"`t`t$permissions"
}

 

September 9, 2009

Powershell v2 for Windows Server 2003!

Filed under: cmdlet,powershell — admin @ 1:06 pm

I asked, they answered.   Aparently news is coming “soon”.  Soon as in “You might want to hit F5 on this page a few times tomorrow”.  But I can script that so:


$wc = new-Object System.Net.WebClient
$url = "http://blogs.msdn.com/powershell/archive/2009/09/08/help-us-improve-microsoft-windows-management-framework.aspx"

$foo = $wc.DownLoadString($url)
While($true){
  $bar = $wc.DownloadString($url)
  if ($bar.length -gt $foo.length){$foo = $bar
    [diagnostics.process]::start($url)
  }else{"no news: $(get-date)"}
 
  Start-Sleep -Seconds 300
}

 

Yay, my F5 key is spared today!

September 2, 2009

ngen, Exchange rollups, mscorsvw.exe

Filed under: Exchange,Quick Tip,powershell — admin @ 10:42 am

When applying Exchange Update Rollups in my lab (overloaded, slow virtual machines) it takes forever to do rollups.  Mostly this is because there are seriously limited resources in the lab, and lots of compiling is done when applying the rollups.  Ngen is the start of all of this, you can check out the details here, and here.  Nutshell ngen launches a process called mscorsvw that does the meat of the compilation.  I haven’t dug into the responsibility of the spawned processes, but they do have short lifespans so seem to be limited to smaller tasks.  I have no idea if it makes any difference to renice the mscorsvw.exe processes, but it makes me feel better.  I’d take a look at the results for any improvement, but with this being a VM on an overloaded host, with a varying load, I wouldn’t trust anything I got anyway.  So again, it’s just to make me feel better– hence the gross hard-to-read one-liner form.


while($true){ $ps = get-process mscorsvw; if($ps.PriorityClass -ne "High"){$ps
.PriorityClass = "High"; $ps}; Start-Sleep -s 5}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    303      30    20180      14756   518     0.16   3904 mscorsvw
    306      30    20188      15136   514     0.14   3756 mscorsvw

 

After running, Exchange 2007 SP1 rollup 9 still took 2+ hours to deploy, so this is not much of a help (or is it?)

Older Posts »

Powered by WordPress