Sunday, March 11, 2012

PowerShell: Profile Settings

Last week, I found a cool link (listed in references) that showed me how to configure a PowerShell profile. Basically, it’s a PowerShell script that runs every time you start an instance of PowerShell. I like the idea of not having to type in the same forest command each time we started up the EMS (Exchange Management Shell); however, when I tried configure the forest command in profile script and clicked the EMS link it would error out since it ran the profile script first then the RemoteExchange.ps1 script used in the EMS shortcut. I got around this by configuring my PS profile to check for the existence of Exchange script and then running it like the EMS shortcut would. Then I added the forest command and whammo I have a PS console ready to go to work.

Here is the code from my Microsoft.PowerShell_profile.ps1 file.

#Dean's PowerShell Console Settings
#Place Code in C:\Users\UserID\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
#Create Required File Using: New-item -path $profile -type file -force

#Change Console Colors to Black and Green
$host.UI.RawUI.BackgroundColor = "Black";
$host.UI.RawUI.ForegroundColor = "Green";

#Other Available Color Choices
#Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White

#Change Error Text Color to White (If You Were Using Red for Normal Text)
((Get-Host).PrivateData).ErrorForegroundColor = "White";
#Change Warning and Verbose Text Color to Magenta (If You Were Using Yellow for Normal Text)
#((Get-Host).PrivateData).WarningForegroundColor = "Magenta";
#((Get-Host).PrivateData).VerboseForegroundColor = "Magenta";
#Change Error and Warning Background Color to Gray (If You Were Using Black for Normal Text)
#((Get-Host).PrivateData).ErrorBackgroundColor = "Gray";
#((Get-Host).PrivateData).WarningBackgroundColor = "Gray";

#Clear the Console to Load New Color Settings
Clear-Host;

#Load Exchange If On System
if(Test-Path $env:ExchangeInstallPath\bin\RemoteExchange.ps1)
{
.$env:ExchangeInstallPath\bin\RemoteExchange.ps1;
Connect-ExchangeServer -auto;
Set-ADServerSettings -ViewEntireForest $true;
}

#Change the Prompt Configuration
function prompt
{
#Get the Current Directory
$path = Get-Location;
#Set Prompt for the Computer Name then Directory Path
"PS [$env:computername] $path>";
}

#Change the Window Title
$host.UI.RawUI.WindowTitle = "Dean's PS Goodness";

#Change Location to the Desktop
$dsktop = [Environment]::GetFolderPath("Desktop").ToString();
cd $dsktop;


References:

How to use a PowerShell Profile to simplify tasks
http://www.techrepublic.com/blog/networking/how-to-use-a-powershell-profile-to-simplify-tasks/5393
Managing Exchange 2010 with Remote PowerShell
http://www.mikepfeiffer.net/2010/02/managing-exchange-2010-with-remote-powershell/

PowerShell: Map Network Drives

Wanted to explore mapping network drives with PowerShell. Quick search online shows that you will still need to use Wscript.network or the old net use command. Since I came into the game with VBScript I went that path. I configured a scheduled task that runs when my domain account logs onto the system. Below are the parameters of the Action tab of the scheduled task.

Action: Start a program

Settings -

Program/Script: C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe

Add arguments: -noexit -command "C:\Users\userID\Documents\Scripts\Dean_Drive_Mappings.ps1"

----Dean_Drive_Mappings.ps1-----------

#Create Object Instance of Wscript.Network
$net = new-object -ComObject WScript.Network

#Gather Collection of All Network Drive Mappings
$mappedDrives = $net.EnumNetworkDrives()

#Remove All Established Network Drive Mappings
for ($d = 0; $d -lt $mappedDrives.Count(); $d = $d + 2)
{
#Remove Network Drive
$net.RemoveNetworkDrive($mappedDrives.item($d).ToString(),$true,$true)
}

#Pause the Script to Prevent Networking Confusion
Start-Sleep -Milliseconds 800

#Map Required Drives
$net.MapNetworkDrive("i:", "\\server1.mycollege.edu\share1", $true)
$net.MapNetworkDrive("j:", "\\server2.mycollege.edu\share2", $true)
$net.MapNetworkDrive("k:", "\\server3.mycollege.edu\share3", $true)
$net.MapNetworkDrive("l:", "\\server4.mycollege.edu\share4", $true)
$net.MapNetworkDrive("s:", "\\server5.mycollege.edu\share5", $true)

Monday, January 30, 2012

PowerShell: Get All Nested Members of an AD Group

A quick PowerShell script to get all nested members of a group. Enjoy.

##############################################################################

# Script Name: AD_Get_All_Nested_Members_Of_Group.ps1

