• 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.


  • PowerShell to assign Exchange 2010 Retention Policies based on AD group membership

    In exchange 2003 we maintained a 6 month and 12 month purge policy that was applied based on group membership. I described that configuration here. I wanted to migrate our purge policies to the new 2010 Recipient policies. I needed to find the members of an AD group and assign a retention policy to their mailbox. Here is my script:

    Get-ADGroupMember mailboxpurge-6m | foreach {
    if ((get-mailbox $_.samaccountname).ServerName -eq "server01" -OR (get-mailbox $_.samaccountname).ServerName -eq "server02" )
    {
    write-host "User" + $_.samaccountname + "'s current policy is: " + (get-mailbox $_.samaccountname).RetentionPolicy
    set-mailbox $_.samaccountname -RetentionPolicy 6MonthPurge
    }
    }
    

  • Retention Policies in Exchange 2010

    I have to say that retention policies in Exchange 2010 are a bit confusing. I understand that they are more powerful and it gives users more control in marking items that should not be purged. But, it is just easier in exchange 2003, you can select the folders you want and select “delete immediately”. For users created folders, you select “all other mail folders”

    In 2010, it took me a while to realize that, to create a purge policy that only removed mail items, I needed to create Calendar, Contacts, and Notes policies and “Disable this Tag” .

    Get-RetentionPolicyTag Calendar-NeverDelete | fl Name,MessageClass, RetentionEnabled,Type
    Name             : Calendar-NeverDelete
    MessageClass     : *
    RetentionEnabled : False
    Type             : Calendar

    . . .  and in the gui:

    That seems backwards to me.


  • Scheduled PowerShell script to resume Mailbox Moves (New-MoveRequest)

    Moving mailboxes from 2003 to 2010 is easy, but there is not a “Gui” to schedule it. So I created the following PowerShell script to find all the suspended MoveRequest(s) with the correct date, and resume them.
    MailboxMove.ps1:

    $TodaysDate = (get-date).day.ToString() + (get-date -format MMMM)
    Get-MoveRequest -MoveStatus Suspended | Get-MoveRequestStatistics | Where {$_.BatchName -like "*$TodaysDate*"} | Resume-MoveRequest
    

    Next I create a scheduled task with the following command to run a script with the code above:

    C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command ". 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto; c:\Scripts\MailboxMove.ps1"
    

    Now when I create a New-MoveRequest,

    New-MoveRequest -Identity username -TargetDatabase MailboxDatabase02 -BatchName "19August" -BadItemLimit 10 -Suspend
    

    I know that a scheduled task will resume the move a the correct time.

    To monitor progress, I have been using this script:

    Get-MoveRequest  | Get-MoveRequestStatistics | ft -auto alias,Status,TotalMailboxSize,PercentComplete,TargetDatabase,TotalInProgressDuration,BytesTransferred,BatchName
    

  • Force the Light Version of Exchange 2010 OWA on a Cisco ASA WebVPN with SSO

    We have a Cisco ASA in front of our Exchange 2010 OWA application. We needed to create a “bookmark” to point to OWA. Since we are using Forms based authentication, we did not want users to have to re-login after just logging into the ASA. I found this link on how to setup the Bookmark for SSO and Exchange 2010 OWA. We wanted to take it a step further and force the “Light version” of OWA until we could upgrade the ASA code (we are using an older rev and OWA JavaScript is not working correctly). To do this you need to change the “flags” post value to “1”.

    So, to “Force the Light Version of Exchange 2010 OWA on a Cisco ASA WebVPN with SSO” you need to do the following:

    Configuration -> Remote Access VPN -> Clientless SSL VPN Access -> Portal -> Bookmarks -> Add/Edit your Bookmarks
    URL: https :///owa/auth/owaauth.dll

    Advanced Options: Post
    destination : https:///owa/
    flags : 1
    forcedownlevel : 0
    trusted : 0
    username : <yourdomain>\CSCO_WEBVPN_USERNAME
    password : CSCO_WEBVPN_PASSWORD
    SubmitCreds : Login
    isUtf8 : 1


  • Fun with Toshiba IPT 2010 phones and NAT

    We have a Toshiba phone system in our office. I don’t know much about it, because it is run by the very capable @mattsix, but today I jumped and did a little troubleshooting.

    The reoccurring issue we had, was that a remote phone would connect to the IPU, ring, but there would be no audio. We were testing with an Airport Extreme hooked up to a DSL modem and an IPT 201-SD hooked to that.

    First, we found this thread. We checked the “NAT/No Peer to Peer ” settings and they seemed right. Then I cam across this post. Wait, what? There are different types of NAT? I did not know that. As soon as we moved the Airport Extreme out of the mix, and put in a D-Link gaming router, the audio started working.

    I guess the Airport Extreme is using Symmetric NAT and the D-Link is using a more capable NAT.

    Mystery solved? We shall see.

    Update: I found this in the “Starta CIX General Description

    The Strata CIX supports the use of IP telephones that are behind NAT firewalls.

    Symmetric — At this time these routers may cause unreliable service or cause unwanted symptoms. These routers are not compatible with Strata CIX and Strata Net IP configurations