Find All OUs with Users, Groups, or Contacts

December 28, 2012 Leave a comment

Recently I found myself involved with a new company helping them with an Active Directory migration. Their plan is to converge separate forests into one new forest. Since I was new to the environment I wanted to really get a better understanding of what I was dealing with. With this I opened my trusty PowerShell script editor and began working out a few scripts to help understand these new environments.

When dealing with an Active Directory migration you really want to get a true grasp of where all of the important objects are, to me important objects are Users (including service accounts), Contacts, and Groups. Over time Active Directory Domains can become quite messy and convoluted ending up with numerous OUs which really have no true purpose any longer, yet no one deleted them. The script below helps you traverse through the muck and find the truly important OUs based on the important objects I’ve defined.

The script below does the following:

  1. Connects to the Active Directory domain(s) you specify
  2. Queries for all OrganizationalUnits within that domain
  3. Queries for all AD Objects within each OrganizationalUnit
  4. Creates 3 variables based on the query stated in step 3. ($users, $groups, $contacts)
  5. Defines the output data for each OU
  6. Counts the number of Users, Contacts, or Groups within the current OU
  7. Adds the content to the arrayed variable of $output
  8. After rolling through all OUs and all Domains the $output file is exported in CSV format to $OutFile defined in the params.

function Get-OUWithObjects
{

<#
.SYNOPSIS
Function to get all OUs that contain Users, Groups, or Contacts.

.DESCRIPTION
This function requires Quest ActiveRoles AD Management to be installed. The purpose of this
script is to go out and find any and all OrganizationalUnits which contain Users, Groups, or
Contacts. It performs a count on each type of object and prints them to a CSV File. This is
a useful tool for any Admin getting ready to perform an Active Directory migration in order
to better understand the existing environment. This script does not require and special
privelages in order to run as you're only reading from Active Directory.

.PARAMETER Domains
This allows you to input as many domains as you'd like to scan against. (ex.
-Domains "domain1","domain2","domain3" )

.PARAMETER OutFile
This specifies the directory path and file name for the CSV output. (ex. -Outfile c:\temp.csv)

.NOTES
Name: Get-OUWithObjects.ps1
Author: Josh Schofield
DateCreated: 12/28/2012

.LINK
http://www.JSchofield22.wordpress.com

.EXAMPLE
Get-OUWithObjects -Domains "Domain1","Domain2" -OutFile "C:\temp\test.csv"

#>

param(

[Parameter(Mandatory=$true)]
$Domains,

[Parameter(Mandatory=$true)]
[string]$OutFile

)

if ((Get-PSSnapin -Registered| where {$_.name -eq "quest.activeroles.admanagement"}) -eq $null){Write-Error "Quest.ActiveRoles.ADManagement NOT Installed"}

else {

Get-PSSnapin -Registered| where {$_.name -eq "quest.activeroles.admanagement"} | Add-PSSnapin | Out-Null

if ((test-path $OutFile) -eq "True"){del $OutFile}

$output = @()

foreach ($domain in $domains) {

Connect-QADService $domain

Get-QADObject -Type "organizationalunit" -IncludedProperties name,type,parentcontainer,dn -SizeLimit 0| %{

$ouname = $_.name
$parentcontainer = $_.parentcontainer

$adobjects = get-qadobject -SearchRoot $_.dn -SearchScope OneLevel -IncludedProperties type,name -SizeLimit 0 | where {(($_.type -eq "contact") -or ($_.type -eq "user") -or ($_.type -eq "group"))}
$users = $adobjects | where {$_.type -eq "user"}
$groups = $adobjects | where {$_.type -eq "group"}
$contacts = $adobjects | where {$_.type -eq "contact"}

$results =  "" | Select Domain, Name, UserCount, GroupCount, ContactCount, ParentContainer
$results.Domain = $domain
$results.Name = $ouname
$results.ParentContainer = $parentcontainer

if ($users -ne $null) {

$results.UserCount = $users.count

} #End of User Check

if ($groups -ne $null) {

$results.GroupCount = $groups.count

} #End of User Check

if ($contacts -ne $null) {

$results.ContactCount = $contacts.count

} #End of User Check

$output += $results

Clear-Variable $results -ErrorAction SilentlyContinue
Clear-Variable $ouname -ErrorAction SilentlyContinue
Clear-Variable $parentcontainer -ErrorAction SilentlyContinue

$adobjects = $null
$users = $null
$groups = $null
$contacts = $null

} #End of Get QADObject OU

}

$output | Export-Csv $OutFile -NoTypeInformation
}}

Advertisement

Increase Exchange 2010 OWA Signature Field

October 26, 2012 5 comments

