I have been searching for a way to rapidly create standalone Developer SharePoint 2013 standalone VM’s joined to a central domain for our in-house developers. Our team has created 60+ SharePoint VMs on Azure and continue to create about 10 per month. We are beginning to treat our VMs needing hours of repair like cattle, and no longer like the family dog if they have issues or “get terminally ill”, VM’s are replaced with a brand new shiny cow within 4 hoursJ
The process of manually creating VMs was not fun, taking over a day per VM in the beginning on average. I did not want to use Sysprep, since I would have to maintain multiple VM Images at a single point in time. For some, this might be the best way to go.
My solution was to create a lightweight PowerShell set of scripts that create the VM in Azure, install the applications, and keep everything consistent. I can create 4+ VMs at one time, all under ~4 hours total. This is a process that scales somewhat, to meet our needs. Perfect.
Alternatives to this manual PowerShell process I went through:
- Sysprep would save a ton of steps, but is not as easy to update OS patches, etc. as newer software comes out
- AzureRM– Azure Resource Manager is a lot easier. However, these topologies seem somewhat isolated and all of our existing VMs and didn’t work well with what we had in place for our network/VPN, etc.
- Azure SharePoint QuickStart templates/images- These were preconfigured and had various OS settings changed. Similar to the above Azure RM solution issues we ran into.
Assumptions:
- You have an Azure subscription all set up, with a virtual network/DNS/Subnet (we have a site-to-site VPN)
- You have domain controller with all of the SharePoint service accounts created for Least Privileges security installation
- You have installed the latest version of Azure PowerShell installed, rebooted after installing it, and performed the Add-AzureAccount command
- You have used AutoSPInstaller before
What you need:
-
The following information from Azure
- Subscription ID
-
Virtual Network info
- Network Name
- Subnet Name
- Resource Group
-
The following pre-existing VMs:
-
DC
- Service Accounts
-
Fileshare VM
-
All of the necessary ISO’s and EXE’s
-
Installer files:
- 7zipInstall.msi
- ccleaner.exe
- fiddler4setup.exe
- Copy-Item ‘C:\Fileshare\applications\Firefox Setup Stub 36.0.4.exe’
- iview438_setup.exe
- LINQPad4Setup.exe
- npp.6.7.5.Installer.exe
- paint.net.4.0.5.install.exe
- PowerGUI.3.8.0.129.msi
- cutepdf-writer.exe
- CKS.Dev11.vsix
- codecompare.exe
-
-
Stand Alone EXE’s
- ULSViewer.zip
- U2U.SharePoint.CQB2010.zip
-
Applications (Extracted into their own folder with configuration.ini files)
- CamlDesigner2013
- Visual Studio 2012
- Visual Studio 2015
- SharePoint Designer 2013
- en_sql_server_2014_enterprise_edition_with_service_pack_1_x64_dvd_6669618
- AutoSPInstaller for dev
- sql2014config file for dev
- en_sharepoint_server_2013_with_sp1_x64_dvd_3823428
- SharePoint 2013 June 2015 CU (note, if you download this from the internet, uncheck the security property so you don’t get prompted during the AutoSPInstaller process for UAC- right click all 3 CU files and go to Security and unblock, you only have to do this one time on the fileshare.)
-
-
-
Silent install for software (one-time prep, then save the folder on the Fileshare VM)
- SharePoint Designer- http://www.itninja.com/software/microsoft/sharepoint-designer/14-597
- SQL Server- https://technet.microsoft.com/en-us/library/dd239405(v=sql.110).aspx
- Visual Studio- https://msdn.microsoft.com/en-us/library/ee225237.aspx
- AutoSPInstaller – https://autospinstaller.codeplex.com/
Configure PowerShell variables for the new standalone developer SharePoint VM
#VM Name will be ASP13D08
#IP will be 192.168.1.87
#Cloud service Company-Redondo-D08 (each developer has their own cloud service so they can power on VMs without having to wait for the other developers to start at the same time)
#service accounts
#Single SharePoint 2013 developer VM
$varVMLocation = "A" $varVMServerType = "SP" $varVMSPVersion = "13" $varVMType = "D" $varVMIntanceNum = "08" $varVMReduxSuffix = "" # I sometimes append a version letter to the end of the developers VM, if they are getting an additional VM of the same role. $spsetupname = "svc_spsetup" $spsetuppasstext = "passw0rdspsetup" $users = @("svc_spsetup", "svc_spfarm", "eric.schrader", "dev1", "etc"); $varVMStaticIP = "192.168.1.87" $varStorageAccount = "Company" + $varVMLocation + $varVMType + $varVMIntanceNum #unique $varStorageAccount = $varStorageAccount.ToLower() $service = "Company-Redondo-" + $varVMType + $varVMIntanceNum $instancesize = "Basic_A4" $subscriptionid = "12345678-12345-123456" $subscriptionName = "Microsoft Azure Enterprise" $imageFamily = "Windows Server 2012 R2 Datacenter" #Azure VM Image name, the latest will be used below. $localadminname = "company.admin" #cant be "administrator", etc. $localadminPassword = "passw0rdlocaladmin" $joindomain = "Domain.local" $domainname = "Domain" $machineOU = "OU=Azure,OU=Development,OU=Servers,OU=Seattle,DC=Company,DC=local" $timezone = "Pacific Standard Time" $domainusername = "svc_spsetup" $domainpassword = "passw0rdspsetup" $datadiskGB = 127 $datadiskLUN = 0 $datadiskCACHE = "None" $vmsubnet = "Subnet-1" $vmaffinitygroup = "VPN-Linked"
Create the Azure storage account if it doesn’t exist, the set it as the default for PowerShell
#Get-AzureStorageAccount | ft
#Change varStorageAccount to lowercase
$lowerStorageAccount = $varStorageAccount.ToLower() Try{ get-azurestorageaccount -storageaccountname $varStorageAccount -ErrorAction Stop #if this fails to get it, it will create it below. Need above error action } Catch { #you got an error trying to get it, so create it. Write-output "creating storage account $varStorageAccount" New-AzureStorageAccount -StorageAccountName $lowerStorageAccount -Label $lowerStorageAccount -AffinityGroup $vmaffinitygroup }
#now that it exists, set it as default.
Set-AzureSubscription -CurrentStorageAccountName $varStorageAccount -SubscriptionId $subscriptionid Select-AzureSubscription -SubscriptionId $subscriptionid -Current
Create the VM using above variables
#try to fix the DNS error in the Comapny DC, WARNING: The specified DNS name is already taken.
#New-AzureService -Label $service -Description $service -AffinityGroup $vmaffinitygroup -ServiceName $service
</span>New-AzureVMConfig -Name $name -InstanceSize $instancesize -ImageName $image | Add-AzureProvisioningConfig -AdminUserName $localadminname -EnableWinRMHttp -TimeZone $timezone -DisableAutomaticUpdates –Password $localadminPassword -WindowsDomain -JoinDomain $joindomain -Domain $domainname -DomainUserName $domainusername -DomainPassword $domainpassword -MachineObjectOU $machineOU | Add-AzureDataDisk -CreateNew -DiskSizeInGB $datadiskGB -DiskLabel $datadiskname -LUN $datadiskLUN -HostCaching $datadiskCACHE | Set-AzureSubnet –SubnetNames $vmsubnet | Set-AzureStaticVNetIP -IPAddress $varVMStaticIP| New-AzureVM –ServiceName $service -AffinityGroup $vmaffinitygroup
Configures Secure Remote PowerShell Access to Windows Azure Virtual Machines
Download PS1 file from this blog post to your local computer with Azure PowerShell. https://gallery.technet.microsoft.com/scriptcenter/Configures-Secure-Remote-b137f2fe
#CD to location in PowerShell, for example, your desktop:
Cd C:\users\eric.schrader\desktop
#Create WInRM Cert to new VM
.\InstallWinRMCertAzureVM.ps1 -SubscriptionName $subscriptionname -ServiceName $service -Name $name
Connect to remote session
#Connect via remote powershell as local azure.admin
#uses variables from when the VM was created above
$passwordsec = convertto-securestring $localadminPassword -asplaintext -force $user = $name +"\"+ $localadminname $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$passwordsec $uri = Get-AzureWinRMUri -ServiceName $service -Name $name Enter-PSSession -ConnectionUri $uri -Credential $cred $env:computername
#Run variables Again!!!
# IMPORTANT, COPY AND PASTE THE ABOVE VARIABLES SECTION IN AGAIN. This is a new remote session to the new Azure VM.
$varVMLocation = "A" $varVMServerType = "SP" $varVMSPVersion = "13" $varVMType = "D" $varVMIntanceNum = "08" $varVMReduxSuffix = "" # I sometimes append a version letter to the end of the developers VM, if they are getting an additional VM of the same role. $spsetupname = "svc_spsetup" $spsetuppasstext = "passw0rdspsetup" $users = @("svc_spsetup", "svc_spfarm", "eric.schrader", "dev1", "etc"); $varVMStaticIP = "192.168.1.87" $varStorageAccount = "Company" + $varVMLocation + $varVMType + $varVMIntanceNum #unique $varStorageAccount = $varStorageAccount.ToLower() $service = "Company-Redondo-" + $varVMType + $varVMIntanceNum $instancesize = "Basic_A4" $subscriptionid = "12345678-12345-123456" $subscriptionName = "Microsoft Azure Enterprise" $imageFamily = "Windows Server 2012 R2 Datacenter" #Azure VM Image name, the latest will be used below. $localadminname = "company.admin" #cant be "administrator", etc. $localadminPassword = "passw0rdlocaladmin" $joindomain = "Domain.local" $domainname = "Domain" $machineOU = "OU=Azure,OU=Development,OU=Servers,OU=Seattle,DC=Company,DC=local" $timezone = "Pacific Standard Time" $domainusername = "svc_spsetup" $domainpassword = "passw0rdspsetup" $datadiskGB = 127 $datadiskLUN = 0 $datadiskCACHE = "None" $vmsubnet = "Subnet-1" $vmaffinitygroup = "VPN-Linked"
Set proper storage account in remote session
# Now that you set the variables, set the storage account for the remote session
Set-AzureSubscription -CurrentStorageAccountName $varStorageAccount -SubscriptionId $subscriptionid Select-AzureSubscription -SubscriptionId $subscriptionid -Current
Format F drive for SharePoint/SQL, permission service accounts
#Format F drive
$labels = @("DATA1","DATA2") Write-Host "Initializing and formatting raw disks" $disks = Get-Disk | Where partitionstyle -eq 'raw' | sort number ## start at F: because sometimes E: shows up as a CD drive in Azure $letters = 70..89 | ForEach-Object { ([char]$_) } $count = 0 foreach($d in $disks) { $driveLetter = $letters[$count].ToString() $d | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -UseMaximumSize -DriveLetter $driveLetter | Format-Volume -FileSystem NTFS -NewFileSystemLabel $labels[$count] ` -Confirm:$false -Force $count++ } GET-WMIOBJECT –query "SELECT * from win32_logicaldisk where DriveType = '3'"
#add developer, and admins/spsetup/spfarm, set in $users variable above
foreach($user in $users) { $domainuser= $domainname + "\"+$user $Group = "Administrators" $de = [ADSI]"WinNT://$name/$Group,group" $de.Add("WinNT://$domainname/$user") Write-Host "Done, $domainuser has been permissioned to this computer." } net localgroup administrators
#create folder and share for apps
New-Item -Path F:\tools -ItemType directory -Value Tools New-SMBShare –Name "Tools" –Path "F:\Tools" -ChangeAccess "Everyone"
Disable UAC (for developers), restart computer, set execution policy, etc.
#Disable UAC
Set-ItemProperty -Path HKLM:\Software\Microsoft\Windows\CurrentVersion\policies\system -Name EnableLUA -Value 0
#allow scripts
Set-executionpolicy unrestricted -force
#reboot
Restart-computer
#wait 5 minutes for reboot
#Reconnect to powershell
(exit, reconnect to remote powershell, re-run vars)
Connect to remote session
#Connect via remote powershell as local azure.admin
#uses variables from when the VM was created above
$passwordsec = convertto-securestring $localadminPassword -asplaintext -force $user = $name +"\"+ $localadminname $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$passwordsec $uri = Get-AzureWinRMUri -ServiceName $service -Name $name Enter-PSSession -ConnectionUri $uri -Credential $cred $env:computername
#Run variables Again!!!
# IMPORTANT, COPY AND PASTE THE ABOVE VARIABLES SECTION IN AGAIN. This is a new remote session to the new Azure VM.
</span>$varVMLocation = "A" $varVMServerType = "SP" $varVMSPVersion = "13" $varVMType = "D" $varVMIntanceNum = "08" $varVMReduxSuffix = "" # I sometimes append a version letter to the end of the developers VM, if they are getting an additional VM of the same role. $spsetupname = "svc_spsetup" $spsetuppasstext = "passw0rdspsetup" $users = @("svc_spsetup", "svc_spfarm", "eric.schrader", "dev1", "etc"); $varVMStaticIP = "192.168.1.87" $varStorageAccount = "Company" + $varVMLocation + $varVMType + $varVMIntanceNum #unique $varStorageAccount = $varStorageAccount.ToLower() $service = "Company-Redondo-" + $varVMType + $varVMIntanceNum $instancesize = "Basic_A4" $subscriptionid = "12345678-12345-123456" $subscriptionName = "Microsoft Azure Enterprise" $imageFamily = "Windows Server 2012 R2 Datacenter" #Azure VM Image name, the latest will be used below. $localadminname = "company.admin" #cant be "administrator", etc. $localadminPassword = "passw0rdlocaladmin" $joindomain = "Domain.local" $domainname = "Domain" $machineOU = "OU=Azure,OU=Development,OU=Servers,OU=Seattle,DC=Company,DC=local" $timezone = "Pacific Standard Time" $domainusername = "svc_spsetup" $domainpassword = "passw0rdspsetup" $datadiskGB = 127 $datadiskLUN = 0 $datadiskCACHE = "None" $vmsubnet = "Subnet-1" $vmaffinitygroup = "VPN-Linked"
Install SQL pre-reqs
#Install .net 3.5 for SQL prereq on SQL server
Install-WindowsFeature –name NET-Framework-Core
Now that the VM is ready for software installs, lets copy the software over. Due to the Windows “triple hop” issue of credentials, I cannot remote into the VM then copy from a 3rd remote location to the vm. I will have to RDP manually
RDP to fileshare computer as svc_SPSetup
Run variables on fileshare computers PowerShell
#Run variables Again!!!
# IMPORTANT, COPY AND PASTE THE ABOVE VARIABLES SECTION IN AGAIN. This is a new VM session.
$varVMLocation = "A" $varVMServerType = "SP" $varVMSPVersion = "13" $varVMType = "D" $varVMIntanceNum = "08" $varVMReduxSuffix = "" # I sometimes append a version letter to the end of the developers VM, if they are getting an additional VM of the same role. $spsetupname = "svc_spsetup" $spsetuppasstext = "passw0rdspsetup" $users = @("svc_spsetup", "svc_spfarm", "eric.schrader", "dev1", "etc"); $varVMStaticIP = "192.168.1.87" $varStorageAccount = "Company" + $varVMLocation + $varVMType + $varVMIntanceNum #unique $varStorageAccount = $varStorageAccount.ToLower() $service = "Company-Redondo-" + $varVMType + $varVMIntanceNum $instancesize = "Basic_A4" $subscriptionid = "12345678-12345-123456" $subscriptionName = "Microsoft Azure Enterprise" $imageFamily = "Windows Server 2012 R2 Datacenter" #Azure VM Image name, the latest will be used below. $localadminname = "company.admin" #cant be "administrator", etc. $localadminPassword = "passw0rdlocaladmin" $joindomain = "Domain.local" $domainname = "Domain" $machineOU = "OU=Azure,OU=Development,OU=Servers,OU=Seattle,DC=Company,DC=local" $timezone = "Pacific Standard Time" $domainusername = "svc_spsetup" $domainpassword = "passw0rdspsetup" $datadiskGB = 127 $datadiskLUN = 0 $datadiskCACHE = "None" $vmsubnet = "Subnet-1" $vmaffinitygroup = "VPN-Linked"
Copy the software
#Run from Fileshare as SPSetup in PowerShell
#re-run variables
#run installers
#Copy applications from local computer S drive on \\fileshare to server F drive
Copy-Item C:\Fileshare\applications\7zipInstall.msi -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\ccleaner.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\fiddler4setup.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item 'C:\Fileshare\applications\Firefox Setup Stub 36.0.4.exe' -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\iview438_setup.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\LINQPad4Setup.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\npp.6.7.5.Installer.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\paint.net.4.0.5.install.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\PowerGUI.3.8.0.129.msi -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\cutepdf-writer.exe -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\CKS.Dev11.vsix -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\codecompare.exe -Destination <a href="///\\$name\tools">\\$name\tools</a>
#copy exes to F:\Tools
Copy-Item C:\Fileshare\applications\ULSViewer.zip -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\U2U.SharePoint.CQB2010.zip -Destination <a href="///\\$name\tools">\\$name\tools</a> Copy-Item C:\Fileshare\applications\CamlDesigner2013\* -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse Copy-Item "C:\Fileshare\Visual Studio 2012\*" -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse Copy-Item "C:\Fileshare\Visual Studio 2015\*" -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse Copy-Item "C:\Fileshare\SharePoint Designer 2013\*" -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse
#copy SQL to SQL server (Copy ISO CONTENTS)
Copy-Item C:\Fileshare\en_sql_server_2014_enterprise_edition_with_service_pack_1_x64_dvd_6669618\* -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse Copy-Item C:\Fileshare\AutoSPInstallerDev2013\* -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse Copy-Item C:\Fileshare\sql2014configdevint\* -Destination <a href="///\\$name\tools">\\$name\tools</a> -Recurse -force Copy-Item C:\Fileshare\en_sharepoint_server_2013_with_sp1_x64_dvd_3823428\* -Destination <a href="///\\$name\tools\AutoSPInstaller\SP\2013\SharePoint">\\$name\tools\AutoSPInstaller\SP\2013\SharePoint</a> -Recurse -force Copy-Item "C:\Fileshare\SharePoint 2013 June 2015 CU" -Destination <a href="///\\$name\tools\AutoSPInstaller\SP\2013\Updates">\\$name\tools\AutoSPInstaller\SP\2013\Updates</a> -Recurse -force
Close RDP to fileshare and go back to your local computers PowerShell. We are ready to install the software
#re-run variables
#Run variables Again!!!
# IMPORTANT, COPY AND PASTE THE ABOVE VARIABLES SECTION IN AGAIN. This is a new remote session to the new Azure VM.
$varVMLocation = "A" $varVMServerType = "SP" $varVMSPVersion = "13" $varVMType = "D" $varVMIntanceNum = "08" $varVMReduxSuffix = "" # I sometimes append a version letter to the end of the developers VM, if they are getting an additional VM of the same role. $spsetupname = "svc_spsetup" $spsetuppasstext = "passw0rdspsetup" $users = @("svc_spsetup", "svc_spfarm", "eric.schrader", "dev1", "etc"); $varVMStaticIP = "192.168.1.87" $varStorageAccount = "Company" + $varVMLocation + $varVMType + $varVMIntanceNum #unique $varStorageAccount = $varStorageAccount.ToLower() $service = "Company-Redondo-" + $varVMType + $varVMIntanceNum $instancesize = "Basic_A4" $subscriptionid = "12345678-12345-123456" $subscriptionName = "Microsoft Azure Enterprise" $imageFamily = "Windows Server 2012 R2 Datacenter" #Azure VM Image name, the latest will be used below. $localadminname = "company.admin" #cant be "administrator", etc. $localadminPassword = "passw0rdlocaladmin" $joindomain = "Domain.local" $domainname = "Domain" $machineOU = "OU=Azure,OU=Development,OU=Servers,OU=Seattle,DC=Company,DC=local" $timezone = "Pacific Standard Time" $domainusername = "svc_spsetup" $domainpassword = "passw0rdspsetup" $datadiskGB = 127 $datadiskLUN = 0 $datadiskCACHE = "None" $vmsubnet = "Subnet-1" $vmaffinitygroup = "VPN-Linked"
RDP to Developer VM using svc_SPSetup and install SQL by PowerSHell.
#via SPSetup , possibly have to sign into RDP instead of remote PS. Takes 20 minutes
start-process F:\tools\sql\Setup.exe -ArgumentList "/q /SkipRules=VSShellInstalledRule RebootRequiredCheck /ConfigurationFile=F:\Tools\ConfigurationFile.ini /ERRORREPORTING=1 /IACCEPTSQLSERVERLICENSETERMS" -Wait
Close RDP to developer VM once SQL is done.
From local computer, Connect via remote powershell as spsetup. There is code in here to install SQL remotely, but it too has a triple hop credential issue since I use service accounts. I just RDP to the VM and install SQL via PowerShell there.
#uses variables from when the VM was created above
$passwordsec = convertto-securestring $spsetuppasstext -asplaintext -force $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $spsetupname ,$passwordsec $uri = Get-AzureWinRMUri -ServiceName $service -Name $name Enter-PSSession -ConnectionUri $uri -Credential $cred $env:computername
#FIX – maybe to install SQL remotely
#Invoke-Command -ComputerName $name -Authentication CredSSP -credential $cred -scriptblock {
#F:\tools\sql\Setup.exe -ArgumentList “/q /SkipRules=VSShellInstalledRule RebootRequiredCheck /ConfigurationFile=F:\Tools\ConfigurationFile.ini /ERRORREPORTING=1 /IACCEPTSQLSERVERLICENSETERMS” -Wait
#}
Set SQL max memory to 10GB, set Max Degree of parallelism to 1 (this is a huge script, maybe you can shorten it)
#Set Max degree of parallelism to 1
## Sets the 'max degree of parallelism' value to 1 for the specified SQL server instance ## Port 1433 is used if not specified ## 2012-10-08 ## <a href="http://www.pointbeyond.com">www.pointbeyond.com</a> ## NOTE: This function requires at least serveradmin level permissions within SQL server function SetMaxDegreeOfParallelism() { Param( $server, $port="1433") $conn = new-object System.Data.SqlClient.SqlConnection try { $connectionString = "Server="+$server+","+$port+";Database=master;Integrated Security=True;" $conn.ConnectionString = $connectionString $conn.Open() $cmd = new-object System.Data.SqlClient.SqlCommand $cmd.Connection = $conn # Ensure advanced options are available $commandText = "sp_configure 'show advanced options', 1;RECONFIGURE WITH OVERRIDE;" $cmd.CommandText = $commandText $r = $cmd.ExecuteNonQuery() # Set the Max Degree of Parallelism value to 1 write-host "Setting 'max degree of parallelism' value to 1 for server $server..." $commandText = "sp_configure 'max degree of parallelism', 1;RECONFIGURE WITH OVERRIDE" $cmd.CommandText = $commandText $r = $cmd.ExecuteNonQuery() write-host "Success" } catch { write-host "An error occurred trying to set the MaxDegreeOfParallelism value to 1 for server $server" -Fore Red write-host "Ensure that server and port parameters are correct and that the current user has at least serveradmin permissions within SQL" -Fore Red } finally { $conn.Close() } } # Call the function passing in SQL server name/instance/alias and port number SetMaxDegreeOfParallelism -server $name -port "1433"
#Set SQL Max memory – 3GB
Function Test-SqlSa { <# .SYNOPSIS Ensures sysadmin account access on SQL Server. $server is an SMO server object. .EXAMPLE if (!(Test-SQLSA $server)) { throw "Not a sysadmin on $source. Quitting." } .OUTPUTS $true if syadmin $false if not #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [object]$server ) try { return ($server.ConnectionContext.FixedServerRoles -match "SysAdmin") } catch { return $false } } Function Get-ParamSqlCmsGroups { <# .SYNOPSIS Returns System.Management.Automation.RuntimeDefinedParameterDictionary filled with server groups from specified SQL Server Central Management server name. .EXAMPLE Get-ParamSqlCmsGroups sqlserver #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Server ) if ([Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") -eq $null) {return} if ([Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.RegisteredServers") -eq $null) {return} $cmserver = New-Object Microsoft.SqlServer.Management.Smo.Server $server $sqlconnection = $cmserver.ConnectionContext.SqlConnectionObject try { $cmstore = new-object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($sqlconnection)} catch { return } if ($cmstore -eq $null) { return } $newparams = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $paramattributes = New-Object System.Management.Automation.ParameterAttribute $paramattributes.ParameterSetName = "__AllParameterSets" $paramattributes.Mandatory = $false $argumentlist = $cmstore.DatabaseEngineServerGroup.ServerGroups.name if ($argumentlist -ne $null) { $validationset = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $argumentlist $combinedattributes = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] $combinedattributes.Add($paramattributes) $combinedattributes.Add($validationset) $SqlCmsGroups = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("SqlCmsGroups", [String[]], $combinedattributes) $newparams.Add("SqlCmsGroups", $SqlCmsGroups) return $newparams } else { return } } Function Get-SqlCmsRegServers { <# .SYNOPSIS Returns array of server names from CMS Server. If -Groups is specified, only servers within the given groups are returned. .EXAMPLE Get-SqlCmsRegServers -Server sqlserver -Groups "Accounting", "HR" #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$server, [string[]]$groups ) if ([Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") -eq $null) {return} if ([Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.RegisteredServers") -eq $null) {return} $cmserver = New-Object Microsoft.SqlServer.Management.Smo.Server $server $sqlconnection = $cmserver.ConnectionContext.SqlConnectionObject try { $cmstore = new-object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($sqlconnection)} catch { throw "Cannot access Central Management Server" } $servers = @() if ($groups -ne $null) { foreach ($group in $groups) { $cms = $cmstore.ServerGroups["DatabaseEngineServerGroup"].ServerGroups[$group] $servers += ($cms.GetDescendantRegisteredServers()).servername } } else { $cms = $cmstore.ServerGroups["DatabaseEngineServerGroup"] $servers = ($cms.GetDescendantRegisteredServers()).servername } return $servers } Function Get-SqlMaxMemory { <# .SYNOPSIS Displays information relating to SQL Server Max Memory configuration settings. Works on SQL Server 2000-2014. .DESCRIPTION Inspired by Jonathan Kehayias's post about SQL Server Max memory (<a href="http://bit.ly/sqlmemcalc">http://bit.ly/sqlmemcalc</a>), this script displays a SQL Server's: total memory, currently configured SQL max memory, and the calculated recommendation. Jonathan notes that the formula used provides a *general recommendation* that doesn't account for everything that may be going on in your specific environment. .PARAMETER Servers Allows you to specify a comma separated list of servers to query. .PARAMETER ServersFromFile Allows you to specify a list that's been populated by a list of servers to query. The format is as follows server1 server2 server3 .PARAMETER SqlCms Reports on a list of servers populated by the specified SQL Server Central Management Server. .PARAMETER SqlCmsGroups This is a parameter that appears when SqlCms has been specified. It is populated by Server Groups within the given Central Management Server. .NOTES Author : Chrissy LeMaire Requires: PowerShell Version 3.0, SQL Server SMO, sysadmin access on SQL Servers DateUpdated: 2015-May-21 .LINK <a href="https://gallery.technet.microsoft.com/scriptcenter/Get-Set-SQL-Max-Memory-19147057">https://gallery.technet.microsoft.com/scriptcenter/Get-Set-SQL-Max-Memory-19147057</a> .EXAMPLE Get-SqlMaxMemory -SqlCms sqlcluster Get Memory Settings for all servers within the SQL Server Central Management Server "sqlcluster" .EXAMPLE Get-SqlMaxMemory -SqlCms sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-SqlMaxMemory -UseRecommended Find all servers in CMS that have Max SQL memory set to higher than the total memory of the server (think 2147483647) #> [CmdletBinding()] Param( [parameter(Position=0)] [string[]]$Servers, # File with one server per line [string]$ServersFromFile, # Central Management Server [string]$SqlCms ) DynamicParam { if ($SqlCms) { return (Get-ParamSqlCmsGroups $SqlCms) } } PROCESS { if ([string]::IsNullOrEmpty($SqlCms) -and [string]::IsNullOrEmpty($ServersFromFile) -and [string]::IsNullOrEmpty($servers)) { throw "You must specify a server list source using -Servers or -SqlCms or -ServersFromFile" } if ([Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") -eq $null ) { throw "Quitting: SMO Required. You can download it from <a href="http://goo.gl/R4yA6u">http://goo.gl/R4yA6u</a>" } if ([Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.RegisteredServers") -eq $null ) { throw "Quitting: SMO Required. You can download it from <a href="http://goo.gl/R4yA6u">http://goo.gl/R4yA6u</a>" } $SqlCmsGroups = $psboundparameters.SqlCmsGroups if ($SqlCms) { $servers = Get-SqlCmsRegServers -server $SqlCms -groups $SqlCmsGroups } If ($ServersFromFile) { $servers = Get-Content $ServersFromFile } $collection = @() foreach ($servername in $servers) { Write-Verbose "Attempting to connect to $servername" $server = New-Object Microsoft.SqlServer.Management.Smo.Server $servername try { $server.ConnectionContext.Connect() } catch { Write-Warning "Can't connect to $servername. Moving on."; continue } $maxmem = $server.Configuration.MaxServerMemory.ConfigValue $reserve = 1 $totalMemory = $server.PhysicalMemory # Some servers underreport by 1MB. if (($totalmemory % 1024) -ne 0) { $totalMemory = $totalMemory + 1 } if ($totalMemory -ge 4096) { $currentCount = $totalMemory while ($currentCount/4096 -gt 0) { if ($currentCount -gt 16384) { $reserve += 1 $currentCount += -8192 } else { $reserve += 1 $currentCount += -4096 } } } $recommendedMax = [int]($totalMemory-($reserve*1024)) $object = New-Object PSObject -Property @{ Server = $server.name TotalMB = $totalMemory SqlMaxMB = $maxmem RecommendedMB = $recommendedMax } $server.ConnectionContext.Disconnect() $collection += $object } return ($collection | Sort-Object Server | Select Server, TotalMB, SqlMaxMB, RecommendedMB) } } Function Set-SqlMaxMemory { <# .SYNOPSIS Sets SQL Server max memory then displays information relating to SQL Server Max Memory configuration settings. Works on SQL Server 2000-2014. .PARAMETER Servers Allows you to specify a comma separated list of servers to query. .PARAMETER ServersFromFile Allows you to specify a list that's been populated by a list of servers to query. The format is as follows server1 server2 server3 .PARAMETER SqlCms Reports on a list of servers populated by the specified SQL Server Central Management Server. .PARAMETER SqlCmsGroups This is a parameter that appears when SqlCms has been specified. It is populated by Server Groups within the given Central Management Server. .PARAMETER MaxMB Specifies the max megabytes .PARAMETER UseRecommended Inspired by Jonathan Kehayias's post about SQL Server Max memory (<a href="http://bit.ly/sqlmemcalc">http://bit.ly/sqlmemcalc</a>), this uses a formula to determine the default optimum RAM to use, then sets the SQL max value to that number. Jonathan notes that the formula used provides a *general recommendation* that doesn't account for everything that may be going on in your specific environment. .NOTES Author : Chrissy LeMaire Requires: PowerShell Version 3.0, SQL Server SMO, sysadmin access on SQL Servers DateUpdated: 2015-May-21 .LINK <a href="https://gallery.technet.microsoft.com/scriptcenter/Get-Set-SQL-Max-Memory-19147057">https://gallery.technet.microsoft.com/scriptcenter/Get-Set-SQL-Max-Memory-19147057</a> .EXAMPLE Set-SqlMaxMemory sqlserver 2048 Set max memory to 2048 MB on just one server, "sqlserver" .EXAMPLE Get-SqlMaxMemory -SqlCms sqlcluster | Where-Object { $_.SqlMaxMB -gt $_.TotalMB } | Set-SqlMaxMemory -UseRecommended Find all servers in CMS that have Max SQL memory set to higher than the total memory of the server (think 2147483647), then pipe those to Set-SqlMaxMemory and use the default recommendation .EXAMPLE Set-SqlMaxMemory -SqlCms sqlcluster -SqlCmsGroups Express -MaxMB 512 -Verbose Specifically set memory to 512 MB for all servers within the "Express" server group on CMS "sqlcluster" #> [CmdletBinding()] Param( [parameter(Position=0)] [string[]]$Servers, [parameter(Position=1)] [int]$MaxMB, [string]$ServersFromFile, [string]$SqlCms, [switch]$UseRecommended, [Parameter(ValueFromPipeline=$True)] [object]$collection ) DynamicParam { if ($SqlCms) { return (Get-ParamSqlCmsGroups $SqlCms)} } PROCESS { if ([string]::IsNullOrEmpty($SqlCms) -and [string]::IsNullOrEmpty($ServersFromFile) -and [string]::IsNullOrEmpty($servers) -and $collection -eq $null) { throw "You must specify a server list source using -Servers or -SqlCms or -ServersFromFile or you can pipe results from Get-SqlMaxMemory" } if ($MaxMB -eq 0 -and $UseRecommended -eq $false -and $collection -eq $null) { throw "You must specify -MaxMB or -UseRecommended" } if ($collection -eq $null) { $SqlCmsGroups = $psboundparameters.SqlCmsGroups if ($SqlCmsGroups -ne $null) { $collection = Get-SqlMaxMemory -Servers $servers -SqlCms $SqlCms -ServersFromFile $ServersFromFile -SqlCmsGroups $SqlCmsGroups } else { $collection = Get-SqlMaxMemory -Servers $servers -SqlCms $SqlCms -ServersFromFile $ServersFromFile } } $collection | Add-Member -NotePropertyName OldMaxValue -NotePropertyValue 0 foreach ($row in $collection) { $server = New-Object Microsoft.SqlServer.Management.Smo.Server $row.server try { $server.ConnectionContext.Connect() } catch { Write-Warning "Can't connect to $servername. Moving on."; continue } if (!(Test-SqlSa $server)) { Write-Warning "Not a sysadmin on $servername. Moving on." $server.ConnectionContext.Disconnect() continue } $row.OldMaxValue = $row.SqlMaxMB try { if ($UseRecommended) { Write-Verbose "Changing $($row.server) SQL Server max from $($row.SqlMaxMB) to $($row.RecommendedMB) MB" $server.Configuration.MaxServerMemory.ConfigValue = $row.RecommendedMB $row.SqlMaxMB = $row.RecommendedMB } else { Write-Verbose "Changing $($row.server) SQL Server max from $($row.SqlMaxMB) to $MaxMB MB" $server.Configuration.MaxServerMemory.ConfigValue = $MaxMB $row.SqlMaxMB = $MaxMB } $server.Configuration.Alter() } catch { Write-Warning "Could not modify Max Server Memory for $($row.server)" } $server.ConnectionContext.Disconnect() } return $collection | Select Server, TotalMB, OldMaxValue, @{name="CurrentMaxValue";expression={$_.SqlMaxMB}} } } Set-SqlMaxMemory $name 10000
Install Developer APPS
start-process F:\tools\7zipInstall.msi -ArgumentList "/q" -Wait start-process F:\tools\ccleaner.exe -argumentlist "/S" -Wait start-process F:\tools\fiddler4setup.exe -ArgumentList "/S" -Wait start-process 'F:\tools\Firefox Setup Stub 36.0.4.exe' -ArgumentList "/S" -Wait start-process F:\tools\iview438_setup.exe -ArgumentList "/silent" -Wait start-process F:\tools\LINQPad4Setup.exe -ArgumentList "/silent" -Wait start-process F:\tools\npp.6.7.5.Installer.exe -ArgumentList "/S" -Wait start-process F:\tools\paint.net.4.0.5.install.exe -ArgumentList "/S" -Wait start-process F:\tools\paint.net.4.0.5.install.exe -ArgumentList "/auto" -Wait start-process F:\tools\PowerGUI.3.8.0.129.msi -ArgumentList "/q"
#start print spooler for cutepdf
net start spooler sc query spooler
#fix hanging http://d4rkcell.com/archives/1217
start-process F:\tools\cutepdf-writer.exe -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-" start-process F:\tools\VisualStudio2012\vs_premium.exe -ArgumentList "/adminfile AdminDeployment.xml /passive /norestart" -Wait -NoNewWindow -PassThru $vsixInstallerPath = "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\VSIXInstaller.exe" $extensionPath = "F:\tools\CKS.Dev11.vsix" Start-Process -FilePath $vsixInstallerPath -ArgumentList "/q $extensionPath" -NoNewWindow -Wait start-process F:\tools\SPDesigner\setup.exe -ArgumentList "/adminfile updates\adminfile.msp" -wait start-process F:\tools\en_visual_studio_enterprise_2015_with_update_1_x86_x64_web_installer_8234346.exe -ArgumentList "/S /AdminFile F:\Tools\AdminDeployment.xml" -Wait
RDP to developer VM as svc_SPSetup and Launch AutoSPInstaller bat file
start-process F:\tools\AutoSPInstaller\SP\AutoSPInstaller\AutoSPInstallerLaunch.bat -wait
#Run autospinstaller
#Pre-reqs- 10 minutes w Restart
#Install binaries- 15 minutes
#Automation Fix- CU prompts for internet trusted file. 30 minutes (or right click all 3 CU files and go to Security and unblock, already done for June 2015 CU on AzureShare.).
#UPS Sync- we have to do this manually per install guide
#Add developer as full control of web applications
SharePoint- Configure User Profile AD Sync by hand
AD Connection:
Active Directory Company
Company.local
Company\svc_spups
passw0rd
(Sync All OUs)
Enable timerjob 1am daily:
Start full sync:
Manually add developer as full control of web applications.
Done!