Archive | Windows

PowerShell Active Directory wrapper scripts.

In my PowerShell profile I have a routine the sources all the *.ps1 files in a folder. One of these .ps1 files contains Active Directory “wrapper” functions. I call them “wrapper” functions because they simplify the parameters needed to return data in a format we want. The idea is that I can write a series of “wrapper” scripts, alias them, and then everyone on our team can use the same script. All a user has to do is type “jbmurphy” (if that is the script starts with) and hit TAB  and the command completion will cycle through all the wrapper scripts. Some of the scripts are not much different than the actual “wrapped” script, but there usually is simplified formatting to just dump the needed info. Here are two wrapper scripts to interact with ActiveDirectory (AD).

First one finds users in a group:

function jbmurphy-GetGroupMembers {
    Param($GroupName)
    return Get-ADGroupMember $GroupName | ft -hidetableheaders name
}

Second one finds the groups a users is a member of:

function jbmurphy-AD-GetGroupMembership {
Param($UserName)
   $user=Get-ADUser -properties memberof $UserName
   return $user.MemberOf -replace '^cn=([^,]+).+$','$1'
}

PowerShell script to silently install RSAT 2008 R2 SP1

I often re-image my machine – that way I know my SCCM OSD is working properly and has the most recent “secondary apps” (Reader,Flash,FireFox . . .). Because I am always re-imaging, I like to automate installing the software I always need like Remote Server Administration Tools – RSAT. I wanted a function to install the software and automatically add all the features. Below is that script:

function Install-RSAT {
$ScriptPath = "\\server\sahre\path\to\RAST\installers\"
Set-Location $ScriptPath

$os=(Get-WMIObject win32_operatingsystem).OSArchitecture
$sp=(Get-WmiObject Win32_OperatingSystem).CSDVersion
if (($os -eq "64-bit") -and ($sp -eq "")) {$files = get-childitem -recurse -filter amd64*.msu}
if (($os -eq "32-bit") -and ($sp -eq "")) {$files = get-childitem -recurse -filter x86*.msu}
if (($os -eq "64-bit") -and ($sp -eq "Service Pack 1")) {$files = get-childitem -recurse -filter *KB958830*amd64*.msu}
if (($os -eq "32-bit") -and ($sp -eq "Service Pack 1")) {$files = get-childitem -recurse -filter *KB958830*x86*.msu}

foreach ($file in $files)
{
Start-Process -wait -FilePath "C:\WINDOWS\SYSTEM32\wusa.exe" -ArgumentList "$file.FullName /quiet /norestart"
}

DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS-SnapIns /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS-AdministrativeCenter /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS-NIS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-LDS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-Powershell /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-ServerManager /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-CertificateServices /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-CertificateServices-CA /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-CertificateServices-OnlineResponder /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-DHCP /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-DNS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices-Dfs /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices-Fsrm /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices-StorageMgmt /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-HyperV /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-RDS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-BitLocker /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-Clustering /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-GP /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-LoadBalancing /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-SmtpServer /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-StorageExplorer /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-StorageManager
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-Wsrm
}

PowerShell to set ACLs that prevent renaming of top-level directories in a share

We have a shared folder that is replicated to all our sites using the fantastic GlobalScape WAFS (@GlobalScape). This folder is huge.

People go into the folder, and “type ahead” to get the subfolder they want,  but what keeps happening (and I am sure you have seen this) is that when users click and try to type ahead, they don’t realize that they have clicked a folder and their “type ahead” is actually renaming the folder. We end up with a lot of folders named “p” or “j” or “m”. Everyone has full access to the folder and all files/folders below. The impact is that when a large folder gets renamed, this has to be replicated across the country via WAFS, which is slowing down the other offices.

To solve this I had to sit down and figure out ACLs and using PowerShell to change them. The security on the top-level folders are inherited from above, and they are simple: Administrator = FULL, System = Full, Creator Owner = Modify, and BUILTIN\USERS = Modify.

The script below, loops through the top-level folders (in this case just the A’s) and  copies the inherited permissions to the folder. Next it sets the folder so it does not inherit permission from the folder above (Lines 1 – 6). Then, the script  creates an ACL rule that removes the BUILTIN\USERS = Modify ACL (Line  8 ). Finally it creates 2 ACL rules that add, for BUILTIN\USERS,  “ReadAndExecute” for “This folder only” and a “Modify” for “Subfolder and files below” (Lines 9 – 10).

And this is what the result looks like:

PowerShell script:

Get-ChildItem a* | Where {$_.psIsContainer -eq $true} | foreach {
$filename = $_
write-host "Changing $_"
$objACL=Get-ACL $filename
$objACL.SetAccessRuleProtection($true,$true)
set-acl $filename $objACL

$RMAccessRule = New-Object Security.AccessControl.FileSystemAccessRule("BUILTIN\Users",@("Modify", "Synchronize"),"ContainerInherit, ObjectInherit","None","Allow")
$AddAccessRule1 = New-Object Security.AccessControl.FileSystemAccessRule("BUILTIN\Users",@("ReadAndExecute", "Synchronize"),"None","None","Allow")
$AddAccessRule2 = New-Object Security.AccessControl.FileSystemAccessRule("BUILTIN\Users",@("Modify, Synchronize"),"ContainerInherit, ObjectInherit","InheritOnly","Allow")

$objACL=Get-ACL $filename
$objACL.RemoveAccessRule($RMAccessRule)
$objACL.AddAccessRule($AddAccessRule1)
$objACL.AddAccessRule($AddAccessRule2)
set-acl $filename $objACL
}

PowerShell, Event Triggers, MailboxMoves and email notification