After recently receiving a new signature format this morning I was faced with an issue regarding the length of my signature. Typically you can get around this by using transport rules, however this was not an option based on how the signature format was given to us.
After about 30 minutes of Google searching I came up empty handed with no way to fix the problem. All searching led me to believe that you had to use Transport Rules to accomplish this. Knowing some about web programming, I knew that this had to be simple restriction within the web programming decided to look there.
I opened the ECP page and found the following within the source code the following line:

 

ID”:”Messaging”,”Sprite”:”MainNavigationSprite Signature32″,”Title”:”Mail”,”Url”:”\/ecp\/Customize\/Messaging.aspx

 

This shows that the signature form is located within the ecp/customize/messaging.aspx file.
Once in IIS on the CAS servers I right clicked and ‘explored’ the customize within the ecp page. I opened messaging.aspx with notepad and found that it actually calls EMailSignature.ascx as seen below:

 

Register Src=”~/Customize/EMailSignature.ascx” TagName=”ES” TagPrefix=”ecp”
Register Src=”~/Customize/MessageFormat.ascx” TagName=”MF” TagPrefix=”ecp”
Register Src=”~/Customize/MessageOptions.ascx” TagName=”MO” TagPrefix=”ecp”
Register Src=”~/Customize/ReadReceipts.ascx” TagName=”RR” TagPrefix=”ecp”
Register Src=”~/Customize/ReadingPane.ascx” TagName=”RP” TagPrefix=”ecp”
Register Src=”~/Customize/Conversations.ascx” TagName=”CS” TagPrefix=”ecp”

 

Within the EMailSignature.ascx file there is an option which sets limitations on the length:

 

ecp:RichTextEditor ID=”rteSignatureHtml” runat=”server” DataBoundProperty=”SignatureHtml” MaxLength=”4000″ SetRoles=”Set-MailboxMessageConfiguration?SignatureHtml@W:Self”

 

I changed the 4000 option to be 12000. Once this was changed and the settings page was reloaded I was then able to change my signature to the new format. Thankfully this change did not require a restart of the website, so there was no outage.

 

I am working on a powershell script now that will make these changes automatically after Exchange updates so we don’t have to remember to change them back if the field is modified.

Ping Multiple Hosts with PowerShell and Return Useful Information

September 4, 2012 6 comments

A lot of our time in IT is spent making sure systems are up and running. Most times our first test when troubleshooting an issue is to perform a PING command. For that reason I have created the script below which in essence just performs the Test-Connection cmdlet. This script only provides me the useful information that I want from the cmdlet, both onscreen and in a csv file.

If you want to get really fancy you could perform a get-qadcomputer command and pipe the output to a CSV file. Then use that CSV file as the input file for the script below. If you did this you would be able to know which machines in your domain can respond to a PING response.


###########################################################################
#
# NAME: Ping-Host.ps1
#
# AUTHOR: Joshua Schofield
#
# COMMENT: If using a CSV file, you must have a column named DNSName in order for script to complete.
#
# EXAMPLE: c:\scripts\ping-host.ps1 -sourcefile c:\scripts\csv\servers.csv -oufile c:\scripts\logs\logfile.csv
#
# VERSION HISTORY: 1
#
# VERSION DATE:    8/21/2012
#
# VERSION COMMENTS: Tested and Validated
#
#
###########################################################################

param (

	[Parameter(Mandatory = $true)]
	$SourceFile,

	[Parameter(Mandatory = $true)]
	$OutFile

)

Function Ping-Hosts {

param ($server)

$test = Test-Connection $server -Count 1 -Quiet -ErrorAction SilentlyContinue
$ip = Test-Connection $server -Count 1 | select ipv4address -ErrorAction SilentlyContinue
$ip = $ip.IPV4Address

if ($test.ToString() -like "true") {

	Write-Host "$server $ip is pingable" -ForegroundColor green
	Write-Output "$server,$ip,yes" | Out-File $OutFile -Append

}
else {
	Write-Host "$server not pingable" -ForegroundColor Red
	Write-Output "$server,$ip,no" | Out-File $OutFile -Append

}

$test = $null
$name = $null
$server = $null
$ip = $null
}

$filetype = $SourceFile.split(".")[1]

Write-Output "ServerName,IP,RespondsToPING" | Out-File $OutFile -force

if ($filetype -eq "txt"){

	gc $sourcefile | % {

		ping-hosts $_

}
}

Elseif ($filetype -eq "csv"){

	Import-Csv $sourcefile | % {

		ping-hosts $_.dnsname

}
}

else{

Write-Host "Filetype: $filetype not recognized. Filetype must be .csv or .txt . Please try again." -ForegroundColor DarkRed -BackgroundColor White

}