# Version: 1.0

# Author: Dean Bunn

# Last Edited: 01/30/2012

# Description: Get All Nested Members of an AD Group

##############################################################################

#Function for Group Members Check

function checkMember([string]$objCN)

{

#Var for LDAP Path

$ldapPath = "LDAP://" + $objCN

#Retrieve AD Object

$adObj = [ADSI]$ldapPath

#Null Check on Class

if($adObj.Class)

{

#Switch Statement for Object Type

Switch($adObj.Class.ToString().ToLower())

{

#Group Objects

"group"

{

#Loop Through Group Members and Perform Recursive Member Check

foreach($member in $adObj.member)

{

#Check to See If Member Has Been Checked Already

if(!$htUniqueMembers.ContainsKey($member))

{

#Add Member to Unique HashTable

$htUniqueMembers.add($member,"1")

#Run checkMember on This Group Member

checkMember $member.ToString()

}

}

}#End of Group

#User Objects

"user"

{

#Check to See If Member Has Been Added to Unique Member. If Not Then Add

if(!$htUniqueMembers.ContainsKey($objCN))

{

$htUniqueMembers.add($objCN,"1")

}

}#End of User

#Add Other Classes Here If Necessary

}#End of Class Switch Statement

}#End Class Null Check

}#End of checkMember Function


#HashTable for All Unique Members

$htUniqueMembers = @{}


#Run checkMember on Top Group

checkMember "CN=STATS-ALL-MEMBERS,OU=DEPARTMENTS,DC=MYCOLLEGE,DC=EDU"


#Loop Through Each Unique Group Member

foreach($uCN in $htUniqueMembers.Keys)

{

Write-Host $uCN

}

Saturday, January 28, 2012

PowerShell: Remote PSSession Files Filling Up Drive

Thanks to colleague's help, I was finally able to figure out why available disk size on one of my utility servers was shrinking so fast. Basically, remote PSSessions in PowerShell cause the system to download any required modules from the remote system. Since one of my scheduled scripts creates a remote PowerShell session to one of my unit's Lync servers, it was downloading 4 MB of Lync PowerShell files every time it runs (which is every 15 minutes). By the end of the day, the drive had lost another 400 MBs of space.

The PSSession files are created in a folder with a random name and have these three different file extensions.

.ps1xml

.psd1 (windows PowerShell Data)

.psm1 (windows PowerShell Module)

So I added to another maintenance script this PowerShell one liner to remove files and folders that are older than three hours in my admin account temp directory.

Get-ChildItem "C:\Users\adminaccount\AppData\Local\Temp" -recurse | Where {$_.CreationTime -lt (Get-Date).AddHours(-3) } | Remove-Item -Force –Recurse

PowerShell: Delete Child Domain DNS Records Using DNSCMD

One of the departments on campus recently upgraded their systems (located in child domain) and requested that my unit clean up their old DNS records and the IPv6 records for systems they forgot to disable IPv6 on. So I sent them an Excel spreadsheet of their records and then sent me back which ones to delete. Then I created the script below to remove the 400+ records. Due to the way the DNSCMD command works, I had to add some serious variable checking and a five second delay between records so that it would work.

##############################################################################

# Script Name: DNS_Delete_Records_Listed_in_CSV.ps1

# Version: 1.0

# Author: Dean Bunn

# Last Edited: 01/27/2012

# Description: Deletes Child Domain DNS Entries from CSV File

##############################################################################

#Var for DNS Server

$dnsServer = "dc1.mycollege.edu"

#Var for DNS Zone

$dnsZone = "mycollege.edu"

#Var for Child Domain Name

$childDomain = "adax"

#Load CSV of Records to Be Deleted (Headers: Name, Type, and Data)

$dnsDeleteRecords = Import-Csv "c:\Users\adminaccount\Desktop\DNS_Delete.csv"

foreach($ddr in $dnsDeleteRecords)