We are in the process of moving our Mailboxes from Exchange 2003 to Exchange 2010. I am using this script to automate the moves. I wanted to find a way to get an email notification when the move is complete. I figured I could keep the PowerShell script  running in a “while loop” until Get-MailBoxMoveStatistics reports “Completed”, but I wanted to write a more generic notification script.

One of the features that I had been itching to play with in 2008 is the “attach a task to this event”, so I wrote the following generic PowerShell script to receive a set of values from a triggered event. This could be used for any event that you can attach a task to. Big picture is that I am passing the event id to the script, and then using PowerShell to query the event log to get the rest of the log entry. This is then emailed.

Before we get to the script, by default, the scheduled task created by the wizard does not pass any values to the script it is running. I found this article that described how to modify your “Event Viewer Task” to pass values to the attached action. Use the method he describes to export the task and the re-import with the following :

      <ValueQueries>
        <Value name="eventData">Event/EventData/Data</Value>
        <Value name="eventRecordID">Event/System/EventRecordID</Value>
      </ValueQueries>

The actual command that is attached to the event is:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command SendEventRecord.ps1 -subject "MailBox Move Completed" -eventid $(eventRecordID)

And here is the PowerShell script called SendEventRecord.ps1. This script received paramaters from the command above. This script uses the eventRecordID in the event log item to look up the actual error and email the contents.

Param($Subject, $Body,$EventID)

$Body += Get-EventLog -LogName Application | Where-Object {$_.Index -eq $EventID} | fl | out-string

$emailFrom = "[email protected]"
$emailTo = "[email protected]"
$emailsubject = $Subject
$emailbody = $Body
$smtpServer = "server.ip.address"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $emailsubject, $emailbody)

Now, when a 1107 appears in the event log, the eventRecordID is passed to a PowerShell script that looks up the record/event and emails it to the right people! I like this one.

Adventures in Load Balancing: Kemp (@KempTech)

My current project is a migration from Exchange 2003 to Exchange 2010. We wanted to load balance our CAS servers and do some SSL offloading. I have never worked with a load balancer before – pretty cool stuff. We have been using a couple of Kemp 2600’s in an active passive configuration. The Kemp devices have a nice price point and seem to have all the functionality that we need. Plus the support has been excellent. They have really helped us get up and running. Things I have learned while implementing these devices:

  • You actually set the CAS servers IP gateways to the load balancer. I guess the device acts like a router when it receives new traffic that did not originally pass through the device.
  • Clients and servers can not be in the same subnet if you want to use Layer 7 transparency. Traffic will hit the load balancer and it will pass it along to the server. The server will see that the traffic originated on the same subnet, and it will send the return straight back to the server, not through the load balancer. Timeouts result.
  • The documentation repeatedly refers to “clients”. A “client” can be a workstation, but it can also be a service.  Our BES server was connecting to the CAS to find the “/Autodiscover/Autodiscover.xml” info. Since it was on the same subnet as the CAS servers, they replied back directly and not through the load balancer. Timeouts again.
  • I really like the idea of a drain stop. I can move all traffic to one CAS and work on the other.
  • We ended up turning off Layer 7 transparency since we have all servers on the same subnet. The only other real choice would be to move the load balanced servers to their own subnet. The loss of transparency means that all connections seem to originate on the load balancer. So logs become pretty useless. Trouble shooting will occur on the Kemp. We can always ssh in and run a TCPDUMP.
Now I need to find other cool things we can do with these cool Kemp boxes.

Changing NIC order in 2008 R2 SP1

I can never remember how to change the NIC order in 2008 R2. Navigate to “Networking Connections” and then hit “Alt” to bring up the menu. There you can find “Advanced” menu and the “Advanced Settings” option. There has to be an easier way, but that is the only way I know hot to get to it.

Poor design.

xcopy: permissions,recursive,incremental

I can never remember xcopy’s flags, so I am creating a post for myself.

xcopy SourceDrivePath DestDrivePath /X /C /E /H /Y /D

/X Perms
/C continue on error
/E Recursive with empty folders
/H (hidden and system) Copy hidden and system files
/Y (yes) No Prompts
/D (date) SourceDate is newer then DestDate (incremental)

Powershell script to install Cygwin

I like having Cygwin installed on my machine, and since I always re-image, I needed a script to install Cygwin automatically.

function Install-Cygwin {
   param ( $TempCygDir="$env:temp\cygInstall" )
   if(!(Test-Path -Path $TempCygDir -PathType Container))
    {
       $null = New-Item -Type Directory -Path $TempCygDir -Force
    }
   $client = new-object System.Net.WebClient
   $client.DownloadFile("http://cygwin.com/setup.exe", "$TempCygDir\setup.exe" )
   Start-Process -wait -FilePath "$TempCygDir\setup.exe" -ArgumentList "-q -n -l $TempCygDir -s http://mirror.nyi.net/cygwin/ -R c:\Cygwin"
   Start-Process -wait -FilePath "$TempCygDir\setup.exe" -ArgumentList "-q -n -l $TempCygDir -s http://mirror.nyi.net/cygwin/ -R c:\Cygwin -P openssh"
}

This will download and install Cygwin and install the openssh package.

OpenVPN on windows with a TUN device

Since my laptop hard drive  died, I did not have access to my home VPN. I needed to set up OpenVPN on windows. I setup my PKI and installed the portable version of OpenVPN. Tried connecting and got this error:

There is a problem in your selection of –ifconfig endpoints [local=X.X.X.X, remote=X.X.X.X]. The local and remote VPN endpoints cannot use the first or last address within a given 255.255.255.252 subnet.

The fix? Add the following to your server config:

topology subnet