Environments/SharePoint-AllVersions/dsc/ConfigureSPLegacy.ps1 (800 lines of code) (raw):
configuration ConfigureSPVM
{
param
(
[Parameter(Mandatory)] [String]$DNSServerIP,
[Parameter(Mandatory)] [String]$DomainFQDN,
[Parameter(Mandatory)] [String]$DCServerName,
[Parameter(Mandatory)] [String]$SQLServerName,
[Parameter(Mandatory)] [String]$SQLAlias,
[Parameter(Mandatory)] [String]$SharePointVersion,
[Parameter(Mandatory)] [String]$SharePointSitesAuthority,
[Parameter(Mandatory)] [String]$SharePointCentralAdminPort,
[Parameter(Mandatory)] [Boolean]$EnableAnalysis,
[Parameter()] [System.Object[]] $SharePointBits,
[Parameter(Mandatory)] [Boolean]$ConfigureADFS,
[Parameter(Mandatory)] [System.Management.Automation.PSCredential]$DomainAdminCreds,
[Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSetupCreds,
[Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPFarmCreds,
[Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPAppPoolCreds,
[Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPPassphraseCreds
)
Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 9.1.0 # Custom
Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.0.0
Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.4.0
Import-DscResource -ModuleName xCredSSP -ModuleVersion 1.4.0
Import-DscResource -ModuleName WebAdministrationDsc -ModuleVersion 4.1.0
Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.5.0
Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.0
Import-DscResource -ModuleName CertificateDsc -ModuleVersion 5.1.0
Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 16.5.0
Import-DscResource -ModuleName cChoco -ModuleVersion 2.6.0.0 # With custom changes to implement retry on package downloads
Import-DscResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 9.1.0
# Init
[String] $InterfaceAlias = (Get-NetAdapter | Where-Object Name -Like "Ethernet*" | Select-Object -First 1).Name
[String] $ComputerName = Get-Content env:computername
[String] $DomainNetbiosName = (Get-NetBIOSName -DomainFQDN $DomainFQDN)
# Format credentials to be qualified by domain name: "domain\username"
[System.Management.Automation.PSCredential] $DomainAdminCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($DomainAdminCreds.UserName)", $DomainAdminCreds.Password)
[System.Management.Automation.PSCredential] $SPSetupCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($SPSetupCreds.UserName)", $SPSetupCreds.Password)
[System.Management.Automation.PSCredential] $SPFarmCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($SPFarmCreds.UserName)", $SPFarmCreds.Password)
[System.Management.Automation.PSCredential] $SPAppPoolCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($SPAppPoolCreds.UserName)", $SPAppPoolCreds.Password)
# Setup settings
[String] $SetupPath = "C:\DSC Data"
[String] $DCSetupPath = "\\$DCServerName\C$\DSC Data"
[String] $DscStatusFilePath = "$SetupPath\dsc-status-$ComputerName.log"
[String] $SAMLTrustRealm = "urn:sharepoint:spsites"
[String] $LDAPCPFileFullPath = Join-Path -Path $SetupPath -ChildPath "Binaries\LDAPCP.wsp"
[String] $AdfsDnsEntryName = "adfs"
# SharePoint settings
[String] $SPDBPrefix = "SP$($SharePointVersion)_"
[String] $TrustedIdChar = "e"
[String] $SPTeamSiteTemplate = "STS#3"
if ([String]::Equals($SharePointVersion, "2016")) {
$SPTeamSiteTemplate = "STS#0"
}
Node localhost
{
LocalConfigurationManager
{
ConfigurationMode = 'ApplyOnly'
RebootNodeIfNeeded = $true
}
Script DscStatus_Start
{
SetScript =
{
$destinationFolder = $using:SetupPath
if (!(Test-Path $destinationFolder -PathType Container)) {
New-Item -ItemType Directory -Force -Path $destinationFolder
}
"$(Get-Date -Format u)`t$($using:ComputerName)`tDSC Configuration starting..." | Out-File -FilePath $using:DscStatusFilePath -Append
}
GetScript = { } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { return $false } # If the TestScript returns $false, DSC executes the SetScript to bring the node back to the desired state
}
# cChocoInstaller fails with this error: Get-FileDownload for 'https://chocolatey.org/install.ps1' failed on attempt 1 with this error: .NET Framework 4.8 was installed, but a reboot is required. Please reboot the system and try to install/upgrade Chocolatey again.
# But running it right at the beginning eventually works, and somehow it does not propagate the error to the Azure DSC extension (and doing a reboot juste before has no effect)
cChocoInstaller InstallChoco { InstallDir = "C:\Chocolatey"; }
#**********************************************************
# Initialization of VM - Do as much work as possible before waiting on AD domain to be available
#**********************************************************
WindowsFeature AddADTools { Name = "RSAT-AD-Tools"; Ensure = "Present"; }
WindowsFeature AddADPowerShell { Name = "RSAT-AD-PowerShell"; Ensure = "Present"; }
WindowsFeature AddDnsTools { Name = "RSAT-DNS-Server"; Ensure = "Present"; }
DnsServerAddress SetDNS { Address = $DNSServerIP; InterfaceAlias = $InterfaceAlias; AddressFamily = 'IPv4' }
# xCredSSP is required forSharePointDsc resources SPUserProfileServiceApp and SPDistributedCacheService
xCredSSP CredSSPServer { Ensure = "Present"; Role = "Server"; DependsOn = "[DnsServerAddress]SetDNS" }
xCredSSP CredSSPClient { Ensure = "Present"; Role = "Client"; DelegateComputers = "*.$DomainFQDN", "localhost"; DependsOn = "[xCredSSP]CredSSPServer" }
# Allow NTLM on HTTPS sites when site host name is different than the machine name - https://docs.microsoft.com/en-US/troubleshoot/windows-server/networking/accessing-server-locally-with-fqdn-cname-alias-denied
Registry DisableLoopBackCheck { Key = "HKLM:\System\CurrentControlSet\Control\Lsa"; ValueName = "DisableLoopbackCheck"; ValueData = "1"; ValueType = "Dword"; Ensure = "Present" }
# Enable TLS 1.2 - https://learn.microsoft.com/en-us/azure/active-directory/app-proxy/application-proxy-add-on-premises-application#tls-requirements
# This allows xRemoteFile to download releases from GitHub: https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/405
Registry EnableTLS12RegKey1 { Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client'; ValueName = 'DisabledByDefault'; ValueType = 'Dword'; ValueData = '0'; Ensure = 'Present' }
Registry EnableTLS12RegKey2 { Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client'; ValueName = 'Enabled'; ValueType = 'Dword'; ValueData = '1'; Ensure = 'Present' }
Registry EnableTLS12RegKey3 { Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server'; ValueName = 'DisabledByDefault'; ValueType = 'Dword'; ValueData = '0'; Ensure = 'Present' }
Registry EnableTLS12RegKey4 { Key = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server'; ValueName = 'Enabled'; ValueType = 'Dword'; ValueData = '1'; Ensure = 'Present' }
# Enable strong crypto by default for .NET Framework 4 applications - https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls#configuring-security-via-the-windows-registry
Registry SchUseStrongCrypto { Key = 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319'; ValueName = 'SchUseStrongCrypto'; ValueType = 'Dword'; ValueData = '1'; Ensure = 'Present' }
Registry SchUseStrongCrypto32 { Key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319'; ValueName = 'SchUseStrongCrypto'; ValueType = 'Dword'; ValueData = '1'; Ensure = 'Present' }
Registry SystemDefaultTlsVersions { Key = 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319'; ValueName = 'SystemDefaultTlsVersions'; ValueType = 'Dword'; ValueData = '1'; Ensure = 'Present' }
Registry SystemDefaultTlsVersions32 { Key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319'; ValueName = 'SystemDefaultTlsVersions'; ValueType = 'Dword'; ValueData = '1'; Ensure = 'Present' }
Registry DisableIESecurityRegKey1 { Key = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}'; ValueName = 'IsInstalled'; ValueType = 'Dword'; ValueData = '0'; Force = $true ; Ensure = 'Present' }
Registry DisableIESecurityRegKey2 { Key = 'HKLM:\Software\Policies\Microsoft\Internet Explorer\Main'; ValueName = 'DisableFirstRunCustomize'; ValueType = 'Dword'; ValueData = '1'; Force = $true ; Ensure = 'Present' }
Registry DisableIESecurityRegKey3 { Key = 'HKLM:\Software\Policies\Microsoft\Internet Explorer\TabbedBrowsing'; ValueName = 'NewTabPageShow'; ValueType = 'Dword'; ValueData = '0'; Force = $true ; Ensure = 'Present' }
# From https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=powershell :
# Starting in Windows 10, version 1607, MAX_PATH limitations have been removed from common Win32 file and directory functions. However, you must opt-in to the new behavior.
Registry SetLongPathsEnabled { Key = "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem"; ValueName = "LongPathsEnabled"; ValueType = "DWORD"; ValueData = "1"; Force = $true; Ensure = "Present" }
Registry ShowWindowsExplorerRibbon { Key = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer"; ValueName = "ExplorerRibbonStartsMinimized"; ValueType = "DWORD"; ValueData = "4"; Force = $true; Ensure = "Present" }
if ($SharePointVersion -eq "2019") {
# Allow OneDrive NGSC to connect to SharePoint Subscription / 2019 - https://learn.microsoft.com/en-us/sharepoint/install/configure-syncing-with-the-onedrive-sync-app
Registry SetOneDriveUrl { Key = "HKLM:\Software\Policies\Microsoft\OneDrive"; ValueName = "SharePointOnPremFrontDoorUrl"; ValueType = "String"; ValueData = "http://{0}" -f $SharePointSitesAuthority; Ensure = "Present" }
Registry SetOneDriveName { Key = "HKLM:\Software\Policies\Microsoft\OneDrive"; ValueName = "SharePointOnPremTenantName"; ValueType = "String"; ValueData = "{0} - {1}" -f $DomainNetbiosName, $SharePointSitesAuthority; Ensure = "Present" }
}
SqlAlias AddSqlAlias { Ensure = "Present"; Name = $SQLAlias; ServerName = $SQLServerName; Protocol = "TCP"; TcpPort= 1433 }
Script EnableFileSharing
{
TestScript = {
# Test if firewall rules for file sharing already exist
$rulesSet = Get-NetFirewallRule -DisplayGroup "File And Printer Sharing" -Enabled True -ErrorAction SilentlyContinue | Where-Object{$_.Profile -eq "Domain"}
if ($null -eq $rulesSet) {
return $false # Run SetScript
} else {
return $true # Rules already set
}
}
SetScript = {
Set-NetFirewallRule -DisplayGroup "File And Printer Sharing" -Enabled True -Profile Domain -Confirm:$false
}
GetScript = { }
}
# Create the rules in the firewall required for the distributed cache - https://learn.microsoft.com/en-us/sharepoint/administration/plan-for-feeds-and-the-distributed-cache-service#firewall
Script CreateFirewallRulesForDistributedCache
{
TestScript = {
# Test if firewall rules already exist
$icmpRuleName = "File and Printer Sharing (Echo Request - ICMPv4-In)"
$icmpFirewallRule = Get-NetFirewallRule -DisplayName $icmpRuleName -ErrorAction SilentlyContinue
$spRuleName = "SharePoint Distributed Cache"
$firewallRule = Get-NetFirewallRule -DisplayName $spRuleName -ErrorAction SilentlyContinue
if ($null -eq $icmpFirewallRule -or $null -eq $firewallRule) {
return $false # Run SetScript
} else {
return $true # Rules already set
}
}
SetScript = {
$icmpRuleName = "File and Printer Sharing (Echo Request - ICMPv4-In)"
$icmpFirewallRule = Get-NetFirewallRule -DisplayName $icmpRuleName -ErrorAction SilentlyContinue
if ($null -eq $icmpFirewallRule) {
New-NetFirewallRule -Name Allow_Ping -DisplayName $icmpRuleName `
-Description "Allow ICMPv4 ping" `
-Protocol ICMPv4 `
-IcmpType 8 `
-Enabled True `
-Profile Any `
-Action Allow
}
Enable-NetFirewallRule -DisplayName $icmpRuleName
$spRuleName = "SharePoint Distributed Cache"
$firewallRule = Get-NetFirewallRule -DisplayName $spRuleName -ErrorAction SilentlyContinue
if ($null -eq $firewallRule) {
New-NetFirewallRule -Name "SPDistCache" `
-DisplayName $spRuleName `
-Protocol TCP `
-LocalPort 22233-22236 `
-Group "SharePoint"
}
Enable-NetFirewallRule -DisplayName $spRuleName
}
GetScript = { }
}
#**********************************************************
# Install applications using Chocolatey
#**********************************************************
Script DscStatus_InstallApps
{
SetScript =
{
"$(Get-Date -Format u)`t$($using:ComputerName)`tInstall applications..." | Out-File -FilePath $using:DscStatusFilePath -Append
}
GetScript = { } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { return $false } # If the TestScript returns $false, DSC executes the SetScript to bring the node back to the desired state
}
cChocoPackageInstaller InstallEdge
{
Name = "microsoft-edge"
Ensure = "Present"
DependsOn = "[cChocoInstaller]InstallChoco"
}
cChocoPackageInstaller InstallNotepadpp
{
Name = "notepadplusplus.install"
Ensure = "Present"
DependsOn = "[cChocoInstaller]InstallChoco"
}
cChocoPackageInstaller Install7zip
{
Name = "7zip.install"
Ensure = "Present"
DependsOn = "[cChocoInstaller]InstallChoco"
}
cChocoPackageInstaller InstallVscode
{ # Install takes about 30 secs
Name = "vscode"
Ensure = "Present"
DependsOn = "[cChocoInstaller]InstallChoco"
}
cChocoPackageInstaller InstallAzureDataStudio
{ # Install takes about 40 secs
Name = "azure-data-studio"
Ensure = "Present"
DependsOn = "[cChocoInstaller]InstallChoco"
}
# if ($EnableAnalysis) {
# # This resource is only for analyzing dsc logs using a custom Python script
# cChocoPackageInstaller InstallPython
# {
# Name = "python"
# Ensure = "Present"
# DependsOn = "[cChocoInstaller]InstallChoco"
# }
# }
WebAppPool RemoveDotNet2Pool { Name = ".NET v2.0"; Ensure = "Absent"; }
WebAppPool RemoveDotNet2ClassicPool { Name = ".NET v2.0 Classic"; Ensure = "Absent"; }
WebAppPool RemoveDotNet45Pool { Name = ".NET v4.5"; Ensure = "Absent"; }
WebAppPool RemoveDotNet45ClassicPool { Name = ".NET v4.5 Classic"; Ensure = "Absent"; }
WebAppPool RemoveClassicDotNetPool { Name = "Classic .NET AppPool"; Ensure = "Absent"; }
WebAppPool RemoveDefaultAppPool { Name = "DefaultAppPool"; Ensure = "Absent"; }
WebSite RemoveDefaultWebSite { Name = "Default Web Site"; Ensure = "Absent"; PhysicalPath = "C:\inetpub\wwwroot"; }
#**********************************************************
# Join AD forest
#**********************************************************
# DNS record for ADFS is created only after the ADFS farm was created and DC restarted (required by ADFS setup)
# This turns out to be a very reliable way to ensure that VM joins AD only when the DC is guaranteed to be ready
# This totally eliminates the random errors that occured in WaitForADDomain with the previous logic (and no more need of WaitForADDomain)
Script WaitForADFSFarmReady
{
SetScript =
{
$dnsRecordFQDN = "$($using:AdfsDnsEntryName).$($using:DomainFQDN)"
$dnsRecordFound = $false
$sleepTime = 15
do {
try {
[Net.DNS]::GetHostEntry($dnsRecordFQDN)
$dnsRecordFound = $true
}
catch [System.Net.Sockets.SocketException] {
# GetHostEntry() throws SocketException "No such host is known" if DNS entry is not found
Write-Host "DNS record '$dnsRecordFQDN' not found yet: $_"
Start-Sleep -Seconds $sleepTime
}
} while ($false -eq $dnsRecordFound)
}
GetScript = { return @{ "Result" = "false" } } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { try { [Net.DNS]::GetHostEntry("$($using:AdfsDnsEntryName).$($using:DomainFQDN)"); return $true } catch { return $false } }
DependsOn = "[DnsServerAddress]SetDNS"
}
# # If WaitForADDomain does not find the domain whtin "WaitTimeout" secs, it will signar a restart to DSC engine "RestartCount" times
# WaitForADDomain WaitForDCReady
# {
# DomainName = $DomainFQDN
# WaitTimeout = 1800
# RestartCount = 2
# WaitForValidCredentials = $True
# Credential = $DomainAdminCredsQualified
# DependsOn = "[DnsServerAddress]SetDNS"
# }
# # WaitForADDomain sets reboot signal only if WaitForADDomain did not find domain within "WaitTimeout" secs
# PendingReboot RebootOnSignalFromWaitForDCReady
# {
# Name = "RebootOnSignalFromWaitForDCReady"
# SkipCcmClientSDK = $true
# DependsOn = "[WaitForADDomain]WaitForDCReady"
# }
Computer JoinDomain
{
Name = $ComputerName
DomainName = $DomainFQDN
Credential = $DomainAdminCredsQualified
DependsOn = "[Script]WaitForADFSFarmReady"
}
PendingReboot RebootOnSignalFromJoinDomain
{
Name = "RebootOnSignalFromJoinDomain"
SkipCcmClientSDK = $true
DependsOn = "[Computer]JoinDomain"
}
Registry ShowFileExtensions { Key = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"; ValueName = "HideFileExt"; ValueType = "DWORD"; ValueData = "0"; Force = $true; Ensure = "Present"; PsDscRunAsCredential = $DomainAdminCredsQualified }
# This script is still needed
Script CreateWSManSPNsIfNeeded
{
SetScript =
{
# A few times, deployment failed because of this error:
# "The WinRM client cannot process the request. A computer policy does not allow the delegation of the user credentials to the target computer because the computer is not trusted."
# The root cause was that SPNs WSMAN/SP and WSMAN/sp.contoso.local were missing in computer account contoso\SP
# Those SPNs are created by WSMan when it (re)starts
# Restarting service causes an error, so creates SPNs manually instead
# Restart-Service winrm
# Create SPNs WSMAN/SP and WSMAN/sp.contoso.local
$domainFQDN = $using:DomainFQDN
$computerName = $using:ComputerName
Write-Verbose -Message "Adding SPNs 'WSMAN/$computerName' and 'WSMAN/$computerName.$domainFQDN' to computer '$computerName'"
setspn.exe -S "WSMAN/$computerName" "$computerName"
setspn.exe -S "WSMAN/$computerName.$domainFQDN" "$computerName"
}
GetScript = { }
# If the TestScript returns $false, DSC executes the SetScript to bring the node back to the desired state
TestScript =
{
$computerName = $using:ComputerName
$samAccountName = "$computerName$"
if ((Get-ADComputer -Filter {(SamAccountName -eq $samAccountName)} -Property serviceprincipalname | Select-Object serviceprincipalname | Where-Object {$_.ServicePrincipalName -like "WSMAN/$computerName"}) -ne $null) {
# SPN is present
return $true
}
else {
# SPN is missing and must be created
return $false
}
}
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
}
#**********************************************************
# Do SharePoint pre-reqs that require membership in AD domain
#**********************************************************
# Create DNS entries used by SharePoint
DnsRecordCname AddTrustedSiteDNS
{
Name = $SharePointSitesAuthority
ZoneName = $DomainFQDN
DnsServer = $DCServerName
HostNameAlias = "$ComputerName.$DomainFQDN"
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
}
#**********************************************************
# Provision required accounts for SharePoint
#**********************************************************
ADUser CreateSPSetupAccount
{
DomainName = $DomainFQDN
UserName = $SPSetupCreds.UserName
Password = $SPSetupCreds
UserPrincipalName = "$($SPSetupCreds.UserName)@$DomainFQDN"
PasswordNeverExpires = $true
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
}
ADUser CreateSParmAccount
{
DomainName = $DomainFQDN
UserName = $SPFarmCreds.UserName
UserPrincipalName = "$($SPFarmCreds.UserName)@$DomainFQDN"
Password = $SPFarmCreds
PasswordNeverExpires = $true
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
}
Group AddSPSetupAccountToAdminGroup
{
GroupName = "Administrators"
Ensure = "Present"
MembersToInclude = @("$($SPSetupCredsQualified.UserName)")
Credential = $DomainAdminCredsQualified
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[ADUser]CreateSPSetupAccount"
}
ADUser CreateSPAppPoolAccount
{
DomainName = $DomainFQDN
UserName = $SPAppPoolCreds.UserName
UserPrincipalName = "$($SPAppPoolCreds.UserName)@$DomainFQDN"
Password = $SPAppPoolCreds
PasswordNeverExpires = $true
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
}
# Since this DSC may run on multiple SP servers, each with a diferent SPN (spsites201x), SPN cannot be set in ADUser.ServicePrincipalNames because each config removes the SPNs of the previous one
ADServicePrincipalName SetSPAppPoolSPN1
{
ServicePrincipalName = "HTTP/$SharePointSitesAuthority.$DomainFQDN"
Account = $SPAppPoolCreds.UserName
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[ADUser]CreateSPAppPoolAccount"
}
ADServicePrincipalName SetSPAppPoolSPN2
{
ServicePrincipalName = "HTTP/$SharePointSitesAuthority"
Account = $SPAppPoolCreds.UserName
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[ADUser]CreateSPAppPoolAccount"
}
# Fiddler must be installed as $DomainAdminCredsQualified because it's a per-user installation
cChocoPackageInstaller InstallFiddler
{
Name = "fiddler"
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[cChocoInstaller]InstallChoco", "[PendingReboot]RebootOnSignalFromJoinDomain"
}
# Install ULSViewer as $DomainAdminCredsQualified to ensure that the shortcut is visible on the desktop
cChocoPackageInstaller InstallUlsViewer
{
Name = "ulsviewer"
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[cChocoInstaller]InstallChoco"
}
Script WaitForSQL
{
SetScript =
{
$retrySleep = 30
$server = $using:SQLAlias
$db = "master"
$retry = $true
while ($retry) {
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection "Data Source=$server;Initial Catalog=$db;Integrated Security=True;Enlist=False;Connect Timeout=3"
try {
$sqlConnection.Open()
Write-Verbose "Connection to SQL Server $server succeeded"
$sqlConnection.Close()
$retry = $false
}
catch {
Write-Verbose "SQL connection to $server failed, retry in $retrySleep secs..."
Start-Sleep -s $retrySleep
}
}
}
GetScript = { return @{ "Result" = "false" } } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { return $false } # If the TestScript returns $false, DSC executes the SetScript to bring the node back to the desired state
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[SqlAlias]AddSqlAlias", "[Group]AddSPSetupAccountToAdminGroup", "[ADUser]CreateSParmAccount", "[ADUser]CreateSPAppPoolAccount", "[Script]CreateWSManSPNsIfNeeded"
}
#**********************************************************
# Create SharePoint farm
#**********************************************************
SPFarm CreateSPFarm
{
DatabaseServer = $SQLAlias
FarmConfigDatabaseName = $SPDBPrefix + "Config"
Passphrase = $SPPassphraseCreds
FarmAccount = $SPFarmCredsQualified
PsDscRunAsCredential = $SPSetupCredsQualified
AdminContentDatabaseName = $SPDBPrefix + "AdminContent"
CentralAdministrationPort = $SharePointCentralAdminPort
# If RunCentralAdmin is false and configdb does not exist, SPFarm checks during 30 mins if configdb got created and joins the farm
RunCentralAdmin = $true
IsSingleInstance = "Yes"
SkipRegisterAsDistributedCacheHost = $false
Ensure = "Present"
DependsOn = "[Script]WaitForSQL"
}
# Distributed Cache is now enabled directly by the SPFarm resource
# SPDistributedCacheService EnableDistributedCache
# {
# Name = "AppFabricCachingService"
# CacheSizeInMB = 1000 # Default size is 819MB on a server with 16GB of RAM (5%)
# CreateFirewallRules = $true
# ServiceAccount = $SPFarmCredsQualified.UserName
# PsDscRunAsCredential = $SPSetupCredsQualified
# Ensure = "Present"
# DependsOn = "[SPFarm]CreateSPFarm"
# }
SPManagedAccount CreateSPAppPoolManagedAccount
{
AccountName = $SPAppPoolCredsQualified.UserName
Account = $SPAppPoolCredsQualified
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPFarm]CreateSPFarm"
}
SPWebApplication CreateMainWebApp
{
Name = "SharePoint - 80"
ApplicationPool = "SharePoint - 80"
ApplicationPoolAccount = $SPAppPoolCredsQualified.UserName
AllowAnonymous = $false
DatabaseName = $SPDBPrefix + "Content_80"
WebAppUrl = "http://$SharePointSitesAuthority/"
Port = 80
Ensure = "Present"
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPFarm]CreateSPFarm"
}
if ($ConfigureADFS -eq $true) {
# Delay this operation significantly, so that DC has time to generate and copy the certificates
File CopyCertificatesFromDC
{
Ensure = "Present"
Type = "Directory"
Recurse = $true
SourcePath = "$DCSetupPath"
DestinationPath = "$SetupPath\Certificates"
Credential = $DomainAdminCredsQualified
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
}
SPTrustedRootAuthority TrustRootCA
{
Name = "$DomainFQDN root CA"
CertificateFilePath = "$SetupPath\Certificates\ADFS Signing issuer.cer"
Ensure = "Present"
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPFarm]CreateSPFarm"
}
SPTrustedIdentityTokenIssuer CreateSPTrust
{
Name = $DomainFQDN
Description = "Federation with $DomainFQDN"
Realm = $SAMLTrustRealm
SignInUrl = "https://adfs.$DomainFQDN/adfs/ls/"
IdentifierClaim = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
ClaimsMappings = @(
MSFT_SPClaimTypeMapping{
Name = "upn"
IncomingClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
}
MSFT_SPClaimTypeMapping{
Name = "Role"
IncomingClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
}
)
SigningCertificateFilePath = "$SetupPath\Certificates\ADFS Signing.cer"
#ClaimProviderName = "" # Should not be set if there is none
ProviderSignOutUri = "https://adfs.$DomainFQDN/adfs/ls/"
UseWReplyParameter = $true
Ensure = "Present"
DependsOn = "[SPFarm]CreateSPFarm"
PsDscRunAsCredential = $SPSetupCredsQualified
}
# Update GPO to ensure the root certificate of the CA is present in "cert:\LocalMachine\Root\", otherwise certificate request will fail
Script UpdateGPOToTrustRootCACert
{
SetScript =
{
gpupdate.exe /force
}
GetScript = { }
TestScript =
{
$domainNetbiosName = $using:DomainNetbiosName
$dcName = $using:DCServerName
$rootCAName = "$domainNetbiosName-$dcName-CA"
$cert = Get-ChildItem -Path "cert:\LocalMachine\Root\" -DnsName "$rootCAName"
if ($null -eq $cert) {
return $false # Run SetScript
} else {
return $true # Root CA already present
}
}
DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain"
PsDscRunAsCredential = $DomainAdminCredsQualified
}
SPWebApplicationExtension ExtendMainWebApp
{
WebAppUrl = "http://$SharePointSitesAuthority/"
Name = "SharePoint - 443"
AllowAnonymous = $false
Url = "https://$SharePointSitesAuthority.$DomainFQDN"
Zone = "Intranet"
Port = 443
Ensure = "Present"
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPWebApplication]CreateMainWebApp"
}
SPWebAppAuthentication ConfigureMainWebAppAuthentication
{
WebAppUrl = "http://$SharePointSitesAuthority/"
Default = @(
MSFT_SPWebAppAuthenticationMode {
AuthenticationMethod = "WindowsAuthentication"
WindowsAuthMethod = "NTLM"
}
)
Intranet = @(
MSFT_SPWebAppAuthenticationMode {
AuthenticationMethod = "Federated"
AuthenticationProvider = $DomainFQDN
}
)
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPWebApplicationExtension]ExtendMainWebApp"
}
CertReq GenerateMainWebAppCertificate
{
CARootName = "$DomainNetbiosName-$DCServerName-CA"
CAServerFQDN = "$DCServerName.$DomainFQDN"
Subject = "$SharePointSitesAuthority.$DomainFQDN"
SubjectAltName = "dns=*.$DomainFQDN"
KeyLength = '2048'
Exportable = $true
ProviderName = '"Microsoft RSA SChannel Cryptographic Provider"'
OID = '1.3.6.1.5.5.7.3.1'
KeyUsage = '0xa0'
CertificateTemplate = 'WebServer'
AutoRenew = $true
DependsOn = "[Script]UpdateGPOToTrustRootCACert", "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication"
Credential = $DomainAdminCredsQualified
}
Website SetHTTPSCertificate
{
Name = "SharePoint - 443"
BindingInfo = DSC_WebBindingInformation
{
Protocol = "HTTPS"
Port = 443
CertificateStoreName = "My"
CertificateSubject = "$SharePointSitesAuthority.$DomainFQDN"
}
Ensure = "Present"
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[CertReq]GenerateMainWebAppCertificate"
}
SPSite CreateRootSite
{
Url = "http://$SharePointSitesAuthority/"
OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)"
SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN"
Name = "Team site"
Template = $SPTeamSiteTemplate
CreateDefaultGroups = $true
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication"
}
}
else {
SPWebAppAuthentication ConfigureMainWebAppAuthentication
{
WebAppUrl = "http://$SharePointSitesAuthority/"
Default = @(
MSFT_SPWebAppAuthenticationMode {
AuthenticationMethod = "WindowsAuthentication"
WindowsAuthMethod = "NTLM"
}
)
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPWebApplication]CreateMainWebApp"
}
SPSite CreateRootSite
{
Url = "http://$SharePointSitesAuthority/"
OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)"
Name = "Team site"
Template = $SPTeamSiteTemplate
CreateDefaultGroups = $true
PsDscRunAsCredential = $SPSetupCredsQualified
DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication"
}
}
Script WarmupSites
{
SetScript =
{
$warmupJobBlock = {
$uri = $args[0]
try {
Write-Verbose "Connecting to $uri..."
# -UseDefaultCredentials: Does NTLM authN
# -UseBasicParsing: Avoid exception because IE was not first launched yet
# Expected traffic is HTTP 401/302/200, and $Response.StatusCode is 200
$Response = Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -UseBasicParsing -ErrorAction SilentlyContinue
Write-Verbose "Connected successfully to $uri"
}
catch {
}
}
$spsite = "http://$($using:ComputerName):5000/"
Write-Verbose "Warming up '$spsite'..."
$job1 = Start-Job -ScriptBlock $warmupJobBlock -ArgumentList @($spsite)
$spsite = "http://$($using:SharePointSitesAuthority)/"
Write-Verbose "Warming up '$spsite'..."
$job2 = Start-Job -ScriptBlock $warmupJobBlock -ArgumentList @($spsite)
# Must wait for the jobs to complete, otherwise they do not actually run
Receive-Job -Job $job1 -AutoRemoveJob -Wait
Receive-Job -Job $job2 -AutoRemoveJob -Wait
}
GetScript = { return @{ "Result" = "false" } } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { return $false } # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run.
PsDscRunAsCredential = $DomainAdminCredsQualified
DependsOn = "[SPSite]CreateRootSite"
}
Script CreateShortcuts
{
SetScript =
{
$sharePointVersion = $using:SharePointVersion
$directoryVersion = "16"
$WshShell = New-Object -comObject WScript.Shell
# Shortcut to the setup folder
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\Setup data.lnk")
$Shortcut.TargetPath = $using:SetupPath
$Shortcut.Save()
# Shortcut for SharePoint Central Administration
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\SharePoint $sharePointVersion Central Administration.lnk")
$Shortcut.TargetPath = "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\$directoryVersion\BIN\psconfigui.exe"
$Shortcut.Arguments = "-cmd showcentraladmin"
$Shortcut.Save()
# Shortcut for SharePoint Products Configuration Wizard
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\SharePoint $sharePointVersion Products Configuration Wizard.lnk")
$Shortcut.TargetPath = "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\$directoryVersion\BIN\psconfigui.exe"
$Shortcut.Save()
# Shortcut for SharePoint Management Shell
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\SharePoint $sharePointVersion Management Shell.lnk")
$Shortcut.TargetPath = "C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe"
$Shortcut.Arguments = " -NoExit -Command ""& 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\$directoryVersion\CONFIG\POWERSHELL\Registration\SharePoint.ps1'"""
$Shortcut.Save()
}
GetScript = { return @{ "Result" = "false" } } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { return $false } # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run.
PsDscRunAsCredential = $DomainAdminCredsQualified
}
# if ($EnableAnalysis) {
# # This resource is for analysis of dsc logs only and totally optionnal
# Script parseDscLogs
# {
# TestScript = { return $false }
# SetScript = {
# $setupPath = $using:SetupPath
# $localScriptPath = "$setupPath\parse-dsc-logs.py"
# New-Item -ItemType Directory -Force -Path $setupPath
# $url = "https://gist.githubusercontent.com/Yvand/777a2e97c5d07198b926d7bb4f12ab04/raw/parse-dsc-logs.py"
# $downloader = New-Object -TypeName System.Net.WebClient
# $downloader.DownloadFile($url, $localScriptPath)
# $dscExtensionPath = "C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC"
# $folderWithMaxVersionNumber = Get-ChildItem -Directory -Path $dscExtensionPath | Where-Object { $_.Name -match "^[\d\.]+$"} | Sort-Object -Descending -Property Name | Select-Object -First 1
# $fullPathToDscLogs = [System.IO.Path]::Combine($dscExtensionPath, $folderWithMaxVersionNumber)
# # Start python script
# Write-Verbose -Message "Run python `"$localScriptPath`" `"$fullPathToDscLogs`"..."
# #Start-Process -FilePath "powershell" -ArgumentList "python `"$localScriptPath`" `"$fullPathToDscLogs`""
# #invoke-expression "cmd /c start powershell -Command { $localScriptPath $fullPathToDscLogs }"
# python "$localScriptPath" "$fullPathToDscLogs"
# }
# GetScript = { }
# DependsOn = "[cChocoPackageInstaller]InstallPython"
# PsDscRunAsCredential = $DomainAdminCredsQualified
# }
# }
Script DscStatus_Finished
{
SetScript =
{
"$(Get-Date -Format u)`t$($using:ComputerName)`tDSC Configuration on finished." | Out-File -FilePath $using:DscStatusFilePath -Append
}
GetScript = { } # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String.
TestScript = { return $false } # If the TestScript returns $false, DSC executes the SetScript to bring the node back to the desired state
}
}
}
function Get-NetBIOSName
{
[OutputType([string])]
param(
[string]$DomainFQDN
)
if ($DomainFQDN.Contains('.')) {
$length=$DomainFQDN.IndexOf('.')
if ( $length -ge 16) {
$length=15
}
return $DomainFQDN.Substring(0,$length)
}
else {
if ($DomainFQDN.Length -gt 15) {
return $DomainFQDN.Substring(0,15)
}
else {
return $DomainFQDN
}
}
}
<#
help ConfigureSPVM
$password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force
$DomainAdminCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password
$SPSetupCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spsetup", $password
$SPFarmCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spfarm", $password
$SPAppPoolCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spapppool", $password
$SPPassphraseCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "Passphrase", $password
$DNSServerIP = "10.1.1.4"
$DomainFQDN = "contoso.local"
$DCServerName = "DC"
$SQLServerName = "SQL"
$SQLAlias = "SQLAlias"
$SharePointVersion = "2019"
$ConfigureADFS = $true
$EnableAnalysis = $true
$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureSPLegacy.0\ConfigureSPVM"
ConfigureSPVM -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPAppPoolCreds $SPAppPoolCreds -SPPassphraseCreds $SPPassphraseCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -ConfigureADFS $ConfigureADFS -EnableAnalysis $EnableAnalysis -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath
Set-DscLocalConfigurationManager -Path $outputPath
Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force
C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC\2.83.2.0
#>