SCRIPT: Set-Lync2010Features.ps1

Script Name: Set-Lync2010Features.ps1

Script Authors: MVP Ståle Hansen, MVP Pat Richard and Marjus Sirvinsks

Log:


#Requires -Version 2.0
[string] $FilenameTranscript = $MyInvocation.MyCommand.Name + " " + (hostname)+ " {0:yyyy-MM-dd hh-mmtt}.log" -f (Get-Date)
Start-Transcript -path .\$FilenameTranscript | Out-Null
$error.clear()
# Detect correct OS here and exit if no match (we intentionally truncate the last character to account for service packs)
if ((Get-WMIObject win32_OperatingSystem).Version -notmatch '6.1.760'){
	Write-Host "`nThis script requires a version of Windows Server 2008 R2, which this is not. Exiting...`n" -ForegroundColor Red
	Stop-Transcript
	Exit
} #end OS detection

Clear-Host
pushd
[string] $TargetFolder = "c:\_Install"
[bool] $global:RebootRequired = $false
if ((Get-Module BitsTransfer).installed -eq $true){
	[bool] $WasInstalled = $true
}else{
	[bool] $WasInstalled = $false
}
[string] $MenuOption = "None"
[bool] $HasInternetAccess = ([Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]'{DCB00C01-570F-4A9B-8D69-199FDBA5723B}')).IsConnectedToInternet)
[string] $Menu = @'

	**********************************
	Lync Server 2010 - Features script
	**********************************

	Please select an option from the choices below.

	Preinstallation steps
	========================
	1) Front-End
	2) Director
	3) Monitoring/Archiving
	4) Communicator Web Access (OCS 2007 R2)
	5) Mediation/Edge/XMPP GW/AV Conferencing
	6) Install Unified Communications Managed API 3.0 Runtime (alpha)
	7) GroupChat
	11) Disable IPv6
	12) Install telnet client
	13) Install Visual C++ 2008 Redistributable
	16) Check group membership
	20) OWA/Lync integration (run on Exchange server) (alpha)
	22) Install Microsoft Silverlight
	23) Install Windows Media Format Runtime
	27) Install loopback adapter (beta)
	29) Download Lync Server 2010 (trial)
	31) Check Local Security Settings
'@

# front end server
if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{78D5615E-F753-40C1-B301-9AAD2727B9C8}" -ErrorAction SilentlyContinue){
	# {3F297EB8-2526-4B0E-A7A4-143E202EF0DE} for monitoring server
	# {9521B708-9D80-46A3-9E58-A74ACF4E343E} for core components
	[bool]$isLyncInstalled = $true
$Menu += @'

	Post installation steps
	========================
	8) Install Lync Server 2010 Resource Kit
	9) Install Lync Server 2010 Best Practices Analyzer
	10) Install Lync Server 2010 Stress and Performance Tool
	14) Install Lync 'IM an Expert' (alpha)
	15) Launch Windows Update
	17) Download 07-25-2011 Lync Server 2010 updates & start updater program
	18) Install 07-20-2011 Microsoft Lync Server 2010 Documentation Help File
	19) Backup local Lync server configuration
	21) Install 'Find Lync Versions' (includes .Net 4 installation)
	26) Enable federation with Office365
	28) Install Windows Update PowerShell module
	30) Restrict features to Standard CAL
'@
}else{
	[bool]$isLyncInstalled = $false
}
$Menu += @'

	98) Restart the server
	99) Exit

Select an option.. [1-99]?
'@

function New-FileDownload ([string]$SourceFile,[string]$DestFolder,[string]$DestFile) {
	# I should switch this to using a param block and pipelining from property name - just for consistency
	# I should clean up the display text to be consistent with other functions
	$error.clear()
	if (!($DestFolder)){$DestFolder = $TargetFolder}
	Get-ModuleStatus -name BitsTransfer
	if (!($DestFile)){[string] $DestFile = $SourceFile.Substring($SourceFile.LastIndexOf("/") + 1)}
	if (Test-Path $DestFolder){
		Write-Host "Folder: `"$DestFolder`" exists."
	} else{
		Write-Host "Folder: `"$DestFolder`" does not exist, creating..." -NoNewline
		New-Item $DestFolder -type Directory
		Write-Host "Done! " -ForegroundColor Green
	}
	if (Test-Path "$DestFolder\$DestFile"){
		Write-Host "File: $DestFile exists."
	}else{
		if ($HasInternetAccess){
			Write-Host "File: $DestFile does not exist, downloading..." -NoNewLine
			Start-BitsTransfer -Source "$SourceFile" -Destination "$DestFolder\$DestFile"
			Write-Host "Done! " -ForegroundColor Green
		}else{
			Write-Host "Internet access not detected. Please resolve and try again." -ForegroundColor red
		}
	}
} # end function New-FileDownload

