Archive

Posts Tagged ‘Function’

Function: Set-TimeZone – Configure Time Zone via PowerShell

March 4, 2013 1 comment

Powershell_logo-137x137It’s easy to just double-click on the time in the system tray and set the time and time zone when building a new server. You may even have a GPO that does this for you. That’s all fine and dandy. But GPOs don’t work for member servers, and if you build a lot of servers, like I do, it’s a repetitive task – something perfectly suited for PowerShell.

With this function, we do nothing more than call tzutil.exe with the timezone name. Nothing fancy. I added some validation to make sure that the timezone specified is truly one of the 100 valid time zones that tzutile.exe should recognize and accept.

So, now, in your server provisioning script, merely call the function, Set-TimeZone, with the desired timezone. For example:

Set-TimeZone "Pacific Standard Time"

The script defaults to “Eastern Standard Time” if you don’t supply a timezone. That’s it. There is no screen output for this function. Here’s the function:

function Set-TimeZone {
  [CmdletBinding(SupportsShouldProcess = $True)]
  param( 
    [Parameter(ValueFromPipeline = $False, ValueFromPipelineByPropertyName = $True, Mandatory = $False)]
    [ValidateSet("Dateline Standard Time","UTC-11","Hawaiian Standard Time","Alaskan Standard Time","Pacific Standard Time (Mexico)","Pacific Standard Time","US Mountain Standard Time","Mountain Standard Time (Mexico)","Mountain Standard Time","Central America Standard Time","Central Standard Time","Central Standard Time (Mexico)","Canada Central Standard Time","SA Pacific Standard Time","Eastern Standard Time","US Eastern Standard Time","Venezuela Standard Time","Paraguay Standard Time","Atlantic Standard Time","Central Brazilian Standard Time","SA Western Standard Time","Pacific SA Standard Time","Newfoundland Standard Time","E. South America Standard Time","Argentina Standard Time","SA Eastern Standard Time","Greenland Standard Time","Montevideo Standard Time","Bahia Standard Time","UTC-02","Mid-Atlantic Standard Time","Azores Standard Time","Cape Verde Standard Time","Morocco Standard Time","UTC","GMT Standard Time","Greenwich Standard Time","W. Europe Standard Time","Central Europe Standard Time","Romance Standard Time","Central European Standard Time","W. Central Africa Standard Time","Namibia Standard Time","Jordan Standard Time","GTB Standard Time","Middle East Standard Time","Egypt Standard Time","Syria Standard Time","E. Europe Standard Time","South Africa Standard Time","FLE Standard Time","Turkey Standard Time","Israel Standard Time","Arabic Standard Time","Kaliningrad Standard Time","Arab Standard Time","E. Africa Standard Time","Iran Standard Time","Arabian Standard Time","Azerbaijan Standard Time","Russian Standard Time","Mauritius Standard Time","Georgian Standard Time","Caucasus Standard Time","Afghanistan Standard Time","Pakistan Standard Time","West Asia Standard Time","India Standard Time","Sri Lanka Standard Time","Nepal Standard Time","Central Asia Standard Time","Bangladesh Standard Time","Ekaterinburg Standard Time","Myanmar Standard Time","SE Asia Standard Time","N. Central Asia Standard Time","China Standard Time","North Asia Standard Time","Singapore Standard Time","W. Australia Standard Time","Taipei Standard Time","Ulaanbaatar Standard Time","North Asia East Standard Time","Tokyo Standard Time","Korea Standard Time","Cen. Australia Standard Time","AUS Central Standard Time","E. Australia Standard Time","AUS Eastern Standard Time","West Pacific Standard Time","Tasmania Standard Time","Yakutsk Standard Time","Central Pacific Standard Time","Vladivostok Standard Time","New Zealand Standard Time","UTC+12","Fiji Standard Time","Magadan Standard Time","Tonga Standard Time","Samoa Standard Time")]
    [ValidateNotNullOrEmpty()]
    [string]$TimeZone = "Eastern Standard Time"
  ) 

  $process = New-Object System.Diagnostics.Process 
  $process.StartInfo.WindowStyle = "Hidden" 
  $process.StartInfo.FileName = "tzutil.exe" 
  $process.StartInfo.Arguments = "/s `"$TimeZone`"" 
  $process.Start() | Out-Null 
} # end function Set-TimeZone

Function: New-PSUpdateHelpScheduledTask – Auto Update PowerShell Help

March 1, 2013 3 comments

Powershell_logo-137x137Probably not of much value to people unless you build a fair amount of servers and have it scripted, but…

One thing you want to do with servers running PowerShell v3.0, such as Windows Server 2012, is to regularly run Update-Help from a PowerShell session to update the help files PowerShell uses for various modules. But this is something that can be easily forgotten, and, who wants to manually do a recurring task, anyways?

Here is a function you can toss in your server build script to create a scheduled task that will automatically update PowerShell help every day at 7pm. The task runs as ‘system’, so we don’t have to worry about storing credentials. Here’s the function

function New-PSUpdateHelpScheduledTask {
	[CmdletBinding(SupportsShouldProcess = $true)]
	param()
	BEGIN{
		[string] $TaskArguments = '-NonInteractive -WindowStyle Normal -NoLogo -NoProfile -Command "& {Update-Help -Force}"'
		[string] $TaskProgram =  "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
	}
	PROCESS{	
		$TaskAction = New-ScheduledTaskAction -Execute $TaskProgram -Argument $TaskArguments
		$TaskTrigger = New-ScheduledTaskTrigger -Daily -At 19:00
		Register-ScheduledTask -TaskName "Update PowerShell Help" -Action $TaskAction -Trigger $TaskTrigger -RunLevel Highest -User "system" -Description "Automatically updates PowerShell help." | Out-Null
	}
	END {
		Remove-Variable TaskAction
		Remove-Variable TaskTrigger
	}
} # end function New-PSUpdateHelpTask

and merely call it using

New-PsUpdateHelpScheduledTask

You’ll notice a new scheduled task in Task Scheduler, and we see that it completed successfully.
Update PowerShell Help
Just a couple of lines of code and we’ve removed the need to manually run Update-Help on a server.

Hope this helps!

Function: Set-AdminUser – Clear AdminCount and Enable Security Inheritance

October 22, 2012 9 comments

Powershell_logo-137x137Description

While migrating some users during a Lync migration, I needed to disable users for Lync in one forest, and enable them in another. I ran into a problem where many users in the legacy forest had adminCount set to 1, and security inheritance disabled. The problem is that my account didn’t have rights to disable them in Lync, and I was getting an access denied error. This is common when the user is/was a member of a protected group. I won’t go into the background of adminCount, as it’s well documented.

I knew that as the migration progressed, we would identify more users where this was the case. So I wanted to find a better way of dealing with these. There are several ways of finding users with adminCount set using PowerShell, including

([adsisearcher]"(AdminCount=1)").findall()

and using the ActiveDirectory PowerShell module via

Get-ADuser -LDAPFilter "(admincount=1)" | select name

and we can look at groups, too, using

Get-ADgroup -LDAPFilter "(admincount=1)" | select name

Turns out that many of the users (1000+) were no longer members of protected groups, what’s often referred to as Orphaned AdminSD Objects. So we could clear adminCount and enable security inheritance. But doing this manually on 1000+ users isn’t something that any of us wanted to spend time doing.

We can clear adminCount with a one-liner:

Get-AdUser [user name] | Set-AdObject -clear adminCount

But that doesn’t take care of security inheritance, which is the real culprit in my issue. We can use dsacls:

$User = [ADSI] $_.Path
dsacls $User.distinguishedName /p:n

Unfortunately, this involves multiple steps in native PowerShell. So, a function was born.

Set-AdminUser takes input from either the $UserName parameter, or via the pipeline, and clears adminCount, then enables security inheritance. It can process a single user or multiple, such as with the output of Get-ADGroupMember.

Syntax

Set-AdminUser [[-UserName] ] [-WhatIf] [-Confirm] []

Examples

Set-AdminUser [user name]
Get-AdGroupMember [group name] | Set-AdminUser

Code

Here is the function to copy. You can also download it in the download section below.

function Set-AdminUser	{  
	<# 
	.SYNOPSIS
			Clears adminCount, and enables inherited security on a user account.

	.DESCRIPTION
			Clears adminCount, and enables inherited security on a user account.

	.NOTES
	    Version    	      	: v1.0
	    Wish list						: 
	    Rights Required			: UserAdministrator
	    Sched Task Req'd		: No
	    Lync Version				: N/A
	    Lync Version				: N/A
	    Author       				: Pat Richard, Exchange MVP
	    Email/Blog/Twitter	: pat@innervation.com 	http://www.ehloworld.com @patrichard
	    Dedicated Post			: http://www.ehloworld.com/1621
	    Disclaimer   				: You running this script means you won't blame me if this breaks your stuff.
	    Info Stolen from 		: http://serverfault.com/questions/304627/powershell-script-to-find-ad-users-with-admincount-0
													: http://morgansimonsen.wordpress.com/2012/01/26/adminsdholder-protected-groups-sdprop-and-moving-mailboxes-in-exchange/

	.LINK     

http://www.ehloworld.com/1621

	.INPUTS
			You can pipeline input to this command		

	.PARAMETER UserName
			Create the scheduled task to run the script daily. It does NOT create the required Exchange receive connector.

	.EXAMPLE 
			Set-AdminUser -UserName [user name]

			Description
			-----------
			Clears the adminCount of the specified user, and enabled inherited security

	.EXAMPLE 
			Get-AdGroupMember [group name] | Set-AdminUser

			Description
			-----------
			Clears the adminCount of all group members, and enabled inherited security

	#>
	#Requires -Version 2.0	

	[CmdletBinding(SupportsShouldProcess = $True)]
	param (
		[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $True, Mandatory = $False)]
	  [ValidateNotNullOrEmpty()]
		[string]$UserName
	)
	Begin{
		## allows inheritance 
		[bool]$isProtected = $false
		## preserves inherited rules 
		[bool]$PreserveInheritance = $true
	}
	Process{
		[string]$dn = (Get-ADUser $UserName).DistinguishedName
		Set-AdObject -identity $dn -clear adminCount
		$user = [ADSI]"LDAP://$dn"
		$acl = $user.objectSecurity
		Write-Verbose $dn
		Write-Verbose "Original permissions blocked:"
		Write-Verbose $acl.AreAccessRulesProtected
		if ($acl.AreAccessRulesProtected){
			$acl.SetAccessRuleProtection($isProtected,$PreserveInheritance)
			$inherited = $acl.AreAccessRulesProtected
			$user.commitchanges()
			Write-Verbose "Updated permissions blocked:"
			Write-Verbose $acl.AreAccessRulesProtected
		}
	}
	End{
		remove-variable acl
		remove-variable UserName
		remove-variable isProtected
		remove-variable PreserveInheritance
		remove-variable dn
		remove-variable user
	}
} # end function Set-AdminUser

Download

v1.0 – 10-20-2012 Set-AdminUser.v1.0.zip

Categories: PowerShell Tags: ,

Function: New-SignedScript – Easily Sign One or Many Scripts with Your Code Signing Cert

September 20, 2012 Leave a comment

Signs a PowerShell script with a code signing certificate.

Syntax

New-SignedScript [[-path] ] [-Verbose] [-Debug] [-ErrorAction ] [-WarningAction ] [-ErrorVariable ] [-WarningVariable ] [-OutVariable ] [-OutBuffer ] [-WhatIf] [-Confirm]

Detailed Description

One of the concerns about using a PowerShell script is that it often requires the user to change the Execution Policy on the machine the script is running on. This can cause security concerns, because when the Execution Policy is lowered, any script can run, including those with malicious intent. For more information on setting the Execution Policy, see Set-ExecutionPolicy.

Of course, you need a code signing certificate in order to sign scripts. Fellow Exchange MVP Mike Pfeiffer wrote an informative article, Obtaining a Code Signing Certificate and Signing PowerShell Scripts that covers using an internal Certificate Authority. Third party Certificate Authorities (CAs) such as Digicert also provide code signing certificates. I can’t recommend Digicert enough. I have both a standard code signing certificate and an Extended Validation code signing certificate.

But signing scripts manually can be a little cumbersome. This function gets the current code signing certificate, verifies it’s not expired, and then signs the script. The script will only sign .ps1 files, and will not attempt to sign a script that’s already signed.

Example

New-SignedScript -path [path to script]

such as

New-SignedScript -path .\myscript.ps1

You can also pipeline files to this function, for example:

Get-Item *.ps1 | New-SignedScript

Installation

Nothing special here. Once you have a valid code signing certificate installed, the function should work as designed.

Download

v1.0 – New-SignedScript.v1.0.zip

Changelog

None at this time.

Categories: PowerShell Tags: , ,

Function: New-FirewallRule – Add Windows Firewall Rules Via PowerShell

September 14, 2012 Leave a comment

Some of my scripts, namely Get-CsConnections.ps1, require specific firewall rules be created in order to operate correctly. So I set out to automate as much as possible the creation of these rules. A function was born.

This little function can do pretty much everything the Windows Firewall wizard can do. You can specify local and remote ports, local and remote IP addresses, programs, services, direction Inbound or Outbound), TCP/UDP or Any, and more. I borrowed some of the info from the Windows Firewall chm file for the comment-based help file in the function. Just run

Get-Help New-FirewallRule

for detailed help.

I tried to think of (and test) common requirements for firewall rules, but if I’ve left something out, or something isn’t working as expected, feel free to leave a comment below.

Syntax

New-FirewallRule [[-name] ] [[-localPorts] ] [[-remotePorts] ] [[-localAddresses] ]
 [[-remoteAddresses] ] [[-program] ] [[-serviceName] ] [[-description] ] [-outbound] 
[-udp] [-block] [-readonly] [-any] [-domain] [-public] [-private] [-WhatIf] [-Confirm] []

Name  This is the name of the firewall rule. As a best practice, give the firewall rule a unique name. If two rules have the same name, then you cannot easily manage them by using the netsh or PowerShell commands. Do not use the name “all” for a firewall rule because that is the name of a Netsh command-line tool keyword.

LocalPorts  If you are using the TCP or UDP protocol type, you can specify the local port by using one of the choices from the drop-down list or by specifying a port or a list of ports. The local port is the port on the computer on which the firewall profile is applied.

RemotePorts  If you are using the TCP or UDP protocol type, you can specify the local port and remote port by using one of the choices from the drop-down list or by specifying a port or a list of ports. The remote port is the port on the computer that is attempting to communicate with the computer on which the firewall profile is applied.

LocalAddresses  The local IP address is used by the local computer to determine if the rule applies. The rule applies only to network traffic that goes through a network adapter that is configured to use one of the specified local IP addresses.

RemoteAddresses  Specify the remote IP addresses to which the rule applies. Network traffic matches the rule if the destination IP address is one of the addresses in the list.

Program  Use this option to match network packets going to or from a specified program. If the program is not running, then no packets match the rule. Type the complete path to the program. You can include environment variables, where appropriate. When you add a program to the rule, Windows Firewall with Advanced Security dynamically opens (unblocks) and closes (blocks) the ports required by the program. When the program is running and listening for incoming traffic, Windows Firewall with Advanced Security opens the required ports; when the program is not running or is not listening for incoming traffic, Windows Firewall with Advanced Security closes the ports. Because of this dynamic behavior, adding programs to a rule is the recommended method for allowing unsolicited incoming traffic through Windows Firewall.

ServiceName  Use this option to apply the rule only to services, not to other processes. Specify the short name of the service to which you want the rule to be applied.

Description  This is a description of the rule. Use this to provide information about the rule, such as the rule owner, the rule requester, the purpose of the rule, a version number, or the date of creation.

Outbound  Configures the rule as outbound. If not specified, the rule is created as inbound.

UDP  Use this option to specify that the rule should filter UDP traffic. If not specified, and -Any is also not specified, the rule will filter TCP traffic. Cannot be used with -ANY.

Block  Use this option to explicitly block any network packet that matches the firewall rule criteria. The block action takes precedence over the allow action, unless the Override block rules option is selected when the firewall rule is created.

ReadOnly  If used, the rule will be created and attributes such as Program, Protocols, and Ports cannot be edited after creation. To change these settings, delete the rule and recreate it.

Any  Use the option to filter traffic from any protocol. Cannot be used with -UDP.

Domain  Applies when a computer is connected to a network that contains an Active Directory domain controller in which the computer’s domain account resides.

Private  Applies when a computer is connected to a network in which the computer’s domain account does not reside, such as a home network. The private profile settings should be more restrictive than the domain profile settings. A network is assigned the private type by a local administrator.

Public  Applies when a computer is connected to a domain through a public network, such as one available in airports and coffee shops. The public profile settings should be the most restrictive because the computer is connected to a public network where the security cannot be as tightly controlled as it is in an IT environment. By default, newly discovered networks are assigned the public type.

Examples

There is a vast number of combinations that can be used to create rules. I’ve tested a bunch, but cannot possibly test every conceivable combination. Here are a couple of examples:

New-FirewallRule -Name "Test Rule" -Description "My cool Lync rule" -Domain -Public -Private -Any -Program "C:\Program Files\Microsoft Lync Server 2010\File Transfer Agent\FileTransferAgent.exe" -ReadOnly
New-FirewallRule -name "World Wide Web Services" -description "An inbound rule to allow HTTPS traffic for Internet Information Services (IIS) [TCP 443]" -domain -private -public -localports "443" -Program "System"

Installation

No installation needed. But the function does need to run in an elevated session.

Download

v1.0 (09-14-2012) New-FirewallRule.v1.0.zip

Changelog

See the changelog for information on features and bugs fixed in various versions.

Categories: PowerShell Tags:

Function: Remove-NicGateway – Removing the Default Gateway on Selected NICs Via PowerShell

March 13, 2012 Leave a comment

When deploying Database Availability Groups (DAGs) with Exchange 2010, multiple network are generally used. You’ll have a client or “MAPI” network and at least a replication network. I’ve seen some organizations that also deploy backup networks. Each has their own NICs or NIC teams. Only the client network should have a default gateway defined. The rest should not. Static routes are added for the others using the NETSH command.

Setting the NIC properties is sometimes  a manual task, and sometimes a scripted task via PowerShell. On a large project, I needed to run a validation script to ensure that the servers were consistent and ready for the Exchange build, and fix those that could be done via script. I noticed that servers were coming with gateways defined on all of the NIC teams, so I need to resolve this. Turns out, it was a little challenging to do it via PowerShell.

There is apparently no easy way to just remove the gateway. We can easily set it, but my assumption that setting it to $null would work was incorrect. What I ended up doing, with the assistance of Serkan Varoglu, was to change the NIC from static to DHCP, then back to static, defining only the IP address and subnet mask. Not the most direct method, but it works. And, it appears to leave other parameters intact, including DNS servers, suffixes, WINS, etc.

First we use WMI to grab the NIC by name ($NicName):

$Adapter = Get-WmiObject -Class Win32_NetworkAdapter -Filter "NetConnectionID='$NicName'"

Then we get the configuration for the NIC by calling it using the index number of the NIC we got from above:

$Nic = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=$($Adapter.Index)"

Next, we need to grab the NIC’s IP and subnet mask so we can assign them again later:

$NicIP = $Nic.IpAddress[0]
$NicMask = $Nic.IpSubnet[0]

The, we set the NIC to DHCP,

$Nic.EnableDhcp() | Out-Null

And then back to static, using the IP and mask we retrieved from above:

$Nic.EnableStatic($NicIp,$NicMask) | Out-Null

We can wrap this into a function and call it in our validation scripts.

function Remove-NicGateway	{
<#
.SYNOPSIS
	Removes the default gateway on a specified network interface card (NIC)

.DESCRIPTION
	Removes the default gateway on a specified network interface card (NIC) by first setting the NIC to DHCP, and then setting it back to static and not specifying the gateway - just the IP and subnet mask

.NOTES
  Version      				: 1.0
  Rights Required			: Local admin on server
  										: ExecutionPolicy of RemoteSigned or Unrestricted

	Author       				: Pat Richard, Exchange MVP
	Email/Blog/Twitter	: pat@innervation.com 	http://www.ehloworld.com @patrichard
	Dedicated Blog			: http://www.ehloworld.com/152

	Author       				: Serkan Varoglu
	Email/Blog/Twitter	: N/A	http://www.get-mailbox.org	@SRKNVRGL

	Disclaimer   				: You running this script means you won't blame me if this breaks your stuff.
	Info Stolen from 		: 

.EXAMPLE
	Remove-NIcGateway -NicName [name of NIC]

.INPUTS
	None. You cannot pipe objects to this script.

#Requires -Version 2.0
#>
	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No NIC name specified")]
		[ValidateNotNullOrEmpty()]
		[string]$NicName
	)
	$Adapter = Get-WmiObject -Class Win32_NetworkAdapter -Filter "NetConnectionID='$NicName'"
	$Nic = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=$($Adapter.Index)"
	$NicIP = $Nic.IpAddress[0]
	$NicMask = $Nic.IpSubnet[0]
	Write-Verbose "$NicIP $NicMask"
	$Nic.EnableDhcp() | Out-Null
	Start-Sleep -s 5
	Write-Verbose "Setting $NicName to $NicIP $NicMask"
	$Nic.EnableStatic($NicIp,$NicMask) | Out-Null
} # end function Remove-NicGateway

And call it via:

Remove-NicGateway -NicName [NIC/Team Name]

such as

Remove-NicGateway -NicName "Replication"

Hopefully, this will be useful to you.

Function: Set-DriveLabel – Change the Label of a Drive Via PowerShell

March 6, 2012 1 comment

Powershell_logo-137x137Here’s a simple function to change the label of a drive.

function Set-DriveLabel	{
	<#
	.SYNOPSIS
	  Sets the label on a drive.

	.DESCRIPTION
	  Sets the label on a drive to a user specified value

	.NOTES
	    Version      			: 1.0
	    Rights Required			: Local admin on server
	    					: ExecutionPolicy of RemoteSigned or Unrestricted
	    Exchange Version			: N/A
            Author     				: Pat Richard, Exchange MVP
            Email/Blog/Twitter	                : pat@innervation.com 	http://www.ehloworld.com @patrichard
            Dedicated Blog			: http://www.ehloworld.com/1097
            Disclaimer   			: You running this script means you won't blame me if this breaks your stuff.

	.EXAMPLE
		Set-DriveLabel -DriveLetter "d:" -DriveLabel "Data"

	.INPUTS
		None. You cannot pipe objects to this script.

	#Requires -Version 2.0
	#>
	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No drive letter specified")]
		[string]$DriveLetter,
		[parameter(Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No drive label specified")]
		[string]$DriveLabel
	)
	Write-Host "Setting drive label - drive $DriveLetter"
	$drive = Get-WmiObject -Class Win32_Volume -Filter "DriveLetter = '$DriveLetter'"
	Set-WmiInstance -input $drive -Arguments @{Label="$DriveLabel"} | Out-Null
	If ((Get-WmiObject -Class Win32_Volume -Filter "DriveLetter = '$DriveLetter'").Label -eq $DriveLabel){
		return $true
	}else{
		return $false
	}
} # end function Set-DriveLabel

You would then call it as such:

Set-DriveLabel -DriveLetter [drive] -DriveLabel [label]

such as

Set-DriveLabel -DriveLetter d: -DriveLabel "Data"

Comment based help is available via

Get-Help Set-DriveLabel
Categories: PowerShell Tags: ,

Function: New-BalloonTip – PowerShell Function to Show Balloon Tips

February 21, 2012 5 comments

Sometimes, we run scripts that may take quite a while to process. Rather than sit idly and watch the script process, we can trigger balloon tips to let us know when it’s done. Something like:

Balloon Tip

Balloon Tip

These are the same type of balloon tips that alert us to outdated or disabled security settings, pending updates, etc.

Windows Update balloon tip

Windows Update balloon tip

This handy little function makes it very easy to use this useful feature. I took the info from PS1 at http://powershell.com/cs/blogs/tips/archive/2011/09/27/displaying-balloon-tip.aspx and put it into a function and optimized it a little. You can specify the icon (none, info, warning, error), the title text, and the tip text.

function New-BalloonTip	{
	<#
	.SYNOPSIS
		Displays a balloon tip in the lower right corner of the screen.

	.DESCRIPTION
		Displays a balloon tip in the lower right corner of the screen. Icon, title, and text can be customized.

	.NOTES
		Version      			: 1.1
		Wish List					:
		Rights Required		: Local admin on server
   										: If script is not signed, ExecutionPolicy of RemoteSigned (recommended) or 
   										  Unrestricted (not recommended)
   										: If script is signed, ExecutionPolicy of AllSigned (recommended), RemoteSigned, 
   										  or Unrestricted (not recommended)
    Sched Task Req'd	: No
    Lync Version			: N/A
    Author(s)    			: © Pat Richard (pat@innervation.com) 	http://www.ehloworld.com @patrichard
	  Dedicated Post		: http://www.ehloworld.com/1038
    Disclaimer   			: You running this script means you won't blame me if this breaks your stuff. This script is
    										provided AS IS without warranty of any kind. I disclaim all implied warranties including, 
    										without limitation, any implied warranties of merchantability or of fitness for a particular
    										purpose. The entire risk arising out of the use or performance of the sample scripts and 
    										documentation remains with you. In no event shall I be liable for any damages whatsoever 
    										(including, without limitation, damages for loss of business profits, business interruption,
    										loss of business information, or other pecuniary loss) arising out of the use of or inability
    										to use the script or documentation. 
    Acknowledgements 	: 
  	Assumptions				: 
    Limitations				: 
    Known issues			: None yet, but I'm sure you'll find some!    										

	.EXAMPLE
		New-BalloonTip -icon [none|info|warning|error] -title [title text] -text [text]

		Description
		-----------
		Creates a balloon tip in the lower right corner.

	.LINK

http://www.ehloworld.com/1038

	.INPUTS
		This function does support pipeline input.

	#Requires -Version 2.0
	#>

	[CmdletBinding(SupportsShouldProcess = $true)]
	param(
		# Specifies the type of icon shown in the balloon tip
		[parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
		[ValidateSet("None", "Info", "Warning", "Error")]
		[ValidateNotNullOrEmpty()]
		[string] $Icon = "Info",

		# Defines the actual text shown in the balloon tip
		[parameter(Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No text specified!")]
		[ValidateNotNullOrEmpty()]
		[string] $Text,

		# Defines the title of the balloon tip
		[parameter(Position = 2, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No title specified!")]
		[ValidateNotNullOrEmpty()]
		[string] $Title
	)
	PROCESS {
	  [system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null
	  $balloon = New-Object System.Windows.Forms.NotifyIcon
	  $path = Get-Process -id $pid | Select-Object -ExpandProperty Path
	  $balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
	  $balloon.BalloonTipIcon = $Icon
	  $balloon.BalloonTipText = $Text
	  $balloon.BalloonTipTitle = $Title
	  $balloon.Visible = $true
	  $balloon.ShowBalloonTip(10000)
	  Remove-Variable balloon
	} # end PROCESS
} # end function New-BalloonTip

And you can call it by either supplying the parameter names:

New-BalloonTip -icon info -text "PowerShell script has finished processing" -title "Completed"

or not:

New-BalloonTip info "PowerShell script has finished processing" "Completed"
Categories: PowerShell Tags: ,

Functions: Get-UACStatus Set-UACStatus – PowerShell Functions for Getting and Setting UAC Status

February 20, 2012 6 comments

Windows-logo-128x128User Account Control, also known as UAC, was designed to reduce vulnerability by requiring confirmation when system settings are being changed. Some people hate it, some don’t mind it. But most understand it’s intent.

In any case, when deploying servers, it’s key to know what state the UAC settings are in, so that we can script accordingly. Normally, I just set the registry value to whatever I need it to be, using a one-liner such as:

To disable UAC:

Set-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\policies\system -Name EnableLUA -Value 0

To enable UAC:

Set-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\policies\system -Name EnableLUA -Value 1

UAC changes how a token is assembled when you log on. If we’re making changes to this, remember that a reboot is required before the new setting takes effect.

But what if we just need to programatically peek at what UAC is set to, so that we can act accordingly? Well, this handy little function should help:

function Get-UACStatus {
	<#
	.SYNOPSIS
	   	Gets the current status of User Account Control (UAC) on a computer.

	.DESCRIPTION
	    Gets the current status of User Account Control (UAC) on a computer. $true indicates UAC is enabled, $false that it is disabled.

	.NOTES
	    Version      			: 1.0
	    Rights Required			: Local admin on server
	    					: ExecutionPolicy of RemoteSigned or Unrestricted
	    Author(s)    			: Pat Richard (pat@innervation.com)
	    Dedicated Post			: http://www.ehloworld.com/1026
	    Disclaimer   			: You running this script means you won't blame me if this breaks your stuff.

	.EXAMPLE
		Get-UACStatus

		Description
		-----------
		Returns the status of UAC for the local computer. $true if UAC is enabled, $false if disabled.

	.EXAMPLE
		Get-UACStatus -Computer [computer name]

		Description
		-----------
		Returns the status of UAC for the computer specified via -Computer. $true if UAC is enabled, $false if disabled.

	.LINK

http://www.ehloworld.com/1026

	.INPUTS
		None. You cannot pipe objects to this script.

	#Requires -Version 2.0
	#>

	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
		[string]$Computer
	)
	[string]$RegistryValue = "EnableLUA"
	[string]$RegistryPath = "Software\Microsoft\Windows\CurrentVersion\Policies\System"
	[bool]$UACStatus = $false
	$OpenRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Computer)
	$Subkey = $OpenRegistry.OpenSubKey($RegistryPath,$false)
	$Subkey.ToString() | Out-Null
	$UACStatus = ($Subkey.GetValue($RegistryValue) -eq 1)
	write-host $Subkey.GetValue($RegistryValue)
	return $UACStatus
} # end function Get-UACStatus

You can call it via

Get-UACStatus

to see the status for the local machine, and

Get-UACStatus -Computer [computer name]

to see the status of a remote machine. Full help is available via

Get-Help Get-UACStatus

And if we need a little function to deal with enabling or disabling, for building into deployment scripts, we have this one, which includes functionality for rebooting:

function Set-UACStatus {
	<#
	.SYNOPSIS
		Enables or disables User Account Control (UAC) on a computer.

	.DESCRIPTION
		Enables or disables User Account Control (UAC) on a computer.

	.NOTES
		Version      			: 1.0
		Rights Required			: Local admin on server
						: ExecutionPolicy of RemoteSigned or Unrestricted
		Author(s)    			: Pat Richard (pat@innervation.com)
		Dedicated Post			: http://www.ehloworld.com/1026
		Disclaimer   			: You running this script means you won't blame me if this breaks your stuff.

	.EXAMPLE
		Set-UACStatus -Enabled [$true|$false]

		Description
		-----------
		Enables or disables UAC for the local computer.

	.EXAMPLE
		Set-UACStatus -Computer [computer name] -Enabled [$true|$false]

		Description
		-----------
		Enables or disables UAC for the computer specified via -Computer.

	.LINK

http://www.ehloworld.com/1026

	.INPUTS
		None. You cannot pipe objects to this script.

	#Requires -Version 2.0
	#>

	param(
		[cmdletbinding()]
		[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $false)]
		[string]$Computer = $env:ComputerName,
		[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $true)]
		[bool]$enabled
	)
	[string]$RegistryValue = "EnableLUA"
	[string]$RegistryPath = "Software\Microsoft\Windows\CurrentVersion\Policies\System"
	$OpenRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Computer)
	$Subkey = $OpenRegistry.OpenSubKey($RegistryPath,$true)
	$Subkey.ToString() | Out-Null
	if ($enabled -eq $true){
		$Subkey.SetValue($RegistryValue, 1)
	}else{
		$Subkey.SetValue($RegistryValue, 0)
	}
	$UACStatus = $Subkey.GetValue($RegistryValue)
	$UACStatus
	$Restart = Read-Host "`nSetting this requires a reboot of $Computer. Would you like to reboot $Computer [y/n]?"
	if ($Restart -eq "y"){
		Restart-Computer $Computer -force
		Write-Host "Rebooting $Computer"
	}else{
		Write-Host "Please restart $Computer when convenient"
	}
} # end function Set-UACStatus