Connect to Multiple vCenter Servers with Password File

September 4, 2012 3 comments

 

I’ve been getting tired lately of having to use the “get-credential” cmdlet in Powershell when I want to connect to various items. Thanks fully after some research I came across a cmdlet that is provided as part of the PowerCLI Toolkit. Thanks to Chris Nakagaki (http://tech.zsoldier.com/2011/09/save-powercli-login-credentials-to-xml.html) for originally posting about this a year ago. I am going to take his instructions a step further and show you how to not only create the file but also connecto to multiple vCenter/ESX Servers at one time.

First we need to create and add content to our new xml file


# Create VI Credential File
New-VICredentialStoreItem -Host vCenterServerFQDN -File C:\PATHTOFILE\xmlfile.xml -User username -Password password

* As you may notice the string above will require that your password be viewed in plaintext on the screen, I would suggest not saving this but simply just creating this from the PowerCLI Shell and then once you’re done adding your entries closing that shell.

You want to run the above string as many times as you you need to. For my environment I only have 2 production vCenter servers that I need to connect to, so I ran the command twice providing the information needed for both vCenter servers.

Now let’s connect to the vCenter Servers.

You can do this one of 2 ways:

1. Connect to all vCenter Servers in the XML File


Get-VICredentialStoreItem -File c:\PATHTOFILE\xmlfile.xml | %{
Connect-VIServer -Server $_.host -User $_.User -Password $_.Password
}

2. Connect to an array of vCenter Servers which are defined in a Powershell Array


# Using the VI Credential File for multiple vCenter Servers
# vCenterServerNames have to match what is listed as the hostname in the VI Credential File

$Hosts = @("vCenterServerA","vCenterServerB")  $Hosts | %{
$creds = Get-VICredentialStoreItem -File c:\PATHTOFILE\xmlfile.xml -Host $_
Connect-VIServer -Server $creds.host -User $creds.User -Password $creds.Password
}
$creds = $null

Copy Group Memberships from one user to another

August 22, 2012 11 comments

Have you ever found yourself needing to copy 1 user’s group memberships to another user in order to make sure both had identical permissions needed for their daily job? I’ve had to do this far more times than I’d like to count. Typically it’s easiest to do this upon creation of the new user’s account because you can simply copy the source user’s account through Active Directory Users and Computers. But what if both users already exist?

Well the script below will help alleviate this issue by using Powershell in conjuction with the Quest AD Tools.

The first way to do this is to simply run the script within an IDE like PowerGUI Script Editor. Simply copy and paste the code listed below and then modify the source/target users and domain controller and the script should run on your machine.



add-pssnapin quest.activeroles.admanagement

#   Be sure to change "domaincontroller" to the domain controller which you want to make these changes on. You can use
#   the -UseGlobalCatalog switch if you'd like in order to make the changes to your GC 

connect-qadservice domaincontroller -credential (get-credential)  



$sourceuser = samaccountname                 
$targetuser = samaccountname                  


#   This will store all applicable groups into a variable called groupmembership

$groupmembership = get-qaduser $sourceuser | select -ExpandProperty memberof     

#    This will loop through all groups in groupmembership and apply the memberships

foreach ($group in $groupmembership) { add-qadgroupmember -identity $group -member $targetuser }     


The second way to do this is to simply build a reusable ps1 file which asks for params in order to complete the task. Simply copy and paste the code below into notepad then save as a .ps1 file.


###########################################################################
#
# NAME: Copy-GroupMemberships.ps1
#
# AUTHOR: Joshua Schofield
#
# COMMENT: Must have Quest Active Roles Installed
#
# EXAMPLE: c:\scripts\copy-groupmemberships.ps1 -domaincontroller MYDC01 -sourceuser JDOE -targetuser JSMITH
#
# VERSION HISTORY: 1
#
# VERSION DATE:    8/21/2012
#
# VERSION COMMENTS: Tested and Validated
#
#
###########################################################################


param (

	[Parameter(Mandatory = $true)]
	$SourceUser,
	
	[Parameter(Mandatory = $true)]
	$TargetUser,
	
	[Parameter(Mandatory = $true)]
	$DomainController
)

add-pssnapin quest.activeroles.admanagement -ErrorAction SilentlyContinue | Out-Null

#   You can use the -UseGlobalCatalog switch if you'd like in order to make the changes to your GC
connect-qadservice $DomainController -credential (get-credential)  

#   This will store all applicable groups into a variable called groupmembership
$groupmembership = get-qaduser $sourceuser | select -ExpandProperty memberof    

#    This will loop through all groups in groupmembership and apply the memberships
foreach ($group in $groupmembership) { add-qadgroupmember -identity $group -member $targetuser }     


Browse for File to POSH Variable

August 10, 2012 3 comments

After many months of getting tired of typing in an entire directory path when using the “Read-Host” command to import a text file or CSV file I went looking for commands to present a popup box to let me browse for a file, then have that file’s path be presented to a variable. This example shows that $file will be the file path for the file specified in the Get-FileName function.

A big thank you goes to the blog over at Sapien.com for showing me this wonderfulness 🙂

http://www.sapien.com/forums/scriptinganswers/forum_posts.asp?TID=4115



Function Get-FileName($initialDirectory)

{

[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |

Out-Null



$OpenFileDialog = <strong>New-Object</strong> System.Windows.Forms.OpenFileDialog

$OpenFileDialog.initialDirectory = $initialDirectory

$OpenFileDialog.Title = "PowerShell Master Says...Select File:"

$OpenFileDialog.filter = "All files (*.*)| *.*"

$OpenFileDialog.ShowDialog() | Out-Null

$OpenFileDialog.filename


} #end function Get-FileName



# *** Entry Point to Script ***

$file = Get-FileName <em>-initialDirectory</em> "c:\"


Powershell VMWare Inventory

August 10, 2012 Leave a comment



###########################################################################
#
# NAME: VM Inventory
#
# AUTHOR: Joshua Schofield
#
# COMMENT: Script to use when wanting Datastore,Host,Name,PowerState,OS,IP, and vCenter server information for all VMs. 
#
#		   
# VERSION HISTORY: 6
#
# VERSION DATE:    08/10/2012
#			
#			
# PreReqs:   Must have vSphere Client and PowerCLI installed		
#					
#					
#					
#
###########################################################################

# Adding all snapins to current PS Session

Get-PSSnapin -Registered | Add-PSSnapin

# Set Variables for Outfile and vCenter Server(s)
$outfile = Read-Host -Prompt "Type the entire path and file name (c:\temp\test.csv) for output file:"
$vc = Read-Host -Prompt 'Enter vCenter Server FQDN or IP Address (If multiple type @("hostname","hostname2"):'

# Popup Window asking to input credentials
[System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”) | Out-Null
[Windows.Forms.MessageBox]::Show("Please Supply Your vCenter Server Credentials in the next Window”,0, [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Information)
$vccred = (Get-Credential)

# Connect to vCenter Server
Connect-VIServer -Server $vc -Credential $vccred

Write-Output "Datastore,Host,VMName,FQDN,Power,OS,IP,vCenter Server" | Out-File $outfile -Force # -Force will overwrite any existing file

Get-Datastore -Server $vc | Sort-Object {$_.name} | % {

$datastore = $_.name

Get-VM -Datastore $datastore | select name,powerstate | % {

$name = $_.name
$power = $_.powerstate
$vmhost = $_.vmhost

Get-VMGuest $name | select hostname,osfullname,ipaddress | % {

$hostname = $_.hostname
$os = $_.osfullname
$ip = $_.IPaddress

Write-Output "$datastore,$vmhost,$name,$hostname,$power,$os,$ip,$vc" | Out-File $outfile -Append

}}} 



Find CD Drive Letter(s)

August 10, 2012 1 comment

I was presented with an issue the other day where I needed to mount an ISO to a VM, then run a program which was housed on that CD Drive. I could not find a central, easy way using PowerShell to find what the letter the CD Drive was so I decided I would create one. I know this could have been done with WMI, but I decided I would go a different route for this occasion since I have used FSUTIL for other items in the past.

First off we start off by querying all drives on the machine using the FSUTIL command, we then pipe these results to our variable of $drives. Line 1 will give us a result of the drive letters that are in use on the machine. With line 2 we are actually getting rid of part of the content of the $drives variable. We do not want to have the “Drives: ” content in our variable. Now that this is complete we can then split up our $drives variable into an array. We do this by using the .split function. This allows us to break up our variable into multiple parts, in this case we are telling PowerShell to break up the $drives variable everywhere there is a space, this is indicated by the ” ” portion.

Now that we have broken our variable up into an array we can now perform a foreach loop to determine each drive letters type. This foreach loop can be completed by typing “ForEach” or “%” either will start the loop. Once that loop completes writing the results to the $drivetype variable we can then process each CD Drive that resides on the host machine.

Categories: Windows OS

Hello WordPress world!

August 10, 2012 2 comments

Welcome to my blog. After nearly 10 years of doing system admin work, I have finally decided to start a blog discussing what I do.

I hope you all enjoy my tidbits about Powershell and other system admin related topics.

 

Categories: Uncategorized
%d bloggers like this: