Archive

Archive for the ‘PowerShell’ Category

Functions: Get-LocalAdminGroupMembership and Set-LocalAdminGroupMembership – Local Admin Group Membership on Remote Machines

December 22nd, 2011 No comments

PowerShell-logo-128x84While writing some PowerShell scripts to automate the installation of Exchange on over 100 servers, I needed to set and then verify that a group (in this case, “Exchange Trusted Subsystem”) was a member of the local admins group on some remote servers.

We start with Get-LocalAdminGroupMembership. This function merely checks the local admins group on a remote server to see if the group to be added is already a member. If it is, it returns $true, if not, $false. We need to pass it two variables: $ComputerName, and $Member. We don’t need to run this function. It’s called from the second function.

function Get-LocalAdminGroupMembership	{
	[CmdletBinding()]
	Param(
		[Parameter(Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
		$ComputerName = ".",
		[Parameter(Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
		$Member
	)
	if($ComputerName -eq "."){$ComputerName = (get-WmiObject win32_computersystem).Name}
	$computer = [ADSI]("WinNT://" + $ComputerName + ",computer")
	$Group = $computer.psbase.children.find("Administrators")
	$members= $Group.psbase.invoke("Members") | % {$_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null)}
	if ($members -match $member){return $true}else{return $false}
} # end function Get-LocalAdminGroupMembership

 

The second function does all the heavy lifting.

function Set-LocalAdminGroupMembership {
	[CmdletBinding()]
	Param(
		[Parameter(Position=0, Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
		[string]$ComputerName = ".",
		[Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
		[string]$Member,
		[Parameter(Position=2, Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
		[string]$Domain = $env:USERDNSDOMAIN
	)

	Process{
		if (!(Get-LocalAdminGroupMembership -ComputerName "$ComputerName" -Member "$Member")){
			if($ComputerName -eq "."){$ComputerName = $env:ComputerName.ToUpper()}    

			if($Domain){
  			$adsi = [ADSI]"WinNT://$ComputerName/administrators,group"
    		$adsi.Add("WinNT://$Domain/$Member,group")
			}else{
	  		Write-Host "Not connected to a domain." -ForegroundColor "red"
			}
		} else {
			Write-Host "`"$Account`" is already a local admin on $ComputerName" -ForegroundColor yellow
		}
		Get-LocalAdminGroupMembership -ComputerComputer "$ComputerName" -Member "$Member"
	}# Process
} # end function Set-LocalAdminGroupMembership

We call Set-LocalAdminGroupMembership and pass it the same parameters, $ComputerName and $Member

Set-LocalAdminGroupMembership -ComputerName mycomputer -Member "Exchange Trusted Subsystem"

The function will add the group to the local admins group, and then do a Get-LocalAdminGroupMembership for that same group and dump the results to the screen.

Function: New-Pause – Pausing PowerShell Scripts

December 21st, 2011 No comments

Yesterday, I wrote about a sleep function to cause a predetermined delay in a script. Today, I give you a short function, New-Pause. New-Pause stops a script and waits for the user to press a key before continuing.

function New-Pause {
 Write-Host "Press any key to continue" -ForegroundColor green
 $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
} # end function New-Pause

We can add some flexibility into it by allowing for the function to be called with alternative values for the text color and the text itself.

function New-Pause {
  [CmdletBinding(SupportsShouldProcess, SupportsPaging, HelpUri = 'https://ucunleashed.com/885')]
  param(
    # Text that is displayed as the prompt when paused.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ValidateNotNullOrEmpty()]
    $PausePrompt = 'Press any key to continue',
    
    # Color of the prompt.
    [Parameter(ValueFromPipelineByPropertyName)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet('Black', 'DarkMagenta', 'DarkRed', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkYellow', 'Red', 'Blue', 'Green', 'Cyan', 'Magenta', 'Yellow', 'DarkGray', 'Gray', 'White')]
    $PauseColor = 'Green'
  )
  Write-Host $PausePrompt -ForegroundColor $PauseColor
  $null = $host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
  Write-Host 'Continuing...'
} # end function New-Pause

Call the function using

New-Pause
New-Pause

New-Pause

Once any key is pressed, your script can continue.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Categories: PowerShell Tags: ,

Function: New-Sleep – When You Need a Delay

December 20th, 2011 6 comments

Description

On a recent project, I needed some PowerShell scripts to wait for a few seconds just to ensure that some other processes were finished and I wasn’t issuing too many commands to some Exchange servers too quickly. I came up with this little function:

function New-Sleep {
	[cmdletbinding()]
	param(
		[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No time specified")]
		[int]$s
	)
	for ($i=1; $i -lt $s; $i++) {
	[int]$TimeLeft=$s-$i
	Write-Progress -Activity "Waiting $s seconds..." -PercentComplete (100/$s*$i) -CurrentOperation "$TimeLeft seconds left ($i elapsed)" -Status "Please wait"
	Start-Sleep -Seconds 1
	}
	Write-Progress -Completed $true -Status "Please wait"
} # end function New-Sleep

Call the function like this:

New-Sleep -s 60

Where the value of $s is the number of seconds you want to sleep. The display tells you how long your sleeping for, how much time is left, and how much time has elapsed.

New-Sleep

New-Sleep

Download the function below.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.0 New-Sleep.ps1

Categories: PowerShell Tags: ,

Script: New-HandBrakeConversion.ps1 – Convert Entire Folders of Files to .mp4 With HandBrake

October 4th, 2011 19 comments

I watch at LOT of movies. Since I’m generally away from home at least 200 days a year, I spend a lot of time in airports, on airplanes, in hotels, etc. When I work from home, I watch movies and TV shows as well. My iPad and my 5TB iTunes library keep me entertained.

Many of the videos I have start in something other than the .mp4/.m4v format that iTunes requires. Either it’s something I’ve downloaded, something someone sent me, training (CBT) videos, or something pulled from my HD video camera’s hard drive. The files usually are either .avi or .mkv.

There are plenty of utilities out there that will convert video files from one format to another. Some of them are free, and some cost a few bucks. By far the best, most flexible utility I’ve found is the popular HandBrake. HandBrake has many options that let you get down “in the weeds” with every setting conceivable for converting videos. It has a popular queuing feature that lets you add several files to the queue, and HandBrake will process them, one at a time. Processing time depends on the source file size and format, as well as the destination format. And, it’s FREE.

Since I am away from my monster desktop machine so much, I put it’s i7 processor’s idle time to good use by having it process videos, saving them as .mp4 videos. But manually adding a bunch of videos to HandBrake’s queue can take a while. Plus, since I’m not around when the processing is taking place, I don’t need the fancy GUI from HandBrake. When HandBrake is installed, a CLI version is also automatically installed So I created a PowerShell script that will use the CLI version, take a source folder, and process every video in that directory that doesn’t have a corresponding .mp4 file of the same name. That way, if the script stops and you need to restart it, it won’t re-process video’s it’s already converted. It will only attempt to convert files with .avi, mkv, .ogm, and .wmv extensions, but only the first two, .avi, and .mkv, have been extensively tested.

The script will automatically detect if/where HandBrake is installed, so there is nothing really to configure. Run the script with no switches, and you’ll be prompted for the source folder that contains the videos to be converted:

.\New-HandBrakeConversion.ps1

Or, you can specify the source folder and avoid the prompt:

.\New-HandBrakeConversion.ps1 -dir "c:\path\to\files"

For those of you who are hardcore HandBrake users, the configuration settings I use are the following:

-e x264 -b 1500 -a 1 -E faac -B 160 -R Auto -6 dpl2 -f mp4 -m -2 -T -x ref=2:bframes=2:me=umh -n eng

Let me know if you have any questions or comments.

Installation

Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.

There is no installation routine for this script. It’s meant to be manually run.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.3 New-HandBrakeConversion.v1.3.zip

Changelog

See the changelog for this script for all version information

Categories: PowerShell Tags: ,

Script: New-ADPasswordReminder.ps1 – Email Users When Their Password Will Soon Expire

August 27th, 2011 348 comments

Note: Development on this script has moved to Github. Please see the repo at https://github.com/patrichard/New-AdPasswordReminder.

Description

In today’s increasingly mobile workforce, many users don’t login to Active Directory on a domain joined computer. Some use only Outlook Web Access/App, some use non-domain joined machines external to the company network, some just use mobile devices such as smartphones and tablets. And others may use Macs.

Users who login via domain joined machines on the company network get the reminder several days ahead. The default is 14 days, but can be configured in the Default Domain Group Policy under Interactive logon: Prompt user to change password before expiration.

OWA users see a notification when they login as well. In OWA 2007 running on IIS6, this can be adjusted via PasswordExpirePrenotifyDays. In fact, with OWA 2007 and 2010, you can even change your password after it expires, using the Password Reset Feature in Exchange 2007 and 2010. However, there are times when that’s just not a remedy. The password reset feature requires the Exchange server to be running on Windows 2008 or later, as it relies on IIS 7. Many Exchange 2007 shops are not on that platform yet.

Anyone who’s ever worked on a Help Desk knows that a LOT of users call to say they can’t login, only to determine it’s because their password expired. Many, if not most, are those types of users mentioned above. Others don’t notice, or simply ignore the notice when logging in. So let’s really make sure we notify them of the pending expiration. There are some third-party tools, including some that run on SharePoint, that enable a user to reset their password. But this is after the fact. Sure, we could use some third-party application to send a reminder, but… well… why? PowerShell to the rescue!

In the pre-Windows 2008 domain functional level days, we could just peek at the Default Domain GPO, and grab the Maximum Password Age parameter, since it was a global setting. Then we could go through Active Directory, find users who are not set to “never expire”, use some math, and come up with a list of users whose password expired soon.

But with the changes implemented with Windows Server 2008, we can now have Fine Grained Password policies, which allows us to have more than just one password policy in our organization. So, Executives get one password, IT people with elevated rights get another, etc. Cool in theory, but frustrating in our endeavor to notify users when they’ll expire.

I blatantly admit that I used part of a script by M. Ali, who wrote a blog post Find out when your Password Expires. The script looks checks Get-AdDomain, and looks at the DomainMode parameter in the results. From here, we know whether we can just peek at the Default Domain policy, or if we need to look deeper. Regardless of which way, we look through the users using Get-AdUser, and grab the PasswordExpired, PasswordNeverExpires, and PasswordLastSet fields. Obviously, if the account is expired, no need to keep reminding the user. And if the password never expires, then we also don’t need to notify the user. With PasswordLastSet, our math comes into play to determine when the password will expire. Not terribly short and sweet, but effective.

Once we know when the password will expire, we can then set a window for when we should notify the users. It makes sense to match what’s in the GPO so that notifications are consistent regardless of platform. This script is set to 14 days by default.

Next, we need to craft some information that we want to convey to the user. In this case, we’ll use some HTML formatting so that we can properly convey the importance of the info, as well as include some additional formatting. I’ve mocked up something based on some third-party tools, and on the comments and recommendations of IT Professionals and users. It’s simple enough to change, but be warned that many clients, including Outlook, don’t strictly adhere to HTML standards. So it can take quite a bit of trial and error to find out what does actually appear the way you want it to.

Installation and Setup

First, you need a receive connector that will accept mail from PowerShell. I cover that in Creating a receive connector to use for sending email from PowerShell. Next, the script must run on a machine with PowerShell 2.0 installed. This is a prerequisite for Exchange 2010 (and installed by default on Windows 2008 R2), but not for Exchange 2007. If you’re reluctant to upgrade PowerShell on your 2007 box, it can be run from any other box that has PowerShell 2.0 and the Exchange Management tools installed. Note: Exchange Management tools should always be updated and patched to the same level that your Exchange servers are.

Second, you’ll need the ActiveDirectory module available on the machine that will run the script. The ActiveDirectory module is installed when you add the Remote-Server Administration Tools feature in Windows Server 2008 R2. If the module is not detected, the script will attempt to install it automatically the first time it runs.

Next, grab the latest zip file from the DOWNLOAD section below. It includes the script and ScriptImages.zip contains a couple of images that are used in the warning for users who’s password expires in < 24 hours (seen in the Outlook screenshot above). The images need to be accessible to all users who will receive the reminder emails. This is likely to be a public web site.

Crack open the script in your favorite editor and update the lines in the param() block to match your environment. This includes $Company, $OwaUrl, $PSEmailServer, $EmailFrom, $HelpDeskPhone, $HelpDeskURL and $DaysToWarn. If you want to target a specific OU, set $OU. Also, set $ImagePath to a path holding the included image files (or those you add/edit). This path should be available to all users who may receive the reminder message. This is probably a public server.

param(
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Demo,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Preview,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Install,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string]$PreviewUser,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Transcript,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a company name.")]
	[ValidateNotNullOrEmpty()]
	[string]$Company = "Contoso Ltd",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify an OWA URL")]
	[ValidateNotNullOrEmpty()][ValidatePattern("^https://")]
	[string]$OwaUrl = "https://mail.contoso.com",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify the IP address of your email server")]
	[ValidateNotNullOrEmpty()][ValidatePattern("\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b")]
	[string]$PSEmailServer = "10.9.0.11",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a name and email address for the email 'from' field")]
	[ValidateNotNullOrEmpty()][ValidatePattern("\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b")]
	[string]$EmailFrom = "Help Desk ",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string]$HelpDeskPhone = "(586) 555-1010",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[ValidatePattern("^http")]
	[string]$HelpDeskURL = "https://intranet.contoso.com/",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string] $TranscriptFilename = $MyInvocation.MyCommand.Name + " " + (hostname)+ " {0:yyyy-MM-dd hh-mmtt}.log" -f (Get-Date),
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="This must be zero")]
	[ValidateNotNullOrEmpty()]
	[int]$global:UsersNotified = 0,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify how many days before expiration that users should begin to be notified.")]
	[ValidateNotNullOrEmpty()]
	[int]$DaysToWarn = 14,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string] $ImagePath = "http://www.contoso.com/images/new-passwordreminder.ps1",
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)]
	[ValidateNotNullOrEmpty()]
	[string] $ScriptName = $MyInvocation.MyCommand.Name,
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)]
	[ValidateNotNullOrEmpty()]
	[string] $ScriptPathAndName = $MyInvocation.MyCommand.Definition,
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="Please specify an Organizational Unit")]
	[ValidateNotNullOrEmpty()]
	[string] $ou
)

Open an Exchange Management Shell session and run the script in demo mode to see a list of users that are expiring soon.The script won’t email the users in demo mode. It merely shows you who it WOULD, and how long till their password expires.

.\New-PasswordReminder.ps1 -demo

As we see in the example screenshot, Claudia’s password expires in 5 days, and the password policy that applies to her requires the password to be changed every 42 days. If we run the script normally, Claudia will receive the email reminder since it’s within the 14 day window defined in the script.

To run the script normally (non-demo mode), manually, just omit the -demo. There is no output to the screen when run normally, as the script is designed to be run as a scheduled task.

Once you’re satisfied that the script is running correctly, we can set it to run as a scheduled task. I have a blog post Running PowerShell scripts via Scheduled Tasks that details everything. In my production environment, it runs at 6am each day.

One of the hardest parts was getting a decently formatted email that looked good. This could take some trial and error, and the original script didn’t really have a way built in to preview what the end user would see. As a result, some hapless users would be flooded with your “test” messages. I fixed that by creating a preview mode. Manually run the script with the preview switch, and a user to send the email to. For example

.\New-PasswordReminder.ps1 -Preview -PreviewUser bgates

This will send an email to the user, bgates. The email is formatted for a password that expires in one day, so the user gets the additional banner near the top as well.

Next up was creating a scheduled task. Not really terribly difficult to do manually, but I could see where it might take some trial and error. So, I added the install switch, which will create a scheduled task for the script, configuring it to run at 6am each day. Of course, that time can be manually adjusted by opening the scheduled task once it’s created. The install mode will ask for credentials to run the scheduled task under. Install it as so:

.\New-PasswordReminder.ps1 -Install

Note: The scheduled task is configured to point to where the script is when you run the install switch. So don’t move it later!

To send an email that does not contain the images or their related formatting, specify $NoImages when running the script. This will send essentially an HTML formatted text email.

Next up, I added some simple logging to the application event log. The script will write a single entry when it starts, and a single entry when it finishes, noting how many users were processed (sent an email). I would love to hear how this script works in large environments. If you’re willing, please let me know (via comments below) how long it’s taking to run in your environment, and the number of users in AD.

Please send me your suggestions!

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Syntax

New-PasswordReminder.ps1 [-Demo] [-Install] [[-PreviewUser] ] [-NoImages] [-WhatIf] [-Confirm] []

Demo Runs the script in demo mode. Demo mode displays users who are expiring soon, but does not send them the reminder email.

Install Creates a scheduled task to run the script automatically every day at 6:00am

PreviewUser
Defines the user to send the preview email to.

NoImages
Specifies that a HTML text only message should be sent instead of one that contains the fancy formatting.

Installation

Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.

In addition to the info listed above:

If you leave the following parameters blank, the related text will be removed from the email sent to users: $HelpDeskURL. This will get expanded in the future.

You can change the format of the date displayed in the email by changing the value of $DateFormat. The default is “d”, which yields a date such as 09/07/2012 (MM/dd/yyyy). If you’d like the European style, use “MM/dd/yyyy” instead.

Frequently Asked Questions

Question: Does this work with Exchange Server 2013

Answer: Yes

Download

v2.9 – 09-13-2013 New-ADPasswordReminder.v2.9.zip

v2.8 – 05-03-2013 New-ADPasswordReminder.v2.8.zip

v2.7 New-PasswordReminder.v2.7.zip

v2.6 New-PasswordReminder.v2.6.zip

v2.4 New-PasswordReminder.v2.4.zip

New-PasswordReminder.zip

ScriptImages.zip – image files used in emails

Changelog

See the changelog for this script which details all versions and their features.

Disabling Loopbackcheck Programatically with PowerShell

August 17th, 2011 No comments

In some versions of Exchange, some cmdlets such as Test-OutlookWebServices can generate a 401 error when run from the Exchange server itself. Microsoft released a KB article (896861) that details disabling via the reqistry the loopback check. Other applications such as Lync, SharePoint, or  Symantec’s Enterprise Vault sometimes require this change as well to resolve issues. While it’s pretty easy to just create the registry entry in regedit (see the aforementioned KB article), if you’re putting together server-build scripts, or just like to use PowerShell to do the work, this method might be easier.

This little tidbit is broken down into two parts. The first part looks to see if the registry key exists, and if so, if it’s not set to the correct value (1). If it meets this criteria, we set the dword’s value to 1. The second part of the script determines if the dword exists at all, and if not, creates it and sets it to 1. If we make it all the way through, we know the dword exists and has the right value.

if ((Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\Lsa -name DisableLoopbackCheck -ErrorAction SilentlyContinue) -and (((Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\Lsa -name DisableLoopbackCheck -ErrorAction SilentlyContinue).DisableLoopbackCheck)-ne 1)){
    Set-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\Lsa -name DisableLoopbackCheck -value 1
}elseif (!(Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Control\Lsa -name DisableLoopbackCheck -ErrorAction SilentlyContinue)){
    New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -Value "1" -PropertyType dword
}

Exchange 2010 and Lync 2010 PowerShell Syntax Highlighting File for UltraEdit

July 20th, 2011 No comments

If you write a lot of PowerShell code, you probably have a favorite text editor. Maybe you use Notepad, Notepad+, PowerShell ISE, etc. Mine is IDM Computer Solutions’ UltraEdit. I’ve been using UltraEdit for many years, including while doing web dev work. It’s feature packed and well worth the few dollars that it costs.

One of UltraEdit’s great features is the ability to do syntax highlighting, with support for many different languages. By default, UltraEdit doesn’t ship with a wordfile for PowerShell. There is a community that helps develop new syntax highlighting wordfiles, and lo and behold, someone created a PowerShell wordfile.

I’ve taken that file and added the Exchange 2010 and Lync 2010 cmdlets so that they are properly highlighted. The file can be downloaded from powershell.zip. Grab it and toss it into your %appdata%\IDMComp\UltraEdit\wordfiles folder. Enjoy!

Categories: PowerShell Tags:

Function: New-Share – Creating File Shares Via PowerShell

July 13th, 2011 4 comments

Description

Often we need to create file shares, and this is generally fairly boring. PowerShell can help streamline this process. This function will create the share, but does not set sharing permissions. We’ll cover that later.

This is based partly on How to Use PowerShell to create shared folders in Windows 7. I resolved some minor issues and added the pipeline parameters and some minor error checking, and the description. The script will create the folder if it doesn’t exist, and then share it.

function New-Share {
 param (
  [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No folder name specified")]
  [string]$FolderName,
    [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No share name specified")]
    [string]$ShareName,
    [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="No description specified")]
    [string]$Description
 )
 $error.clear()
 # Check for folder; Create it if it doesn't exist
 If (!(Test-Path $FolderName)) {
  New-Item $FolderName -type Directory | Out-Null
 }
 # Check for share; Create it if it doesn't exist
 $Shares=[WMICLASS]"WIN32_Share"
 if (!(Get-WMIObject Win32_share -filter "name='$ShareName'")){
  $Shares.Create($FolderName,$ShareName,0,65535,$Description) | Out-Null
  if (!($error)){
   # the share was created
   return $true
  } else {
   # there was an error
   return $false
  }
 } else {
  # the share already exists
  return $false
  }
} # end function New-Share

And we can then call the function with something like:

New-Share -FolderName "c:\LyncShare" -ShareName "LyncShare" -Description "Used by Lync server to store Address Book files, phone updates, and other important files."

As you can see, it’s pretty straight forward. We’ll cover setting both NTFS and Share permissions soon.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Categories: PowerShell Tags: ,

Function: Remove-ScriptVariables – Cleaning Up Script Variables in PowerShell

July 8th, 2011 3 comments

PowerShell-logo-128x84Description

When writing scripts that use variables, especially those that contain a fair amount of data, it’s best practice to clean up the environment when exiting. This frees up memory for other purposes, and allows you to leave the environment as clean as possible. This is accomplished using the Remove-Variable cmdlet.

As scripts become more complex and evolve over time, it can be tough to keep track of all variables in order to remove them at the end. I created this function to help deal with this. The function takes the path of the script file, inspects the file, compiles a list of variables in the script, and runs them through the Remove-Variable cmdlet. It builds on some of the code from Auto-Documenting Script Variables.

function Remove-ScriptVariables($path) { 
 $result = Get-Content $path | 
 ForEach { if ( $_ -match '(\$.*?)\s*=') {     
   $matches[1]  | ? { $_ -notlike '*.*' -and $_ -notmatch 'result' -and $_ -notmatch 'env:'}  
  } 
 } 
 ForEach ($v in ($result | Sort-Object | Get-Unique)){ 
  Write-Host "Removing" $v.replace("$","")
  Remove-Variable ($v.replace("$","")) -ErrorAction SilentlyContinue
 }
} # end function Get-ScriptVariables

We then call the function, passing the built-in $MyInvocation.MyCommand.Name value, which automatically contains the path and name of the currently running script. Essentially, we tell the function to run against it’s own script file:

Remove-ScriptVariables($MyInvocation.MyCommand.Name)

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Categories: PowerShell Tags: ,

Script: Set-WindowsEmailAddress.ps1 – Set WindowsEmailAddress Field for Users in a Non-Exchange Environment

June 12th, 2011 No comments

Windows-logo-128x128A friend of mine had a dilemma. Her environment doesn’t have Exchange, but does has some LDAP apps that query AD. They needed to set the WindowsEmailAddress field on all AD user accounts to first.last@domain.com. In Exchange, this is automatically set when a user is mail or mailbox enabled. No such luck here (and not for a lack of me trying!). And, since there are hundreds and hundreds of users, manually setting each user was a task best left for those with no life. So – PowerShell to the rescue.

In the ActiveDirectory module, available on Domain Controllers, we have both Get-AdUser and Set-AdUser. These are really the only two cmdlets we need to fill the requirement. We essentially use Get-AdUser to get all of the users in a specific OU:

[object] $objUsers = Get-AdUser -filter * -ResultSetSize $null | Where-Object {$_.DistinguishedName -match $ou -and $_.GivenName -ne $null -and $_.Surname -ne $null}

$ou is defined as the target OU containing all users we want to process. The script will include all children OUs as well. Not in this OU are service accounts and non email enabled accounts. So we have an array with users. We’re 1/2 way there. We then cycle through the array using a ForEach and set the email address by combining the first name, a “.”, the second name, an “@”, and the SMTP domain. But I wanted to do some simple cleanup of the addresses. First concern was people with multiple names in either the first name or last name fields. Think “Billy Ray”.  This was simple enough using a simple replace:

$strEmail = ($objUser.GivenName).replace(" ","") + "." + ($objUser.Surname).replace(" ","") + "@" + $SMTPDomainName

The second concern was consistent formatting of case. I’m a big believer that Capitalizing each word in an email or URL address (aka “CamelCase”) makes them easier to read quickly. Doing some digging around, I found Get-Culture.TextInfo.ToTitleCase. This will capitalize each word, so “billy ray cyrus” becomes “Billy Ray Cyrus”. Unfortunately, “BILLY RAY CYRUS” stays the same. A little ToLower() magic and poof – perfection:

$strEmail = ((Get-Culture).TextInfo.ToTitleCase($objUser.GivenName.ToLower()).replace(" ","")) + "." + ((Get-Culture).TextInfo.ToTitleCase($objUser.Surname.ToLower()).replace(" ","")) + "@" + $strSMTPDomainName

So we’ve got the users, and we’ve figured out how to create the email address. Lastly, we actually make the change. Within our loop, we simply run

Set-AdUser $objUser.SamAccountName -emailaddress $strEmail

Results in just a few lines!

Now, my friend hasn’t embraced the PowerShell way of life yet, so I figured I’d spruce things up a little. Rather than have to manually type the OU path, I found a post that showed how to create a GUI based OU picker. It got me about 3/4 the way there, and added ~100 lines to the script. I added the “OK” button and dumped the results back into the script. I added a prompt for the SMTP domain name as well. The script now adds a transcript for a record of what was done and some code to ensure the ActiveDirectory module is available and loaded (just in case the script is run from a normal PowerShell session and not the Active Directory Module for Windows PowerShell session).

I didn’t really add any error checking, as this is a one-off script meant to just save a little time.

if this is the first script you’re running, then

.\Set-WindowsEmailAddress.ps1 -SMTPDomainName [SMTP domain]

to execute the script. Follow the prompts. Pretty simple stuff.

Installation

Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.2 – 01-27-2014 Set-WindowsEmailAddress.v1.2.zip

v1.1 – 10-01-2011 Set-WindowsEmailAddress.v1.1.zip

v1.0 – 06-12-2011 Set-WindowsEmailAddress.zip

Changelog

See the changelog for this script which details all versions and their features.

Categories: PowerShell Tags: ,