{

#Var for Host Name

$hostName = $null

#Var for Record Type

$rType = $null

#Var for IP Address

$ipAddr = $null

#Check Length of Host Name Before Assigning Var

if($ddr.Name.ToString().Trim().Length -gt 1)

{

#Compose Hostname As SystemName.ChildDomain

$hostName = $ddr.Name.ToString().Trim() + "." + $childDomain.ToString()

}

#Switch Statement for Record Type

switch($ddr.Type.ToString().Trim())

{

"Host (A)"

{

$rType = "A"

}

"IPv6 Host (AAAA)"

{

$rType = "AAAA"

}

#Add Additional Record Types When Necessary

}

#Check the Length of the IP Address Information

if($ddr.Data.ToString().Trim().Length -gt 8)

{

#Assign IP Addr Var

$ipAddr = $ddr.Data.ToString().Trim()

}

if($rType -ne $null -and $hostName -ne $null -and $ipAddr -ne $null)

{

#Compose DNSCMD with Variables

#Should Look Like "dnscmd dc1.mycollege.edu /RecordDelete mycollege.edu dept-webstudent.adax A 192.168.60.49 `/f"

[string]$dnsDltCmd = "dnscmd $dnsServer /RecordDelete $dnsZone $hostName $rType $ipAddr `/f"

#Run the DNSCMD and Wait Five Seconds Before Moving to Next Entry

Invoke-Expression $dnsDltCmd

Start-Sleep -Seconds 5

}

else

{

Write-Host "Not Enough Info for " $ddr.Name

}

}#End of Foreach on $dnsDeleteRecords



Wednesday, January 4, 2012

PowerShell: Protect All AD Objects in Specific OUs

A strong need came up to set all AD objects in specific OUs with "Protect Object from Accidental Deletion"; however, some of these OUs were in different domains and this would require that would require a specific server setting for the Get-ADObject command. I solved this issue by using a hashtable to store the OU and DC info.

Enjoy.

##############################################################################

# Script Name: AD_Prevent_Accidental_Deletion.ps1

# Version: 1.5

# Author: Dean Bunn

# Last Edited: 01/01/2011

# Description: Protect All AD Objects in Select OUs from Accidental Deletion

##############################################################################

#Add Active Directory Module

Import-Module ActiveDirectory

#Create a HashTable to Hold OUs

$hOU = @{}

#Add OU and DC Info into HashTable

$hOU["ou=Payroll,dc=campus,dc=edu"] = "dccmp1.campus.edu"

$hOU["ou=Payroll,dc=campus,dc=edu"] = "dccmp1.campus.edu"

$hOU["ou=Marketing,dc=phys,dc=campus,dc=edu"] = "dcphys1.phys.campus.edu"

$hOU["ou=Marketing,dc=phys,dc=campus,dc=edu"] = "dcphys1.phys.campus.edu"

#Loop Through the OU HashTable

foreach($key in $hOU.keys)

{

#Assign OU and Server Names to Local Variables (Easier to Read Command)

$srchBase = $key.ToString()

$srvr = $hOU[$key].ToString()

#Set "Protect Object from Accidental Deletion" on All OU AD Objects

Get-ADObject -Filter * -SearchBase $srchBase -ResultPageSize 500 `

-SearchScope Subtree -Properties ProtectedFromAccidentalDeletion -Server $srvr `

| Where {$_.ProtectedFromAccidentalDeletion -eq $false} `

| Set-ADObject -ProtectedFromAccidentalDeletion $true -Server $srvr

}


Monday, November 28, 2011

PowerShell: Change Local Admin Password

A colleague needed a way to change the local admin password on several desktops without storing the password in a script or group policy. I came up with the script listed below.


#############################################################################
# Script Name: Change_Local_Admin_Password.ps1
# Version: 1.0
# Last Edited: 11/28/2011
# Description: Changes Local Administrator Account Password on Remote Systems
#############################################################################

#Write Out Blank Line (Easier on the Eyes)
Write-Host ""

#Prompt User for New Password (Stored as Secure String)
$sPwd = Read-Host -assecurestring "Please Enter The New Local Admin Password"

#Create PSCredential with Secure String Password (Needed for Secure String Conversion)
$tempCred = New-Object System.Management.Automation.PSCredential("nada",$sPwd)

#Create Network Credential Using The PSCredential Object
$nc = $tempCred.GetNetworkCredential()

#Pull Plain Text Password from Network Credential Object
$uPwd = $nc.Password.ToString()

Write-Host "" #Same Eyes

#Array of Computer Names that Need Local Admin Password Changed
$computers = @("dept-wrks1.my.domain.net","dept-wrks2.my.domain.net","dept-wrks3..my.domain.net")

foreach($computer in $computers)
{

#Ping Computer Before Attempting Password Change
if (test-connection -computername $computer -quiet)
{

try
{

#Var for WinNT Path to Local Administrator Account on Remote System
$WinNTPath = "WinNT://" + $computer + "/Administrator,User"

#Attach to Local Admin Account
$lAdmin = [ADSI]$WinNTPath

#Set Local Admin Password
$lAdmin.setpassword($uPwd)

Write-Host "Successfully Set Password on " $computer

}
catch
{
#Notify Admin of Failure
Write-Host "Error When Setting Password on " $computer
}

}
else
{
#Notify Admin of Failed Ping
Write-Host "Ping Failed to" $computer
}

}