function New-UnzippedFile	{
	param (
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No zip file specified")]
		[string]$ZipFile,
    [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No file to unzip specified")]
    [string]$UnzipFile,
    [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No location to unzip file specified")]
    [string]$UnzipFileLocation
	)
	$error.clear()
	Write-Host "Zip File........................................................[" -NoNewLine
	Write-Host "Unzipping" -ForegroundColor yellow -NoNewLine
	Write-Host "]" -NoNewLine
	if (Get-Item $zipfile){
		$objShell = new-object -com shell.application
		# where the .zip is
		$FuncZipFolder = $objShell.namespace($ZipFile)
		# the item in the zip
		$FuncFile = $FuncZipFolder.parsename($UnzipFile)
		# where the item is to go
		$FuncTargetFolder = $objShell.namespace($UnzipFileLocation)
		# do the copy of zipfile item to target folder
		$FuncTargetFolder.copyhere($FuncFile)
	}
	if ($error){
		Write-Host "`b`b`b`b`b`b`b`b`b`bfailed!" -ForegroundColor red -NoNewLine
	}else{
		Write-Host "`b`b`b`b`b`b`b`b`b`bdone!" -ForegroundColor green -NoNewLine
	}
	Write-Host "]    "
} # end function New-UnzippedFile

function New-Shortcut	{
	param (
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No shortcut name specified")]
		[string]$ShortcutName,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No shortcut file specified")]
		[string]$ShortcutFile,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No shortcut working directory specified")]
		[string]$ShortcutWorkingDir,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No shortcut description specified")]
		[string]$ShortcutDescription
	)
	$error.clear()
	# AllUsers start menu = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs"
	Write-Host "Shortcut........................................................[" -NoNewLine
	Write-Host "Creating" -ForegroundColor yellow -NoNewLine
	Write-Host "]" -NoNewLine
	if (Get-Item $ShortcutFile){
		$wshshell = New-Object -ComObject WScript.Shell
		if (($ShortcutName.Substring($ShortcutName.Length – 4, 4) -ne ".lnk") -and ($ShortcutName.Substring($ShortcutName.Length – 4, 4) -ne ".url")){
			$ShortcutName += ".lnk"
		}
		$lnk = $wshshell.CreateShortcut($ShortcutName)
		$lnk.TargetPath = $ShortcutFile
		#maps to Start in: on GUI
		$lnk.WorkingDirectory = $ShortcutWorkingDir
		#corresponds to the Comment field on the shortcut tab.
		$lnk.Description = $ShortcutDescription
		$lnk.Save()
	}
	if ($error){
		Write-Host "`b`b`b`b`b`b`b`b`b`bfailed!" -ForegroundColor red -NoNewLine
	}else{
		Write-Host "`b`b`b`b`b`b`b`b`bcreated!" -ForegroundColor green -NoNewLine
	}
	Write-Host "]    "
} # end function New-Shortcut

function New-ProgramInstallation	{
	param (
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No installation file specified")]
		[string]$InstallFile,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No installation switches specified")]
		[string]$InstallSwitches
	)
	$error.clear()
	$DestFile = $InstallFile.Substring($InstallFile.LastIndexOf("\") + 1)
	Write-Host "File: $DestFile" -NoNewLine
	for($i=0;$i -lt (64 - ($DestFile.length + 6));$i++){Write-Host "." -NoNewLine}
	Write-Host "[" -NoNewLine
	Write-Host "Installing" -ForegroundColor yellow -NoNewLine
	Write-Host "]" -NoNewLine
	$install = $InstallFile + " " + $InstallSwitches
	Invoke-Expression $install
	if ($error){
		Write-Host "`b`b`b`b`b`b`b`b`b`b`bFailed" -ForegroundColor Red -NoNewLine
	}else{
		Write-Host "`b`b`b`b`b`b`b`b`b`b`binstalled!" -ForegroundColor Green -NoNewLine
	}
	Write-Host "]      "
} # end function New-ProgramInstallation

function New-FileShare {
	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-FileShare

function New-EventLogEntry	{
	param (
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No event log text specified")]
		[string]$EventLogText,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No event type specified")]
		[ValidateSet("Error","Warning","Information","SuccessAudit","FailureAudit")]
		[string]$EventType,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No event source specified")]
		[string]$EventSource,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No event log specified")]
		[ValidateSet("Application","Security","Setup","System","Hardware Events","Internet Explorer","Key Management Service","Lync Server")]
		[string]$EventLog,
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No event ID specified")]
		[ValidateRange(0,65535)]
		[int]$EventID
	)
	$objEvt 					=	new-object System.Diagnostics.EventLog("System")
	# $objEvt.Source	=	$strScriptName
	$objEvt.Source 		=	$EventSource
	# to see all possible event types: [enum]::getvalues([system.diagnostics.eventlogentrytype])
	# $infoevent=[System.Diagnostics.EventLogEntryType]::Information
	$objEvt.WriteEntry($EventLogText,$EventType,$eventID)
} # end function New-EventLogEntry

function Install-WindowsMediaFormatRuntime	{
	# Requires local admin
	# required if installing Lync on Front End servers running Server 2008 SP1
	# If this isn't done BEFORE installing Lync, an installation error may occur as per
	# http://support.microsoft.com/kb/2522454
	if ((Get-WMIObject win32_OperatingSystem).Version -eq '6.1.7601'){
		$error.clear()
		New-ProgramInstallation -InstallFile "$env:systemroot\system32\dism.exe" -InstallSwitches "/online /add-package /packagepath:$env:windir\servicing\Packages\Microsoft-Windows-Media-Format-Package~31bf3856ad364e35~amd64~~6.1.7601.17514.mum /ignorecheck /NoRestart /quiet /logpath:$TargetFolder\dism.log"
		$global:RebootRequired = $true
	}
} # end function Install-WindowsMediaFormatRuntime

function Install-TelnetClient	{
	Get-ModuleStatus -name "ServerManager"
	if ((Get-WindowsFeature Telnet-Client).installed -eq $false){
		$a = New-Object -comobject wscript.shell
		$intAnswer = $a.popup("Do you want to include Telnet client?",0,"Telnet client",4)
		if ($intAnswer -eq 6) {
			#yes
			# Get-ModuleStatus -name "ServerManager"
			#if ((Get-WindowsFeature Telnet-Client).installed -eq $false){
				Add-WindowsFeature Telnet-Client | Out-Null
			#} else {
				#Write-Host "Telnet client already installed"
			#}
		} else {
			#no
			# $a.popup("You answered no.")
		}
	}
} # end function Install-TelnetClient

function Install-DocViewer	{
	$a = new-object -comobject wscript.shell
	$intAnswer = $a.popup("Lync Resource Kit documentation is based on Microsoft Word. Would you like to install the free Word viewer?",0,"Word Viewer",4)
	if ($intAnswer -eq 6) {
		#yes
		if (!(Get-Item "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{90850409-6000-11D3-8CFE-0150048383C9}" -ErrorAction SilentlyContinue)){
			New-FileDownload "http://download.microsoft.com/download/6/a/6/6a689355-b155-4fa7-ad8a-dfe150fe7ac6/wordview_en-us.exe"
			New-ProgramInstallation -InstallFile "$TargetFolder\wordview_en-us.exe" -InstallSwitches "/q"
		}
		if (!(Get-Item "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{90120000-0020-0409-0000-0000000FF1CE}" -ErrorAction SilentlyContinue)){
			New-FileDownload "http://download.microsoft.com/download/9/2/2/9222D67F-7630-4F49-BD26-476B51517FC1/FileFormatConverters.exe"
			New-ProgramInstallation -InstallFile "$TargetFolder\FileFormatConverters.exe" -InstallSwitches "/q"
		}
	} else {
  	#no
  	# $a.popup("You answered no.")
	}
} # end function Install-DocViewer

function Install-SilverLight	{
	# http://dwlpr.wordpress.com/2010/04/15/silverlight-4-final-download-direct-link/
	if (!(Get-Item "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{89F4137D-6C26-4A84-BDB8-2E5A4BB71E00}" -ErrorAction SilentlyContinue)){
		# 4.0.50401.0 (doesn't include hotfix from KB 2512827)
		# New-FileDownload "http://download.microsoft.com/download/2/2/C/22CABA89-3580-4611-8E0D-56749D2120DF/runtime/Silverlight.exe"
		# 4.0.60531.0 (http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=26488)
		New-FileDownload "http://download.microsoft.com/download/C/0/B/C0B404F0-F025-4156-95B0-164AD7CA07AA/runtime/Silverlight.exe"
		New-ProgramInstallation -InstallFile "$TargetFolder\silverlight.exe" -InstallSwitches "/q"
	}
} # end function Install-SilverLight

function Install-VisualC++Redist	{
	if (!(Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{4B6C7001-C7D6-3710-913E-5BC23FCE91E6}" -ErrorAction SilentlyContinue)){
		# 9.0.30729.4148
		# New-FileDownload "http://download.microsoft.com/download/9/7/7/977B481A-7BA6-4E30-AC40-ED51EB2028F2/vcredist_x64.exe"
		# New-ProgramInstallation -InstallFile "$TargetFolder\vcredist_x64.exe" -InstallSwitches "/qb"
		# should add update from KB 2538243 (MS11-025) install with /q (see http://www.microsoft.com/technet/security/bulletin/MS11-025.mspx)(http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=26368)
		# 9.0.30729.6161
		New-FileDownload "http://download.microsoft.com/download/5/D/8/5D8C65CB-C849-4025-8E95-C3966CAFD8AE/vcredist_x64.exe"
		New-ProgramInstallation -InstallFile "$TargetFolder\vcredist_x64.exe" -InstallSwitches "/qb"
		# validate by checking HKLM:\SOFTWARE\Microsoft\DevDiv\VC\Servicing\9.0\RED\1033 for "Install" = dword:1
		Do {Start-Sleep -m 100}while(!(Get-ItemProperty HKLM:\SOFTWARE\Microsoft\DevDiv\VC\Servicing\9.0\RED\1033 -Name Install -ErrorAction SilentlyContinue))
	}
} # end function Install-VisualC++Redist

function Get-ModuleStatus {
	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"
			# module was imported
			return $true
		} else {
			# module was not available
			return $false
		}
	}else {
		# module was already imported
		# Write-Host "$name module already imported"
		return $true
	}
} # end function Get-ModuleStatus

function Get-WebPage{
	param	(
		[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No URL specified!")]
		[string]$url
	)
    $ie = new-object -comobject internetexplorer.application;
    $ie.Navigate($url);
    $ie.Visible = $true;
} # end function Get-WebPage

function Remove-IPv6	{
	$error.clear()
	Write-Host "TCP/IP v6......................................................[" -NoNewLine
	Write-Host "removing" -ForegroundColor yellow -NoNewLine
	Write-Host "]" -NoNewLine
	Set-ItemProperty -path HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters -name DisabledComponents -value 0xffffffff -type dword
	if ($error){
		Write-Host "`b`b`b`b`b`b`b`bfailed!" -ForegroundColor red -NoNewLine
	}else{
		Write-Host "`b`b`b`b`b`b`b`b`bdone!" -ForegroundColor green -NoNewLine
	}
	Write-Host "]    "
	$global:RebootRequired = $true
} # end function Remove-IPv6

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

function Get-RegistryValue { # http://powershell.com/cs/blogs/tips/archive/2009/11/25/reading-registry-values.aspx
	[CmdletBinding(SupportsShouldProcess=$true)]
	param(
    [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No registry key specified.")]
		$key,
    [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage="No value specified!")]
		$value
	)
	(Get-ItemProperty $key $value).$value
} # end function Get-RegistryValue

Do {
	if ($global:RebootRequired -eq $true){Write-Host "`t`t`t`t`t`t`t`t`t`n`t`t`t`tREBOOT REQUIRED!`t`t`t`n`t`t`t`t`t`t`t`t`t`n`t`tDO NOT INSTALL Lync BEFORE REBOOTING!`t`t`n`t`t`t`t`t`t`t`t`t" -ForegroundColor yellow}
	if ($MenuOption -ne "None") {Write-Host "`nLast command: "$MenuOption -BackgroundColor Yellow -ForegroundColor black}
	$MenuOption = Read-Host $Menu

	switch ($MenuOption) {
		1	{ # Front-End
			Get-ModuleStatus -name "ServerManager"
			Install-VisualC++Redist
			Install-TelnetClient
			Add-WindowsFeature NET-Framework-Core, msmq-server, msmq-directory, RSAT-ADDS, Web-Static-Content, Web-Default-Doc, Web-Http-Errors, Web-Asp-Net, Web-Net-Ext, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Http-Logging, Web-Log-Libraries, Web-Http-Tracing, Web-Windows-Auth, Web-Filtering, Web-Stat-Compression, Web-Mgmt-Console, Web-Scripting-Tools, Web-Client-Auth, Web-Dyn-Compression, Desktop-Experience | Out-Null
			Install-SilverLight
			Install-WindowsMediaFormatRuntime
			$global:RebootRequired = $true
		}
		2	{ # Director
			Get-ModuleStatus -name "ServerManager"
			Install-VisualC++Redist
			Install-TelnetClient
			Add-WindowsFeature NET-Framework-Core, msmq-server, msmq-directory, Web-Static-Content, Web-Default-Doc, Web-Http-Errors, Web-Asp-Net, Web-Net-Ext, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Http-Logging, Web-Log-Libraries, Web-Http-Tracing, Web-Windows-Auth, Web-Filtering, Web-Stat-Compression, Web-Mgmt-Console, Web-Scripting-Tools, Web-Client-Auth, Web-Dyn-Compression | Out-Null
			Install-SilverLight
			$global:RebootRequired = $true
		}
		3	{ # Monitoring/Archiving
			Get-ModuleStatus -name "ServerManager"
			Install-VisualC++Redist
			Install-TelnetClient
			Add-WindowsFeature NET-Framework-Core, msmq-server, msmq-directory
		}
		4	{ # Communicator Web Access (OCS 2007 R2)
			Get-ModuleStatus -name "ServerManager"
			Install-TelnetClient
			Add-WindowsFeature NET-Framework-Core, web-server, web-http-redirect, web-asp-net, web-http-logging, web-log-libraries, web-request-monitor, web-http-tracing, web-basic-auth, web-windows-auth, web-mgmt-console, web-scripting-tools, web-mgmt-compat, was-process-model, was-net-environment, rsat-adds
			$global:RebootRequired = $true
		}
		5	{ # Mediation/Edge/XMPP GW/AV Conferencing
			Get-ModuleStatus -name "ServerManager"
			Install-VisualC++Redist
			Install-TelnetClient
			Add-WindowsFeature NET-Framework-Core, msmq-server, msmq-directory
		}
		6	{ # Unified Communications Managed API 3.0 Runtime
			New-FileDownload "http://download.microsoft.com/download/3/B/1/3B13662A-8445-4238-A685-F15899032755/UcmaRuntimeSetup.exe"
			New-ProgramInstallation -InstallFile "$TargetFolder\UcmaRuntimeSetup.exe" -InstallSwitches "/qb"
		}
		7	{ # GroupChat
			Get-ModuleStatus -name "ServerManager"
			Install-TelnetClient
			Add-WindowsFeature NET-Framework-Core, web-server, web-mgmt-compat, web-asp-net, msmq-server, msmq-directory, rsat-adds
			$global:RebootRequired = $true
		}
		8	{ # Lync Server 2010 Resource Kit
			if (!(Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{9C44E652-CBBC-4E1C-8172-21A7D46CAB85}" -ErrorAction SilentlyContinue)){
				$error.clear()
				New-FileDownload "http://download.microsoft.com/download/1/8/A/18ADD884-E16F-4AD3-8422-FC80073439AA/OCSReskit.msi"
				Get-ModuleStatus -name "ServerManager"
				Add-WindowsFeature NET-Framework-Core
				New-ProgramInstallation -InstallFile "$TargetFolder\OCSReskit.msi" -InstallSwitches "/qb"
			}
			Install-DocViewer
		}
		9	{ # Lync Server 2010 Best Practices Analyzer
			if ((!(Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{88033F09-23A0-44EC-9063-7CBAA4BE4975}" -ErrorAction SilentlyContinue))-and $isLyncInstalled){
				$error.clear()
				New-FileDownload "http://download.microsoft.com/download/3/3/8/338D2314-1D23-47C1-8BB4-80FD39B84EA6/RTCBPA.msi"
				Get-ModuleStatus -name "ServerManager"
				Add-WindowsFeature NET-Framework-Core
				New-ProgramInstallation -InstallFile "$TargetFolder\RTCBPA.msi" -InstallSwitches "/qb"
			}
		}
		10	{ # Lync Server 2010 Stress and Performance Tool
			if ((!(Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{94FD6E73-38F6-4DBE-AA3B-E403678868C9}" -ErrorAction SilentlyContinue)) -and $isLyncEnabled){
				Get-ModuleStatus -name "ServerManager"
				Add-WindowsFeature NET-Framework-Core
				Install-VisualC++Redist
				New-FileDownload "http://download.microsoft.com/download/7/1/E/71EC25F9-575C-47BF-A4C0-E5DFFD42AA56/CapacityPlanningTool.msi"
				New-ProgramInstallation -InstallFile "$TargetFolder\CapacityPlanningTool.msi" -InstallSwitches "/qb"
			}
			Install-DocViewer
		}
		11	{ # Disable IPv6
			Remove-IPv6
		}
		12	{ # telnet client
			Install-TelnetClient
		}
		13	{ # Visual C++ 2008 Redistributable
			Install-VisualC++Redist
		}
		14	{ # Lync 'IM an Expert'
			if ($isLyncInstalled){
				Get-ModuleStatus -name "ServerManager"
				Add-WindowsFeature NET-Framework-Core
				New-FileDownload "http://download.microsoft.com/download/3/B/1/3B13662A-8445-4238-A685-F15899032755/UcmaRuntimeSetup.exe"
				New-ProgramInstallation -InstallFile "$TargetFolder\UcmaRuntimeSetup.exe" -InstallSwitches "/q"

				New-FileDownload "http://download.microsoft.com/download/9/E/2/9E2CBE89-A25F-4158-A2FC-CD227FC4ED14/IManExpert.msi"
				New-ProgramInstallation -InstallFile "$TargetFolder\IManExpert.msi" -InstallSwitches "/qb"
			}
		}
		15	{ # Windows Update
			Invoke-Expression "$env:windir\system32\wuapp.exe startmenu"
		}
		16	{ # Check group membership
			Whoami /groups /fo list | findstr /I BUILTIN\Administrators; Whoami /groups /fo list | findstr /I Cs; Whoami /groups /fo list | findstr /I Rtc
		}
		17	{ # Lync Server 2010 updates
			if ((!(Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{9521B708-9D80-46A3-9E58-A74ACF4E343E}" -ErrorAction SilentlyContinue))-and $isLyncInstalled){
				New-FileDownload "http://download.microsoft.com/download/3/4/1/341C256C-0E74-4968-B6FA-EEA87600E283/LyncServerUpdateInstaller.exe"
				New-ProgramInstallation -InstallFile "$TargetFolder\LyncServerUpdateInstaller.exe" -InstallSwitches ""
			}
		}
		18	{ # Microsoft Lync Server 2010 Documentation Help File (07-20-2011)
			if ($isLyncInstalled){
				# http://www.microsoft.com/download/en/details.aspx?id=23888
				New-FileDownload "http://download.microsoft.com/download/E/F/B/EFBBCD0C-CC33-4E91-B921-05D47550B9BF/Lync_ITPro.exe"
				New-ProgramInstallation -InstallFile "$TargetFolder\Lync_ITPro.exe" -InstallSwitches "-auto"
				Do {Start-Sleep -m 100}while(Get-Process | ?{$_.ProcessName -match "Lync_ITPro"})
				New-Shortcut -ShortcutName "$env:public\Desktop\Lync Server 2010 Help Documentation.lnk" -ShortcutFile "C:\Program Files\Microsoft Lync Server 2010\Documentation\Lync_ITPro.chm" -ShortcutWorkingDir "C:\Program Files\Microsoft Lync Server 2010\Documentation" -ShortcutDescription "Lync Server 2010 Help Documentation"
			}
		}
		19	{ # Backup local Lync server configuration
			if ($isLyncInstalled){
				# http://blog.danovich.com.au/2010/12/30/scheduled-task-powershell-script-to-backup-lync-server-2010-config/
				[string] $FileNameConfigBackup = "$TargetFolder\config backup " + (hostname)+ " {0:yyyy-MM-dd hh-mmtt}.zip" -f (Get-Date)
 				Export-CsConfiguration -Filename .\$FileNameConfigBackup -Force:$True
 				# Should also incorporate http://blogs.technet.com/b/uc_mess/archive/2011/03/17/lync_2d00_server_2d00_2010_2d00_backup_2d00_instructions.aspx
 			}
		}
		20	{ # OWA/Lync integration
			Get-ModuleStatus ServerManager
			if ((Get-WindowsFeature Net-Framework).Installed -eq $true){
				# to do: determine if we're running within the Exchange management shell
				popd
				# if ($env:ExchangeInstallPath){
					if (!(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{02FD4158-9AFC-455F-B8FD-5C7C75D973AA}" -Name UninstallString -ErrorAction SilentlyContinue)){
						New-FileDownload "http://download.microsoft.com/download/C/D/6/CD65672F-E0D1-471B-8404-B7B63966A8AA/CWAOWASSPMain.msi"
						New-ProgramInstallation -InstallFile "$TargetFolder\CWAOWASSPMain.msi" -InstallSwitches "/qb"
						Do {Start-Sleep -m 100}while(!(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{02FD4158-9AFC-455F-B8FD-5C7C75D973AA}" -Name UninstallString -ErrorAction SilentlyContinue))
					}
					$InstallDir = "C:\Web Service Provider Installer Package"

					Set-Location $InstallDir
					Start-Sleep -s 15
					New-ProgramInstallation -InstallFile ".\vcredist_x64.exe" -InstallSwitches "/qb"
					Do {Start-Sleep -m 100}while(Get-Process "vcredist_x64.exe" -ErrorAction SilentlyContinue)
					# Do {Start-Sleep -m 100}while(!(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{350AA351-21FA-3270-8B7A-835434E766AD}" -Name UninstallString -ErrorAction SilentlyContinue))
					Start-Sleep -s 15

					New-ProgramInstallation -InstallFile ".\UcmaRedist.msi" -InstallSwitches "/qb"
					# Do {Start-Sleep -m 100}while(Get-Process "msiexec*" -ErrorAction SilentlyContinue)
					Do {Start-Sleep -m 100}while(!(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{7EB901DD-CB50-4046-A434-3E9A112E8F86}" -Name UninstallString -ErrorAction SilentlyContinue))

					New-FileDownload "http://download.microsoft.com/download/3/6/9/3693C940-1B9A-4386-836F-A21C7F4AE9C6/UcmaRedist.msp" $InstallDir
					New-ProgramInstallation -InstallFile ".\UcmaRedist.msp" -InstallSwitches "/qb"
					# Do {Start-Sleep -m 100}while(Get-Process "msiexec*" -ErrorAction SilentlyContinue)
					Start-Sleep -s 15

					New-FileDownload "http://download.microsoft.com/download/D/9/6/D967F77F-D0BD-43FB-987B-6FBF7C82251B/UcmaRedist.msp" $InstallDir "UcmaRedist-R2.msp"
					New-ProgramInstallation -InstallFile ".\UcmaRedist-R2.msp" -InstallSwitches "/qb"
					# Do {Start-Sleep -m 100}while(Get-Process "msiexec*" -ErrorAction SilentlyContinue)
					Start-Sleep -s 15

					# This does a detected for the CAS role
					New-ProgramInstallation -InstallFile ".\CWAOWASSP.msi" -InstallSwitches "/qb"
					# Do {Start-Sleep -m 100}while(Get-Process "msiexec*" -ErrorAction SilentlyContinue)
					Do {Start-Sleep -m 100}while(!(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{D702B231-2EA5-4AD7-85E1-713538CBD656}" -Name NoRemove -ErrorAction SilentlyContinue))

					New-FileDownload "http://download.microsoft.com/download/C/2/7/C27C449E-76FB-4BBF-A899-A82C2BBEB47C/CWAOWASSP.msp" $InstallDir
					New-ProgramInstallation -InstallFile ".\CWAOWASSP.msp" -InstallSwitches "/qb"
					# Do {Start-Sleep -m 100}while(Get-Process "msiexec*" -ErrorAction SilentlyContinue)
					Start-Sleep -s 15
					[string] $LyncPool = Read-Host "Enter FQDN of Lync front end server/pool"

					[string] $CertThumbprint = (Get-ExchangeCertificate |  Where {$_.Services -ilike "*IIS*"}).Thumbprint
					[string] $CertSubject = (Get-ExchangeCertificate |  Where {$_.Services -ilike "*IIS*"}).Subject
					$CertSubject = $subject.substring(3,$subject.indexof(",")-3)
					Get-ExchangeServer (hostname) | Get-OWAVirtualDirectory | Set-OWAVirtualDirectory -InstantMessagingType OCS -InstantMessagingEnabled:$true -InstantMessagingCertificateThumbprint $CertThumbprint -InstantMessagingServerName $LyncPool
					Get-ExchangeServer (hostname) | Get-OWAVirtualDirectory | fl name,instant*

					# prompt user to create CSTrustedApp using CN from certificate
					Write-Host "Create CsTrustedApplication using the name $CertSubject"

				#}else{
					#Write-Host "Exchange not detected. Cannot complete." -ForegroundColor red
				#}
			}	else	{ Write-Warning "Net-Framework not installed"
			}
 		}
		21	{ # Find Lync Versions
			if ($isLyncInstalled){
				if (!(Get-ItemProperty "HKLM:\Software\Microsoft\NET Framework Setup\NDP\v4\Full" -Name install -ErrorAction SilentlyContinue | Out-Null)){
				New-FileDownload "http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe"
				New-ProgramInstallation -InstallFile "$TargetFolder\dotNetFx40_Full_x86_x64.exe" -InstallSwitches "/q"
				}
				New-FileDownload "http://www.stumper66.com/software/files/Find Lync Versions.zip"
				New-UnzippedFile -ZipFile "$TargetFolder\Find Lync Versions.zip" -UnzipFile "Find Lync Versions.exe" -UnzipFileLocation "C:\Program Files\Microsoft Lync Server 2010\Administrative Tools"
				New-Shortcut -ShortcutName "$env:public\Desktop\Find Lync Versions" -ShortcutFile "C:\Program Files\Microsoft Lync Server 2010\Administrative Tools\Find Lync Versions.exe" -ShortcutWorkingDir "C:\Program Files\Microsoft Lync Server 2010\Administrative Tools" -ShortcutDescription "Display client version and connection info"
			}
		}
		22	{ # SilverLight
			Install-SilverLight
		}
		23	{ # WindowsMediaFormatRuntime
			Install-WindowsMediaFormatRuntime
		}
		24	{ # Upload phone firmware update

		}
		25	{ # create file share
			# New-FileShare -FolderName "c:\LyncShare" -ShareName "LyncShare" -Description "Used by Lync server to store Address Book files, phone updates, and other important files."
		}
		26	{ # enable federation with Office365
			if ($isLyncInstalled){
				$error.clear()
				# need menu display
				Set-CsExternalAccessPolicy "global" -EnableFederationAccess $true

				Write-Host "Office365 federation............................................[" -NoNewLine
				Write-Host "configuring" -ForegroundColor yellow -NoNewLine
				Write-Host "]" -NoNewLine
				if (Get-ModuleStatus -name "Lync") {
				if(!(Get-CsHostingProvider | ? {$_.proxyFqdn -match "sipfed.online.lync.com"})){
					New-CSHostingProvider –identity "Office365" –ProxyFqdn "sipfed.online.lync.com" –Enabled $true -ErrorAction SilentlyContinue | Out-Null
					if (!($error)){
						Write-Host "`b`b`b`b`b`b`b`b`b`b`b`bdone!" -ForegroundColor green -NoNewLine
					}else{
						Write-Host "`b`b`b`b`b`b`b`b`b`b`b`bFailed!" -ForegroundColor red -NoNewLine
					}
				} else	{
					Write-Host "`b`b`b`b`b`b`b`b`b`b`b`balready enabled!" -ForegroundColor green -NoNewLine
				}
				}
				Write-Host "]          "
			}else{
				Write-Host "***** Lync not installed *****" -ForegroundColor red
			}
		}
		27	{ # add loopback adapter
			New-LoopbackAdapter
		}
		28	{ # install Windows Update module
			$ModulePath = $env:PSModulePath.Substring($env:PSModulePath.LastIndexOf(";")+1)+"JH-WindowsUpdate"
			if (!(Test-Path $ModulePath)){New-Item -path $ModulePath -type directory}
			New-FileDownload "http://jdhitsolutions.com/blog/wp-content/uploads/2010/08/JH-WindowsUpdate-v1.5.zip"
			New-UnzippedFile -ZipFile "$TargetFolder\JH-WindowsUpdate-v1.5.zip" -UnzipFile "JH-WindowsUpdate\Backup-WindowsUpdateLog.ps1" -UnzipFileLocation $ModulePath
			New-UnzippedFile -ZipFile "$TargetFolder\JH-WindowsUpdate-v1.5.zip" -UnzipFile "JH-WindowsUpdate\Clear-WindowsUpdateLog.ps1" -UnzipFileLocation $ModulePath
			New-UnzippedFile -ZipFile "$TargetFolder\JH-WindowsUpdate-v1.5.zip" -UnzipFile "JH-WindowsUpdate\Get-WindowsUpdate.ps1" -UnzipFileLocation $ModulePath
			New-UnzippedFile -ZipFile "$TargetFolder\JH-WindowsUpdate-v1.5.zip" -UnzipFile "JH-WindowsUpdate\Install-WindowsUpdate.ps1" -UnzipFileLocation $ModulePath
			New-UnzippedFile -ZipFile "$TargetFolder\JH-WindowsUpdate-v1.5.zip" -UnzipFile "JH-WindowsUpdate\JH-WindowsUpdate.psd1" -UnzipFileLocation $ModulePath
			New-UnzippedFile -ZipFile "$TargetFolder\JH-WindowsUpdate-v1.5.zip" -UnzipFile "JH-WindowsUpdate\JH-WindowsUpdate.psm1" -UnzipFileLocation $ModulePath
			Remove-Item "$TargetFolder\JH-WindowsUpdate-v1.5.zip"
			Import-Module JH-WindowsUpdate
		}
		29	{ # Download Lync Server 2010 (trial)
			Get-WebPage -url http://technet.microsoft.com/en-us/evalcenter/ff808407.aspx
		}
		30	{ # Restrict features to those available to Standard user CALs
			Set-CsConferencingPolicy –Identity "Global" –AllowIPAudio $false –AllowIPVideo $false –AllowUserToScheduleMeetingsWithAppSharing $false –AllowPolls $false –EnableAppDesktopSharing None –EnableDialinConferencing $false
			Set-CsMeetingConfiguration -DesignateAsPresenter None
			Write-Host "A GPO to disable client side features in the Lync Outlook plug must also be configured. See http://howdouc.blogspot.com/2011/09/understanding-and-enforcing-licensing_21.html for more information." -ForegroudColor yellow
		}
		31	{
			# Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0" NtlmMinClientSec
			# Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0" NtlmMinServerSec
			Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0" -Name "NtlmMinClientSec" -Value 0
			Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0" -Name "NtlmMinServerSec" -Value 0
			$global:RebootRequired = $true
		}
		98 { # Exit and restart
			if (($WasInstalled -eq $false) -and (Get-Module BitsTransfer)){
				Write-Host "Removing BitsTransfer..." -NoNewLine
				Remove-Module BitsTransfer
				Write-Host "removed!" -ForegroundColor Green
			}
			Write-Host "Exiting and rebooting..."
			New-EventLogEntry -EventType Information -EventId 70 -EventLogText "Server shutdown initiated. See transcript file $pwd.path\$FilenameTranscript for more information." -EventSource $MyInvocation.MyCommand.Name -EventLog Application
			Stop-Transcript
			Restart-Computer
		}
		99 { # Exit
			if (($WasInstalled -eq $false) -and (Get-Module BitsTransfer)){
				Write-Host "Removing BitsTransfer..." -NoNewLine
				Remove-Module BitsTransfer
				Write-Host "removed!" -ForegroundColor Green
			}
			popd
			Remove-ScriptVariables($MyInvocation.MyCommand.Name)
			Write-Host "Exiting..."
		}
		default {
			Write-Host "You haven't selected any of the available options."
		}
	}
} while ($MenuOption -ne 99)
Stop-Transcript | Out-Null

36 thoughts on “SCRIPT: Set-Lync2010Features.ps1

  1. Excellent script! However, it should include installation of Web-Http-Redirect for 1 Frontend to avoid issues with easy URL for admin and Lync Control Panel.

  2. Wow – just heard about this from some Lync people at the MVP Summit. Glad to see the script that Anderson, Bhargav, and I created was able to help you Lync guys. Even now more than anything, as I’ve been roped into doing a Lync install for a customer, and can use this.

    I like the idea to install the BPA, and may incorporate that in the new version of the Exchange prereq script coming out soon.

    • I have noticed that, by now I have run the monitoring role on the Front End as well, I will look into adding msmq to the roles where AD integration is required. For OCS it was released a CU that removed this at a point, guess they are back in Lync :)

  3. Hi, but on the Front-End server, i know that is necessary to install also Message Queueing Directory Services:

    In server manager to install the feature “Message Queueing”, when you select Message Queueing it will automatically select one of the 2 features you need, for the other expand Message Queueing and choose “Directory Services Integration”

    Right???

    • When installing MSMQ I install Server and Directory and that works (msmq-server,msmq-directory). For the Front End, I don’t think you need MSMQ if you don’t have monitoring or archiving server deployed. By default monitoring is selected on the Front End server when you install, thats why you get the event viewer entries on MSMQ.

  4. BTW – I have the new version done. I need to test it a little more before posting. I’ve added quite a few options, and cleaned up the code.

  5. I’ll add that one thing I really tried to do, but failed, was to automate the installation of Silverlight. I just could not get a direct link to the installer. If someone can figure that out, I’m happy to add it, but the link MUST be to a microsoft.com location. Going to http://go.microsoft.com/fwlink/?LinkID=149156 will give you the file from silverlight.dlservice.microsoft.com, but I need the exact link.

  6. If anyone has some requested features, please let me know. I’ve got the following added for the next version:

    added group membership check
    added 04-20-2011 Lync Server update download
    added chm file download and install & shortcut
    added standalone AV server prereq option
    added PowerShell transcript
    added backup Lync configuration

    and the following are on the wishlist:

    Better menu during various steps
    Better error trapping
    Auto install of SilverLight

  7. The Lync PG released a new version of the .chm file. The link for that file is the same as what I already have in the script. So even though it says it’s the 4/14/2011 chm file, you’re actually getting the 5/19/2011 version. I’ll update the menu on the next release.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s