Call it via

Set-UACStatus -Computer [computer name] -Enabled [$true|$false]

And, like Get-UACStatus, full help is available via

Get-Help Set-UACStatus

Function: New-LocalExchangeConnection – Ensure Your PowerShell Script is Connected to Exchange 2010

December 28, 2011 9 comments

When writing scripts that execute commands on an Exchange server, for Exchange 2010, it’s important to ensure that you’re running within a session connected to an Exchange server, and that all Exchange cmdlets are available. In Exchange 2007, we could load the Exchange snapins. But 2010 doesn’t use snapins. Some people would say that you can simply load the modules, but loading the modules bypasses RBAC, and is thus, not recommended. Mike Pfeiffer wrote a great article Managing Exchange 2010 with Remote PowerShell that sheds some light. It’s worth a read.

A solution around this is to run a PowerShell script that comes built into Exchange 2010. This makes available the Connect-ExchangeServer cmdlet, which will connect via remote PowerShell to Exchange. We can specify a server, or let the -auto option connect to the best server via autodiscover. New-LocalExchangeConnection is a function I wrote to connect:

function New-LocalExchangeConnection	{ 
	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
	)
	Write-Verbose "Checking for Exchange Management Shell"
	$Sessions = Get-PSSession | Where-Object {$_.ConfigurationName -eq "Microsoft.Exchange"}
	if (!($Sessions)){
		if (Test-Path "$env:ExchangeInstallPath\bin\RemoteExchange.ps1"){
			Write-Verbose "Exchange Management Shell not found - Loading..."
			. "$env:ExchangeInstallPath\bin\RemoteExchange.ps1"
			Write-Verbose "Exchange Management Shell loaded"
			Write-Verbose "Connecting to Exchange server"
			Connect-ExchangeServer -auto
			if (Get-PSSession | Where-Object {$_.ConfigurationName -eq "Microsoft.Exchange"}){
				Write-Verbose "Connected to Exchange Server"
			}else{
				Write-Host "An error has occurred" -ForegroundColor red
			}
		}else{
			Write-Warning "Exchange Management Shell is not available on this computer"
		}
	}else{
		Write-Verbose "Exchange Management Shell already loaded" -ForegroundColor Yellow
	}
} # end function New-LocalExchangeConnection

Calling this within your script will make ensuring that your script is running with access to Exchange cmdlets much simpler.