Archive

Archive for the ‘PowerShell’ Category

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.

Set-DriveLabel – change the label of a drive via PowerShell

Here’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:

Programatically add keys and values to edgetransport.exe.config for Exchange 2010

February 22, 2012 Leave a comment

Recently, some testing on some new Exchange 2010 hub transport servers yielded some less than expected performance results. Processor utilization was much higher during sustained load testing of message throughput in some dedicated message journal sites.

A colleague worked to determine a solution, and came up with adding two keys and respective values to the EdgeTransport.exe.config file in [Exchange installation folder]\bin. This caused a substantial drop in processor utilization, but caused another problem – how to deploy this solution easily, in a repeatable fashion? We certainly don’t want to have to manually edit dozens of XML files across the production environment.

Our deployment method was entirely scripted, so I set out to find a way to incorporate the fix into the server provisioning scripts. Having not had to deal with editing XML files before, I did a fair amount of searching online, but had trouble with nearly everything I found. Obscure errors, and overly complex code had me just cobbling some things together until it worked. I finally came up with the New-AppSetting function below. It’s lean and mean, but it works.

function New-AppSetting {
	<#
	.SYNOPSIS
	  Adds keys and values to the EdgeTransport.exe.config file for Exchange 2010 	

	.DESCRIPTION
	  Adds user defined keys and values to the EdgeTransport.exe.config file for Exchange 2010 and restarts MSExchangeTransport service 

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

	.EXAMPLE
		New-AppSetting -key [key] -value [value]

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

	#Requires -Version 2.0
	#>

	[cmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(Position = 0, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No key specified")]
		[string]$key,
		[parameter(Position = 0, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No value specified")]
		[string]$value
	)
	[string]$configfile = $env:ExchangeInstallPath+"bin\EdgeTransport.exe.config"
	if (Test-Path $ConfigFile){
		[xml]$xml = Get-Content $ConfigFile
		[string]$currentDate = (get-date).tostring("MM_dd_yyyy-hh_mm_s")
		[string]$backup = $configfile + "_$currentDate"
		Copy-Item $configfile $backup
		$new = $xml.CreateElement('add')
		$new.SetAttribute('key', $key)
		$new.SetAttribute('value', $value)
		$xml.configuration.appSettings.AppendChild($new) | Out-Null
		$xml.Save($ConfigFile)
	}
	Restart-Service MSExchangeTransport
} # end function New-AppSetting

You call it via:

New-AppSetting -key [key name] -value [value]

such as

 New-AppSetting -key "RecipientThreadLimit" -value "20"

And it will add the key at the bottom of the list in EdgeTransport.exe.config and restart the transport service for the change to take effect. Prior to making the change, it creates a backup copy of EdgeTransport.exe.config for safe keeping.

One caveat – I didn’t have a lot of time to add some error checking or validation. The script does not check to see if the key is already present in the list (in our case, it’s not). So if you run the function multiple times with the same key name, you’ll end up with that key appearing multiple times in EdgeTransport.exe.config. I worked around this quickly in my script by using the following:

if ((Get-Content ($env:ExchangeInstallPath+"bin\edgetransport.exe.config")) -notmatch "RecipientThreadLimit"){
	New-AppSetting -key "RecipientThreadLimit" -value "20"
}

If I get some free cycles, I’ll streamline this a little more. But it works, and we’re able to continue deploying dozens of hub transport servers.

PowerShell function to show balloon tips

February 21, 2012 Leave a comment

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.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/1038
	    Disclaimer   			: You running this script means you won't blame me if this breaks your stuff.

	.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
		None. You cannot pipe objects to this script.

	#Requires -Version 2.0
	#>

	[CmdletBinding(SupportsShouldProcess = $true)]
	param(
		[parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $false, HelpMessage = "No icon specified. Options are None, Info, Warning, and Error!")]
		[ValidateSet("None", "Info", "Warning", "Error")]
		[string]$Icon = "error",
		[parameter(Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No text specified!")]
		[string]$Text,
		[parameter(Position = 2, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory = $true, HelpMessage = "No title specified!")]
		[string]$Title
	)
  [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)
} # 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:

Two PowerShell functions for getting and setting UAC status

February 20, 2012 Leave a comment

User 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 0

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

PowerShell function for loading and verifying modules

December 27, 2011 11 comments

Often in my PowerShell scripts, I need to either load a specific module, or verify it’s loaded. Manually, of course, we can use Get-Module, Import-Module, etc. But in automation scripts, we need to programtically make sure the module is loaded or load it if it’s not. I wrote this function and it’s worked well for me. introducing Get-ModuleStatus:

function Get-ModuleStatus { # http://www.ehloworld.com/938
	[CmdletBinding(SupportsShouldProcess=$true)]
	param	(
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No module name specified!")]
		[string]$name
	)
	if(!(Get-Module -name "$name")) {
		if(Get-Module -ListAvailable | ? {$_.name -eq "$name"}) {
			Import-Module -Name "$name"
			# Write-Host "module $name was imported"
			return $true
		} else {
			# Write-Host "module $name was not available (Windows feature isn't installed)"
			return $false
		}
	}else {
		# Write-Host "module $name was already imported"
		return $true
	}
} # end function Get-ModuleStatus

Call it supplying the module name, such as

Get-ModuleStatus Lync

You can use logic such as

if (Get-ModuleStatus Lync){Write-Host "Lync module is loaded}else{Write-Host "Lync module is NOT loaded" -ForegroundColor red}

Simple and effective.

Download

v1.0 Get-ModuleStatus.ps1

Categories: PowerShell Tags:

Creating passwords with PowerShell

December 26, 2011 2 comments

When creating new accounts, an admin needs to assign a password. We often then check the box to force a user to change their password when they logon for the first time. Some organizations will use a ‘default’ password for all new accounts. That’s fraught with security implications, and I’ve never recommended it. The downside is that you, as an admin, need to think up a password for each new account. I know how it is – you look around at things on your desk, items on the wall, looking for ideas. Then you have to make sure your super password meets your organizations password requirements, including length and complexity. Well, no more!

Enter New-Password. This function takes one simple input – length. It then spits out a password of said length, using upper and lower case letters, numbers, and punctuation, as well as a phonetic version. If you choose not to use some of the punctuation characters, feel free to just put a ‘#’ in front of that particular line.

function New-Password	{
	[CmdletBinding(SupportsShouldProcess=$true)]
	param(
		[int]$length
	)
	$password = -join ([Char[]]'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%$!#^{}<>' | Get-Random -count $length)
	Write-Host "`n$password`n" -ForegroundColor green
	# Write-Host
	ForEach ($character in [char[]]"$password"){
		[string]$ThisLetter = $character
		switch ($ThisLetter)	{
			a	{$ThisWord = "alpha"}
			b	{$ThisWord = "bravo"}
			c	{$ThisWord = "charlie"}
			d	{$ThisWord = "delta"}
			e	{$ThisWord = "echo"}
			f	{$ThisWord = "foxtrot"}
			g	{$ThisWord = "golf"}
			h	{$ThisWord = "hotel"}
			i	{$ThisWord = "india"}
			j	{$ThisWord = "juliett"}
			k	{$ThisWord = "kilo"}
			l	{$ThisWord = "lima"}
			m	{$ThisWord = "mike"}
			n	{$ThisWord = "november"}
			o	{$ThisWord = "oscar"}
			p	{$ThisWord = "papa"}
			q	{$ThisWord = "quebec"}
			r	{$ThisWord = "romeo"}
			s	{$ThisWord = "sierra"}
			t	{$ThisWord = "tango"}
			u	{$ThisWord = "uniform"}
			v	{$ThisWord = "victor"}
			w	{$ThisWord = "whiskey"}
			x	{$ThisWord = "xray"}
			y	{$ThisWord = "yankee"}
			z	{$ThisWord = "zulu"}
			1	{$ThisWord = "one"}
			2	{$ThisWord = "two"}
			3	{$ThisWord = "three"}
			4	{$ThisWord = "four"}
			5	{$ThisWord = "five"}
			6	{$ThisWord = "six"}
			7	{$ThisWord = "seven"}
			8	{$ThisWord = "eight"}
			9	{$ThisWord = "nine"}
			0	{$ThisWord = "zero"}
			!	{$ThisWord = "!"}
			$	{$ThisWord = "$"}
			%	{$ThisWord = "%"}
			^	{$ThisWord = "^"}
			*	{$ThisWord = "*"}
			-	{$ThisWord = "-"}
			_	{$ThisWord = "_"}
			:	{$ThisWord = ":"}
			`;	{$ThisWord = ";"}
			`{	{$ThisWord = "{"}
			`}	{$ThisWord = "}"}
			`/	{$ThisWord = "/"}
			`<	{$ThisWord = "<"}
			`>	{$ThisWord = ">"}
			`#	{$ThisWord = "#"}
			`{	{$ThisWord = "{"}
			`}	{$ThisWord = "}"}
		}
		if ($ThisLetter -cmatch $ThisLetter.ToUpper()){
			$ThisWord = $ThisWord.ToUpper()
		}
		Write-Host "$ThisWord " -NoNewLine -ForegroundColor yellow
	}
	Write-Host "`n"
} # end function New-Password

Now, stick that function in your PowerShell profile. Each time you need a new password, use

New-Password -length [number]

such as

New-Password -length 12

And you now have a password to use.

New-Password

New-Password

 

Categories: PowerShell Tags: ,

Getting and settings local admin group membership on remote machines via PowerShell

December 22, 2011 1 comment

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

Pausing PowerShell scripts

December 21, 2011 Leave a comment

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

Call the function using

New-Pause
New-Pause
New-Pause

Once any key is pressed, your script can continue.

Categories: PowerShell Tags:

Sleep function for PowerShell when you need a delay

December 20, 2011 5 comments

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

Download

v1.0 New-Sleep.ps1

Categories: PowerShell Tags: