diff --git a/application-workloads/sharepoint/sharepoint-adfs/CHANGELOG.md b/application-workloads/sharepoint/sharepoint-adfs/CHANGELOG.md index 5bd99434a48f..cef74f4f15c0 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/CHANGELOG.md +++ b/application-workloads/sharepoint/sharepoint-adfs/CHANGELOG.md @@ -1,6 +1,16 @@ -# Change log for Azure template SharePoint-ADFS +# Change log for Azure template SharePoint -## Enhancements & bug-fixes - Published in November 19, 2025 +## Unreleased + +## Enhancements & bug-fixes - Published in April 15, 2026 + +### Changed + +- Value `Subscription-Latest` for parameter `sharePointVersion` now installs the April 2026 PU for SharePoint Subscription +- This template now uses the DSC configurations published in repository https://github.com/Yvand/SharePointInfraDsc, specifically the [v2.3.0](https://github.com/Yvand/SharePointInfraDsc/releases/tag/releases%2Fv2.3.0). Major version 2 contains [significant improvements](https://github.com/Yvand/SharePointInfraDsc/blob/main/CHANGELOG.md). +- Updates the version of Bicep modules and resources + +## Enhancements & bug-fixes - Published in December 5, 2025 ### Changed diff --git a/application-workloads/sharepoint/sharepoint-adfs/README.md b/application-workloads/sharepoint/sharepoint-adfs/README.md index 70da0e2247b2..aeb1ff8b2ffb 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/README.md +++ b/application-workloads/sharepoint/sharepoint-adfs/README.md @@ -9,9 +9,7 @@ languages: - bicep - json --- -# SharePoint Subscription / 2019 / 2016 fully configured - -## Deploy the template +# A template to deploy SharePoint Subscription / 2019 / 2016 ![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/sharepoint/sharepoint-adfs/PublicLastTestDate.svg) ![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/sharepoint/sharepoint-adfs/PublicDeployment.svg) @@ -24,46 +22,65 @@ languages: ![Bicep Version](https://azurequickstartsservice.blob.core.windows.net/badges/application-workloads/sharepoint/sharepoint-adfs/BicepVersion.svg) -[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fsharepoint%2Fsharepoint-adfs%2Fazuredeploy.json) -[![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fsharepoint%2Fsharepoint-adfs%2Fazuredeploy.json) -[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fsharepoint%2Fsharepoint-adfs%2Fazuredeploy.json) +This template creates a secure, highly customizable SharePoint Subscription / 2019 / 2016 farm, using [Azure Verified Modules](https://azure.github.io/Azure-Verified-Modules/), and the [project SharePointInfraDsc](https://github.com/Yvand/SharePointInfraDsc) to apply the DSC (desired state configuration) to the virtual machines. -## Features +## Main objectives -This template creates a DC, a SQL Server 2025, and from 1 to 5 server(s) hosting a SharePoint Subscription / 2019 / 2016 farm with an extensive configuration, including trusted authentication, user profiles with personal sites, an OAuth trust (using a certificate), a dedicated IIS site for hosting high-trust add-ins, etc... -The latest version of key softwares (including Fiddler, vscode, np++, 7zip, ULS Viewer) is installed. -SharePoint machines have additional fine-tuning to make them immediately usable (remote administration tools, custom policies for Edge and Chrome, shortcuts, etc...). +- A highly secure, customizable environment, under your full control (you set the AD domain name, admin account name, all accounts password). +- A SharePoint farm installed with the PU of your choice (including the latest one), and up-to-date Windows and softwares before you first log-in. +- Eliminate the burden of doing tedious configuration: Many SharePoint features and services are configured, doing this manually would take ages. +- Truly ready-to-use virtual machines right at the first log-in, with everything a SharePoint administrator needs. +- A state-of-the-art configuration that showcases the best practices for a well-configured SharePoint farm. +- A fast deployment time: A fully configured SharePoint farm installed with the latest PU takes only about 1h15 mins to be fully ready (if you think it is not so fast, compare this with the time it takes to install a SharePoint PU in your farm). +- Easy to create, use, and destroy. You want to test a SharePoint setting/config but you are afraid to mess your existing farm? You want to test a specific SharePoint build? Or test OIDC? Use this template. -There are some differences in the configuration, depending on the SharePoint version: +## Virtual machines -### Common to all SharePoint versions +- The DC and SharePoint Subscription machines use the latest image of [Windows Server 2025 Datacenter: Azure Edition](https://marketplace.microsoft.com/en-us/product/microsoftwindowsserver.windowsserver?tab=PlansAndPrice). +- SQL machine uses the latest image of [SQL Server 2025 Standard Developer on Windows Server 2025](https://marketplace.microsoft.com/en-us/product/microsoftsqlserver.sql2025-ws2025?tab=PlansAndPrice). -- An Active Directory forest with AD CS and AD FS configured. LDAPS (LDAP over SSL) is also configured. -- SharePoint service applications configured: User Profiles, add-ins, session state. -- SharePoint User Profiles service is configured with a directory synchronization connection, and the MySite host is a host-named site collection. -- SharePoint has 1 web application with path based and host-named site collections, and contains 2 zones: - - Default zone: HTTP using Windows authentication. - - Intranet zone: HTTPS using federated (ADFS) authentication. -- An OAuth trust is created, as well as a custom IIS site to host your high-trust add-ins. -- Custom claims provider [LDAPCP](https://www.ldapcp.com/) is installed and configured. +About SharePoint legacy: SharePoint 2016 / 2019 use outdated images ([2016](https://marketplace.microsoft.com/en-us/product/sharepointserver.2016?tab=Overview) and [2019](https://marketplace.microsoft.com/en-us/product/sharepointserver.2019?tab=Overview)) published by SharePoint Engineering. -### Specific to SharePoint Subscription +## Usage -- SharePoint virtual machines are created using the latest disk image of [Windows Server 2025 Azure Edition](https://learn.microsoft.com/windows-server/get-started/editions-comparison?pivots=windows-server-2025) available, and SharePoint binaries (install + cumulative updates) are downloaded and installed from scratch. -- The HTTPS site certificate is managed by SharePoint, which has the private key and sets the binding itself in the IIS site. -- Federated authentication with ADFS is configured using OpenID Connect. +[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fsharepoint%2Fsharepoint-adfs%2Fazuredeploy.json) +[![Deploy To Azure US Gov](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazuregov.svg?sanitize=true)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fsharepoint%2Fsharepoint-adfs%2Fazuredeploy.json) +[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-quickstart-templates%2Fmaster%2Fapplication-workloads%2Fsharepoint%2Fsharepoint-adfs%2Fazuredeploy.json) -### Specific to SharePoint 2019 / 2016 -- SharePoint virtual machines are created using a disk image built and maintained by SharePoint Engineering. -- The HTTPS site certificate is positioned by the DSC script. -- Federated authentication with ADFS is configured using SAML 1.1. +## SharePoint configuration + +- Parameter `sharePointVersion` sets which version of SharePoint will be installed: + - `Subscription-Latest` (default): SharePoint Subscription with the latest public update available at the time of publishing this version: April 2026 ([KB5002853](https://support.microsoft.com/help/5002853)). + - `Subscription-25H2`: SharePoint Subscription with the [Feature Update 25H2](https://learn.microsoft.com/sharepoint/what-s-new/new-improved-features-sharepoint-server-subscription-edition-2025-h2-release) (September 2025 PU / [KB5002784](https://support.microsoft.com/help/5002784)). + - `Subscription-25H1`: SharePoint Subscription with the [Feature Update 25H1](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-25h1-release) (March 2025 PU / [KB5002698](https://support.microsoft.com/help/5002698)). + - `Subscription-24H2`: SharePoint Subscription with the [Feature Update 24H2](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-24h2-release) (September 2024 PU / [kb5002640](https://support.microsoft.com/help/5002640)). + - `Subscription-24H1`: SharePoint Subscription with the [Feature Update 24H1](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-24h1-release) (March 2024 PU / [KB5002564](https://support.microsoft.com/help/5002564)). + - `Subscription-23H2`: SharePoint Subscription with the [Feature Update 23H2](https://learn.microsoft.com/SharePoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-23h2-release) (September 2023 PU / [KB5002474](https://support.microsoft.com/help/5002474)). + - `Subscription-23H1`: SharePoint Subscription with the [Feature Update 23H1](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-23h1-release) (March 2023 PU / [KB5002355](https://support.microsoft.com/help/5002355)). + - `Subscription-22H2`: SharePoint Subscription with the [Feature Update 22H2](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-22h2-release) (September 2022 PU / [KB5002270](https://support.microsoft.com/help/5002270) and [KB5002271](https://support.microsoft.com/help/5002271)). + - `Subscription-RTM`: SharePoint Subscription RTM [published here](https://www.microsoft.com/en-us/download/details.aspx?id=103599). + - `2019` (deprecated): Uses the [image](https://marketplace.microsoft.com/en-us/product/sharepointserver.2019?tab=Overview) built and maintained by SharePoint Engineering. + - `2016` (deprecated): Uses the [image](https://marketplace.microsoft.com/en-us/product/sharepointserver.2016?tab=Overview) built and maintained by SharePoint Engineering. +- Parameter `sharePointConfigurationLevel` sets how much configuration is done: + - `Minimum`: Creates a web application with its default zone only. + - `Light`: Everything in `Minimum`, plus: + - Provisions the State Service Application. + - Configures the trusted authentication (OIDC with ADFS). + - `Medium`: Everything in `Light`, plus: + - Provisions the User Profile Service Application. + - Extends the web application in zone `Intranet`. + - `Full`: Everything in `Medium`, plus: + - Configures all the resources to run and deploy add-ins. + - Creates additional host-named site collections. +- Parameter `defaultZoneMustBeHttps`: `true` if the default zone must use HTTPS, `false` if it may use HTTP (if compatible with the configuration selected). +- Parameter `frontEndServersCount` lets you add up to 4 additional SharePoint servers to the farm with the [MinRole Front-end](https://learn.microsoft.com/sharepoint/install/planning-for-a-minrole-server-deployment-in-sharepoint-server). ## Outbound access to internet During the provisionning, virtual machines require an outbound access to internet to be able to download and apply their configuration. -The outbound access method depends on the parameter `outboundAccessMethod`: -- `PublicIPAddress`: Virtual machines use a [Public IP](https://learn.microsoft.com/azure/virtual-network/ip-services/virtual-network-public-ip-address), associated to their network card. +The outbound access method depends on parameter `outboundAccessMethod`: +- `PublicIPAddress`: Virtual machines use a [Public IP](https://learn.microsoft.com/azure/virtual-network/ip-services/virtual-network-public-ip-address), associated with their network card. - `AzureFirewallProxy`: Virtual machines use [Azure Firewall](https://azure.microsoft.com/products/azure-firewall/) as an [HTTP proxy](https://learn.microsoft.com/azure/firewall/explicit-proxy). ## Remote access @@ -75,57 +92,44 @@ The remote access to the virtual machines depends on the following parameters: - `*` or `Internet`: RDP traffic is allowed from everywhere. - CIDR notation (e.g. `192.168.99.0/24` or `2001:1234::/64`) or an IP address (e.g. `192.168.99.0` or `2001:1234::`): RDP traffic is allowed from the IP address / pattern specified. - Parameter `enableAzureBastion`: - - if `true`: Deploy [Azure Bastion Developer](https://learn.microsoft.com/azure/bastion/quickstart-developer), to allow a secure remote access to virtual machines, at no extra cost. - - if `false` (default): [Azure Bastion Developer](https://learn.microsoft.com/azure/bastion/quickstart-developer) is not deployed. + - if `true`: Configure service [Azure Bastion](https://azure.microsoft.com/services/azure-bastion/) with Developer SKU, to allow a secure remote access to virtual machines. + - if `false` (default): Service [Azure Bastion](https://azure.microsoft.com/services/azure-bastion/) is not created. IMPORTANT: If you set parameter `outboundAccessMethod` to `AzureFirewallProxy`, you have to either enable Azure Bastion, or manually add a public IP address later, to be able to connect to a virtual machine. -## Input parameters - -- Parameter `sharePointVersion` lets you choose which version of SharePoint to install: - - `Subscription-Latest` (default): Same as `Subscription-RTM`, then installs the latest cumulative update available at the time of publishing this version: November 2025 ([KB5002800](https://support.microsoft.com/help/5002800)). - - `Subscription-25H2`: Same as `Subscription-RTM`, then installs the [Feature Update 25H2](https://learn.microsoft.com/sharepoint/what-s-new/new-improved-features-sharepoint-server-subscription-edition-2025-h2-release) (September 2025 CU / [KB5002784](https://support.microsoft.com/help/5002784)). - - `Subscription-25H1`: Same as `Subscription-RTM`, then installs the [Feature Update 25H1](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-25h1-release) (March 2025 CU / [KB5002698](https://support.microsoft.com/help/5002698)). - - `Subscription-24H2`: Same as `Subscription-RTM`, then installs the [Feature Update 24H2](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-24h2-release) (September 2024 CU / [kb5002640](https://support.microsoft.com/help/5002640)). - - `Subscription-24H1`: Same as `Subscription-RTM`, then installs the [Feature Update 24H1](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-24h1-release) (March 2024 CU / [KB5002564](https://support.microsoft.com/help/5002564)). - - `Subscription-23H2`: Same as `Subscription-RTM`, then installs the [Feature Update 23H2](https://learn.microsoft.com/SharePoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-23h2-release) (September 2023 CU / [KB5002474](https://support.microsoft.com/help/5002474)). - - `Subscription-23H1`: Same as `Subscription-RTM`, then installs the [Feature Update 23H1](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-23h1-release) (March 2023 CU / [KB5002355](https://support.microsoft.com/help/5002355)). - - `Subscription-22H2`: Same as `Subscription-RTM`, then installs the [Feature Update 22H2](https://learn.microsoft.com/sharepoint/what-s-new/new-and-improved-features-in-sharepoint-server-subscription-edition-22h2-release) (September 2022 CU / [KB5002270](https://support.microsoft.com/help/5002270) and [KB5002271](https://support.microsoft.com/help/5002271)). - - `Subscription-RTM`: Uses a fresh Windows Server 2025 image, on which SharePoint Subscription RTM is downloaded and installed. - - `2019`: Uses an image built and maintained by SharePoint Engineering, with SharePoint 2019 bits already installed. - - `2016`: Uses an image built and maintained by SharePoint Engineering, with SharePoint 2016 bits already installed. -- Parameter `frontEndServersCount` lets you add up to 4 additional SharePoint servers to the farm with the [MinRole Front-end](https://learn.microsoft.com/sharepoint/install/planning-for-a-minrole-server-deployment-in-sharepoint-server). +## Other input parameters + +- The resource group name is used: + - As the name of the Azure resource group which hosts all the resources that will be created. + - As part of the public DNS name of the virtual machines, if they get a public IP (parameter `outboundAccessMethod`), and a DNS name associated with it (parameter `addNameToPublicIpAddresses`). - Parameter `enableHybridBenefitServerLicenses` allows you to enable Azure Hybrid Benefit to use your on-premises Windows Server licenses and reduce cost, if you are eligible. See [this page](https://docs.microsoft.com/azure/virtual-machines/windows/hybrid-use-benefit-licensing) for more information.. ## Outputs -The template returns multiple values to record the logins and the public IP address of virtual machines. +Upon completion, the deployment returns multiple values such as the logins, passwords, the public IP address of virtual machines, and other useful information. ## Cost of the resources deployed By default, virtual machines use [Basv2 series](https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/basv2-series), ideal for such template and much cheaper than other comparable series. -Here is the default size and storage type per virtual machine role: +Below is the default size and storage used per virtual machine role: - DC: Size [Standard_B2als_v2](https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/basv2-series) (2 vCPU / 4 GiB RAM) and OS disk is a 32 GiB [standard SSD E4](https://learn.microsoft.com/azure/virtual-machines/disks-types#standard-ssds). - SQL Server: Size [Standard_B2as_v2](https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/basv2-series) (2 vCPU / 8 GiB RAM) and OS disk is a 128 GiB [standard SSD E10](https://learn.microsoft.com/azure/virtual-machines/disks-types#standard-ssds). -- SharePoint: Size [Standard_B4as_v2](https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/basv2-series) (4 vCPU / 16 GiB RAM) and OS disk is a 128 GiB [standard SSD E10](https://learn.microsoft.com/azure/virtual-machines/disks-types#standard-ssds) (for SharePoint Subscription SharePoint 2016), or a 32 GiB [standard SSD E4](https://learn.microsoft.com/azure/virtual-machines/disks-types#standard-ssds) (for SharePoint 2019). +- SharePoint: Size [Standard_B4as_v2](https://learn.microsoft.com/azure/virtual-machines/sizes/general-purpose/basv2-series) (4 vCPU / 16 GiB RAM) and OS disk is a 128 GiB [standard SSD E10](https://learn.microsoft.com/azure/virtual-machines/disks-types#standard-ssds) (for SharePoint Subscription and SharePoint 2016), or a 32 GiB [standard SSD E4](https://learn.microsoft.com/azure/virtual-machines/disks-types#standard-ssds) (for SharePoint 2019). -You can visit to estimate the monthly cost of the template in the region/currency of your choice, assuming it is created using the default settings and runs 24*7. +You can use to estimate the monthly cost of deploying the resources in this module, in the region/currency of your choice, assuming it is created using the default settings and runs 24*7. ## Known issues -- The password for the User Profile directory synchronization connection (value of parameter `otherAccountsPassword`) needs to be re-entered in the "Edit synchronization connection" page, otherwise the import fails (password decryption error). -- When parameter `outboundAccessMethod` is `AzureFirewallProxy`, most of the softwares installed through Chocolatey fail to download and are not installed. -- When deploying SharePoint 2016 or 2019, the trial enterprise license has already expired, so you must enter your own in the central administration, then run iisreset and restart the SPTimerV4 service on all the servers. -- When deploying SharePoint 2016 or 2019, the installation of softwares through Chocolatey fails for most of them. - -## More information +- The password for the User Profile directory synchronization connection (parameter `otherAccountsPassword`) needs to be re-entered in the "Edit synchronization connection" page, otherwise the profile import fails (password decryption error in the logs). +- When setting `outboundAccessMethod` to `AzureFirewallProxy`, most of the softwares installed through Chocolatey fail to download and are not installed. +- The deployment of Azure Bastion fails pretty frequently. This has little impact, since it is very easy to redeploy through the portal. +- SharePoint 2016 and 2019 are outdated and deprecated. Their corresponding DSC configurations receive little maintenance to ensure they continue to deploy, but receive no improvement. As such, parameters `sharePointConfigurationLevel` and `defaultZoneMustBeHttps` have no effect on them. -Additional notes: +## Additional information - Using the default options, the complete deployment takes about 1h (but it is worth it). -- Deploying any post-RTM SharePoint Subscription build adds only an extra 5-10 minutes to the total deployment time (compared to RTM), partly because the updates are installed before the farm is created. -- Once it is completed, the template will return valuable information in the 'Outputs' of the deployment. -- For various (very good) reasons, in SQL and SharePoint VMs, the name of the local (not domain) administrator is set with a string that is unique to your subscription (e.g. `"local-[q1w2e3r4t5]"`). It is recorded in the 'Outputs' of the deployment once it is completed. +- Installing a SharePoint PU adds less than 10 minutes to the total deployment time, mostly because the PU is installed before the farm is created. +- For various (very good) reasons, in SQL and SharePoint VMs, the name of the local (not domain) administrator is set with a string that is unique to your subscription (e.g. `l-[q1w2e3r4t5]`). It is recorded in the 'Outputs' of the deployment once it is completed. -`Tags: Microsoft.Network/networkSecurityGroups, Microsoft.Network/virtualNetworks, Microsoft.Network/publicIPAddresses, Microsoft.Network/networkInterfaces, Microsoft.Compute/virtualMachines, extensions, DSC, Microsoft.Compute/virtualMachines/extensions, Microsoft.DevTestLab/schedules, Microsoft.Network/virtualNetworks/subnets, Microsoft.Network/bastionHosts` +`Tags: Microsoft.Network/networkSecurityGroups, Microsoft.Network/virtualNetworks, Microsoft.Network/publicIPAddresses, Microsoft.Network/networkInterfaces, Microsoft.Compute/virtualMachines, extensions, DSC, Microsoft.Compute/virtualMachines/extensions, Microsoft.DevTestLab/schedules, Microsoft.Network/virtualNetworks/subnets, Microsoft.Network/bastionHosts` \ No newline at end of file diff --git a/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.json b/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.json index e6286151b2cc..7fd0a1c08bc5 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.json +++ b/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "6530546489819712834" + "version": "0.42.1.51946", + "templateHash": "4076176866059780150" }, "description": "Create a DC, a SQL Server 2025, and from 1 to 5 server(s) hosting a SharePoint Subscription / 2019 / 2016 farm with an extensive configuration, including trusted authentication, user profiles with personal sites, an OAuth trust (using a certificate), a dedicated IIS site for hosting high-trust add-ins, etc... The latest version of key softwares (including Fiddler, vscode, np++, 7zip, ULS Viewer) is installed. SharePoint machines have additional fine-tuning to make them immediately usable (remote administration tools, custom policies for Edge and Chrome, shortcuts, etc...).", "author": "Yvand" @@ -38,12 +38,24 @@ "description": "Version of the SharePoint farm to create." } }, - "domainFqdn": { + "sharePointConfigurationLevel": { "type": "string", - "defaultValue": "contoso.local", - "minLength": 5, + "defaultValue": "Light", + "allowedValues": [ + "Minimum", + "Light", + "Medium", + "Full" + ], "metadata": { - "description": "FQDN of the Active Directory forest." + "description": "Level of configuration to apply on the SharePoint farm." + } + }, + "defaultZoneMustBeHttps": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Set to true if the default zone of the main web application must use HTTPS protocol." } }, "frontEndServersCount": { @@ -57,7 +69,15 @@ 4 ], "metadata": { - "description": "Number of servers with MinRole Front-end to add to the farm." + "description": "Number of additional servers with the MinRole front-end to add to the farm." + } + }, + "domainFqdn": { + "type": "string", + "defaultValue": "contoso.local", + "minLength": 5, + "metadata": { + "description": "FQDN of the Active Directory forest." } }, "adminUsername": { @@ -109,14 +129,14 @@ "Yes" ], "metadata": { - "description": "Set if the Public IP addresses of virtual machines should have a name label." + "description": "If a virtual machine has a public IP address, specify if it should also have a public DNS name." } }, "enableAzureBastion": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Specify if Azure Bastion should be provisioned. See https://azure.microsoft.com/en-us/services/azure-bastion for more information." + "description": "Specify if Azure Bastion Developer should be provisioned. See https://go.microsoft.com/fwlink/?linkid=2249215 for more information." } }, "enableHybridBenefitServerLicenses": { @@ -272,7 +292,7 @@ ], "minLength": 2, "metadata": { - "description": "Time zone of the virtual machines. Type \"[TimeZoneInfo]::GetSystemTimeZones().Id\" in PowerShell to get the list." + "description": "Time zone of the virtual machines (also used for the parameter autoShutdownTime)." } }, "autoShutdownTime": { @@ -355,9 +375,9 @@ }, "_artifactsLocation": { "type": "string", - "defaultValue": "[deployment().properties.templateLink.uri]", + "defaultValue": "[uri(deployment().properties.templateLink.uri, 'dsc/')]", "metadata": { - "description": "The base URI where artifacts required by this template are located including a trailing '/'" + "description": "The base URI where artifacts required by this template are located." } }, "_artifactsLocationSasToken": { @@ -397,7 +417,7 @@ }, "sharePointSubscriptionBits": [ { - "Label": "RTM", + "Label": "SPRTM", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/3/f/5/3f5f8a7e-462b-41ff-a5b2-04bdf5821ceb/OfficeServer.iso", @@ -407,7 +427,7 @@ ] }, { - "Label": "22H2", + "Label": "SP22H2", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/8/d/f/8dfcb515-6e49-42e5-b20f-5ebdfd19d8e7/wssloc-subscription-kb5002270-fullfile-x64-glb.exe", @@ -422,7 +442,7 @@ ] }, { - "Label": "23H1", + "Label": "SP23H1", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/c/6/a/c6a17105-3d86-42ad-888d-49b22383bfa1/uber-subscription-kb5002355-fullfile-x64-glb.exe" @@ -430,7 +450,7 @@ ] }, { - "Label": "23H2", + "Label": "SP23H2", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/f/5/5/f5559e3f-8b24-419f-b238-b09cf986e927/uber-subscription-kb5002474-fullfile-x64-glb.exe" @@ -438,7 +458,7 @@ ] }, { - "Label": "24H1", + "Label": "SP24H1", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/b/a/b/bab0c7cc-0454-474b-8538-7927f75e6486/uber-subscription-kb5002564-fullfile-x64-glb.exe" @@ -446,7 +466,7 @@ ] }, { - "Label": "24H2", + "Label": "SP24H2", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/6/6/a/66a0057f-79af-4307-8263-103ee75ef5c6/uber-subscription-kb5002640-fullfile-x64-glb.exe" @@ -454,7 +474,7 @@ ] }, { - "Label": "25H1", + "Label": "SP25H1", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/0b131072-7ee6-41ea-b33a-b3410865f3a0/uber-subscription-kb5002698-fullfile-x64-glb.exe" @@ -462,7 +482,7 @@ ] }, { - "Label": "25H2", + "Label": "SP25H2", "Packages": [ { "DownloadUrl": "https://download.microsoft.com/download/0ae39b29-890d-428c-bcee-c93eeca2053b/uber-subscription-kb5002784-fullfile-x64-glb.exe" @@ -470,10 +490,10 @@ ] }, { - "Label": "Latest", + "Label": "SPLatest", "Packages": [ { - "DownloadUrl": "https://download.microsoft.com/download/2ac104e1-555a-4186-9f83-ffca3ec88258/uber-subscription-kb5002800-fullfile-x64-glb.exe" + "DownloadUrl": "https://download.microsoft.com/download/f839c57c-7b4e-4213-b03b-2c1508e13588/uber-subscription-kb5002853-fullfile-x64-glb.exe" } ] } @@ -495,7 +515,8 @@ "dcPrivateIPAddress": "10.1.1.100", "sharePointSitesAuthority": "spsites", "sharePointCentralAdminPort": 5000, - "sharePointBitsSelected": "[if(variables('sharePointSettings').isSharePointSubscription, variables('sharePointSettings').sharePointSubscriptionBits, '')]", + "sharePointBitsDsc": "[if(variables('sharePointSettings').isSharePointSubscription, variables('sharePointSettings').sharePointSubscriptionBits, '')]", + "sharePointVersion": "[if(variables('sharePointSettings').isSharePointSubscription, format('SP{0}', split(parameters('sharePointVersion'), '-')[1]), parameters('sharePointVersion'))]", "localAdminUserName": "[format('l-{0}', uniqueString(subscription().subscriptionId))]", "enableAnalysis": false, "applyBrowserPolicies": true, @@ -563,9 +584,9 @@ "dscSettings": { "wmfVersion": "latest", "configuration": { - "url": "[uri(parameters('_artifactsLocation'), format('dsc/ConfigureDCVM.zip{0}', parameters('_artifactsLocationSasToken')))]", - "script": "ConfigureDCVM.ps1", - "function": "ConfigureDCVM" + "url": "[uri(parameters('_artifactsLocation'), format('dsc-dc.zip{0}', parameters('_artifactsLocationSasToken')))]", + "script": "dsc-dc.ps1", + "function": "ConfigDc" }, "configurationArguments": { "domainFQDN": "[parameters('domainFqdn')]", @@ -588,6 +609,14 @@ "AdfsSvcCreds": { "UserName": "[variables('environmentSettings').adfsSvcUserName]", "Password": "[parameters('otherAccountsPassword')]" + }, + "SqlSvcCreds": { + "UserName": "[variables('environmentSettings').sqlSvcUserName]", + "Password": "[parameters('otherAccountsPassword')]" + }, + "SPSetupCreds": { + "UserName": "[variables('environmentSettings').spSetupUserName]", + "Password": "[parameters('otherAccountsPassword')]" } } } @@ -612,13 +641,14 @@ "dscSettings": { "wmfVersion": "latest", "configuration": { - "url": "[uri(parameters('_artifactsLocation'), format('dsc/ConfigureSQLVM.zip{0}', parameters('_artifactsLocationSasToken')))]", - "script": "ConfigureSQLVM.ps1", - "function": "ConfigureSQLVM" + "url": "[uri(parameters('_artifactsLocation'), format('dsc-sql.zip{0}', parameters('_artifactsLocationSasToken')))]", + "script": "dsc-sql.ps1", + "function": "ConfigSql" }, "configurationArguments": { "DNSServerIP": "[variables('environmentSettings').dcPrivateIPAddress]", - "DomainFQDN": "[parameters('domainFqdn')]" + "DomainFQDN": "[parameters('domainFqdn')]", + "SPSetupUserName": "[variables('environmentSettings').spSetupUserName]" }, "privacy": { "dataCollection": "enable" @@ -633,10 +663,6 @@ "SqlSvcCreds": { "UserName": "[variables('environmentSettings').sqlSvcUserName]", "Password": "[parameters('otherAccountsPassword')]" - }, - "SPSetupCreds": { - "UserName": "[variables('environmentSettings').spSetupUserName]", - "Password": "[parameters('otherAccountsPassword')]" } } } @@ -661,9 +687,9 @@ "dscSettings": { "wmfVersion": "latest", "configuration": { - "url": "[uri(parameters('_artifactsLocation'), format('{0}{1}', if(variables('sharePointSettings').isSharePointSubscription, 'dsc/ConfigureSPSE.zip', 'dsc/ConfigureSPLegacy.zip'), parameters('_artifactsLocationSasToken')))]", - "script": "[if(variables('sharePointSettings').isSharePointSubscription, 'ConfigureSPSE.ps1', 'ConfigureSPLegacy.ps1')]", - "function": "ConfigureSPVM" + "url": "[uri(parameters('_artifactsLocation'), format('{0}{1}', if(variables('sharePointSettings').isSharePointSubscription, 'dsc-spse-main.zip', 'dsc-splegacy-main.zip'), parameters('_artifactsLocationSasToken')))]", + "script": "[if(variables('sharePointSettings').isSharePointSubscription, 'dsc-spse-main.ps1', 'dsc-splegacy-main.ps1')]", + "function": "ConfigSpMain" }, "configurationArguments": { "DNSServerIP": "[variables('environmentSettings').dcPrivateIPAddress]", @@ -671,11 +697,13 @@ "DCServerName": "[variables('templateSettings').vmDCName]", "SQLServerName": "[variables('templateSettings').vmSQLName]", "SQLAlias": "[variables('environmentSettings').sqlAlias]", - "SharePointVersion": "[parameters('sharePointVersion')]", + "SharePointVersion": "[variables('environmentSettings').sharePointVersion]", "SharePointSitesAuthority": "[variables('environmentSettings').sharePointSitesAuthority]", "SharePointCentralAdminPort": "[variables('environmentSettings').sharePointCentralAdminPort]", "EnableAnalysis": "[variables('environmentSettings').enableAnalysis]", - "SharePointBits": "[variables('environmentSettings').sharePointBitsSelected]" + "SharePointBits": "[variables('environmentSettings').sharePointBitsDsc]", + "DefaultZoneMustBeHttps": "[parameters('defaultZoneMustBeHttps')]", + "ConfigurationLevel": "[parameters('sharePointConfigurationLevel')]" }, "privacy": { "dataCollection": "enable" @@ -742,9 +770,9 @@ "dscSettings": { "wmfVersion": "latest", "configuration": { - "url": "[uri(parameters('_artifactsLocation'), format('{0}{1}', if(variables('sharePointSettings').isSharePointSubscription, 'dsc/ConfigureFESE.zip', 'dsc/ConfigureFELegacy.zip'), parameters('_artifactsLocationSasToken')))]", - "script": "[if(variables('sharePointSettings').isSharePointSubscription, 'ConfigureFESE.ps1', 'ConfigureFELegacy.ps1')]", - "function": "ConfigureFEVM" + "url": "[uri(parameters('_artifactsLocation'), format('{0}{1}', if(variables('sharePointSettings').isSharePointSubscription, 'dsc-spse-frontend.zip', 'dsc-splegacy-frontend.zip'), parameters('_artifactsLocationSasToken')))]", + "script": "[if(variables('sharePointSettings').isSharePointSubscription, 'dsc-spse-frontend.ps1', 'dsc-splegacy-frontend.ps1')]", + "function": "ConfigSpFrontend" }, "configurationArguments": { "DNSServerIP": "[variables('environmentSettings').dcPrivateIPAddress]", @@ -752,10 +780,12 @@ "DCServerName": "[variables('templateSettings').vmDCName]", "SQLServerName": "[variables('templateSettings').vmSQLName]", "SQLAlias": "[variables('environmentSettings').sqlAlias]", - "SharePointVersion": "[parameters('sharePointVersion')]", + "SharePointVersion": "[variables('environmentSettings').sharePointVersion]", "SharePointSitesAuthority": "[variables('environmentSettings').sharePointSitesAuthority]", "EnableAnalysis": "[variables('environmentSettings').enableAnalysis]", - "SharePointBits": "[variables('environmentSettings').sharePointBitsSelected]" + "SharePointBits": "[variables('environmentSettings').sharePointBitsDsc]", + "DefaultZoneMustBeHttps": "[parameters('defaultZoneMustBeHttps')]", + "ConfigurationLevel": "[parameters('sharePointConfigurationLevel')]" }, "privacy": { "dataCollection": "enable" @@ -817,8 +847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "4631342604570416240" + "version": "0.42.1.51946", + "templateHash": "2010300059964324862" } }, "parameters": { @@ -3289,8 +3319,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "5941619593631508021" + "version": "0.42.1.51946", + "templateHash": "3160452397084943087" } }, "parameters": { @@ -12674,8 +12704,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "5941619593631508021" + "version": "0.42.1.51946", + "templateHash": "3160452397084943087" } }, "parameters": { @@ -22010,8 +22040,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17320525992216658507" + "version": "0.42.1.51946", + "templateHash": "18318687127388919519" } }, "parameters": { @@ -22056,8 +22086,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "8154163068428418954" + "version": "0.39.26.7824", + "templateHash": "7741601918225805390" }, "name": "Bastion Hosts", "description": "This module deploys a Bastion Host." @@ -22251,7 +22281,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.9.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.10.0" } } }, @@ -22513,7 +22543,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.9.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.10.0" } } }, @@ -22535,7 +22565,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.9.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.10.0" } } }, @@ -22839,7 +22869,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.8.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.8.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -22857,7 +22887,7 @@ }, "azureBastion": { "type": "Microsoft.Network/bastionHosts", - "apiVersion": "2024-07-01", + "apiVersion": "2025-01-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[coalesce(parameters('tags'), createObject())]", @@ -22941,8 +22971,8 @@ "publicIPAddress": { "condition": "[and(and(empty(parameters('bastionSubnetPublicIpResourceId')), not(equals(parameters('skuName'), 'Developer'))), not(parameters('enablePrivateOnlyBastion')))]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('{0}-Bastion-PIP', uniqueString(deployment().name, parameters('location')))]", + "apiVersion": "2025-04-01", + "name": "[format('{0}-Bastion-PIP', uniqueString(subscription().id, resourceGroup().id, parameters('location')))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -23008,8 +23038,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.36.177.2456", - "templateHash": "14921988046704902194" + "version": "0.39.26.7824", + "templateHash": "16564959277054027786" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address." @@ -23226,7 +23256,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" } } }, @@ -23251,12 +23281,19 @@ "metadata": { "description": "Optional. Specify the type of lock." } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } } }, "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" } } }, @@ -23331,7 +23368,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" } } } @@ -23444,6 +23481,17 @@ "description": "Optional. The DDoS protection plan configuration associated with the public IP address." } }, + "deleteOption": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Delete", + "Detach" + ], + "metadata": { + "description": "Optional. The delete option for the public IP address." + } + }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", @@ -23477,10 +23525,13 @@ }, "tags": { "type": "object", - "nullable": true, "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Network/publicIPAddresses@2025-01-01#properties/tags" + }, "description": "Optional. Tags of the resource." - } + }, + "nullable": true }, "diagnosticSettings": { "type": "array", @@ -23519,7 +23570,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.9.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.10.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -23537,7 +23588,7 @@ }, "publicIpAddress": { "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2024-05-01", + "apiVersion": "2025-01-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -23553,7 +23604,8 @@ "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", - "ipTags": "[parameters('ipTags')]" + "ipTags": "[parameters('ipTags')]", + "deleteOption": "[parameters('deleteOption')]" } }, "publicIpAddress_lock": { @@ -23564,7 +23616,7 @@ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", - "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + "notes": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]" }, "dependsOn": [ "publicIpAddress" @@ -23668,7 +23720,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('publicIpAddress', '2024-05-01', 'full').location]" + "value": "[reference('publicIpAddress', '2025-01-01', 'full').location]" } } } @@ -23702,7 +23754,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('azureBastion', '2024-07-01', 'full').location]" + "value": "[reference('azureBastion', '2025-01-01', 'full').location]" }, "ipConfAzureBastionSubnet": { "type": "object", @@ -23755,8 +23807,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17535510757781683036" + "version": "0.42.1.51946", + "templateHash": "13233699021818851542" } }, "parameters": { @@ -23791,7 +23843,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[format('{0}/{1}', parameters('virtualNetworkName'), 'AzureFirewallSubnet')]", "properties": { "addressPrefix": "[parameters('addressPrefix')]", @@ -23800,7 +23852,7 @@ }, { "type": "Microsoft.Network/firewallPolicies", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "firewall-policy-proxy", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -23819,7 +23871,7 @@ }, { "type": "Microsoft.Network/firewallPolicies/ruleCollectionGroups", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[format('{0}/{1}', 'firewall-policy-proxy', 'rules')]", "properties": { "priority": 100, diff --git a/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.parameters.json b/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.parameters.json index 233b433db8d1..7cd83bc1cee9 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.parameters.json +++ b/application-workloads/sharepoint/sharepoint-adfs/azuredeploy.parameters.json @@ -22,6 +22,9 @@ }, "vmSharePointSize": { "value": "Standard_B4ms" + }, + "sharePointConfigurationLevel": { + "value": "Minimum" } } } diff --git a/application-workloads/sharepoint/sharepoint-adfs/bastion.bicep b/application-workloads/sharepoint/sharepoint-adfs/bastion.bicep index 1d7f2c7dfa38..252ec879e59c 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/bastion.bicep +++ b/application-workloads/sharepoint/sharepoint-adfs/bastion.bicep @@ -2,12 +2,12 @@ param virtualNetworkName string @description('Tags to apply on the resources.') param tags object -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-10-01' existing = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2025-05-01' existing = { scope: resourceGroup() name: virtualNetworkName } -module bastionHost 'br/public:avm/res/network/bastion-host:0.8.0' = { +module bastionHost 'br/public:avm/res/network/bastion-host:0.8.2' = { scope: resourceGroup() name: 'bastion-module-avm' params: { diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureDCVM.ps1 b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-dc.ps1 similarity index 87% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureDCVM.ps1 rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-dc.ps1 index 576c69696dbe..2de708da7643 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureDCVM.ps1 +++ b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-dc.ps1 @@ -1,4 +1,4 @@ -configuration ConfigureDCVM +configuration ConfigDc { param ( @@ -9,29 +9,30 @@ [Parameter(Mandatory)] [String]$SharePointCentralAdminPort, [Parameter ()] [Boolean]$ApplyBrowserPolicies = $true, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$Admincreds, - [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$AdfsSvcCreds + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$AdfsSvcCreds, + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SqlSvcCreds, + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSetupCreds ) - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.0 + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.1 Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.1.0 Import-DscResource -ModuleName ActiveDirectoryCSDsc -ModuleVersion 5.0.0 Import-DscResource -ModuleName CertificateDsc -ModuleVersion 6.0.0 - Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.1 - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 # Custom + Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.3 + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 Import-DscResource -ModuleName AdfsDsc -ModuleVersion 1.4.0 # Init - [String] $InterfaceAlias = (Get-NetAdapter| Where-Object InterfaceDescription -Like "Microsoft Hyper-V Network Adapter*" | Select-Object -First 1).Name + [String] $InterfaceAlias = (Get-NetAdapter | Where-Object InterfaceDescription -Like "Microsoft Hyper-V Network Adapter*" | Select-Object -First 1).Name [String] $ComputerName = Get-Content env:computername [String] $DomainNetbiosName = (Get-NetBIOSName -DomainFQDN $DomainFQDN) [String] $AdditionalUsersPath = "OU=AdditionalUsers,DC={0},DC={1}" -f $DomainFQDN.Split('.')[0], $DomainFQDN.Split('.')[1] + [String] $SetupPath = "C:\DSC Data" # Format credentials to be qualified by domain name: "domain\username" [System.Management.Automation.PSCredential] $DomainCredsNetbios = New-Object System.Management.Automation.PSCredential ("${DomainNetbiosName}\$($Admincreds.UserName)", $Admincreds.Password) [System.Management.Automation.PSCredential] $AdfsSvcCredsQualified = New-Object System.Management.Automation.PSCredential ("${DomainNetbiosName}\$($AdfsSvcCreds.UserName)", $AdfsSvcCreds.Password) - [String] $SetupPath = "C:\DSC Data" - # ADFS settings [String] $ADFSSiteName = "adfs" [String] $AdfsOidcAGName = "SPS-Subscription-OIDC" @@ -39,8 +40,8 @@ # SharePoint settings [String] $centralAdminUrl = "http://{0}:{1}/" -f $SPServerName, $SharePointCentralAdminPort - [String] $rootSiteDefaultZone = "http://{0}/" -f $SharePointSitesAuthority - [String] $rootSiteIntranetZone = "https://{0}.{1}/" -f $SharePointSitesAuthority, $DomainFQDN + [String] $urlMainWebAppHttp = "http://{0}/" -f $SharePointSitesAuthority + [String] $urlMainWebAppSsl = "https://{0}.{1}/" -f $SharePointSitesAuthority, $DomainFQDN [String] $AppDomainFQDN = "{0}{1}.{2}" -f $DomainFQDN.Split('.')[0], "Apps", $DomainFQDN.Split('.')[1] [String] $AppDomainIntranetFQDN = "{0}{1}.{2}" -f $DomainFQDN.Split('.')[0], "Apps-Intranet", $DomainFQDN.Split('.')[1] @@ -140,12 +141,12 @@ @{ policyValueName = "ManagedFavorites"; policyCanBeRecommended = $false; - policyValueValue = "[{ ""toplevel_name"": ""SharePoint"" }, { ""name"": ""Central administration"", ""url"": ""$centralAdminUrl"" }, { ""name"": ""Root site - Default zone"", ""url"": ""$rootSiteDefaultZone"" }, { ""name"": ""Root site - Intranet zone"", ""url"": ""$rootSiteIntranetZone"" }]"; + policyValueValue = "[{ ""toplevel_name"": ""SharePoint"" }, { ""name"": ""Central administration"", ""url"": ""$centralAdminUrl"" }, { ""name"": ""Main web app - HTTP"", ""url"": ""$urlMainWebAppHttp"" }, { ""name"": ""Main web app - SSL"", ""url"": ""$urlMainWebAppSsl"" }]"; }, @{ policyValueName = "NewTabPageManagedQuickLinks"; policyCanBeRecommended = $true; - policyValueValue = "[{""pinned"": true, ""title"": ""Central administration"", ""url"": ""$centralAdminUrl"" }, { ""pinned"": true, ""title"": ""Root site - Default zone"", ""url"": ""$rootSiteDefaultZone"" }, { ""pinned"": true, ""title"": ""Root site - Intranet zone"", ""url"": ""$rootSiteIntranetZone"" }]"; + policyValueValue = "[{""pinned"": true, ""title"": ""Central administration"", ""url"": ""$centralAdminUrl"" }, { ""pinned"": true, ""title"": ""Main web app - HTTP"", ""url"": ""$urlMainWebAppHttp"" }, { ""pinned"": true, ""title"": ""Main web app - SSL"", ""url"": ""$urlMainWebAppSsl"" }]"; } ) @@ -198,7 +199,7 @@ @{ policyValueName = "ManagedBookmarks"; policyCanBeRecommended = $false; - policyValueValue = "[{ ""toplevel_name"": ""SharePoint"" }, { ""name"": ""Central administration"", ""url"": ""$centralAdminUrl"" }, { ""name"": ""Root site - Default zone"", ""url"": ""$rootSiteDefaultZone"" }, { ""name"": ""Root site - Intranet zone"", ""url"": ""$rootSiteIntranetZone"" }]"; + policyValueValue = "[{ ""toplevel_name"": ""SharePoint"" }, { ""name"": ""Central administration"", ""url"": ""$centralAdminUrl"" }, { ""name"": ""Main web app - HTTP"", ""url"": ""$urlMainWebAppHttp"" }, { ""name"": ""Main web app - SSL"", ""url"": ""$urlMainWebAppSsl"" }]"; } ) @@ -232,30 +233,21 @@ RebootNodeIfNeeded = $true } - # Fix emerging issue "WinRM cannot process the request. The following error with errorcode 0x80090350" while Windows Azure Guest Agent service initiates using https://stackoverflow.com/a/74015954/8669078 - Script SetWindowsAzureGuestAgentDepndencyOnDNS { - GetScript = { } - TestScript = { return $false } - SetScript = { Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\WindowsAzureGuestAgent' -Name "DependOnService" -Type MultiString -Value "DNS" } - } - #********************************************************** # Create AD domain #********************************************************** - # Install AD FS early (before reboot) to workaround error below on resource AdfsApplicationGroup: - # "System.InvalidOperationException: The test script threw an error. ---> System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.IdentityServer.Diagnostics, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencie" - WindowsFeature AddADFS { Name = "ADFS-Federation"; Ensure = "Present"; } - WindowsFeature AddADDS { Name = "AD-Domain-Services"; Ensure = "Present" } - WindowsFeature AddDNS { Name = "DNS"; Ensure = "Present" } - WindowsFeature AddGroupPolicyPowerShellModule { Name = "GPMC"; Ensure = "Present" } + WindowsFeature AddADDS { + Name = "AD-Domain-Services"; Ensure = "Present" + } DnsServerAddress SetDNS { Address = '127.0.0.1' ; InterfaceAlias = $InterfaceAlias; AddressFamily = 'IPv4' } - PendingReboot CheckRebootBeforeCreateADForest { - Name = "CheckRebootBeforeCreateADForest" - DependsOn = "[WindowsFeature]AddGroupPolicyPowerShellModule" + # Avoids rare error in [ADDomain]CreateADForest resource, that fails because a reboot is necessary before forest can be created + PendingReboot RebootBeforeCreateADForest { + Name = "RebootBeforeCreateADForest" + DependsOn = "[DnsServerAddress]SetDNS", "[WindowsFeature]AddADDS" } ADDomain CreateADForest { @@ -265,7 +257,7 @@ DatabasePath = "C:\NTDS" LogPath = "C:\NTDS" SysvolPath = "C:\SYSVOL" - DependsOn = "[DnsServerAddress]SetDNS", "[WindowsFeature]AddADDS" + DependsOn = "[PendingReboot]RebootBeforeCreateADForest" } PendingReboot RebootOnSignalFromCreateADForest { @@ -273,6 +265,11 @@ DependsOn = "[ADDomain]CreateADForest" } + # Install AD FS early to avoid error on resource AdfsApplicationGroup: "System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.IdentityServer.Diagnostics, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencie" + WindowsFeature AddADFS { + Name = "ADFS-Federation"; Ensure = "Present"; DependsOn = "[PendingReboot]RebootOnSignalFromCreateADForest" + } + WaitForADDomain WaitForDCReady { DomainName = $DomainFQDN WaitTimeout = 300 @@ -281,65 +278,6 @@ WaitForValidCredentials = $true DependsOn = "[PendingReboot]RebootOnSignalFromCreateADForest" } - - if ($true -eq $ApplyBrowserPolicies) { - # Set browser policies asap, so that computers that join domain get them immediately, and it runs very quickly (<5 secs) - # Edge - https://learn.microsoft.com/en-us/deployedge/microsoft-edge-policies - Script ConfigureEdgePolicies { - SetScript = { - $domain = Get-ADDomain -Current LocalComputer - $registryKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge" - $policies = $using:EdgePolicies - $gpo = New-GPO -name "Edge_browser" - New-GPLink -Guid $gpo.Id -Target $domain.DistinguishedName -order 1 - - foreach ($policy in $policies) { - $key = $registryKey - if ($true -eq $policy.policyCanBeRecommended) { $key += "\Recommended" } - $valueType = if ($policy.policyValueValue -is [int]) { "DWORD" } else { "STRING" } - Set-GPRegistryValue -Guid $gpo.Id -key $key -ValueName $policy.policyValueName -Type $valueType -value $policy.policyValueValue - } - } - GetScript = { return @{ "Result" = "false" } } - TestScript = { - $policy = Get-GPO -name "Edge_browser" -ErrorAction SilentlyContinue - if ($null -eq $policy) { - return $false - } - else { - return $true - } - } - } - - # Chrome - https://chromeenterprise.google/intl/en_us/policies/ - Script ConfigureChromePolicies { - SetScript = { - $domain = Get-ADDomain -Current LocalComputer - $registryKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome" - $policies = $using:ChromePolicies - $gpo = New-GPO -name "Chrome_browser" - New-GPLink -Guid $gpo.Id -Target $domain.DistinguishedName -order 1 - - foreach ($policy in $policies) { - $key = $registryKey - if ($true -eq $policy.policyCanBeRecommended) { $key += "\Recommended" } - $valueType = if ($policy.policyValueValue -is [int]) { "DWORD" } else { "STRING" } - Set-GPRegistryValue -Guid $gpo.Id -key $key -ValueName $policy.policyValueName -Type $valueType -value $policy.policyValueValue - } - } - GetScript = { return @{ "Result" = "false" } } - TestScript = { - $policy = Get-GPO -name "Chrome_browser" -ErrorAction SilentlyContinue - if ($null -eq $policy) { - return $false - } - else { - return $true - } - } - } - } #********************************************************** # Configuration needed by SharePoint farm @@ -490,7 +428,7 @@ PasswordAuthentication = 'Negotiate' PasswordNeverExpires = $true Ensure = "Present" - DependsOn = "[CertReq]GenerateADFSSiteCertificate", "[CertReq]GenerateADFSSigningCertificate", "[CertReq]GenerateADFSDecryptionCertificate" + DependsOn = "[WaitForADDomain]WaitForDCReady" } # https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/deployment/configure-corporate-dns-for-the-federation-service-and-drs @@ -502,6 +440,7 @@ DependsOn = "[WaitForADDomain]WaitForDCReady" } + # This will do a reboot AdfsFarm CreateADFSFarm { FederationServiceName = "$ADFSSiteName.$DomainFQDN" FederationServiceDisplayName = "$ADFSSiteName.$DomainFQDN" @@ -510,11 +449,11 @@ DecryptionCertificateDnsName = "$ADFSSiteName.Decryption" ServiceAccountCredential = $AdfsSvcCredsQualified Credential = $DomainCredsNetbios - DependsOn = "[WindowsFeature]AddADFS" + DependsOn = "[WindowsFeature]AddADFS", "[ADUser]CreateAdfsSvcAccount", "[Script]ExportCertificates" } - # This DNS record is tested by other VMs to join AD only after it was found - # It is added after DSC resource AdfsFarm, because it is the last operation that triggers a reboot of the DC + # This DNS record is tested by other VMs, to join the domain once it is found + # Added after DSC resource AdfsFarm, because it is the last operation that triggers a reboot DnsRecordA AddADFSHostDNS { Name = $ADFSSiteName ZoneName = $DomainFQDN @@ -523,6 +462,29 @@ DependsOn = "[AdfsFarm]CreateADFSFarm" } + # Create other service accounts + ADUser CreateSqlSvcAccount { + DomainName = $DomainFQDN + UserName = $SqlSvcCreds.UserName + UserPrincipalName = "$($SqlSvcCreds.UserName)@$DomainFQDN" + Password = $SqlSvcCreds + PasswordNeverExpires = $true + Ensure = "Present" + PsDscRunAsCredential = $DomainCredsNetbios + DependsOn = "[WaitForADDomain]WaitForDCReady" + } + + ADUser CreateSPSetupAccount { + DomainName = $DomainFQDN + UserName = $SPSetupCreds.UserName + UserPrincipalName = "$($SPSetupCreds.UserName)@$DomainFQDN" + Password = $SPSetupCreds + PasswordNeverExpires = $true + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[WaitForADDomain]WaitForDCReady" + } + ADFSRelyingPartyTrust CreateADFSRelyingParty { Name = $SharePointSitesAuthority Identifier = "urn:sharepoint:$($SharePointSitesAuthority)" @@ -621,17 +583,64 @@ DependsOn = "[AdfsNativeClientApplication]OidcNativeApp", "[AdfsWebApiApplication]OidcWebApiApp" } - WindowsFeature AddADTools { - Name = "RSAT-AD-Tools"; Ensure = "Present"; - } - WindowsFeature AddDnsTools { - Name = "RSAT-DNS-Server"; Ensure = "Present"; - } - WindowsFeature AddADLDS { - Name = "RSAT-ADLDS"; Ensure = "Present"; - } - WindowsFeature AddADCSManagementTools { - Name = "RSAT-ADCS-Mgmt"; Ensure = "Present"; + if ($true -eq $ApplyBrowserPolicies) { + # Edge - https://learn.microsoft.com/en-us/deployedge/microsoft-edge-policies + Script ConfigureEdgePolicies { + SetScript = { + $domain = Get-ADDomain -Current LocalComputer + $registryKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge" + $policies = $using:EdgePolicies + $gpo = New-GPO -name "Edge_browser" + New-GPLink -Guid $gpo.Id -Target $domain.DistinguishedName -order 1 + + foreach ($policy in $policies) { + $key = $registryKey + if ($true -eq $policy.policyCanBeRecommended) { $key += "\Recommended" } + $valueType = if ($policy.policyValueValue -is [int]) { "DWORD" } else { "STRING" } + Set-GPRegistryValue -Guid $gpo.Id -key $key -ValueName $policy.policyValueName -Type $valueType -value $policy.policyValueValue + } + } + GetScript = { return @{ "Result" = "false" } } + TestScript = { + $policy = Get-GPO -name "Edge_browser" -ErrorAction SilentlyContinue + if ($null -eq $policy) { + return $false + } + else { + return $true + } + } + DependsOn = "[WaitForADDomain]WaitForDCReady" + } + + # Chrome - https://chromeenterprise.google/intl/en_us/policies/ + Script ConfigureChromePolicies { + SetScript = { + $domain = Get-ADDomain -Current LocalComputer + $registryKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome" + $policies = $using:ChromePolicies + $gpo = New-GPO -name "Chrome_browser" + New-GPLink -Guid $gpo.Id -Target $domain.DistinguishedName -order 1 + + foreach ($policy in $policies) { + $key = $registryKey + if ($true -eq $policy.policyCanBeRecommended) { $key += "\Recommended" } + $valueType = if ($policy.policyValueValue -is [int]) { "DWORD" } else { "STRING" } + Set-GPRegistryValue -Guid $gpo.Id -key $key -ValueName $policy.policyValueName -Type $valueType -value $policy.policyValueValue + } + } + GetScript = { return @{ "Result" = "false" } } + TestScript = { + $policy = Get-GPO -name "Chrome_browser" -ErrorAction SilentlyContinue + if ($null -eq $policy) { + return $false + } + else { + return $true + } + } + DependsOn = "[WaitForADDomain]WaitForDCReady" + } } Script EnableFileSharing { @@ -669,6 +678,7 @@ return $true } } + DependsOn = "[WaitForADDomain]WaitForDCReady" } ADOrganizationalUnit AdditionalUsersOU { @@ -695,6 +705,19 @@ DependsOn = "[ADOrganizationalUnit]AdditionalUsersOU" } } + + # WindowsFeature AddADTools { + # Name = "RSAT-AD-Tools"; Ensure = "Present"; + # } + # WindowsFeature AddDnsTools { + # Name = "RSAT-DNS-Server"; Ensure = "Present"; + # } + # WindowsFeature AddADLDS { + # Name = "RSAT-ADLDS"; Ensure = "Present"; + # } + # WindowsFeature AddADCSManagementTools { + # Name = "RSAT-ADCS-Mgmt"; Ensure = "Present"; + # } } } @@ -722,32 +745,21 @@ function Get-NetBIOSName { } <# -# Azure DSC extension logging: C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC\2.80.0.0 -# Azure DSC extension configuration: C:\Packages\Plugins\Microsoft.Powershell.DSC\2.80.0.0\DSCWork - -Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -Install-Module -Name xAdcsDeployment -Install-Module -Name xCertificate -Install-Module -Name xPSDesiredStateConfiguration -Install-Module -Name xCredSSP -Install-Module -Name xWebAdministration -Install-Module -Name xDisk -Install-Module -Name xNetworking - -help ConfigureDCVM +help ConfigDc $password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force $Admincreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password $AdfsSvcCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "adfssvc", $password +$SqlSvcCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "sqlsvc", $password +$SPSetupCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spsetup", $password $DomainFQDN = "contoso.local" -$PrivateIP = "10.1.1.4" +$PrivateIP = "10.1.1.100" $SPServerName = "SP" $SharePointSitesAuthority = "spsites" $SharePointCentralAdminPort = 5000 -$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureDCVM.0\ConfigureDCVM" -ConfigureDCVM -Admincreds $Admincreds -AdfsSvcCreds $AdfsSvcCreds -DomainFQDN $DomainFQDN -PrivateIP $PrivateIP -SPServerName $SPServerName -SharePointSitesAuthority $SharePointSitesAuthority -SharePointCentralAdminPort $SharePointCentralAdminPort -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\dsc-dc.0\ConfigDc" +ConfigDc -Admincreds $Admincreds -AdfsSvcCreds $AdfsSvcCreds -SqlSvcCreds $SqlSvcCreds -SPSetupCreds $SPSetupCreds -DomainFQDN $DomainFQDN -PrivateIP $PrivateIP -SPServerName $SPServerName -SharePointSitesAuthority $SharePointSitesAuthority -SharePointCentralAdminPort $SharePointCentralAdminPort -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath Set-DscLocalConfigurationManager -Path $outputPath Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureDCVM.zip b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-dc.zip similarity index 67% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureDCVM.zip rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-dc.zip index 86a8a5d73470..48e719fff08c 100644 Binary files a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureDCVM.zip and b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-dc.zip differ diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFELegacy.ps1 b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-frontend.ps1 similarity index 96% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFELegacy.ps1 rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-frontend.ps1 index 5aa2cbf30276..0ab7e39e405c 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFELegacy.ps1 +++ b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-frontend.ps1 @@ -1,4 +1,4 @@ -configuration ConfigureFEVM +configuration ConfigSpFrontend { param ( @@ -14,17 +14,19 @@ configuration ConfigureFEVM [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]$SPPassphraseCreds + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPPassphraseCreds, + [Parameter(Mandatory = $false)] [Boolean] $DefaultZoneMustBeHttps, #not used, present to allow parameter to be passed + [Parameter(Mandatory = $false)] [String] $ConfigurationLevel #not used, present to allow parameter to be passed ) - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 # Custom + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.1.0 - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.0 + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.1 Import-DscResource -ModuleName WebAdministrationDsc -ModuleVersion 4.2.1 - Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall - Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.1 + Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall and SPInstallPrereqs + Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.3 Import-DscResource -ModuleName CertificateDsc -ModuleVersion 6.0.0 - Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.1.0 # Custom workaround on SqlSecureConnection + Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.5.1 # Custom workaround on SqlSecureConnection Import-DscResource -ModuleName cChoco -ModuleVersion 2.6.0.0 # With custom changes to implement retry on package downloads # Init @@ -32,6 +34,8 @@ configuration ConfigureFEVM [String] $ComputerName = Get-Content env:computername [String] $DomainNetbiosName = (Get-NetBIOSName -DomainFQDN $DomainFQDN) + # Format username as user@contoso.local to workaround issue https://github.com/dsccommunity/ComputerManagementDsc/issues/413 + [System.Management.Automation.PSCredential] $DomainAdminCredsToJoinDomain = New-Object System.Management.Automation.PSCredential ("$($DomainAdminCreds.UserName)@$($DomainFQDN)", $DomainAdminCreds.Password) # 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) @@ -326,7 +330,7 @@ configuration ConfigureFEVM { Name = $ComputerName DomainName = $DomainFQDN - Credential = $DomainAdminCredsQualified + Credential = $DomainAdminCredsToJoinDomain DependsOn = "[Script]WaitForADFSFarmReady" } @@ -414,7 +418,7 @@ configuration ConfigureFEVM Write-Verbose -Verbose -Message "Trying to connect to $uri..." # -UseDefaultCredentials: Does NTLM authN # -UseBasicParsing: Avoid exception because IE was not first launched yet - $Response = Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 10 -ErrorAction Stop -UseBasicParsing + $Response = Invoke-WebRequest -UseBasicParsing -Uri $uri -UseDefaultCredentials -TimeoutSec 10 -ErrorAction Stop # When it will be actually ready, site will respond 401/302/200, and $Response.StatusCode will be 200 $currentStatusCode = $Response.StatusCode } @@ -628,7 +632,7 @@ configuration ConfigureFEVM # -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 - Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -UseBasicParsing -ErrorAction SilentlyContinue + Invoke-WebRequest -UseBasicParsing -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -ErrorAction SilentlyContinue Write-Verbose -Verbose -Message "Connected successfully to $uri" } catch [System.Exception] { @@ -773,7 +777,7 @@ function Get-SPDSCInstalledProductVersion } <# -help ConfigureFEVM +help ConfigSpFrontend $password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force $DomainAdminCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password @@ -789,8 +793,8 @@ $SharePointVersion = "2019" $EnableAnalysis = $false $SharePointBits = @() -$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureFELegacy.0\ConfigureFEVM" -ConfigureFEVM -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPPassphraseCreds $SPPassphraseCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -EnableAnalysis $EnableAnalysis -SharePointBits $SharePointBits -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureFELegacy.0\ConfigSpFrontend" +ConfigSpFrontend -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPPassphraseCreds $SPPassphraseCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -EnableAnalysis $EnableAnalysis -SharePointBits $SharePointBits -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath Set-DscLocalConfigurationManager -Path $outputPath Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFELegacy.zip b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-frontend.zip similarity index 67% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFELegacy.zip rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-frontend.zip index 3d9716a334d8..a22d53ce0f67 100644 Binary files a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFELegacy.zip and b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-frontend.zip differ diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPLegacy.ps1 b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-main.ps1 similarity index 97% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPLegacy.ps1 rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-main.ps1 index 38e4509e8639..9c13893bef12 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPLegacy.ps1 +++ b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-main.ps1 @@ -1,4 +1,4 @@ -configuration ConfigureSPVM +configuration ConfigSpMain { param ( @@ -20,18 +20,20 @@ configuration ConfigureSPVM [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPADDirSyncCreds, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPPassphraseCreds, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSuperUserCreds, - [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSuperReaderCreds + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSuperReaderCreds, + [Parameter(Mandatory = $false)] [Boolean] $DefaultZoneMustBeHttps, #not used, present to allow parameter to be passed + [Parameter(Mandatory = $false)] [String] $ConfigurationLevel #not used, present to allow parameter to be passed ) - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 # Custom + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.1.0 - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.0 + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.1 Import-DscResource -ModuleName xCredSSP -ModuleVersion 1.4.0 Import-DscResource -ModuleName WebAdministrationDsc -ModuleVersion 4.2.1 - Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall - Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.1 + Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall and SPInstallPrereqs + Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.3 Import-DscResource -ModuleName CertificateDsc -ModuleVersion 6.0.0 - Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.1.0 # Custom workaround on SqlSecureConnection + Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.5.1 # Custom workaround on SqlSecureConnection Import-DscResource -ModuleName cChoco -ModuleVersion 2.6.0.0 # With custom changes to implement retry on package downloads Import-DscResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 9.2.1 @@ -42,6 +44,8 @@ configuration ConfigureSPVM [String] $DomainLDAPPath = "DC=$($DomainFQDN.Split(".")[0]),DC=$($DomainFQDN.Split(".")[1])" [String] $AdditionalUsersPath = "OU=AdditionalUsers,DC={0},DC={1}" -f $DomainFQDN.Split('.')[0], $DomainFQDN.Split('.')[1] + # Format username as user@contoso.local to workaround issue https://github.com/dsccommunity/ComputerManagementDsc/issues/413 + [System.Management.Automation.PSCredential] $DomainAdminCredsToJoinDomain = New-Object System.Management.Automation.PSCredential ("$($DomainAdminCreds.UserName)@$($DomainFQDN)", $DomainAdminCreds.Password) # 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) @@ -345,7 +349,7 @@ configuration ConfigureSPVM { Name = $ComputerName DomainName = $DomainFQDN - Credential = $DomainAdminCredsQualified + Credential = $DomainAdminCredsToJoinDomain DependsOn = "[Script]WaitForADFSFarmReady" } @@ -468,18 +472,6 @@ configuration ConfigureSPVM #********************************************************** # Provision required accounts for SharePoint #********************************************************** - ADUser CreateSPSetupAccount - { # Both SQL and SharePoint DSCs run this SPSetupAccount AD account creation - DomainName = $DomainFQDN - UserName = $SPSetupCreds.UserName - UserPrincipalName = "$($SPSetupCreds.UserName)@$DomainFQDN" - Password = $SPSetupCreds - PasswordNeverExpires = $true - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" - } - ADUser CreateSParmAccount { DomainName = $DomainFQDN @@ -499,7 +491,7 @@ configuration ConfigureSPVM MembersToInclude = @("$($SPSetupCredsQualified.UserName)") Credential = $DomainAdminCredsQualified PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[ADUser]CreateSPSetupAccount" + DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" } ADUser CreateSPSvcAccount @@ -1379,7 +1371,7 @@ configuration ConfigureSPVM # -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 - Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -UseBasicParsing -ErrorAction SilentlyContinue + Invoke-WebRequest -UseBasicParsing -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -ErrorAction SilentlyContinue Write-Verbose -Verbose -Message "Connected successfully to $uri" } catch [System.Exception] { @@ -1443,25 +1435,25 @@ configuration ConfigureSPVM } foreach ($accountName in $accounts) { - $profile = $null + $userProfile = $null try { - $profile = $upm.GetUserProfile($accountName) + $userProfile = $upm.GetUserProfile($accountName) Write-Verbose -Verbose -Message "Got existing user profile for '$accountName'" } catch { - $profile = $upm.CreateUserProfile($accountName); + $userProfile = $upm.CreateUserProfile($accountName); Write-Verbose -Verbose -Message "Successfully created user profile for '$accountName'" } - if ($null -eq $profile) { + if ($null -eq $userProfile) { Write-Verbose -Verbose -Message "Unable to get/create the profile for '$accountName', give up" continue } - if ($null -eq $profile.PersonalSite) { + if ($null -eq $userProfile.PersonalSite) { Write-Verbose -Verbose -Message "Adding creation of personal site for '$accountName' to the queue..." try { - $profile.CreatePersonalSiteEnque($false) + $userProfile.CreatePersonalSiteEnque($false) Write-Verbose -Verbose -Message "Successfully enqueued the creation of personal site for '$accountName'" } catch { @@ -1584,7 +1576,7 @@ function Get-LatestGitHubRelease # Force protocol TLS 1.2 in Invoke-WebRequest to fix TLS/SSL connection error with GitHub in Windows Server 2012 R2, as documented in https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-update-1802 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - $latestRelease = Invoke-WebRequest "https://api.github.com/repos/$Repo/releases/$ReleaseId" -Headers @{"Accept"="application/json"} -UseBasicParsing + $latestRelease = Invoke-WebRequest -UseBasicParsing "https://api.github.com/repos/$Repo/releases/$ReleaseId" -Headers @{"Accept"="application/json"} $json = $latestRelease.Content | ConvertFrom-Json $asset = $json.assets | Where-Object{$_.name -like $Artifact} $assetUrl = $asset.browser_download_url @@ -1623,7 +1615,7 @@ function Get-SPDSCInstalledProductVersion } <# -help ConfigureSPVM +help ConfigSpMain $password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force $DomainAdminCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password @@ -1645,8 +1637,8 @@ $SharePointSitesAuthority = "spsites" $SharePointCentralAdminPort = 5000 $EnableAnalysis = $true -$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureSPLegacy.0\ConfigureSPVM" -ConfigureSPVM -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPSvcCreds $SPSvcCreds -SPAppPoolCreds $SPAppPoolCreds -SPADDirSyncCreds $SPADDirSyncCreds -SPPassphraseCreds $SPPassphraseCreds -SPSuperUserCreds $SPSuperUserCreds -SPSuperReaderCreds $SPSuperReaderCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -SharePointSitesAuthority $SharePointSitesAuthority -SharePointCentralAdminPort $SharePointCentralAdminPort -EnableAnalysis $EnableAnalysis -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureSPLegacy.0\ConfigSpMain" +ConfigSpMain -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPSvcCreds $SPSvcCreds -SPAppPoolCreds $SPAppPoolCreds -SPADDirSyncCreds $SPADDirSyncCreds -SPPassphraseCreds $SPPassphraseCreds -SPSuperUserCreds $SPSuperUserCreds -SPSuperReaderCreds $SPSuperReaderCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -SharePointSitesAuthority $SharePointSitesAuthority -SharePointCentralAdminPort $SharePointCentralAdminPort -EnableAnalysis $EnableAnalysis -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath Set-DscLocalConfigurationManager -Path $outputPath Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPLegacy.zip b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-main.zip similarity index 69% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPLegacy.zip rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-main.zip index 2550e1ff07f8..67834a0de1b4 100644 Binary files a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPLegacy.zip and b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-splegacy-main.zip differ diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFESE.ps1 b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-frontend.ps1 similarity index 82% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFESE.ps1 rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-frontend.ps1 index 45ed016add28..819d45ab8cf2 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFESE.ps1 +++ b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-frontend.ps1 @@ -1,4 +1,4 @@ -configuration ConfigureFEVM +configuration ConfigSpFrontend { param ( @@ -7,52 +7,86 @@ configuration ConfigureFEVM [Parameter(Mandatory)] [String]$DCServerName, [Parameter(Mandatory)] [String]$SQLServerName, [Parameter(Mandatory)] [String]$SQLAlias, - [Parameter(Mandatory)] [String]$SharePointVersion, + [Parameter(Mandatory)] [SharePointBuild] $SharePointVersion, [Parameter(Mandatory)] [String]$SharePointSitesAuthority, [Parameter(Mandatory)] [Boolean]$EnableAnalysis, - [Parameter(Mandatory)] [System.Object[]] $SharePointBits, + [Parameter(Mandatory)] [SharePointBuildInfo[]] $SharePointBits, [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]$SPPassphraseCreds + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPPassphraseCreds, + [Parameter(Mandatory = $false)] [Boolean] $DefaultZoneMustBeHttps = $false, + [Parameter(Mandatory = $false)] [ConfigurationLevel] $ConfigurationLevel = [ConfigurationLevel]::Full ) - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 # Custom + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.1.0 - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.0 + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.1 Import-DscResource -ModuleName WebAdministrationDsc -ModuleVersion 4.2.1 - Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall - Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.1 + Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall and SPInstallPrereqs + Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.3 Import-DscResource -ModuleName CertificateDsc -ModuleVersion 6.0.0 - Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.1.0 # Custom workaround on SqlSecureConnection + Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.5.1 # Custom workaround on SqlSecureConnection Import-DscResource -ModuleName cChoco -ModuleVersion 2.6.0.0 # With custom changes to implement retry on package downloads Import-DscResource -ModuleName StorageDsc -ModuleVersion 6.0.1 Import-DscResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 9.2.1 # Init - [String] $InterfaceAlias = (Get-NetAdapter| Where-Object InterfaceDescription -Like "Microsoft Hyper-V Network Adapter*" | Select-Object -First 1).Name + [String] $InterfaceAlias = (Get-NetAdapter | Where-Object InterfaceDescription -Like "Microsoft Hyper-V Network Adapter*" | Select-Object -First 1).Name [String] $ComputerName = Get-Content env:computername [String] $DomainNetbiosName = (Get-NetBIOSName -DomainFQDN $DomainFQDN) + # Format username as user@contoso.local to workaround issue https://github.com/dsccommunity/ComputerManagementDsc/issues/413 + [System.Management.Automation.PSCredential] $DomainAdminCredsToJoinDomain = New-Object System.Management.Automation.PSCredential ("$($DomainAdminCreds.UserName)@$($DomainFQDN)", $DomainAdminCreds.Password) # 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] $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) + + #################### DUPLICATED #################### + # Provisioning options - set to $true to provision, $false to skip provisioning of the corresponding component. These are set based on the selected configuration level, but can be overridden by setting them directly. + [Boolean] $ProvisionStateServiceApplication = $false + [Boolean] $ProvisionTrustedAuthentication = $false + [Boolean] $ProvisionUserProfilesService = $false + [Boolean] $ProvisionAddins = $false + [Boolean] $ProvisionHnscSites = $false + [Boolean] $ProvisionExtendedZone = $false + if ($ConfigurationLevel -ge [ConfigurationLevel]::Minimum) {} + if ($ConfigurationLevel -ge [ConfigurationLevel]::Light) { + $ProvisionStateServiceApplication = $true + $ProvisionTrustedAuthentication = $true + } + if ($ConfigurationLevel -ge [ConfigurationLevel]::Medium) { + $ProvisionUserProfilesService = $true + $ProvisionExtendedZone = $true + } + if ($ConfigurationLevel -ge [ConfigurationLevel]::Full) { + $ProvisionAddins = $true + $ProvisionHnscSites = $true + } + + # Final value for $DefaultZoneMustBeHttps must be set before setting $WebApplicationUrl + if ($ProvisionTrustedAuthentication -and -not $ProvisionExtendedZone) { + $DefaultZoneMustBeHttps = $true + } + #################### DUPLICATED #################### # Setup settings [String] $SetupPath = "C:\DSC Data" [String] $DCSetupPath = "\\$DCServerName\C$\DSC Data" [String] $DscStatusFilePath = "$SetupPath\dsc-status-$ComputerName.log" - [String] $SharePointBuildLabel = $SharePointVersion.Split("-")[1] [String] $SharePointBitsPath = Join-Path -Path $SetupPath -ChildPath "Binaries" #[environment]::GetEnvironmentVariable("temp","machine") [String] $SharePointIsoFullPath = Join-Path -Path $SharePointBitsPath -ChildPath "OfficeServer.iso" [String] $SharePointIsoDriveLetter = "S" [String] $AdfsDnsEntryName = "adfs" + [String] $NonceCookieCertificateFileName = "SharePoint OIDC nonce cert.pfx" # SharePoint settings [String] $SPDBPrefix = "SPDSC_" [String] $MySiteHostAlias = "OhMy" [String] $HNSC1Alias = "HNSC1" + [String] $WebApplicationUrl = if ($DefaultZoneMustBeHttps) { "https://$SharePointSitesAuthority.$DomainFQDN" } else { "http://$SharePointSitesAuthority" } + [String] $FarmReadinessTestUrl = "$WebApplicationUrl/sites/team" Node localhost { @@ -256,9 +290,9 @@ configuration ConfigureFEVM xRemoteFile DownloadSharePoint { DestinationPath = $SharePointIsoFullPath - Uri = ($SharePointBits | Where-Object { $_.Label -eq "RTM" }).Packages[0].DownloadUrl - ChecksumType = ($SharePointBits | Where-Object { $_.Label -eq "RTM" }).Packages[0].ChecksumType - Checksum = ($SharePointBits | Where-Object { $_.Label -eq "RTM" }).Packages[0].Checksum + Uri = ($SharePointBits | Where-Object { $_.Label -eq [SharePointBuild]::SPRTM }).Packages[0].DownloadUrl + ChecksumType = ($SharePointBits | Where-Object { $_.Label -eq [SharePointBuild]::SPRTM }).Packages[0].ChecksumType + Checksum = ($SharePointBits | Where-Object { $_.Label -eq [SharePointBuild]::SPRTM }).Packages[0].Checksum MatchSource = $false } @@ -289,58 +323,64 @@ configuration ConfigureFEVM DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } - if ($SharePointBuildLabel -ne "RTM") { - foreach ($package in ($SharePointBits | Where-Object { $_.Label -eq $SharePointBuildLabel }).Packages) { + if ($SharePointVersion -ne [SharePointBuild]::SPRTM) { + foreach ($package in ($SharePointBits | Where-Object { $_.Label -eq $SharePointVersion }).Packages) { $packageUrl = [uri] $package.DownloadUrl $packageFilename = $packageUrl.Segments[$packageUrl.Segments.Count - 1] $packageFilePath = Join-Path -Path $SharePointBitsPath -ChildPath $packageFilename - xRemoteFile "DownloadSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" { + xRemoteFile "DownloadSharePointUpdate_$($SharePointVersion)_$packageFilename" { DestinationPath = $packageFilePath Uri = $packageUrl ChecksumType = if ($null -ne $package.ChecksumType) { $package.ChecksumType } else { "None" } Checksum = if ($null -ne $package.Checksum) { $package.Checksum } else { $null } MatchSource = $false - PsDscRunAsCredential = $DomainAdminCredsQualified; + DependsOn = "[SPInstall]InstallBinaries" } - Script "InstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" { + Script "InstallSharePointUpdate_$($SharePointVersion)_$packageFilename" { SetScript = { - $SharePointBuildLabel = $using:SharePointBuildLabel + $SharePointVersion = $using:SharePointVersion $packageFilePath = $using:packageFilePath $packageFile = Get-ChildItem -Path $packageFilePath $exitRebootCodes = @(3010, 17022) $needReboot = $false - Write-Verbose -Verbose -Message "Starting installation of SharePoint update '$SharePointBuildLabel', file '$($packageFile.Name)'..." + Write-Verbose -Verbose -Message "Starting installation of SharePoint update '$SharePointVersion', file '$($packageFile.Name)'..." Unblock-File -Path $packageFile -Confirm:$false $process = Start-Process $packageFile.FullName -ArgumentList '/passive /quiet /norestart' -PassThru -Wait if ($exitRebootCodes.Contains($process.ExitCode)) { $needReboot = $true } Write-Verbose -Verbose -Message "Finished installation of SharePoint update '$($packageFile.Name)'. Exit code: $($process.ExitCode); needReboot: $needReboot" - New-Item -Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointBuildLabel)_$($packageFile.Name)" -Force - Write-Verbose -Verbose -Message "Finished installation of SharePoint build '$SharePointBuildLabel'. needReboot: $needReboot" + New-Item -Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointVersion)_$($packageFile.Name)" -Force + Write-Verbose -Verbose -Message "Finished installation of SharePoint build '$SharePointVersion'. needReboot: $needReboot" if ($true -eq $needReboot) { $global:DSCMachineStatus = 1 } } TestScript = { - $SharePointBuildLabel = $using:SharePointBuildLabel + $SharePointVersion = $using:SharePointVersion $packageFilePath = $using:packageFilePath $packageFile = Get-ChildItem -Path $packageFilePath - return (Test-Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointBuildLabel)_$($packageFile.Name)") + return (Test-Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointVersion)_$($packageFile.Name)") } 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. DependsOn = "[SPInstall]InstallBinaries" - PsDscRunAsCredential = $DomainAdminCredsQualified; } - PendingReboot "RebootOnSignalFromInstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" { - Name = "RebootOnSignalFromInstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" + # SPProductUpdate "InstallSharePointUpdate_$($SharePointVersion)_$packageFilename" + # { + # SetupFile = $packageFilePath + # Ensure = "Present" + # DependsOn = "[SPInstall]InstallBinaries" + # } + + PendingReboot "RebootOnSignalFromInstallSharePointUpdate_$($SharePointVersion)_$packageFilename" { + Name = "RebootOnSignalFromInstallSharePointUpdate_$($SharePointVersion)_$packageFilename" SkipCcmClientSDK = $true - DependsOn = "[Script]InstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" + DependsOn = "[Script]InstallSharePointUpdate_$($SharePointVersion)_$packageFilename" } } } @@ -377,7 +417,8 @@ configuration ConfigureFEVM $shares = [WMICLASS]"WIN32_Share" if ($shares.Create($foldername, $sharename, 0).ReturnValue -ne 0) { Write-Verbose -Verbose -Message "Failed to create file share '$sharename' for folder '$foldername'" - } else { + } + else { Write-Verbose -Verbose -Message "Created file share '$sharename' for folder '$foldername'" } # } @@ -388,7 +429,8 @@ configuration ConfigureFEVM $shareName = "SPLOGS" if (!(Get-WmiObject Win32_Share -Filter "name='$sharename'")) { return $false - } else { + } + else { return $true } } @@ -454,7 +496,7 @@ configuration ConfigureFEVM Computer JoinDomain { Name = $ComputerName DomainName = $DomainFQDN - Credential = $DomainAdminCredsQualified + Credential = $DomainAdminCredsToJoinDomain DependsOn = "[Script]WaitForADFSFarmReady" } @@ -538,7 +580,7 @@ configuration ConfigureFEVM Script WaitForSPFarmReadyToJoin { SetScript = { - $uri = "http://$($using:SharePointSitesAuthority)/sites/team" + $uri = $using:FarmReadinessTestUrl $sleepTime = 30 $currentStatusCode = 0 $expectedStatusCode = 200 @@ -547,7 +589,7 @@ configuration ConfigureFEVM Write-Verbose -Verbose -Message "Trying to connect to $uri..." # -UseDefaultCredentials: Does NTLM authN # -UseBasicParsing: Avoid exception because IE was not first launched yet - $Response = Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 10 -ErrorAction Stop -UseBasicParsing + $Response = Invoke-WebRequest -UseBasicParsing -Uri $uri -UseDefaultCredentials -TimeoutSec 10 -ErrorAction Stop # When it will be actually ready, site will respond 401/302/200, and $Response.StatusCode will be 200 $currentStatusCode = $Response.StatusCode } @@ -582,7 +624,6 @@ configuration ConfigureFEVM GroupName = "Administrators" Ensure = "Present" MembersToInclude = @("$($SPSetupCredsQualified.UserName)") - Credential = $DomainAdminCredsQualified PsDscRunAsCredential = $DomainAdminCredsQualified DependsOn = "[Script]WaitForSPFarmReadyToJoin" } @@ -627,8 +668,8 @@ configuration ConfigureFEVM else { $computerNumber = 0 } - $sleepTimeInSeconds = $computerNumber * 90 # Add a delay of 90 secs between each server - Write-Verbose -Verbose -Message "Computer $computerName is going to wait for $sleepTimeInSeconds seconds before joining the SharePoint farm, to avoid multiple servers joining it at the same time" + $sleepTimeInSeconds = $computerNumber * 120 # Add a delay of 120 secs between each server + Write-Verbose -Verbose -Message "Current machine '$computerName' now waiting for $sleepTimeInSeconds seconds before joining the SharePoint farm, to avoid conflicts due to multiple servers joining at the same time" Start-Sleep -Seconds $sleepTimeInSeconds New-Item -Path HKLM:\SOFTWARE\DscScriptExecution\Flag_WaitToAvoidServersJoiningFarmSimultaneously -Force } @@ -700,7 +741,7 @@ configuration ConfigureFEVM # -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 - Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -UseBasicParsing -ErrorAction SilentlyContinue + Invoke-WebRequest -UseBasicParsing -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -ErrorAction SilentlyContinue Write-Verbose -Verbose -Message "Connected successfully to $uri" } catch [System.Exception] { @@ -712,9 +753,9 @@ configuration ConfigureFEVM } } [System.Management.Automation.Job[]] $jobs = @() - $spsite = "http://$($using:SharePointSitesAuthority)/" - Write-Verbose -Verbose -Message "Warming up '$spsite'..." - $jobs += Start-Job -ScriptBlock $jobBlock -ArgumentList @($spsite) + $uri = $using:WebApplicationUrl + Write-Verbose -Verbose -Message "Warming up '$uri'..." + $jobs += Start-Job -ScriptBlock $jobBlock -ArgumentList @($uri) # Must wait for the jobs to complete, otherwise they do not actually run Receive-Job -Job $jobs -AutoRemoveJob -Wait @@ -725,49 +766,51 @@ configuration ConfigureFEVM DependsOn = "[DnsRecordCname]UpdateDNSAliasSPSites" } - Script SetFarmPropertiesForOIDC { - SetScript = - { - # Import OIDC-specific cookie certificate and set required permissions - $spTrustedSitesName = $using:SharePointSitesAuthority - $DCSetupPath = Join-Path -Path $using:DCSetupPath -ChildPath "Certificates" + if ($ProvisionTrustedAuthentication) { + Script SetFarmPropertiesForOIDC { + SetScript = + { + # Import OIDC-specific cookie certificate and set required permissions + $DCSetupPath = Join-Path -Path $using:DCSetupPath -ChildPath "Certificates" - # Import OIDC-specific cookie certificate created in 1st SharePoint Server of the farm - $cookieCertificateFileName = "SharePoint OIDC nonce cert.pfx" - $cookieCertificateFilePath = Join-Path -Path $DCSetupPath -ChildPath $cookieCertificateFileName - $cert = Import-PfxCertificate -FilePath $cookieCertificateFilePath -CertStoreLocation Cert:\localMachine\My -Exportable - - # Grant the application pool access to the private key of the cookie certificate - $wa = Get-SPWebApplication "http://$spTrustedSitesName" - $apppoolUserName = $wa.ApplicationPool.Username - $rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert) - $fileName = $rsaCert.key.UniqueName - $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$fileName" - $permissions = Get-Acl -Path $path - $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($apppoolUserName, 'Read', 'None', 'None', 'Allow') - $permissions.AddAccessRule($access_rule) - Set-Acl -Path $path -AclObject $permissions - } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } - } - TestScript = - { - # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. - # Import-Module SharePointServer | Out-Null - # $f = Get-SPFarm - # if ($f.Farm.Properties.ContainsKey('SP-NonceCookieCertificateThumbprint') -eq $false) { - if ((Get-ChildItem -Path "cert:\LocalMachine\My\" | Where-Object { $_.Subject -eq "CN=SharePoint Cookie Cert" }) -eq $null) { - return $false + # Import OIDC-specific cookie certificate created in 1st SharePoint Server of the farm + $cookieCertificateFileName = $using:NonceCookieCertificateFileName + $cookieCertificateFilePath = Join-Path -Path $DCSetupPath -ChildPath $cookieCertificateFileName + $cert = Import-PfxCertificate -FilePath $cookieCertificateFilePath -CertStoreLocation Cert:\localMachine\My -Exportable + + # Grant the application pool access to the private key of the cookie certificate + $uri = $using:WebApplicationUrl + $wa = Get-SPWebApplication $uri + $apppoolUserName = $wa.ApplicationPool.Username + $rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert) + $fileName = $rsaCert.key.UniqueName + $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$fileName" + $permissions = Get-Acl -Path $path + $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($apppoolUserName, 'Read', 'None', 'None', 'Allow') + $permissions.AddAccessRule($access_rule) + Set-Acl -Path $path -AclObject $permissions } - else { - return $true + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } } + TestScript = + { + # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. + # Import-Module SharePointServer | Out-Null + # $f = Get-SPFarm + # if ($f.Farm.Properties.ContainsKey('SP-NonceCookieCertificateThumbprint') -eq $false) { + if ((Get-ChildItem -Path "cert:\LocalMachine\My\" | Where-Object { $_.Subject -eq "CN=SharePoint Cookie Cert" }) -eq $null) { + return $false + } + else { + return $true + } + } + DependsOn = "[SPFarm]JoinSPFarm" + PsDscRunAsCredential = $DomainAdminCredsQualified } - DependsOn = "[SPFarm]JoinSPFarm" - PsDscRunAsCredential = $DomainAdminCredsQualified } Script CreateShortcuts { @@ -853,6 +896,7 @@ configuration ConfigureFEVM } } +#################### DUPLICATED #################### function Get-NetBIOSName { [OutputType([string])] param( @@ -876,8 +920,40 @@ function Get-NetBIOSName { } } +enum ConfigurationLevel { + Minimum + Light + Medium + Full +} + +# enum values cannot start with a digit - https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_enum?view=powershell-5.1#syntax +enum SharePointBuild { + SPRTM + SP22H2 + SP23H1 + SP23H2 + SP24H1 + SP24H2 + SP25H1 + SP25H2 + SPLatest +} + +class SharePointBuildInfo { + [ValidateNotNullOrEmpty()][SharePointBuild] $Label + [SharePointPackageInfo[]] $Packages +} + +class SharePointPackageInfo { + [ValidateNotNullOrEmpty()][string] $DownloadUrl + [Parameter(Mandatory = $false)] [string] $ChecksumType + [Parameter(Mandatory = $false)] [string] $Checksum +} +#################### DUPLICATED #################### + <# -help ConfigureFEVM +help ConfigSpFrontend $password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force $DomainAdminCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password @@ -889,25 +965,27 @@ $DomainFQDN = "contoso.local" $DCServerName = "DC" $SQLServerName = "SQL" $SQLAlias = "SQLAlias" -$SharePointVersion = "Subscription-Latest" +$SharePointVersion = "SPRTM" $SharePointSitesAuthority = "spsites" $EnableAnalysis = $true +$DefaultZoneMustBeHttps = $false +$ConfigurationLevel = "Light" $SharePointBits = @( @{ - Label = "RTM"; + Label = "SPRTM"; Packages = @( @{ DownloadUrl = "https://go.microsoft.com/fwlink/?linkid=2171943"; ChecksumType = "SHA256"; Checksum = "C576B847C573234B68FC602A0318F5794D7A61D8149EB6AE537AF04470B7FC05" } ) }, @{ - Label = "22H2"; + Label = "SP22H2"; Packages = @( @{ DownloadUrl = "https://download.microsoft.com/download/8/d/f/8dfcb515-6e49-42e5-b20f-5ebdfd19d8e7/wssloc-subscription-kb5002270-fullfile-x64-glb.exe"; ChecksumType = "SHA256"; Checksum = "7E496530EB873146650A9E0653DE835CB2CAD9AF8D154CBD7387BB0F2297C9FC" }, @{ DownloadUrl = "https://download.microsoft.com/download/3/f/5/3f5b1ee0-3336-45d7-b2f4-1e6af977d574/sts-subscription-kb5002271-fullfile-x64-glb.exe"; ChecksumType = "SHA256"; Checksum = "247011443AC573D4F03B1622065A7350B8B3DAE04D6A5A6DC64C8270A3BE7636" } ) }, @{ - Label = "Latest"; + Label = "SPLatest"; Packages = @( @{ DownloadUrl = "https://download.microsoft.com/download/d/6/d/d6dcc9e7-744e-43e1-b4be-206a6acd4f88/sts-subscription-kb5002331-fullfile-x64-glb.exe" }, @{ DownloadUrl = "https://download.microsoft.com/download/d/3/5/d354b6e2-fa16-48e0-b3f8-423f7ca279a0/wssloc-subscription-kb5002326-fullfile-x64-glb.exe" } @@ -915,8 +993,8 @@ $SharePointBits = @( } ) -$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureFESE.0\ConfigureFEVM" -ConfigureFEVM -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPPassphraseCreds $SPPassphraseCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -SharePointSitesAuthority $SharePointSitesAuthority -EnableAnalysis $EnableAnalysis -SharePointBits $SharePointBits -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureFESE.0\ConfigSpFrontend" +ConfigSpFrontend -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPPassphraseCreds $SPPassphraseCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -SharePointSitesAuthority $SharePointSitesAuthority -EnableAnalysis $EnableAnalysis -DefaultZoneMustBeHttps $DefaultZoneMustBeHttps -ConfigurationLevel $ConfigurationLevel -SharePointBits $SharePointBits -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath Set-DscLocalConfigurationManager -Path $outputPath Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFESE.zip b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-frontend.zip similarity index 70% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFESE.zip rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-frontend.zip index 24b17d452291..f5720a3454cd 100644 Binary files a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureFESE.zip and b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-frontend.zip differ diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPSE.ps1 b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-main.ps1 similarity index 55% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPSE.ps1 rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-main.ps1 index 202626201246..9fe85149208d 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPSE.ps1 +++ b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-main.ps1 @@ -1,4 +1,4 @@ -configuration ConfigureSPVM +configuration ConfigSpMain { param ( @@ -7,11 +7,11 @@ configuration ConfigureSPVM [Parameter(Mandatory)] [String]$DCServerName, [Parameter(Mandatory)] [String]$SQLServerName, [Parameter(Mandatory)] [String]$SQLAlias, - [Parameter(Mandatory)] [String]$SharePointVersion, + [Parameter(Mandatory)] [SharePointBuild] $SharePointVersion, [Parameter(Mandatory)] [String]$SharePointSitesAuthority, [Parameter(Mandatory)] [String]$SharePointCentralAdminPort, [Parameter(Mandatory)] [Boolean]$EnableAnalysis, - [Parameter(Mandatory)] [System.Object[]] $SharePointBits, + [Parameter(Mandatory)] [SharePointBuildInfo[]] $SharePointBits, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$DomainAdminCreds, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSetupCreds, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPFarmCreds, @@ -20,18 +20,20 @@ configuration ConfigureSPVM [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPADDirSyncCreds, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPPassphraseCreds, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSuperUserCreds, - [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSuperReaderCreds + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSuperReaderCreds, + [Parameter(Mandatory = $false)] [Boolean] $DefaultZoneMustBeHttps = $false, + [Parameter(Mandatory = $false)] [ConfigurationLevel] $ConfigurationLevel = [ConfigurationLevel]::Full ) - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 # Custom + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.1.0 - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.0 + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.1 Import-DscResource -ModuleName xCredSSP -ModuleVersion 1.4.0 Import-DscResource -ModuleName WebAdministrationDsc -ModuleVersion 4.2.1 - Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall - Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.1 + Import-DscResource -ModuleName SharePointDsc -ModuleVersion 5.7.0 # Custom workaround on SPInstall and SPInstallPrereqs + Import-DscResource -ModuleName DnsServerDsc -ModuleVersion 3.0.3 Import-DscResource -ModuleName CertificateDsc -ModuleVersion 6.0.0 - Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.1.0 # Custom workaround on SqlSecureConnection + Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.5.1 # Custom workaround on SqlSecureConnection Import-DscResource -ModuleName cChoco -ModuleVersion 2.6.0.0 # With custom changes to implement retry on package downloads Import-DscResource -ModuleName StorageDsc -ModuleVersion 6.0.1 Import-DscResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 9.2.1 @@ -43,19 +45,48 @@ configuration ConfigureSPVM [String] $DomainLDAPPath = "DC=$($DomainFQDN.Split(".")[0]),DC=$($DomainFQDN.Split(".")[1])" [String] $AdditionalUsersPath = "OU=AdditionalUsers,DC={0},DC={1}" -f $DomainFQDN.Split('.')[0], $DomainFQDN.Split('.')[1] + # Format username as user@contoso.local to workaround issue https://github.com/dsccommunity/ComputerManagementDsc/issues/413 + [System.Management.Automation.PSCredential] $DomainAdminCredsToJoinDomain = New-Object System.Management.Automation.PSCredential ("$($DomainAdminCreds.UserName)@$($DomainFQDN)", $DomainAdminCreds.Password) # 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] $SPSvcCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($SPSvcCreds.UserName)", $SPSvcCreds.Password) - [System.Management.Automation.PSCredential] $SPAppPoolCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($SPAppPoolCreds.UserName)", $SPAppPoolCreds.Password) - [System.Management.Automation.PSCredential] $SPADDirSyncCredsQualified = New-Object System.Management.Automation.PSCredential ("$DomainNetbiosName\$($SPADDirSyncCreds.UserName)", $SPADDirSyncCreds.Password) + [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] $SPSvcCredsQualified = New-Object System.Management.Automation.PSCredential ("$($DomainNetbiosName)\$($SPSvcCreds.UserName)", $SPSvcCreds.Password) + [System.Management.Automation.PSCredential] $SPAppPoolCredsQualified = New-Object System.Management.Automation.PSCredential ("$($DomainNetbiosName)\$($SPAppPoolCreds.UserName)", $SPAppPoolCreds.Password) + [System.Management.Automation.PSCredential] $SPADDirSyncCredsQualified = New-Object System.Management.Automation.PSCredential ("$($DomainNetbiosName)\$($SPADDirSyncCreds.UserName)", $SPADDirSyncCreds.Password) + #################### DUPLICATED #################### + # Provisioning options - set to $true to provision, $false to skip provisioning of the corresponding component. These are set based on the selected configuration level, but can be overridden by setting them directly. + [Boolean] $ProvisionStateServiceApplication = $false + [Boolean] $ProvisionTrustedAuthentication = $false + [Boolean] $ProvisionUserProfilesService = $false + [Boolean] $ProvisionAddins = $false + [Boolean] $ProvisionHnscSites = $false + [Boolean] $ProvisionExtendedZone = $false + if ($ConfigurationLevel -ge [ConfigurationLevel]::Minimum) {} + if ($ConfigurationLevel -ge [ConfigurationLevel]::Light) { + $ProvisionStateServiceApplication = $true + $ProvisionTrustedAuthentication = $true + } + if ($ConfigurationLevel -ge [ConfigurationLevel]::Medium) { + $ProvisionUserProfilesService = $true + $ProvisionExtendedZone = $true + } + if ($ConfigurationLevel -ge [ConfigurationLevel]::Full) { + $ProvisionAddins = $true + $ProvisionHnscSites = $true + } + + # Final value for $DefaultZoneMustBeHttps must be set before setting $WebApplicationUrl + if ($ProvisionTrustedAuthentication -and -not $ProvisionExtendedZone) { + $DefaultZoneMustBeHttps = $true + } + #################### DUPLICATED #################### + # Setup settings [String] $SetupPath = "C:\DSC Data" [String] $DCSetupPath = "\\$DCServerName\C$\DSC Data" [String] $DscStatusFilePath = "$SetupPath\dsc-status-$ComputerName.log" - [String] $SharePointBuildLabel = $SharePointVersion.Split("-")[1] [String] $SharePointBitsPath = Join-Path -Path $SetupPath -ChildPath "Binaries" #[environment]::GetEnvironmentVariable("temp","machine") [String] $SharePointIsoFullPath = Join-Path -Path $SharePointBitsPath -ChildPath "OfficeServer.iso" [String] $SharePointIsoDriveLetter = "S" @@ -78,7 +109,8 @@ configuration ConfigureSPVM [String] $TrustedIdChar = "e" [String] $SPTeamSiteTemplate = "STS#3" [String] $AdfsOidcIdentifier = "fae5bd07-be63-4a64-a28c-7931a4ebf62b" - + [String] $WebApplicationUrl = if ($DefaultZoneMustBeHttps) { "https://$SharePointSitesAuthority.$DomainFQDN" } else { "http://$SharePointSitesAuthority" } + Node localhost { LocalConfigurationManager { @@ -93,7 +125,7 @@ configuration ConfigureSPVM 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 + "$(Get-Date -Format u)`t$($using:ComputerName)`tDSC Configuration starting. DefaultZoneMustBeHttps: $($using:DefaultZoneMustBeHttps); Provisioning options: ProvisionStateServiceApplication: $($using:ProvisionStateServiceApplication), ProvisionTrustedAuthentication: $($using:ProvisionTrustedAuthentication), ProvisionUserProfilesService: $($using:ProvisionUserProfilesService), ProvisionAddins: $($using:ProvisionAddins), ProvisionHnscSites: $($using:ProvisionHnscSites), ProvisionExtendedZone: $($using:ProvisionExtendedZone)" | 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 @@ -102,9 +134,6 @@ configuration ConfigureSPVM #********************************************************** # 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 AddDnsTools { Name = "RSAT-DNS-Server"; Ensure = "Present"; } @@ -114,6 +143,10 @@ configuration ConfigureSPVM WindowsFeature AddADCSManagementTools { Name = "RSAT-ADCS-Mgmt"; Ensure = "Present"; } + # Installs WebAdministration Module: https://learn.microsoft.com/en-us/powershell/module/webadministration/?view=windowsserver2025-ps + # WindowsFeature AddWebScriptingTools { + # Name = "Web-Scripting-Tools"; Ensure = "Present"; + # } DnsServerAddress SetDNS { Address = $DNSServerIP; InterfaceAlias = $InterfaceAlias; AddressFamily = 'IPv4' } @@ -288,9 +321,9 @@ configuration ConfigureSPVM xRemoteFile DownloadSharePoint { DestinationPath = $SharePointIsoFullPath - Uri = ($SharePointBits | Where-Object { $_.Label -eq "RTM" }).Packages[0].DownloadUrl - ChecksumType = ($SharePointBits | Where-Object { $_.Label -eq "RTM" }).Packages[0].ChecksumType - Checksum = ($SharePointBits | Where-Object { $_.Label -eq "RTM" }).Packages[0].Checksum + Uri = ($SharePointBits | Where-Object { $_.Label -eq [SharePointBuild]::SPRTM }).Packages[0].DownloadUrl + ChecksumType = ($SharePointBits | Where-Object { $_.Label -eq [SharePointBuild]::SPRTM }).Packages[0].ChecksumType + Checksum = ($SharePointBits | Where-Object { $_.Label -eq [SharePointBuild]::SPRTM }).Packages[0].Checksum MatchSource = $false } @@ -321,92 +354,89 @@ configuration ConfigureSPVM DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } - if ($SharePointBuildLabel -ne "RTM") { - foreach ($package in ($SharePointBits | Where-Object { $_.Label -eq $SharePointBuildLabel }).Packages) { + if ($SharePointVersion -ne [SharePointBuild]::SPRTM) { + foreach ($package in ($SharePointBits | Where-Object { $_.Label -eq $SharePointVersion }).Packages) { $packageUrl = [uri] $package.DownloadUrl $packageFilename = $packageUrl.Segments[$packageUrl.Segments.Count - 1] $packageFilePath = Join-Path -Path $SharePointBitsPath -ChildPath $packageFilename - xRemoteFile "DownloadSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" { + xRemoteFile "DownloadSharePointUpdate_$($SharePointVersion)_$packageFilename" { DestinationPath = $packageFilePath Uri = $packageUrl ChecksumType = if ($null -ne $package.ChecksumType) { $package.ChecksumType } else { "None" } Checksum = if ($null -ne $package.Checksum) { $package.Checksum } else { $null } MatchSource = $false DependsOn = "[SPInstall]InstallBinaries" - PsDscRunAsCredential = $DomainAdminCredsQualified; } - Script "InstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" { + Script "InstallSharePointUpdate_$($SharePointVersion)_$packageFilename" { SetScript = { - $SharePointBuildLabel = $using:SharePointBuildLabel + $SharePointVersion = $using:SharePointVersion $packageFilePath = $using:packageFilePath $packageFile = Get-ChildItem -Path $packageFilePath $exitRebootCodes = @(3010, 17022) $needReboot = $false - Write-Verbose -Verbose -Message "Starting installation of SharePoint update '$SharePointBuildLabel', file '$($packageFile.Name)'..." + Write-Verbose -Verbose -Message "Starting installation of SharePoint update '$SharePointVersion', file '$($packageFile.Name)'..." Unblock-File -Path $packageFile -Confirm:$false $process = Start-Process $packageFile.FullName -ArgumentList '/passive /quiet /norestart' -PassThru -Wait if ($exitRebootCodes.Contains($process.ExitCode)) { $needReboot = $true } Write-Verbose -Verbose -Message "Finished installation of SharePoint update '$($packageFile.Name)'. Exit code: $($process.ExitCode); needReboot: $needReboot" - New-Item -Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointBuildLabel)_$($packageFile.Name)" -Force - Write-Verbose -Verbose -Message "Finished installation of SharePoint build '$SharePointBuildLabel'. needReboot: $needReboot" + New-Item -Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointVersion)_$($packageFile.Name)" -Force + Write-Verbose -Verbose -Message "Finished installation of SharePoint build '$SharePointVersion'. needReboot: $needReboot" if ($true -eq $needReboot) { $global:DSCMachineStatus = 1 } } TestScript = { - $SharePointBuildLabel = $using:SharePointBuildLabel + $SharePointVersion = $using:SharePointVersion $packageFilePath = $using:packageFilePath $packageFile = Get-ChildItem -Path $packageFilePath - return (Test-Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointBuildLabel)_$($packageFile.Name)") + return (Test-Path "HKLM:\SOFTWARE\DscScriptExecution\flag_spupdate_$($SharePointVersion)_$($packageFile.Name)") } 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. DependsOn = "[SPInstall]InstallBinaries" - PsDscRunAsCredential = $DomainAdminCredsQualified; } - # SPProductUpdate "InstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" + # SPProductUpdate "InstallSharePointUpdate_$($SharePointVersion)_$packageFilename" # { # SetupFile = $packageFilePath # Ensure = "Present" # DependsOn = "[SPInstall]InstallBinaries" - # # PsDscRunAsCredential = $SetupAccount # } - PendingReboot "RebootOnSignalFromInstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" { - Name = "RebootOnSignalFromInstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" + PendingReboot "RebootOnSignalFromInstallSharePointUpdate_$($SharePointVersion)_$packageFilename" { + Name = "RebootOnSignalFromInstallSharePointUpdate_$($SharePointVersion)_$packageFilename" SkipCcmClientSDK = $true - DependsOn = "[Script]InstallSharePointUpdate_$($SharePointBuildLabel)_$packageFilename" + DependsOn = "[Script]InstallSharePointUpdate_$($SharePointVersion)_$packageFilename" } } } # IIS cleanup cannot be executed earlier in SharePoint SE: It uses a base image of Windows Server without IIS (installed by SPInstallPrereqs) WebAppPool RemoveDotNet2Pool { - Name = ".NET v2.0"; Ensure = "Absent"; + Name = ".NET v2.0"; Ensure = "Absent"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } WebAppPool RemoveDotNet2ClassicPool { - Name = ".NET v2.0 Classic"; Ensure = "Absent"; + Name = ".NET v2.0 Classic"; Ensure = "Absent"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } WebAppPool RemoveDotNet45Pool { - Name = ".NET v4.5"; Ensure = "Absent"; + Name = ".NET v4.5"; Ensure = "Absent"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } WebAppPool RemoveDotNet45ClassicPool { - Name = ".NET v4.5 Classic"; Ensure = "Absent"; + Name = ".NET v4.5 Classic"; Ensure = "Absent"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } WebAppPool RemoveClassicDotNetPool { - Name = "Classic .NET AppPool"; Ensure = "Absent"; + Name = "Classic .NET AppPool"; Ensure = "Absent"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } WebAppPool RemoveDefaultAppPool { - Name = "DefaultAppPool"; Ensure = "Absent"; + Name = "DefaultAppPool"; Ensure = "Absent"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } WebSite RemoveDefaultWebSite { - Name = "Default Web Site"; Ensure = "Absent"; PhysicalPath = "C:\inetpub\wwwroot"; + Name = "Default Web Site"; Ensure = "Absent"; PhysicalPath = "C:\inetpub\wwwroot"; DependsOn = "[SPInstallPrereqs]InstallPrerequisites" } Script CreateSPLOGSFileShare { @@ -435,6 +465,7 @@ configuration ConfigureSPVM return $true } } + DependsOn = "[SPInstall]InstallBinaries" } #********************************************************** @@ -473,32 +504,13 @@ configuration ConfigureSPVM } 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" + DependsOn = "[DnsServerAddress]SetDNS", "[SPInstall]InstallBinaries" } - # # If WaitForADDomain does not find the domain whtin "WaitTimeout" secs, it will signal a restart to DSC engine "RestartCount" times - # WaitForADDomain WaitForDCReady - # { - # DomainName = $DomainFQDN - # WaitTimeout = 1800 - # RestartCount = 2 - # WaitForValidCredentials = $True - # Credential = $DomainAdminCredsQualified - # DependsOn = "[Script]WaitForADFSFarmReady" - # } - - # # 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 + Credential = $DomainAdminCredsToJoinDomain DependsOn = "[Script]WaitForADFSFarmReady" } @@ -527,6 +539,10 @@ configuration ConfigureSPVM # PsDscRunAsCredential = $DomainAdminCredsQualified; Ensure = "Present" # } + WindowsFeature AddADTools { + Name = "RSAT-AD-Tools"; IncludeAllSubFeature = $true; Ensure = "Present"; + } + # This script is still needed Script CreateWSManSPNsIfNeeded { SetScript = @@ -630,18 +646,6 @@ configuration ConfigureSPVM #********************************************************** # Provision required accounts for SharePoint #********************************************************** - ADUser CreateSPSetupAccount { - # Both SQL and SharePoint DSCs run this SPSetupAccount AD account creation - DomainName = $DomainFQDN - UserName = $SPSetupCreds.UserName - UserPrincipalName = "$($SPSetupCreds.UserName)@$DomainFQDN" - Password = $SPSetupCreds - PasswordNeverExpires = $true - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" - } - ADUser CreateSParmAccount { DomainName = $DomainFQDN UserName = $SPFarmCreds.UserName @@ -657,9 +661,8 @@ configuration ConfigureSPVM GroupName = "Administrators" Ensure = "Present" MembersToInclude = @("$($SPSetupCredsQualified.UserName)") - Credential = $DomainAdminCredsQualified PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[ADUser]CreateSPSetupAccount" + DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" } ADUser CreateSPSvcAccount { @@ -744,7 +747,7 @@ configuration ConfigureSPVM Name = "ulsviewer" Ensure = "Present" PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[cChocoInstaller]InstallChoco" + DependsOn = "[cChocoInstaller]InstallChoco", "[PendingReboot]RebootOnSignalFromJoinDomain" } Script DscStatus_WaitForSQL { @@ -780,7 +783,7 @@ configuration ConfigureSPVM 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" + DependsOn = "[SqlAlias]AddSqlAlias", "[SPInstall]InstallBinaries", "[PendingReboot]RebootOnSignalFromJoinDomain" } #********************************************************** @@ -843,39 +846,41 @@ configuration ConfigureSPVM DependsOn = "[File]CopyCertificatesFromDC" } - SPFarmSolution InstallLdapcpSolution { - LiteralPath = $LDAPCPFileFullPath - Name = "$LdapcpSolutionName.wsp" - Deployed = $true - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" - } - - Script InstallLdapcpFeatures { - SetScript = - { - $solutionId = $using:LdapcpSolutionId - Install-SPFeature -SolutionId $solutionId -AllExistingFeatures - } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } + if ($ProvisionTrustedAuthentication) { + SPFarmSolution InstallLdapcpSolution { + LiteralPath = $LDAPCPFileFullPath + Name = "$LdapcpSolutionName.wsp" + Deployed = $true + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" } - TestScript = - { - # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. - $claimsProviderName = $using:LdapcpSolutionName - if ($null -eq (Get-SPClaimProvider -Identity $claimsProviderName -ErrorAction SilentlyContinue)) { - return $false + + Script InstallLdapcpFeatures { + SetScript = + { + $solutionId = $using:LdapcpSolutionId + Install-SPFeature -SolutionId $solutionId -AllExistingFeatures } - else { - return $true + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } } + TestScript = + { + # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. + $claimsProviderName = $using:LdapcpSolutionName + if ($null -eq (Get-SPClaimProvider -Identity $claimsProviderName -ErrorAction SilentlyContinue)) { + return $false + } + else { + return $true + } + } + DependsOn = "[SPFarmSolution]InstallLdapcpSolution" + PsDscRunAsCredential = $DomainAdminCredsQualified } - DependsOn = "[SPFarmSolution]InstallLdapcpSolution" - PsDscRunAsCredential = $DomainAdminCredsQualified } SPManagedAccount CreateSPSvcManagedAccount { @@ -892,11 +897,13 @@ configuration ConfigureSPVM DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" } - SPStateServiceApp StateServiceApp { - Name = "State Service Application" - DatabaseName = $SPDBPrefix + "StateService" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + if ($ProvisionStateServiceApplication) { + SPStateServiceApp StateServiceApp { + Name = "State Service Application" + DatabaseName = $SPDBPrefix + "StateService" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + } } # Distributed Cache is now enabled directly by the SPFarm resource @@ -915,25 +922,29 @@ configuration ConfigureSPVM # Service instances are started at the beginning of the deployment to give some time between this and creation of service applications # This makes deployment a lot more reliable and avoids errors related to concurrency update of persisted objects, or service instance not found... #********************************************************** - SPServiceInstance UPAServiceInstance { - Name = "User Profile Service" - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + if ($ProvisionUserProfilesService) { + SPServiceInstance UPAServiceInstance { + Name = "User Profile Service" + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + } } - SPServiceInstance StartSubscriptionSettingsServiceInstance { - Name = "Microsoft SharePoint Foundation Subscription Settings Service" - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" - } + if ($ProvisionAddins) { + SPServiceInstance StartSubscriptionSettingsServiceInstance { + Name = "Microsoft SharePoint Foundation Subscription Settings Service" + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + } - SPServiceInstance StartAppManagementServiceInstance { - Name = "App Management Service" - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + SPServiceInstance StartAppManagementServiceInstance { + Name = "App Management Service" + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + } } SPServiceAppPool MainServiceAppPool { @@ -944,13 +955,13 @@ configuration ConfigureSPVM } SPWebApplication CreateMainWebApp { - Name = "SharePoint - 80" - ApplicationPool = "SharePoint - 80" + Name = "SharePoint - main" + ApplicationPool = "SharePoint - main" ApplicationPoolAccount = $SPAppPoolCredsQualified.UserName AllowAnonymous = $false - DatabaseName = $SPDBPrefix + "Content_80" - WebAppUrl = "http://$SharePointSitesAuthority/" - Port = 80 + DatabaseName = $SPDBPrefix + "Content_main" + WebAppUrl = $WebApplicationUrl + Port = if ($DefaultZoneMustBeHttps) { 443 } else { 80 } Ensure = "Present" PsDscRunAsCredential = $DomainAdminCredsQualified DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" @@ -1017,168 +1028,268 @@ configuration ConfigureSPVM # DependsOn = "[Script]ForceRebootBeforeCreatingSPTrust" # } - $apppoolUserName = $SPAppPoolCredsQualified.UserName - $domainAdminUserName = $DomainAdminCredsQualified.UserName - Script SetFarmPropertiesForOIDC { - SetScript = - { - $apppoolUserName = $using:apppoolUserName - $domainAdminUserName = $using:domainAdminUserName - $setupPath = Join-Path -Path $using:SetupPath -ChildPath "Certificates" - if (!(Test-Path $setupPath -PathType Container)) { - New-Item -ItemType Directory -Force -Path $setupPath + if ($ProvisionTrustedAuthentication) { + $apppoolUserName = $SPAppPoolCredsQualified.UserName + $domainAdminUserName = $DomainAdminCredsQualified.UserName + Script SetFarmPropertiesForOIDC { + SetScript = + { + $apppoolUserName = $using:apppoolUserName + $domainAdminUserName = $using:domainAdminUserName + $setupPath = Join-Path -Path $using:SetupPath -ChildPath "Certificates" + if (!(Test-Path $setupPath -PathType Container)) { + New-Item -ItemType Directory -Force -Path $setupPath + } + $DCSetupPath = Join-Path -Path $using:DCSetupPath -ChildPath "Certificates" + if (!(Test-Path $DCSetupPath -PathType Container)) { + New-Item -ItemType Directory -Force -Path $DCSetupPath + } + + # Setup farm properties to work with OIDC + # Create a self-signed certificate in 1st SharePoint Server of the farm + $cookieCertificateName = "SharePoint OIDC nonce cert" + $cookieCertificateFilePath = Join-Path -Path $setupPath -ChildPath "$cookieCertificateName" + $cert = New-SelfSignedCertificate -KeyUsage None -KeyUsageProperty None -CertStoreLocation Cert:\LocalMachine\My -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' -Subject "CN=$cookieCertificateName" + Export-Certificate -Cert $cert -FilePath "$cookieCertificateFilePath.cer" + Export-PfxCertificate -Cert $cert -FilePath "$cookieCertificateFilePath.pfx" -ProtectTo "$domainAdminUserName" + Export-PfxCertificate -Cert $cert -FilePath "$DCSetupPath\$cookieCertificateName.pfx" -ProtectTo "$domainAdminUserName" + + # Grant access to the certificate private key. + $rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert) + $fileName = $rsaCert.key.UniqueName + $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$fileName" + $permissions = Get-Acl -Path $path + $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($apppoolUserName, 'Read', 'None', 'None', 'Allow') + $permissions.AddAccessRule($access_rule) + Set-Acl -Path $path -AclObject $permissions + + # Set farm properties + $f = Get-SPFarm + $f.Farm.Properties['SP-NonceCookieCertificateThumbprint'] = $cert.Thumbprint + $f.Farm.Properties['SP-NonceCookieHMACSecretKey'] = "randomString$domainAdminUserName" + $f.Farm.Update() } - $DCSetupPath = Join-Path -Path $using:DCSetupPath -ChildPath "Certificates" - if (!(Test-Path $DCSetupPath -PathType Container)) { - New-Item -ItemType Directory -Force -Path $DCSetupPath + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } } - - # Setup farm properties to work with OIDC - # Create a self-signed certificate in 1st SharePoint Server of the farm - $cookieCertificateName = "SharePoint OIDC nonce cert" - $cookieCertificateFilePath = Join-Path -Path $setupPath -ChildPath "$cookieCertificateName" - $cert = New-SelfSignedCertificate -KeyUsage None -KeyUsageProperty None -CertStoreLocation Cert:\LocalMachine\My -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' -Subject "CN=$cookieCertificateName" - Export-Certificate -Cert $cert -FilePath "$cookieCertificateFilePath.cer" - Export-PfxCertificate -Cert $cert -FilePath "$cookieCertificateFilePath.pfx" -ProtectTo "$domainAdminUserName" - Export-PfxCertificate -Cert $cert -FilePath "$DCSetupPath\$cookieCertificateName.pfx" -ProtectTo "$domainAdminUserName" - - # Grant access to the certificate private key. - $rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert) - $fileName = $rsaCert.key.UniqueName - $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$fileName" - $permissions = Get-Acl -Path $path - $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($apppoolUserName, 'Read', 'None', 'None', 'Allow') - $permissions.AddAccessRule($access_rule) - Set-Acl -Path $path -AclObject $permissions - - # Set farm properties - $f = Get-SPFarm - $f.Farm.Properties['SP-NonceCookieCertificateThumbprint'] = $cert.Thumbprint - $f.Farm.Properties['SP-NonceCookieHMACSecretKey'] = "randomString$domainAdminUserName" - $f.Farm.Update() + TestScript = + { + # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. + # Import-Module SharePointServer | Out-Null + # $f = Get-SPFarm + # if ($f.Farm.Properties.ContainsKey('SP-NonceCookieCertificateThumbprint') -eq $false) { + if ((Get-ChildItem -Path "cert:\LocalMachine\My\" | Where-Object { $_.Subject -eq "CN=SharePoint Cookie Cert" }) -eq $null) { + return $false + } + else { + return $true + } + } + DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" + PsDscRunAsCredential = $DomainAdminCredsQualified } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } + + SPTrustedIdentityTokenIssuer CreateSPTrust { + Name = $DomainFQDN + Description = "Federation with $DomainFQDN" + DefaultClientIdentifier = $AdfsOidcIdentifier + MetadataEndPoint = "https://adfs.$DomainFQDN/adfs/.well-known/openid-configuration" + # RegisteredIssuerName = "https://adfs.$DomainFQDN/adfs" + # AuthorizationEndPointUri = "https://adfs.$DomainFQDN/adfs/oauth2/authorize" + # SignOutUrl = "https://adfs.$DomainFQDN/adfs/oauth2/logout" + # SigningCertificateFilePath = "$SetupPath\Certificates\ADFS Signing.cer" + + 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 = "group" + IncomingClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + MSFT_SPClaimTypeMapping { + Name = "groupsid" + IncomingClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid" + } + ) + ClaimProviderName = $LdapcpSolutionName + Ensure = "Present" + DependsOn = "[Script]SetFarmPropertiesForOIDC", "[Script]InstallLdapcpFeatures" + PsDscRunAsCredential = $DomainAdminCredsQualified } - TestScript = - { - # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. - # Import-Module SharePointServer | Out-Null - # $f = Get-SPFarm - # if ($f.Farm.Properties.ContainsKey('SP-NonceCookieCertificateThumbprint') -eq $false) { - if ((Get-ChildItem -Path "cert:\LocalMachine\My\" | Where-Object { $_.Subject -eq "CN=SharePoint Cookie Cert" }) -eq $null) { - return $false - } - else { - return $true - } + } + + + if ($ProvisionExtendedZone) { + # ExtendMainWebApp might fail with error: "The web.config could not be saved on this IIS Web Site: C:\\inetpub\\wwwroot\\wss\\VirtualDirectories\\80\\web.config.\r\nThe process cannot access the file 'C:\\inetpub\\wwwroot\\wss\\VirtualDirectories\\80\\web.config' because it is being used by another process." + # So I added resources between it and CreateMainWebApp to avoid it + SPWebApplicationExtension ExtendMainWebApp { + WebAppUrl = $WebApplicationUrl + Zone = "Intranet" + Name = if ($DefaultZoneMustBeHttps) { "SharePoint - main - Intranet HTTP" } else { "SharePoint - main - Intranet HTTPS" } + Url = if ($DefaultZoneMustBeHttps) { "http://$SharePointSitesAuthority/" } else { "https://$SharePointSitesAuthority.$DomainFQDN/" } + Port = if ($DefaultZoneMustBeHttps) { 80 } else { 443 } + AllowAnonymous = $false + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPWebApplication]CreateMainWebApp" } - DependsOn = "[Script]RestartSPTimerAfterCreateSPFarm" - PsDscRunAsCredential = $DomainAdminCredsQualified } - SPTrustedIdentityTokenIssuer CreateSPTrust { - Name = $DomainFQDN - Description = "Federation with $DomainFQDN" - DefaultClientIdentifier = $AdfsOidcIdentifier - MetadataEndPoint = "https://adfs.$DomainFQDN/adfs/.well-known/openid-configuration" - # RegisteredIssuerName = "https://adfs.$DomainFQDN/adfs" - # AuthorizationEndPointUri = "https://adfs.$DomainFQDN/adfs/oauth2/authorize" - # SignOutUrl = "https://adfs.$DomainFQDN/adfs/oauth2/logout" - # SigningCertificateFilePath = "$SetupPath\Certificates\ADFS Signing.cer" - - 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" + if ($ProvisionTrustedAuthentication) { + Script ConfigureLDAPCP { + SetScript = + { + try { + Add-Type -AssemblyName "Yvand.LDAPCPSE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=80be731bc1a1a740" + [Yvand.LdapClaimsProvider.LDAPCPSE]::CreateConfiguration() + } + catch { + Write-Verbose -Verbose -Message "Could not create LDAPCP configuration: $_" + } } - MSFT_SPClaimTypeMapping { - Name = "group" - IncomingClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } } - MSFT_SPClaimTypeMapping { - Name = "groupsid" - IncomingClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid" + TestScript = + { + # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. + try { + Add-Type -AssemblyName "Yvand.LDAPCPSE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=80be731bc1a1a740" + $config = [Yvand.LdapClaimsProvider.LDAPCPSE]::GetConfiguration() + if ($config -eq $null) { + return $false + } + else { + return $true + } + } + catch { + Write-Verbose -Verbose -Message "Could not test if LDAPCP configuration exists: $_" + return $true # Skip set if test fails + } } - ) - ClaimProviderName = $LdapcpSolutionName - Ensure = "Present" - DependsOn = "[Script]SetFarmPropertiesForOIDC", "[Script]InstallLdapcpFeatures" - PsDscRunAsCredential = $DomainAdminCredsQualified - } - - - # ExtendMainWebApp might fail with error: "The web.config could not be saved on this IIS Web Site: C:\\inetpub\\wwwroot\\wss\\VirtualDirectories\\80\\web.config.\r\nThe process cannot access the file 'C:\\inetpub\\wwwroot\\wss\\VirtualDirectories\\80\\web.config' because it is being used by another process." - # So I added resources between it and CreateMainWebApp to avoid it - SPWebApplicationExtension ExtendMainWebApp { - WebAppUrl = "http://$SharePointSitesAuthority/" - Name = "SharePoint - 443" - AllowAnonymous = $false - Url = "https://$SharePointSitesAuthority.$DomainFQDN" - Zone = "Intranet" - Port = 443 - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPWebApplication]CreateMainWebApp" + DependsOn = "[SPTrustedIdentityTokenIssuer]CreateSPTrust" + PsDscRunAsCredential = $DomainAdminCredsQualified + } } - Script ConfigureLDAPCP { - SetScript = - { - try { - Add-Type -AssemblyName "Yvand.LDAPCPSE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=80be731bc1a1a740" - [Yvand.LdapClaimsProvider.LDAPCPSE]::CreateConfiguration() + [System.Array]$configureMainWebAppAuthenticationDependsOn = if ($ProvisionExtendedZone) { @( "[SPWebApplicationExtension]ExtendMainWebApp" ) } else { @() } + if ($ProvisionTrustedAuthentication) { $configureMainWebAppAuthenticationDependsOn += "[SPTrustedIdentityTokenIssuer]CreateSPTrust" } + + # if ($ProvisionExtendedZone) { + # $IntranetArgs = + # if ($DefaultZoneMustBeHttps -or $false -eq $ProvisionTrustedAuthentication) { + # @( + # MSFT_SPWebAppAuthenticationMode { + # AuthenticationMethod = "WindowsAuthentication" + # WindowsAuthMethod = "NTLM" + # } + # ) + # } + # } + + # SPWebAppAuthentication ConfigureMainWebAppAuthentication { + # WebAppUrl = $WebApplicationUrl + # Default = if ($DefaultZoneMustBeHttps) { + # if ($ProvisionTrustedAuthentication) { + # @( + # MSFT_SPWebAppAuthenticationMode { + # AuthenticationMethod = "Federated" + # AuthenticationProvider = $DomainFQDN + # } + # MSFT_SPWebAppAuthenticationMode { + # AuthenticationMethod = "WindowsAuthentication" + # WindowsAuthMethod = "NTLM" + # } + # ) + # } + # else { + # @( + # MSFT_SPWebAppAuthenticationMode { + # AuthenticationMethod = "WindowsAuthentication" + # WindowsAuthMethod = "NTLM" + # } + # ) + # } + # } + # else { + # @( + # MSFT_SPWebAppAuthenticationMode { + # AuthenticationMethod = "WindowsAuthentication" + # WindowsAuthMethod = "NTLM" + # } + # ) + # } + # Intranet = $IntranetArgs + + + # PsDscRunAsCredential = $DomainAdminCredsQualified + # DependsOn = $configureMainWebAppAuthenticationDependsOn + # } + + SPWebAppAuthentication ConfigureMainWebAppAuthentication { + WebAppUrl = $WebApplicationUrl + Default = if ($DefaultZoneMustBeHttps) { + if ($ProvisionTrustedAuthentication) { + @( + MSFT_SPWebAppAuthenticationMode { + AuthenticationMethod = "Federated" + AuthenticationProvider = $DomainFQDN + } + MSFT_SPWebAppAuthenticationMode { + AuthenticationMethod = "WindowsAuthentication" + WindowsAuthMethod = "NTLM" + } + ) } - catch { - Write-Verbose -Verbose -Message "Could not create LDAPCP configuration: $_" + else { + @( + MSFT_SPWebAppAuthenticationMode { + AuthenticationMethod = "WindowsAuthentication" + WindowsAuthMethod = "NTLM" + } + ) } } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } - } - TestScript = - { - # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. - try { - Add-Type -AssemblyName "Yvand.LDAPCPSE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=80be731bc1a1a740" - $config = [Yvand.LdapClaimsProvider.LDAPCPSE]::GetConfiguration() - if ($config -eq $null) { - return $false + else { + @( + MSFT_SPWebAppAuthenticationMode { + AuthenticationMethod = "WindowsAuthentication" + WindowsAuthMethod = "NTLM" } - else { - return $true - } - } - catch { - Write-Verbose -Verbose -Message "Could not test if LDAPCP configuration exists: $_" - return $true # Skip set if test fails - } + ) } - DependsOn = "[SPTrustedIdentityTokenIssuer]CreateSPTrust" - PsDscRunAsCredential = $DomainAdminCredsQualified - } - SPWebAppAuthentication ConfigureMainWebAppAuthentication { - WebAppUrl = "http://$SharePointSitesAuthority/" - Default = @( - MSFT_SPWebAppAuthenticationMode { - AuthenticationMethod = "WindowsAuthentication" - WindowsAuthMethod = "NTLM" + Intranet = if ($ProvisionExtendedZone) { + if ($DefaultZoneMustBeHttps -or $false -eq $ProvisionTrustedAuthentication) { + @( + MSFT_SPWebAppAuthenticationMode { + AuthenticationMethod = "WindowsAuthentication" + WindowsAuthMethod = "NTLM" + } + ) } - ) - Intranet = @( - MSFT_SPWebAppAuthenticationMode { - AuthenticationMethod = "Federated" - AuthenticationProvider = $DomainFQDN + else { + @( + MSFT_SPWebAppAuthenticationMode { + AuthenticationMethod = "Federated" + AuthenticationProvider = $DomainFQDN + } + ) } - ) + } + else { + @($null) # This forces PowerShell to render an empty array, instead of $null + } PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPWebApplicationExtension]ExtendMainWebApp", "[SPTrustedIdentityTokenIssuer]CreateSPTrust" + DependsOn = $configureMainWebAppAuthenticationDependsOn } # Use SharePoint SE to generate the CSR and give the private key so it can manage it @@ -1190,7 +1301,11 @@ configuration ConfigureSPVM $domainNetbiosName = $using:DomainNetbiosName $sharePointSitesAuthority = $using:SharePointSitesAuthority $appDomainIntranetFQDN = $using:AppDomainIntranetFQDN - $setupPath = Join-Path -Path $using:SetupPath -ChildPath "Certificates" + $setupPath = Join-Path -Path $using:SetupPath -ChildPath "Certificates" + $defaultZoneMustBeHttps = $using:DefaultZoneMustBeHttps + $provisionExtendedZone = $using:ProvisionExtendedZone + $webAppUrl = $using:WebApplicationUrl + if (!(Test-Path $setupPath -PathType Container)) { New-Item -ItemType Directory -Force -Path $setupPath } @@ -1217,10 +1332,14 @@ configuration ConfigureSPVM Write-Verbose -Verbose -Message "Adding certificate 'CN=$sharePointSitesAuthority.$domainFQDN' to SharePoint store EndEntity..." $spCert = Import-SPCertificate -Path "$setupPath\$sharePointSitesAuthority.cer" -Exportable -Store EndEntity - Write-Verbose -Verbose -Message "Extending web application to HTTPS zone using certificate 'CN=$sharePointSitesAuthority.$domainFQDN'..." - Set-SPWebApplication -Identity "http://$sharePointSitesAuthority" -Zone Intranet -Port 443 -Certificate $spCert ` - -SecureSocketsLayer:$true -AllowLegacyEncryption:$false -Url "https://$sharePointSitesAuthority.$domainFQDN" - + if ($provisionExtendedZone -or $defaultZoneMustBeHttps) { + $httpsUrl = "https://$sharePointSitesAuthority.$domainFQDN/" + $httpsZoneName = if ($defaultZoneMustBeHttps) { "Default" } else { "Intranet" } + Write-Verbose -Verbose -Message "Setting zone '$httpsZoneName' in web application '$webAppUrl' to HTTPS with certificate '$($spCert.Name)'..." + Set-SPWebApplication -Identity $webAppUrl -Zone $httpsZoneName -Port 443 -Certificate $spCert ` + -SecureSocketsLayer:$true -AllowLegacyEncryption:$false -Url $httpsUrl + } + Write-Verbose -Verbose -Message "Finished." } GetScript = { } @@ -1244,17 +1363,17 @@ configuration ConfigureSPVM } SPCacheAccounts SetCacheAccounts { - WebAppUrl = "http://$SharePointSitesAuthority/" - SuperUserAlias = "$DomainNetbiosName\$($SPSuperUserCreds.UserName)" - SuperReaderAlias = "$DomainNetbiosName\$($SPSuperReaderCreds.UserName)" + WebAppUrl = $WebApplicationUrl + SuperUserAlias = "$($DomainNetbiosName)\$($SPSuperUserCreds.UserName)" + SuperReaderAlias = "$($DomainNetbiosName)\$($SPSuperReaderCreds.UserName)" PsDscRunAsCredential = $DomainAdminCredsQualified DependsOn = "[SPWebApplication]CreateMainWebApp" } SPSite CreateRootSite { - Url = "http://$SharePointSitesAuthority/" + Url = $WebApplicationUrl OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" - SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" + SecondaryOwnerAlias = if ($ProvisionTrustedAuthentication) { "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" } else { "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" } Name = "root site" Template = $SPTeamSiteTemplate CreateDefaultGroups = $true @@ -1262,219 +1381,239 @@ configuration ConfigureSPVM DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" } - # Create this site early, otherwise [SPAppCatalog]SetAppCatalogUrl may throw error "Cannot find an SPSite object with Id or Url: http://SPSites/sites/AppCatalog" - SPSite CreateAppCatalog { - Url = "http://$SharePointSitesAuthority/sites/AppCatalog" - OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" - SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" - Name = "AppCatalog" - Template = "APPCATALOG#0" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" + if ($ProvisionAddins) { + # Create this site early, otherwise [SPAppCatalog]SetAppCatalogUrl may throw error "Cannot find an SPSite object with Id or Url: http://SPSites/sites/AppCatalog" + SPSite CreateAppCatalog { + Url = "$WebApplicationUrl/sites/AppCatalog" + OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" + SecondaryOwnerAlias = if ($ProvisionTrustedAuthentication) { "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" } else { "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" } + Name = "AppCatalog" + Template = "APPCATALOG#0" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" + } } #********************************************************** # Additional configuration #********************************************************** - SPSite CreateMySiteHost { - Url = "http://$MySiteHostAlias/" - HostHeaderWebApplication = "http://$SharePointSitesAuthority/" - OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" - SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" - Name = "MySite host" - Template = "SPSMSITEHOST#0" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" - } - - SPSiteUrl SetMySiteHostIntranetUrl { - Url = "http://$MySiteHostAlias/" - Intranet = "https://$MySiteHostAlias.$DomainFQDN" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPSite]CreateMySiteHost" - } + if ($ProvisionUserProfilesService) { + SPSite CreateMySiteHost { + Url = if ($DefaultZoneMustBeHttps) { "https://$MySiteHostAlias.$DomainFQDN/" } else { "http://$MySiteHostAlias/" } + HostHeaderWebApplication = $WebApplicationUrl + OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" + SecondaryOwnerAlias = if ($ProvisionTrustedAuthentication) { "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" } else { "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" } + Name = "MySite host" + Template = "SPSMSITEHOST#0" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" + } - SPManagedPath CreateMySiteManagedPath { - WebAppUrl = "http://$SharePointSitesAuthority/" - RelativeUrl = "personal" - Explicit = $false - HostHeader = $true - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPSite]CreateMySiteHost" - } + SPSiteUrl SetMySiteHostIntranetUrl { + Url = if ($DefaultZoneMustBeHttps) { "https://$MySiteHostAlias.$DomainFQDN/" } else { "http://$MySiteHostAlias/" } + Intranet = if ($ProvisionExtendedZone) { + if ($DefaultZoneMustBeHttps) { "http://$MySiteHostAlias/" } else { "https://$MySiteHostAlias.$DomainFQDN/" } + } + else { + [string]::Empty + } + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPSite]CreateMySiteHost" + } - SPUserProfileServiceApp CreateUserProfileServiceApp { - Name = $UpaServiceName - ApplicationPool = $ServiceAppPoolName - MySiteHostLocation = "http://$MySiteHostAlias/" - ProfileDBName = $SPDBPrefix + "UPA_Profiles" - SocialDBName = $SPDBPrefix + "UPA_Social" - SyncDBName = $SPDBPrefix + "UPA_Sync" - EnableNetBIOS = $false - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPServiceAppPool]MainServiceAppPool", "[SPServiceInstance]UPAServiceInstance", "[SPSite]CreateMySiteHost" + SPManagedPath CreateMySiteManagedPath { + WebAppUrl = $WebApplicationUrl + RelativeUrl = "personal" + Explicit = $false + HostHeader = $true + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPSite]CreateMySiteHost" + } + + SPUserProfileServiceApp CreateUserProfileServiceApp { + Name = $UpaServiceName + ApplicationPool = $ServiceAppPoolName + MySiteHostLocation = if ($DefaultZoneMustBeHttps) { "https://$MySiteHostAlias.$DomainFQDN/" } else { "http://$MySiteHostAlias/" } + ProfileDBName = $SPDBPrefix + "UPA_Profiles" + SocialDBName = $SPDBPrefix + "UPA_Social" + SyncDBName = $SPDBPrefix + "UPA_Sync" + EnableNetBIOS = $false + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPServiceAppPool]MainServiceAppPool", "[SPServiceInstance]UPAServiceInstance", "[SPSite]CreateMySiteHost" + } } - # Creating this site takes about 1 min but it is not so useful, skip it + # Creating this site takes about 1 min and it is not so useful, skip it # SPSite CreateDevSite # { - # Url = "http://$SharePointSitesAuthority/sites/dev" + # Url = "$WebApplicationUrl/sites/dev" # OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" - # SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" + # SecondaryOwnerAlias = if ($ProvisionTrustedAuthentication) { "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN"} else { "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" } # Name = "Developer site" # Template = "DEV#0" # PsDscRunAsCredential = $DomainAdminCredsQualified # DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" # } - SPSite CreateHNSC1 { - Url = "http://$HNSC1Alias/" - HostHeaderWebApplication = "http://$SharePointSitesAuthority/" - OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" - SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" - Name = "$HNSC1Alias site" - Template = $SPTeamSiteTemplate - CreateDefaultGroups = $true - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" - } - - SPSiteUrl SetHNSC1IntranetUrl { - Url = "http://$HNSC1Alias/" - Intranet = "https://$HNSC1Alias.$DomainFQDN" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPSite]CreateHNSC1" + if ($ProvisionHnscSites) { + SPSite CreateHNSC1 { + Url = if ($DefaultZoneMustBeHttps) { "https://$HNSC1Alias.$DomainFQDN/" } else { "http://$HNSC1Alias/" } + HostHeaderWebApplication = $WebApplicationUrl + OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" + SecondaryOwnerAlias = if ($ProvisionTrustedAuthentication) { "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" } else { "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" } + Name = "$HNSC1Alias site" + Template = $SPTeamSiteTemplate + CreateDefaultGroups = $true + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" + } + + SPSiteUrl SetHNSC1IntranetUrl { + Url = if ($DefaultZoneMustBeHttps) { "https://$HNSC1Alias.$DomainFQDN/" } else { "http://$HNSC1Alias/" } + Intranet = if ($DefaultZoneMustBeHttps) { "http://$HNSC1Alias/" } else { "https://$HNSC1Alias.$DomainFQDN/" } + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPSite]CreateHNSC1" + } } - SPSubscriptionSettingsServiceApp CreateSubscriptionServiceApp { - Name = "Subscription Settings Service Application" - ApplicationPool = $ServiceAppPoolName - DatabaseName = "$($SPDBPrefix)SubscriptionSettings" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPServiceAppPool]MainServiceAppPool", "[SPServiceInstance]StartSubscriptionSettingsServiceInstance" + if ($ProvisionAddins) { + SPSubscriptionSettingsServiceApp CreateSubscriptionServiceApp { + Name = "Subscription Settings Service Application" + ApplicationPool = $ServiceAppPoolName + DatabaseName = "$($SPDBPrefix)SubscriptionSettings" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPServiceAppPool]MainServiceAppPool", "[SPServiceInstance]StartSubscriptionSettingsServiceInstance" + } + + SPAppManagementServiceApp CreateAppManagementServiceApp { + Name = "App Management Service Application" + ApplicationPool = $ServiceAppPoolName + DatabaseName = "$($SPDBPrefix)AppManagement" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPServiceAppPool]MainServiceAppPool", "[SPServiceInstance]StartAppManagementServiceInstance" + } } - SPAppManagementServiceApp CreateAppManagementServiceApp { - Name = "App Management Service Application" - ApplicationPool = $ServiceAppPoolName - DatabaseName = "$($SPDBPrefix)AppManagement" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPServiceAppPool]MainServiceAppPool", "[SPServiceInstance]StartAppManagementServiceInstance" - } - - SPServiceAppSecurity SetUserProfileServiceSecurity { - ServiceAppName = $UpaServiceName - SecurityType = "SharingPermissions" - MembersToInclude = @( - # Grant spsvc full control to UPA to allow newsfeeds to work properly - MSFT_SPServiceAppSecurityEntry { - Username = $SPSvcCredsQualified.UserName - AccessLevels = @("Full Control") - }; - MSFT_SPServiceAppSecurityEntry { - Username = $DomainAdminCredsQualified.UserName - AccessLevels = @("Full Control") - } - ) - PsDscRunAsCredential = $DomainAdminCredsQualified - #DependsOn = "[Script]RefreshLocalConfigCache" - DependsOn = "[SPUserProfileServiceApp]CreateUserProfileServiceApp" + if ($ProvisionUserProfilesService) { + SPServiceAppSecurity SetUserProfileServiceSecurity { + ServiceAppName = $UpaServiceName + SecurityType = "SharingPermissions" + MembersToInclude = @( + # Grant spsvc full control to UPA to allow newsfeeds to work properly + MSFT_SPServiceAppSecurityEntry { + Username = $SPSvcCredsQualified.UserName + AccessLevels = @("Full Control") + }; + MSFT_SPServiceAppSecurityEntry { + Username = $DomainAdminCredsQualified.UserName + AccessLevels = @("Full Control") + } + ) + PsDscRunAsCredential = $DomainAdminCredsQualified + #DependsOn = "[Script]RefreshLocalConfigCache" + DependsOn = "[SPUserProfileServiceApp]CreateUserProfileServiceApp" + } } - # Configure the SPTrustedBackedByUPAClaimProvider as much as possible. The remaining steps are: - # - In User Profile Service: - # - Create a synchronization connection that uses the authentication type "Trusted Claims Provider Authentication" - # - Edit profile property "Claim User Identifier" to remove default mapping, and readd one that uses the LDAP attribute "userPrincipalName" - # - In the trust: Associate the claims provider: $trust = Get-SPTrustedIdentityTokenIssuer "contoso.local"; $trust.ClaimProviderName = "contoso.local"; $trust.Update(); - Script ConfigureUPAClaimProvider { - SetScript = - { - try { - $spTrustName = $using:DomainFQDN - $spSiteUrl = "http://$($using:SharePointSitesAuthority)/" - Write-Verbose -Verbose -Message "Start configuration for ConfigureUPAClaimProvider using spTrustName '$($spTrustName)' and spSiteUrl '$($spSiteUrl)'" - - # LanguageSynchronizationJob must be executed before updating profile properties, to ensure their property DisplayNameLocalized is set with a localized value - # LanguageSynchronizationJob basically populates SQL table [SPDSC_UPA_Profiles].[upa].[PropertyListLoc] - # If this job is not run, $property.CoreProperty.Commit() will throw: Exception calling "Commit" with "0" argument(s): "The display name must be specified in order to create a property." - $job = Get-SPTimerJob -Type "Microsoft.Office.Server.Administration.UserProfileApplication+LanguageSynchronizationJob" - $job.Execute() + if ($ProvisionTrustedAuthentication -and $ProvisionUserProfilesService) { + # Configure the SPTrustedBackedByUPAClaimProvider as much as possible. The remaining steps are: + # - In User Profile Service: + # - Create a synchronization connection that uses the authentication type "Trusted Claims Provider Authentication" + # - Edit profile property "Claim User Identifier" to remove default mapping, and readd one that uses the LDAP attribute "userPrincipalName" + # - In the trust: Associate the claims provider: $trust = Get-SPTrustedIdentityTokenIssuer "contoso.local"; $trust.ClaimProviderName = "contoso.local"; $trust.Update(); + Script ConfigureUPAClaimProvider { + SetScript = + { + try { + $spTrustName = $using:DomainFQDN + $defaultZoneMustBeHttps = $using:DefaultZoneMustBeHttps + $webAppUrl = $using:WebApplicationUrl + Write-Verbose -Verbose -Message "Start configuration for ConfigureUPAClaimProvider using spTrustName '$($spTrustName)' and spSiteUrl '$($spSiteUrl)'" + + # LanguageSynchronizationJob must be executed before updating profile properties, to ensure their property DisplayNameLocalized is set with a localized value + # LanguageSynchronizationJob basically populates SQL table [SPDSC_UPA_Profiles].[upa].[PropertyListLoc] + # If this job is not run, $property.CoreProperty.Commit() will throw: Exception calling "Commit" with "0" argument(s): "The display name must be specified in order to create a property." + $job = Get-SPTimerJob -Type "Microsoft.Office.Server.Administration.UserProfileApplication+LanguageSynchronizationJob" + $job.Execute() - # Gets the trust - $trust = Get-SPTrustedIdentityTokenIssuer -Identity $spTrustName -ErrorAction SilentlyContinue - if ($null -eq $trust) { - Write-Verbose -Verbose -Message "Could not get the trust $spTrustName, give up" - return; - } + # Gets the trust + $trust = Get-SPTrustedIdentityTokenIssuer -Identity $spTrustName -ErrorAction SilentlyContinue + if ($null -eq $trust) { + Write-Verbose -Verbose -Message "Could not get the trust $spTrustName, give up" + return; + } - # Creates the claims provider if it does not already exist - $claimsProvider = Get-SPClaimProvider -Identity $spTrustName -ErrorAction SilentlyContinue - if ($null -eq $claimsProvider) { - $claimsProviderName = "UPA Claim Provider" - $claimsProvider = New-SPClaimProvider -AssemblyName "Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, publicKeyToken=71e9bce111e9429c" -Default:$false ` - -DisplayName $claimsProviderName -Description $claimsProviderName -Type "Microsoft.SharePoint.Administration.Claims.SPTrustedBackedByUPAClaimProvider" ` - -TrustedTokenIssuer $trust - } + # Creates the claims provider if it does not already exist + $claimsProvider = Get-SPClaimProvider -Identity $spTrustName -ErrorAction SilentlyContinue + if ($null -eq $claimsProvider) { + $claimsProviderName = "UPA Claim Provider" + $claimsProvider = New-SPClaimProvider -AssemblyName "Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, publicKeyToken=71e9bce111e9429c" -Default:$false ` + -DisplayName $claimsProviderName -Description $claimsProviderName -Type "Microsoft.SharePoint.Administration.Claims.SPTrustedBackedByUPAClaimProvider" ` + -TrustedTokenIssuer $trust + } + + # Running this set below would set SPTrustedBackedByUPAClaimProvider as the active claims provider for this trust + # But it wouldn't work since properties "SPS-ClaimProviderID" and "SPS-ClaimProviderType" of trusted profiles are not set + # Set-SPTrustedIdentityTokenIssuer $trust -ClaimProvider $claimsProvider -IsOpenIDConnect - # Running this set below would set SPTrustedBackedByUPAClaimProvider as the active claims provider for this trust - # But it wouldn't work since properties "SPS-ClaimProviderID" and "SPS-ClaimProviderType" of trusted profiles are not set - # Set-SPTrustedIdentityTokenIssuer $trust -ClaimProvider $claimsProvider -IsOpenIDConnect - - # Sets the property IsPeoplePickerSearchable on specific profile properties - $site = Get-SPSite -Identity $spSiteUrl -ErrorAction SilentlyContinue - $context = Get-SPServiceContext $site -ErrorAction SilentlyContinue - $psm = [Microsoft.Office.Server.UserProfiles.ProfileSubTypeManager]::Get($context) - $ps = $psm.GetProfileSubtype([Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::GetDefaultProfileName([Microsoft.Office.Server.UserProfiles.ProfileType]::User)) - $properties = $ps.Properties - - $propertyNames = @('FirstName', 'LastName', 'SPS-ClaimID', 'PreferredName') - foreach ($propertyName in $propertyNames) { - $property = $properties.GetPropertyByName($propertyName) - if ($property) { - Write-Verbose -Verbose -Message "Updating property $($propertyName)" - $property.CoreProperty.IsPeoplePickerSearchable = $true - $property.CoreProperty.Commit() - Write-Verbose -Verbose -Message "Updated property $($propertyName) with IsPeoplePickerSearchable: $($property.CoreProperty.IsPeoplePickerSearchable)" + # Sets the property IsPeoplePickerSearchable on specific profile properties + $site = Get-SPSite -Identity $webAppUrl -ErrorAction SilentlyContinue + $context = Get-SPServiceContext $site -ErrorAction SilentlyContinue + $psm = [Microsoft.Office.Server.UserProfiles.ProfileSubTypeManager]::Get($context) + $ps = $psm.GetProfileSubtype([Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::GetDefaultProfileName([Microsoft.Office.Server.UserProfiles.ProfileType]::User)) + $properties = $ps.Properties + + $propertyNames = @('FirstName', 'LastName', 'SPS-ClaimID', 'PreferredName') + foreach ($propertyName in $propertyNames) { + $property = $properties.GetPropertyByName($propertyName) + if ($property) { + Write-Verbose -Verbose -Message "Updating property $($propertyName)" + $property.CoreProperty.IsPeoplePickerSearchable = $true + $property.CoreProperty.Commit() + Write-Verbose -Verbose -Message "Updated property $($propertyName) with IsPeoplePickerSearchable: $($property.CoreProperty.IsPeoplePickerSearchable)" + } } + Write-Verbose -Verbose -Message "Finished configuration for ConfigureUPAClaimProvider" + } + catch [ Microsoft.Office.Server.UserProfiles.PartitionNotFoundException ] { + Write-Verbose -Verbose -Message "Caught PartitionNotFoundException, likely caused by Execute() on LanguageSynchronizationJob. Started after enabling secure SQL connection, which became necessary with Subscription 25H1" + Write-Verbose -Verbose -Message "Exception message: $($_.Exception.Message)" + } + catch { + Write-Verbose -Verbose -Message "An error occurred in ConfigureUPAClaimProvider.Set: $($_.Exception.Message)" } - Write-Verbose -Verbose -Message "Finished configuration for ConfigureUPAClaimProvider" } - catch [ Microsoft.Office.Server.UserProfiles.PartitionNotFoundException ] { - Write-Verbose -Verbose -Message "Caught PartitionNotFoundException, likely caused by Execute() on LanguageSynchronizationJob. Started after enabling secure SQL connection, which became necessary with Subscription 25H1" - Write-Verbose -Verbose -Message "Exception message: $($_.Exception.Message)" + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } } - catch { - Write-Verbose -Verbose -Message "An error occurred in ConfigureUPAClaimProvider.Set: $($_.Exception.Message)" + TestScript = + { + # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. + return $false } + DependsOn = "[SPTrustedIdentityTokenIssuer]CreateSPTrust", "[SPSite]CreateRootSite", "[SPUserProfileServiceApp]CreateUserProfileServiceApp" + PsDscRunAsCredential = $DomainAdminCredsQualified } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } - } - TestScript = - { - # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. - return $false - } - DependsOn = "[SPTrustedIdentityTokenIssuer]CreateSPTrust", "[SPSite]CreateRootSite", "[SPUserProfileServiceApp]CreateUserProfileServiceApp" - PsDscRunAsCredential = $DomainAdminCredsQualified } - SPUserProfileSyncConnection ADImportConnection { - UserProfileService = $UpaServiceName - Forest = $DomainFQDN - Name = $DomainFQDN - ConnectionCredentials = $SPADDirSyncCredsQualified - Server = $DomainLDAPPath - UseSSL = $true - Port = 636 - IncludedOUs = @("CN=Users,$DomainLDAPPath", $AdditionalUsersPath) - Force = $false - ConnectionType = "ActiveDirectory" - UseDisabledFilter = $true - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPUserProfileServiceApp]CreateUserProfileServiceApp" + if ($ProvisionUserProfilesService) { + SPUserProfileSyncConnection ADImportConnection { + UserProfileService = $UpaServiceName + Forest = $DomainFQDN + Name = $DomainFQDN + ConnectionCredentials = $SPADDirSyncCredsQualified + Server = $DomainLDAPPath + UseSSL = $true + Port = 636 + IncludedOUs = @("CN=Users,$DomainLDAPPath", $AdditionalUsersPath) + Force = $false + ConnectionType = "ActiveDirectory" + UseDisabledFilter = $true + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPUserProfileServiceApp]CreateUserProfileServiceApp" + } } SPSecurityTokenServiceConfig ConfigureSTS { @@ -1487,38 +1626,42 @@ configuration ConfigureSPVM DependsOn = "[SPFarm]CreateSPFarm" } - # Execute this action some time after CreateAppManagementServiceApp to avoid this error: An update conflict has occurred, and you must re-try this action. The object AppManagementService was updated by CONTOSO\\spsetup, in the wsmprovhost (5136) process, on machine SP - SPAppDomain ConfigureLocalFarmAppUrls { - AppDomain = $AppDomainFQDN - Prefix = "addin" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPSubscriptionSettingsServiceApp]CreateSubscriptionServiceApp", "[SPAppManagementServiceApp]CreateAppManagementServiceApp" - } - - SPWebApplicationAppDomain ConfigureAppDomainDefaultZone { - WebAppUrl = "http://$SharePointSitesAuthority" - AppDomain = $AppDomainFQDN - Zone = "Default" - Port = 80 - SSL = $false - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPAppDomain]ConfigureLocalFarmAppUrls" - } + if ($ProvisionAddins) { + # Execute this action some time after CreateAppManagementServiceApp to avoid this error: An update conflict has occurred, and you must re-try this action. The object AppManagementService was updated by CONTOSO\\spsetup, in the wsmprovhost (5136) process, on machine SP + SPAppDomain ConfigureLocalFarmAppUrls { + AppDomain = $AppDomainFQDN + Prefix = "addin" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPSubscriptionSettingsServiceApp]CreateSubscriptionServiceApp", "[SPAppManagementServiceApp]CreateAppManagementServiceApp" + } + + SPWebApplicationAppDomain ConfigureAppDomainDefaultZone { + WebAppUrl = $WebApplicationUrl + AppDomain = $AppDomainFQDN + Zone = "Default" + Port = if ($DefaultZoneMustBeHttps) { 443 } else { 80 } + SSL = if ($DefaultZoneMustBeHttps) { $true } else { $false } + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPAppDomain]ConfigureLocalFarmAppUrls" + } - SPWebApplicationAppDomain ConfigureAppDomainIntranetZone { - WebAppUrl = "http://$SharePointSitesAuthority" - AppDomain = $AppDomainIntranetFQDN - Zone = "Intranet" - Port = 443 - SSL = $true - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPAppDomain]ConfigureLocalFarmAppUrls" - } + if ($ProvisionExtendedZone) { + SPWebApplicationAppDomain ConfigureAppDomainIntranetZone { + WebAppUrl = $WebApplicationUrl + AppDomain = $AppDomainIntranetFQDN + Zone = "Intranet" + Port = if ($DefaultZoneMustBeHttps) { 80 } else { 443 } + SSL = if ($DefaultZoneMustBeHttps) { $false } else { $true } + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPAppDomain]ConfigureLocalFarmAppUrls" + } + } - SPAppCatalog SetAppCatalogUrl { - SiteUrl = "http://$SharePointSitesAuthority/sites/AppCatalog" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPSite]CreateAppCatalog", "[SPAppManagementServiceApp]CreateAppManagementServiceApp" + SPAppCatalog SetAppCatalogUrl { + SiteUrl = "$WebApplicationUrl/sites/AppCatalog/" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPSite]CreateAppCatalog", "[SPAppManagementServiceApp]CreateAppManagementServiceApp" + } } # Move this op after all databases were created (instead of just after psconfig.exe as documented), but before other servers can join, to fix SQL permission errors thrown at step 10/10 in SPS config wizard, when installing a CU post-provisionning @@ -1539,154 +1682,158 @@ configuration ConfigureSPVM # This team site is tested by VM FE to wait before joining the farm, so it acts as a milestone and it should be created only when all SharePoint services are created # If VM FE joins the farm while a SharePoint service is creating here, it may block its creation forever. + $createTeamSiteDependsOn = @( "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication" ) + if ($ProvisionAddins) { $createTeamSiteDependsOn += "[SPWebApplicationAppDomain]ConfigureAppDomainDefaultZone", "[SPWebApplicationAppDomain]ConfigureAppDomainIntranetZone", "[SPAppCatalog]SetAppCatalogUrl" } SPSite CreateTeamSite { - Url = "http://$SharePointSitesAuthority/sites/team" + Url = "$WebApplicationUrl/sites/team" OwnerAlias = "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" - SecondaryOwnerAlias = "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" + SecondaryOwnerAlias = if ($ProvisionTrustedAuthentication) { "i:0$TrustedIdChar.t|$DomainFQDN|$($DomainAdminCreds.UserName)@$DomainFQDN" } else { "i:0#.w|$DomainNetbiosName\$($DomainAdminCreds.UserName)" } Name = "Team site" Template = $SPTeamSiteTemplate CreateDefaultGroups = $true PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPWebAppAuthentication]ConfigureMainWebAppAuthentication", "[SPWebApplicationAppDomain]ConfigureAppDomainDefaultZone", "[SPWebApplicationAppDomain]ConfigureAppDomainIntranetZone", "[SPAppCatalog]SetAppCatalogUrl" - } - - CertReq GenerateAddinsSiteCertificate { - CARootName = "$DomainNetbiosName-$DCServerName-CA" - CAServerFQDN = "$DCServerName.$DomainFQDN" - Subject = "$AddinsSiteDNSAlias.$($DomainFQDN)" - FriendlyName = "Provider-hosted addins site certificate" - SubjectAltName = "dns=$AddinsSiteDNSAlias.$($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 - Credential = $DomainAdminCredsQualified - DependsOn = "[Script]UpdateGPOToTrustRootCACert" - } - - File CreateAddinsSiteDirectory { - DestinationPath = "C:\inetpub\wwwroot\addins" - Type = "Directory" - Ensure = "Present" - DependsOn = "[SPFarm]CreateSPFarm" - } - - WebAppPool CreateAddinsSiteApplicationPool { - Name = $AddinsSiteName - State = "Started" - managedPipelineMode = 'Integrated' - managedRuntimeLoader = 'webengine4.dll' - managedRuntimeVersion = 'v4.0' - identityType = "SpecificUser" - Credential = $SPSvcCredsQualified - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[SPFarm]CreateSPFarm" - } + DependsOn = $createTeamSiteDependsOn + } + + if ($ProvisionAddins) { + CertReq GenerateAddinsSiteCertificate { + CARootName = "$DomainNetbiosName-$DCServerName-CA" + CAServerFQDN = "$DCServerName.$DomainFQDN" + Subject = "$AddinsSiteDNSAlias.$($DomainFQDN)" + FriendlyName = "Provider-hosted addins site certificate" + SubjectAltName = "dns=$AddinsSiteDNSAlias.$($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 + Credential = $DomainAdminCredsQualified + DependsOn = "[Script]UpdateGPOToTrustRootCACert" + } - Website CreateAddinsSite { - Name = $AddinsSiteName - State = "Started" - PhysicalPath = "C:\inetpub\wwwroot\addins" - ApplicationPool = $AddinsSiteName - AuthenticationInfo = DSC_WebAuthenticationInformation { - Anonymous = $true - Windows = $true + File CreateAddinsSiteDirectory { + DestinationPath = "C:\inetpub\wwwroot\addins" + Type = "Directory" + Ensure = "Present" + DependsOn = "[SPFarm]CreateSPFarm" } - BindingInfo = @( - DSC_WebBindingInformation { - Protocol = "HTTP" - Port = 20080 - } - DSC_WebBindingInformation { - Protocol = "HTTPS" - Port = 20443 - CertificateStoreName = "My" - CertificateSubject = "$AddinsSiteDNSAlias.$($DomainFQDN)" - } - ) - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[CertReq]GenerateAddinsSiteCertificate", "[File]CreateAddinsSiteDirectory", "[WebAppPool]CreateAddinsSiteApplicationPool" - } - Script CopyIISWelcomePageToAddinsSite { - SetScript = - { - Copy-Item -Path "C:\inetpub\wwwroot\*" -Filter "iisstart*" -Destination "C:\inetpub\wwwroot\addins" + WebAppPool CreateAddinsSiteApplicationPool { + Name = $AddinsSiteName + State = "Started" + managedPipelineMode = 'Integrated' + managedRuntimeLoader = 'webengine4.dll' + managedRuntimeVersion = 'v4.0' + identityType = "SpecificUser" + Credential = $SPSvcCredsQualified + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[SPFarm]CreateSPFarm" } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } + + Website CreateAddinsSite { + Name = $AddinsSiteName + State = "Started" + PhysicalPath = "C:\inetpub\wwwroot\addins" + ApplicationPool = $AddinsSiteName + AuthenticationInfo = DSC_WebAuthenticationInformation { + Anonymous = $true + Windows = $true + } + BindingInfo = @( + DSC_WebBindingInformation { + Protocol = "HTTP" + Port = 20080 + } + DSC_WebBindingInformation { + Protocol = "HTTPS" + Port = 20443 + CertificateStoreName = "My" + CertificateSubject = "$AddinsSiteDNSAlias.$($DomainFQDN)" + } + ) + Ensure = "Present" + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[CertReq]GenerateAddinsSiteCertificate", "[File]CreateAddinsSiteDirectory", "[WebAppPool]CreateAddinsSiteApplicationPool" } - TestScript = - { - if ( (Get-ChildItem -Path "C:\inetpub\wwwroot\addins" -Name "iisstart*") -eq $null) { - return $false + + Script CopyIISWelcomePageToAddinsSite { + SetScript = + { + Copy-Item -Path "C:\inetpub\wwwroot\*" -Filter "iisstart*" -Destination "C:\inetpub\wwwroot\addins" } - else { - return $true + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } } + TestScript = + { + if ( (Get-ChildItem -Path "C:\inetpub\wwwroot\addins" -Name "iisstart*") -eq $null) { + return $false + } + else { + return $true + } + } + PsDscRunAsCredential = $DomainAdminCredsQualified + DependsOn = "[WebSite]CreateAddinsSite" } - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[WebSite]CreateAddinsSite" - } - - CertReq GenerateHighTrustAddinsCert { - CARootName = "$DomainNetbiosName-$DCServerName-CA" - CAServerFQDN = "$DCServerName.$DomainFQDN" - Subject = "HighTrustAddins" - FriendlyName = "Sign OAuth tokens of high-trust add-ins" - 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 - Credential = $DomainAdminCredsQualified - DependsOn = "[Script]UpdateGPOToTrustRootCACert" - } - - Script ExportHighTrustAddinsCert { - SetScript = - { - $destinationPath = Join-Path -Path $using:SetupPath -ChildPath "Certificates" - $certSubject = "HighTrustAddins" - $certName = "HighTrustAddins.cer" - $certFullPath = [System.IO.Path]::Combine($destinationPath, $certName) - Write-Verbose -Verbose -Message "Exporting public key of certificate with subject $certSubject to $certFullPath..." - New-Item $destinationPath -Type directory -ErrorAction SilentlyContinue - $signingCert = Get-ChildItem -Path "cert:\LocalMachine\My\" -DnsName "$certSubject" - $signingCert | Export-Certificate -FilePath $certFullPath - Write-Verbose -Verbose -Message "Public key of certificate with subject $certSubject successfully exported to $certFullPath." - } - GetScript = - { - # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. - return @{ "Result" = "false" } + + CertReq GenerateHighTrustAddinsCert { + CARootName = "$DomainNetbiosName-$DCServerName-CA" + CAServerFQDN = "$DCServerName.$DomainFQDN" + Subject = "HighTrustAddins" + FriendlyName = "Sign OAuth tokens of high-trust add-ins" + 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 + Credential = $DomainAdminCredsQualified + DependsOn = "[Script]UpdateGPOToTrustRootCACert" } - TestScript = - { - # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. - return $false + + Script ExportHighTrustAddinsCert { + SetScript = + { + $destinationPath = Join-Path -Path $using:SetupPath -ChildPath "Certificates" + $certSubject = "HighTrustAddins" + $certName = "HighTrustAddins.cer" + $certFullPath = [System.IO.Path]::Combine($destinationPath, $certName) + Write-Verbose -Verbose -Message "Exporting public key of certificate with subject $certSubject to $certFullPath..." + New-Item $destinationPath -Type directory -ErrorAction SilentlyContinue + $signingCert = Get-ChildItem -Path "cert:\LocalMachine\My\" -DnsName "$certSubject" + $signingCert | Export-Certificate -FilePath $certFullPath + Write-Verbose -Verbose -Message "Public key of certificate with subject $certSubject successfully exported to $certFullPath." + } + GetScript = + { + # This block must return a hashtable. The hashtable must only contain one key Result and the value must be of type String. + return @{ "Result" = "false" } + } + TestScript = + { + # If it returns $false, the SetScript block will run. If it returns $true, the SetScript block will not run. + return $false + } + DependsOn = "[CertReq]GenerateHighTrustAddinsCert" } - DependsOn = "[CertReq]GenerateHighTrustAddinsCert" - } - SPTrustedSecurityTokenIssuer CreateHighTrustAddinsTrustedIssuer { - Name = "HighTrustAddins" - Description = "Trust for Provider-hosted high-trust add-ins" - RegisteredIssuerNameIdentifier = "22222222-2222-2222-2222-222222222222" - IsTrustBroker = $true - SigningCertificateFilePath = "$SetupPath\Certificates\HighTrustAddins.cer" - Ensure = "Present" - DependsOn = "[Script]ExportHighTrustAddinsCert" - PsDscRunAsCredential = $DomainAdminCredsQualified + SPTrustedSecurityTokenIssuer CreateHighTrustAddinsTrustedIssuer { + Name = "HighTrustAddins" + Description = "Trust for Provider-hosted high-trust add-ins" + RegisteredIssuerNameIdentifier = "22222222-2222-2222-2222-222222222222" + IsTrustBroker = $true + SigningCertificateFilePath = "$SetupPath\Certificates\HighTrustAddins.cer" + Ensure = "Present" + DependsOn = "[Script]ExportHighTrustAddinsCert" + PsDscRunAsCredential = $DomainAdminCredsQualified + } } Script WarmupSites { @@ -1699,7 +1846,7 @@ configuration ConfigureSPVM # -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 - Invoke-WebRequest -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -UseBasicParsing -ErrorAction SilentlyContinue + Invoke-WebRequest -UseBasicParsing -Uri $uri -UseDefaultCredentials -TimeoutSec 40 -ErrorAction SilentlyContinue Write-Verbose -Verbose -Message "Connected successfully to $uri" } catch [System.Exception] { @@ -1711,12 +1858,13 @@ configuration ConfigureSPVM } } [System.Management.Automation.Job[]] $jobs = @() - $spsite = "http://$($using:ComputerName):$($using:SharePointCentralAdminPort)/" - Write-Verbose -Verbose -Message "Warming up '$spsite'..." - $jobs += Start-Job -ScriptBlock $jobBlock -ArgumentList @($spsite) - $spsite = "http://$($using:SharePointSitesAuthority)/" - Write-Verbose -Verbose -Message "Warming up '$spsite'..." - $jobs += Start-Job -ScriptBlock $jobBlock -ArgumentList @($spsite) + $uri = "http://$($using:ComputerName):$($using:SharePointCentralAdminPort)/" + Write-Verbose -Verbose -Message "Warming up '$uri'..." + $jobs += Start-Job -ScriptBlock $jobBlock -ArgumentList @($uri) + + $uri = $using:WebApplicationUrl + Write-Verbose -Verbose -Message "Warming up '$uri'..." + $jobs += Start-Job -ScriptBlock $jobBlock -ArgumentList @($uri) # Must wait for the jobs to complete, otherwise they do not actually run Receive-Job -Job $jobs -AutoRemoveJob -Wait @@ -1727,127 +1875,130 @@ configuration ConfigureSPVM DependsOn = "[SPSite]CreateRootSite" } - Script CreatePersonalSites { - SetScript = - { - # Need to wrap the creation of personal sites in a job to avoid the error below when calling CreatePersonalSiteEnque($false): - # Could not enqueue creation of personal site for 'i:0#.w|contoso\yvand': Exception calling "CreatePersonalSiteEnque" with "1" argument(s): "Attempted to perform an unauthorized operation." - $jobBlock = { - $uri = $args[0] - $accountPattern_WinClaims = $args[1] - $accountPattern_Trusted = $args[2] - $directoryBase = $args[3] + if ($ProvisionUserProfilesService) { + Script CreatePersonalSites { + SetScript = + { + # Need to wrap the creation of personal sites in a job to avoid the error below when calling CreatePersonalSiteEnque($false): + # Could not enqueue creation of personal site for 'i:0#.w|contoso\yvand': Exception calling "CreatePersonalSiteEnque" with "1" argument(s): "Attempted to perform an unauthorized operation." + $jobBlock = { + $webAppUrl = $args[0] + $accountPattern_WinClaims = $args[1] + $accountPattern_Trusted = $args[2] + $directoryBase = $args[3] - try { - $site = Get-SPSite -Identity $uri -ErrorAction SilentlyContinue - $context = Get-SPServiceContext $site -ErrorAction SilentlyContinue - $upm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context) - Write-Verbose -Verbose -Message "Got UserProfileManager" - } - catch { - Write-Verbose -Verbose -Message "Unable to get UserProfileManager: $_" - # If Write-Error is called, then the Script resource is going to failed state - # Write-Error -Exception $_ -Message "Unable to get UserProfileManager for '$($account.AccountName)'" - return - } - - # Accessing $using:DomainAdminCredsQualified here somehow causes a deserialization error, so use $env:UserName instead - [object []] $accounts = @( - @{ - "AccountName" = $accountPattern_WinClaims -f $env:UserName; - "PreferredName" = $env:UserName; - }, - @{ - "AccountName" = $accountPattern_Trusted -f $env:UserName; - "PreferredName" = $env:UserName; - } - ) - - $directoryUsers = Get-ADUser -Filter "objectClass -like 'user'" -Properties @("SamAccountName", "displayName") -SearchBase $directoryBase #-ResultSetSize 5 - foreach ($directoryUser in $directoryUsers) { - $accounts += - @{ - "AccountName" = $accountPattern_WinClaims -f $directoryUser.SamAccountName; - "PreferredName" = $directoryUser["displayName"]; - }, - @{ - "AccountName" = $accountPattern_Trusted -f $directoryUser.SamAccountName; - "PreferredName" = $directoryUser["displayName"]; - } - } - - foreach ($account in $accounts) { - $userProfile = $null try { - $userProfile = $upm.GetUserProfile($account.AccountName) - Write-Verbose -Verbose -Message "Got existing user profile for '$($account.AccountName)'" + $site = Get-SPSite -Identity $webAppUrl -ErrorAction SilentlyContinue + $context = Get-SPServiceContext $site -ErrorAction SilentlyContinue + $upm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context) + Write-Verbose -Verbose -Message "Got UserProfileManager" } catch { - $userProfile = $upm.CreateUserProfile($account.AccountName, $account.PreferredName); - Write-Verbose -Verbose -Message "Successfully created user profile for '$($account.AccountName)'" + Write-Verbose -Verbose -Message "Unable to get UserProfileManager: $_" + # If Write-Error is called, then the Script resource is going to failed state + # Write-Error -Exception $_ -Message "Unable to get UserProfileManager for '$($account.AccountName)'" + return } + + # Accessing $using:DomainAdminCredsQualified here somehow causes a deserialization error, so use $env:UserName instead + [object []] $accounts = @( + @{ + "AccountName" = $accountPattern_WinClaims -f $env:UserName; + "PreferredName" = $env:UserName; + }, + @{ + "AccountName" = $accountPattern_Trusted -f $env:UserName; + "PreferredName" = $env:UserName; + } + ) - if ($null -eq $userProfile) { - Write-Verbose -Verbose -Message "Unable to get/create the profile for '$($account.AccountName)', give up" - continue + $directoryUsers = Get-ADUser -Filter "objectClass -like 'user'" -Properties @("SamAccountName", "displayName") -SearchBase $directoryBase #-ResultSetSize 5 + foreach ($directoryUser in $directoryUsers) { + $accounts += + @{ + "AccountName" = $accountPattern_WinClaims -f $directoryUser.SamAccountName; + "PreferredName" = $directoryUser["displayName"]; + }, + @{ + "AccountName" = $accountPattern_Trusted -f $directoryUser.SamAccountName; + "PreferredName" = $directoryUser["displayName"]; + } } - - if ($null -eq $userProfile.PersonalSite) { - Write-Verbose -Verbose -Message "Adding creation of personal site for '$($account.AccountName)' to the queue..." + + foreach ($account in $accounts) { + $userProfile = $null try { - $userProfile.CreatePersonalSiteEnque($false) - Write-Verbose -Verbose -Message "Successfully enqueued the creation of personal site for '$($account.AccountName)'" + $userProfile = $upm.GetUserProfile($account.AccountName) + Write-Verbose -Verbose -Message "Got existing user profile for '$($account.AccountName)'" } catch { - Write-Verbose -Verbose -Message "Could not enqueue creation of personal site for '$($account.AccountName)': $_" + $userProfile = $upm.CreateUserProfile($account.AccountName, $account.PreferredName); + Write-Verbose -Verbose -Message "Successfully created user profile for '$($account.AccountName)'" + } + + if ($null -eq $userProfile) { + Write-Verbose -Verbose -Message "Unable to get/create the profile for '$($account.AccountName)', give up" + continue + } + + if ($null -eq $userProfile.PersonalSite) { + Write-Verbose -Verbose -Message "Adding creation of personal site for '$($account.AccountName)' to the queue..." + try { + $userProfile.CreatePersonalSiteEnque($false) + Write-Verbose -Verbose -Message "Successfully enqueued the creation of personal site for '$($account.AccountName)'" + } + catch { + Write-Verbose -Verbose -Message "Could not enqueue creation of personal site for '$($account.AccountName)': $_" + } + } + else { + Write-Verbose -Verbose -Message "Personal site for '$($account.AccountName)' already exists, nothing to do" } } - else { - Write-Verbose -Verbose -Message "Personal site for '$($account.AccountName)' already exists, nothing to do" - } - } - - try { - # LanguageSynchronizationJob must be executed before updating profile properties, to ensure their property DisplayNameLocalized is set with a localized value - # This is populated in SQL table [SPDSC_UPA_Profiles].[upa].[PropertyListLoc] - # If this value is not set, $property.CoreProperty.Commit() will throw: Exception calling "Commit" with "0" argument(s): "The display name must be specified in order to create a property." - # NOTE: Job LanguageSynchronizationJob will fail IF farm build is between some post-RTM CU and < 2025-08 CU. - $job = Get-SPTimerJob -Type "Microsoft.Office.Server.Administration.UserProfileApplication+LanguageSynchronizationJob" - $job.Execute() - $psm = [Microsoft.Office.Server.UserProfiles.ProfileSubTypeManager]::Get($context) - $ps = $psm.GetProfileSubtype([Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::GetDefaultProfileName([Microsoft.Office.Server.UserProfiles.ProfileType]::User)) - $properties = $ps.Properties - $properties.Count # will call LoadProperties() - $PropertyNames = @('FirstName', 'LastName', 'SPS-ClaimID', 'PreferredName') - foreach ($propertyName in $PropertyNames) { - $property = $properties.GetPropertyByName($propertyName) - if ($property) { - Write-Verbose -Verbose -Message "Checking property $($propertyName)" - $property.CoreProperty.DisplayNameLocalized # Test to avoid error "The display name must be specified in order to create a property." - $m_DisplayNamesValue = $property.CoreProperty.GetType().GetField("m_DisplayNames", [System.Reflection.BindingFlags]"NonPublic, Instance").GetValue($property.CoreProperty) - if ($m_DisplayNamesValue) { - Write-Verbose -Verbose -Message "Property $($propertyName) has m_DisplayNamesValue.DefaultLanguage $($m_DisplayNamesValue.DefaultLanguage) and m_DisplayNamesValue.Count $($m_DisplayNamesValue.Count)" + try { + # LanguageSynchronizationJob must be executed before updating profile properties, to ensure their property DisplayNameLocalized is set with a localized value + # This is populated in SQL table [SPDSC_UPA_Profiles].[upa].[PropertyListLoc] + # If this value is not set, $property.CoreProperty.Commit() will throw: Exception calling "Commit" with "0" argument(s): "The display name must be specified in order to create a property." + # NOTE: Job LanguageSynchronizationJob will fail IF farm build is between some post-RTM CU and < 2025-08 CU. + $job = Get-SPTimerJob -Type "Microsoft.Office.Server.Administration.UserProfileApplication+LanguageSynchronizationJob" + $job.Execute() + + $psm = [Microsoft.Office.Server.UserProfiles.ProfileSubTypeManager]::Get($context) + $ps = $psm.GetProfileSubtype([Microsoft.Office.Server.UserProfiles.ProfileSubtypeManager]::GetDefaultProfileName([Microsoft.Office.Server.UserProfiles.ProfileType]::User)) + $properties = $ps.Properties + $properties.Count # will call LoadProperties() + $PropertyNames = @('FirstName', 'LastName', 'SPS-ClaimID', 'PreferredName') + foreach ($propertyName in $PropertyNames) { + $property = $properties.GetPropertyByName($propertyName) + if ($property) { + Write-Verbose -Verbose -Message "Checking property $($propertyName)" + $property.CoreProperty.DisplayNameLocalized # Test to avoid error "The display name must be specified in order to create a property." + $m_DisplayNamesValue = $property.CoreProperty.GetType().GetField("m_DisplayNames", [System.Reflection.BindingFlags]"NonPublic, Instance").GetValue($property.CoreProperty) + if ($m_DisplayNamesValue) { + Write-Verbose -Verbose -Message "Property $($propertyName) has m_DisplayNamesValue.DefaultLanguage $($m_DisplayNamesValue.DefaultLanguage) and m_DisplayNamesValue.Count $($m_DisplayNamesValue.Count)" + } + $property.CoreProperty.IsPeoplePickerSearchable = $true + $property.CoreProperty.Commit() + Write-Verbose -Verbose -Message "Updated property $($propertyName) with IsPeoplePickerSearchable: $($property.CoreProperty.IsPeoplePickerSearchable)" } - $property.CoreProperty.IsPeoplePickerSearchable = $true - $property.CoreProperty.Commit() - Write-Verbose -Verbose -Message "Updated property $($propertyName) with IsPeoplePickerSearchable: $($property.CoreProperty.IsPeoplePickerSearchable)" } } + catch [System.Exception] { + Write-Verbose -Verbose -Message "Could not execute LanguageSynchronizationJob or update profile properties: $_" + } } - catch [System.Exception] { - Write-Verbose -Verbose -Message "Could not execute LanguageSynchronizationJob or update profile properties: $_" - } + $webAppUrl = $using:WebApplicationUrl + $accountPattern_WinClaims = "i:0#.w|$($using:DomainNetbiosName)\{0}" + $accountPattern_Trusted = "i:0$($using:TrustedIdChar).t|$($using:DomainFQDN)|{0}@$($using:DomainFQDN)" + $job = Start-Job -ScriptBlock $jobBlock -ArgumentList @($webAppUrl, $accountPattern_WinClaims, $accountPattern_Trusted, $using:AdditionalUsersPath) + Receive-Job -Job $job -AutoRemoveJob -Wait } - $uri = "http://$($using:SharePointSitesAuthority)/" - $accountPattern_WinClaims = "i:0#.w|$($using:DomainNetbiosName)\{0}" - $accountPattern_Trusted = "i:0$($using:TrustedIdChar).t|$($using:DomainFQDN)|{0}@$($using:DomainFQDN)" - $job = Start-Job -ScriptBlock $jobBlock -ArgumentList @($uri, $accountPattern_WinClaims, $accountPattern_Trusted, $using:AdditionalUsersPath) - Receive-Job -Job $job -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. + DependsOn = "[SPUserProfileServiceApp]CreateUserProfileServiceApp" + PsDscRunAsCredential = $DomainAdminCredsQualified } - 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 } Script CreateShortcuts { @@ -1918,7 +2069,7 @@ configuration ConfigureSPVM # } # GetScript = { } # DependsOn = "[cChocoPackageInstaller]InstallPython" - # PsDscRunAsCredential = $DomainAdminCredsQualified + # PsDscRunAsCredential = $DomainAdminCredsQualified, "[PendingReboot]RebootOnSignalFromJoinDomain" # } # } @@ -1940,16 +2091,15 @@ function Get-LatestGitHubRelease { [string] $Artifact, [string] $ReleaseId ) - # # Force protocol TLS 1.2 in Invoke-WebRequest to fix TLS/SSL connection error with GitHub in Windows Server 2012 R2, as documented in https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-update-1802 - # [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - $latestRelease = Invoke-WebRequest "https://api.github.com/repos/$Repo/releases/$ReleaseId" -Headers @{"Accept" = "application/json" } -UseBasicParsing + $latestRelease = Invoke-WebRequest -UseBasicParsing "https://api.github.com/repos/$Repo/releases/$ReleaseId" -Headers @{"Accept" = "application/json" } $json = $latestRelease.Content | ConvertFrom-Json $asset = $json.assets | Where-Object { $_.name -like $Artifact } $assetUrl = $asset.browser_download_url return $assetUrl } +#################### DUPLICATED #################### function Get-NetBIOSName { [OutputType([string])] param( @@ -1973,8 +2123,40 @@ function Get-NetBIOSName { } } +enum ConfigurationLevel { + Minimum + Light + Medium + Full +} + +# enum values cannot start with a digit - https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_enum?view=powershell-5.1#syntax +enum SharePointBuild { + SPRTM + SP22H2 + SP23H1 + SP23H2 + SP24H1 + SP24H2 + SP25H1 + SP25H2 + SPLatest +} + +class SharePointBuildInfo { + [ValidateNotNullOrEmpty()][SharePointBuild] $Label + [SharePointPackageInfo[]] $Packages +} + +class SharePointPackageInfo { + [ValidateNotNullOrEmpty()][string] $DownloadUrl + [Parameter(Mandatory = $false)] [string] $ChecksumType + [Parameter(Mandatory = $false)] [string] $Checksum +} +#################### DUPLICATED #################### + <# -help ConfigureSPVM +help ConfigSpMain $password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force $DomainAdminCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password @@ -1986,37 +2168,39 @@ $SPADDirSyncCreds = New-Object -TypeName System.Management.Automation.PSCredenti $SPPassphraseCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "Passphrase", $password $SPSuperUserCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spSuperUser", $password $SPSuperReaderCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spSuperReader", $password -$DNSServerIP = "10.1.1.4" +$DNSServerIP = "10.1.1.100" $DomainFQDN = "contoso.local" $DCServerName = "DC" $SQLServerName = "SQL" $SQLAlias = "SQLAlias" -$SharePointVersion = "Subscription-RTM" +$SharePointVersion = "SPRTM" $SharePointSitesAuthority = "spsites" $SharePointCentralAdminPort = 5000 $EnableAnalysis = $true +$DefaultZoneMustBeHttps = $false +$ConfigurationLevel = "Light" $SharePointBits = @( @{ - Label = "RTM"; + Label = "SPRTM"; Packages = @( @{ DownloadUrl = "https://go.microsoft.com/fwlink/?linkid=2171943"; ChecksumType = "SHA256"; Checksum = "C576B847C573234B68FC602A0318F5794D7A61D8149EB6AE537AF04470B7FC05" } ) }, @{ - Label = "22H2"; + Label = "SP22H2"; Packages = @( @{ DownloadUrl = "https://download.microsoft.com/download/8/d/f/8dfcb515-6e49-42e5-b20f-5ebdfd19d8e7/wssloc-subscription-kb5002270-fullfile-x64-glb.exe"; ChecksumType = "SHA256"; Checksum = "7E496530EB873146650A9E0653DE835CB2CAD9AF8D154CBD7387BB0F2297C9FC" }, @{ DownloadUrl = "https://download.microsoft.com/download/3/f/5/3f5b1ee0-3336-45d7-b2f4-1e6af977d574/sts-subscription-kb5002271-fullfile-x64-glb.exe"; ChecksumType = "SHA256"; Checksum = "247011443AC573D4F03B1622065A7350B8B3DAE04D6A5A6DC64C8270A3BE7636" } ) }, - { - Label = "23H1", + @{ + Label = "SP23H1"; Packages = @( @{ DownloadUrl = "https://download.microsoft.com/download/c/6/a/c6a17105-3d86-42ad-888d-49b22383bfa1/uber-subscription-kb5002355-fullfile-x64-glb.exe" } ) }, @{ - Label = "Latest"; + Label = "SPLatest"; Packages = @( @{ DownloadUrl = "https://download.microsoft.com/download/d/6/d/d6dcc9e7-744e-43e1-b4be-206a6acd4f88/sts-subscription-kb5002331-fullfile-x64-glb.exe" }, @{ DownloadUrl = "https://download.microsoft.com/download/d/3/5/d354b6e2-fa16-48e0-b3f8-423f7ca279a0/wssloc-subscription-kb5002326-fullfile-x64-glb.exe" } @@ -2024,8 +2208,8 @@ $SharePointBits = @( } ) -$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureSPSE.0\ConfigureSPVM" -ConfigureSPVM -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPSvcCreds $SPSvcCreds -SPAppPoolCreds $SPAppPoolCreds -SPADDirSyncCreds $SPADDirSyncCreds -SPPassphraseCreds $SPPassphraseCreds -SPSuperUserCreds $SPSuperUserCreds -SPSuperReaderCreds $SPSuperReaderCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -SharePointSitesAuthority $SharePointSitesAuthority -SharePointCentralAdminPort $SharePointCentralAdminPort -EnableAnalysis $EnableAnalysis -SharePointBits $SharePointBits -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\dsc-spse-main.0\ConfigSpMain" +ConfigSpMain -DomainAdminCreds $DomainAdminCreds -SPSetupCreds $SPSetupCreds -SPFarmCreds $SPFarmCreds -SPSvcCreds $SPSvcCreds -SPAppPoolCreds $SPAppPoolCreds -SPADDirSyncCreds $SPADDirSyncCreds -SPPassphraseCreds $SPPassphraseCreds -SPSuperUserCreds $SPSuperUserCreds -SPSuperReaderCreds $SPSuperReaderCreds -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DCServerName $DCServerName -SQLServerName $SQLServerName -SQLAlias $SQLAlias -SharePointVersion $SharePointVersion -SharePointSitesAuthority $SharePointSitesAuthority -SharePointCentralAdminPort $SharePointCentralAdminPort -EnableAnalysis $EnableAnalysis -DefaultZoneMustBeHttps $DefaultZoneMustBeHttps -ConfigurationLevel $ConfigurationLevel -SharePointBits $SharePointBits -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath Set-DscLocalConfigurationManager -Path $outputPath Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPSE.zip b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-main.zip similarity index 70% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPSE.zip rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-main.zip index 8ff8bac78790..22013de17493 100644 Binary files a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSPSE.zip and b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-spse-main.zip differ diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSQLVM.ps1 b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-sql.ps1 similarity index 53% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSQLVM.ps1 rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-sql.ps1 index f308e6cf1d6e..d82ffd6974ca 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSQLVM.ps1 +++ b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-sql.ps1 @@ -1,47 +1,47 @@ -configuration ConfigureSQLVM +configuration ConfigSql { param ( [Parameter(Mandatory)] [String]$DNSServerIP, [Parameter(Mandatory)] [String]$DomainFQDN, + [Parameter(Mandatory)] [String]$SPSetupUserName, [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$DomainAdminCreds, - [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SqlSvcCreds, - [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SPSetupCreds + [Parameter(Mandatory)] [System.Management.Automation.PSCredential]$SqlSvcCreds ) - Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 # Custom + Import-DscResource -ModuleName ComputerManagementDsc -ModuleVersion 10.0.0 Import-DscResource -ModuleName NetworkingDsc -ModuleVersion 9.1.0 - Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.0 - Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.1.0 # Custom workaround on SqlSecureConnection + Import-DscResource -ModuleName ActiveDirectoryDsc -ModuleVersion 6.7.1 + Import-DscResource -ModuleName SqlServerDsc -ModuleVersion 17.5.1 # Custom workaround on SqlSecureConnection Import-DscResource -ModuleName CertificateDsc -ModuleVersion 6.0.0 WaitForSqlSetup + [String] $InterfaceAlias = (Get-NetAdapter | Where-Object InterfaceDescription -Like "Microsoft Hyper-V Network Adapter*" | Select-Object -First 1).Name + [String] $ComputerName = Get-Content env:computername [String] $DomainNetbiosName = (Get-NetBIOSName -DomainFQDN $DomainFQDN) - $Interface = Get-NetAdapter| Where-Object InterfaceDescription -Like "Microsoft Hyper-V Network Adapter*"| Select-Object -First 1 - $InterfaceAlias = $($Interface.Name) + [String] $DomainLDAPPath = "DC=$($DomainFQDN.Split(".")[0]),DC=$($DomainFQDN.Split(".")[1])" + [String] $DCServerName = "DC" + [String] $AdfsDnsEntryName = "adfs" + # Format username as user@contoso.local to workaround issue https://github.com/dsccommunity/ComputerManagementDsc/issues/413 + [System.Management.Automation.PSCredential] $DomainAdminCredsToJoinDomain = New-Object System.Management.Automation.PSCredential ("$($DomainAdminCreds.UserName)@$($DomainFQDN)", $DomainAdminCreds.Password) # 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] $SQLCredsQualified = New-Object PSCredential ("${DomainNetbiosName}\$($SqlSvcCreds.UserName)", $SqlSvcCreds.Password) - [String] $ComputerName = Get-Content env:computername - [String] $AdfsDnsEntryName = "adfs" + [System.Management.Automation.PSCredential] $DomainAdminCredsQualified = New-Object System.Management.Automation.PSCredential ("$($DomainNetbiosName)\$($DomainAdminCreds.UserName)", $DomainAdminCreds.Password) + [System.Management.Automation.PSCredential] $SQLCredsQualified = New-Object PSCredential ("$($DomainNetbiosName)\$($SqlSvcCreds.UserName)", $SqlSvcCreds.Password) + [String] $SqlSvcUserNameQualified = "$($DomainNetbiosName)\$($SqlSvcCreds.UserName)" + [String] $SPSetupUserNameQualified = "$($DomainNetbiosName)\$SPSetupUserName" Node localhost { - LocalConfigurationManager - { - ConfigurationMode = 'ApplyOnly' + LocalConfigurationManager { + ConfigurationMode = 'ApplyOnly' RebootNodeIfNeeded = $true } #********************************************************** # 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"; } - - DnsServerAddress SetDNS { Address = $DNSServerIP; InterfaceAlias = $InterfaceAlias; AddressFamily = 'IPv4' } - + DnsServerAddress SetDNS { Address = $DNSServerIP; InterfaceAlias = $InterfaceAlias; AddressFamily = 'IPv4' } Script EnableFileSharing { GetScript = { } @@ -61,9 +61,8 @@ configuration ConfigureSQLVM # 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 = + Script WaitForADFSFarmReady { + SetScript = { $dnsRecordFQDN = "$($using:AdfsDnsEntryName).$($using:DomainFQDN)" $dnsRecordFound = $false @@ -78,43 +77,21 @@ configuration ConfigureSQLVM Write-Verbose -Verbose -Message "DNS record '$dnsRecordFQDN' not found yet: $_" Start-Sleep -Seconds $sleepTime } - } while ($false -eq $dnsRecordFound) + } while (-not $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" + 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 = "[Script]WaitForADFSFarmReady" - # } - - # # 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 - { + Computer JoinDomain { Name = $ComputerName DomainName = $DomainFQDN - Credential = $DomainAdminCredsQualified - # DependsOn = "[PendingReboot]RebootOnSignalFromWaitForDCReady" + Credential = $DomainAdminCredsToJoinDomain DependsOn = "[Script]WaitForADFSFarmReady" } - PendingReboot RebootOnSignalFromJoinDomain - { + PendingReboot RebootOnSignalFromJoinDomain { Name = "RebootOnSignalFromJoinDomain" SkipCcmClientSDK = $true DependsOn = "[Computer]JoinDomain" @@ -125,102 +102,139 @@ configuration ConfigureSQLVM #********************************************************** # By default, SPNs MSSQLSvc/SQL.contoso.local:1433 and MSSQLSvc/SQL.contoso.local are set on the machine account # They need to be removed before they can be set on the SQL service account - Script RemoveSQLSpnOnSQLMachine - { - GetScript = { } - TestScript = { return $false } - SetScript = + Script RemoveSQLSpnOnSQLMachine { + GetScript = { return @{ "Result" = "false" } } + TestScript = { - $hostname = $using:ComputerName - $domainFQDN = $using:DomainFQDN - setspn -D "MSSQLSvc/$hostname.$($domainFQDN)" "$hostname" - setspn -D "MSSQLSvc/$hostname.$($domainFQDN):1433" "$hostname" + return $false + # $spn = "MSSQLSvc/$($using:ComputerName).$($using:DomainFQDN)" + # $computerAccount = Get-ADComputer -Identity $using:ComputerName -Properties ServicePrincipalName -ErrorAction SilentlyContinue + # if ([bool]($computerAccount.ServicePrincipalName -contains $Spn) -or [bool]($computerAccount.ServicePrincipalName -contains "$($spn):1433")) { + # return $false # SPN still exists on machine account, need to run SetScript to remove it + # } + # else { + # return $true # SPN already removed from machine account + # } + } + SetScript = + { + $computerName = $using:ComputerName + $spn = "MSSQLSvc/$($using:ComputerName).$($using:DomainFQDN)" + Write-Verbose -Verbose -Message "Removing SPNs '$spn' and '$($spn):1433' from computer $computerName..." + setspn -D "$spn" "$computerName" + setspn -D "$($spn):1433" "$computerName" + + $targetObject = "$($using:SqlSvcUserNameQualified)" + Write-Verbose -Verbose -Message "Adding SPNs '$spn' and '$($spn):1433' to '$targetObject'..." + setspn -U -S "$spn" "$targetObject" + setspn -U -S "$($spn):1433" "$targetObject" } DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" PsDscRunAsCredential = $DomainAdminCredsQualified } - ADUser CreateSqlSvcAccount - { - DomainName = $DomainFQDN - UserName = $SqlSvcCreds.UserName - UserPrincipalName = "$($SqlSvcCreds.UserName)@$DomainFQDN" - Password = $SQLCredsQualified - PasswordNeverExpires = $true - ServicePrincipalNames = @("MSSQLSvc/$ComputerName.$($DomainFQDN):1433", "MSSQLSvc/$ComputerName.$DomainFQDN", "MSSQLSvc/$($ComputerName):1433", "MSSQLSvc/$ComputerName") - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[Script]RemoveSQLSpnOnSQLMachine" - } - - Script EnsureSQLServiceStarted - { - GetScript = { } - TestScript = { return (Get-Service -Name "MSSQLSERVER").Status -like 'Running' } - SetScript = { Start-Service -Name "MSSQLSERVER" } + # Allow SQL Server to automatically register the SPN when its service starts + # Doc: https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/register-a-service-principal-name-for-kerberos-connections?view=sql-server-ver17#automatic-spn-registration + Script GrantWriteSpnPermissionToSqlSvcOnSQLMachine { + SetScript = + { + Function Set-SpnPermission { + param( + [ADSI]$TargetObject, + [Security.Principal.IdentityReference]$Identity + ) + + Write-Verbose -Verbose -Message "Granting delegated permission 'Validated write to service principal name' to '$Identity' on '$($TargetObject.Name)'" + # GUID for "Validated write to service principal name" + $validateWriteSPNGuid = "f3a64788-5306-11d1-a9c5-0000f80367c1" + $activeDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::ReadProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::Self + # Create the ACE (Access Control Entry) + $ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule( + $Identity, + $activeDirectoryRights, + [System.Security.AccessControl.AccessControlType]::Allow, + [guid]$validateWriteSPNGuid, + [System.DirectoryServices.ActiveDirectorySecurityInheritance]::None + ) + # Get the AD object and apply the ACE + $TargetObject.psBase.ObjectSecurity.AddAccessRule($ace) + $TargetObject.psBase.CommitChanges() + } + $targetObject = "LDAP://CN=$($using:ComputerName),CN=Computers,$($using:DomainLDAPPath)" + $identity = [System.Security.Principal.NTAccount] "$($using:SqlSvcUserNameQualified)" + Set-SpnPermission -TargetObject $targetObject -Identity $identity + } + GetScript = { } + TestScript = { + Function Test-SpnPermission { + param( + [ADSI]$TargetObject, + [Security.Principal.IdentityReference]$Identity + ) + + Write-Verbose -Verbose -Message "Checking if delegated permission 'Validated write to service principal name' exists for '$Identity' on '$($TargetObject.Name)'" + # GUID for "Validated write to service principal name" + $validateWriteSPNGuid = [guid]"f3a64788-5306-11d1-a9c5-0000f80367c1" + $expectedRights = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::ReadProperty -bor [System.DirectoryServices.ActiveDirectoryRights]::Self + + # Get the current ACL + $acl = $TargetObject.psBase.ObjectSecurity + $accessRules = $acl.GetAccessRules($true, $false, [System.Security.Principal.NTAccount]) + + # Check if the permission already exists + foreach ($rule in $accessRules) { + if ($rule.IdentityReference -eq $Identity -and + ($rule.ActiveDirectoryRights -band $expectedRights) -eq $expectedRights -and + $rule.ObjectType -eq $validateWriteSPNGuid -and + $rule.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow) { + Write-Verbose -Verbose -Message "Permission already exists" + return $true + } + } + + Write-Verbose -Verbose -Message "Permission does not exist" + return $false + } + $targetObject = "LDAP://CN=$($using:ComputerName),CN=Computers,$($using:DomainLDAPPath)" + $identity = [System.Security.Principal.NTAccount] "$($using:SqlSvcUserNameQualified)" + return Test-SpnPermission -TargetObject $targetObject -Identity $identity + } DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" PsDscRunAsCredential = $DomainAdminCredsQualified } - SqlMaxDop ConfigureMaxDOP { ServerName = $ComputerName; InstanceName = "MSSQLSERVER"; MaxDop = 1; DependsOn = "[Script]EnsureSQLServiceStarted" } - - # Script WorkaroundErrorInSqlServiceAccountResource - # { - # GetScript = { } - # TestScript = { return $false } - # SetScript = { - # [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlWmiManagement") - # $mc = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer - # } - # DependsOn = "[Script]EnsureSQLServiceStarted", "[ADUser]CreateSqlSvcAccount" - # PsDscRunAsCredential = $DomainAdminCredsQualified - # } - - SqlServiceAccount SetSqlInstanceServiceAccount - { + SqlServiceAccount SetSqlInstanceServiceAccount { ServerName = $ComputerName InstanceName = "MSSQLSERVER" ServiceType = "DatabaseEngine" ServiceAccount = $SQLCredsQualified RestartService = $true - DependsOn = "[Script]EnsureSQLServiceStarted", "[ADUser]CreateSqlSvcAccount" - # DependsOn = "[Script]WorkaroundErrorInSqlServiceAccountResource" + DependsOn = "[Script]GrantWriteSpnPermissionToSqlSvcOnSQLMachine" } - SqlLogin AddDomainAdminLogin - { + SqlMaxDop ConfigureMaxDOP { + ServerName = $ComputerName; InstanceName = "MSSQLSERVER"; MaxDop = 1; DependsOn = "[SqlServiceAccount]SetSqlInstanceServiceAccount" + } + + SqlLogin AddDomainAdminLogin { Name = "${DomainNetbiosName}\$($DomainAdminCreds.UserName)" Ensure = "Present" ServerName = $ComputerName InstanceName = "MSSQLSERVER" LoginType = "WindowsUser" - DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" - } - - ADUser CreateSPSetupAccount - { # Both SQL and SharePoint DSCs run this SPSetupAccount AD account creation - DomainName = $DomainFQDN - UserName = $SPSetupCreds.UserName - UserPrincipalName = "$($SPSetupCreds.UserName)@$DomainFQDN" - Password = $SPSetupCreds - PasswordNeverExpires = $true - Ensure = "Present" - PsDscRunAsCredential = $DomainAdminCredsQualified - DependsOn = "[PendingReboot]RebootOnSignalFromJoinDomain" + DependsOn = "[SqlServiceAccount]SetSqlInstanceServiceAccount" } - SqlLogin AddSPSetupLogin - { - Name = "${DomainNetbiosName}\$($SPSetupCreds.UserName)" + SqlLogin AddSPSetupLogin { + Name = $SPSetupUserNameQualified Ensure = "Present" ServerName = $ComputerName InstanceName = "MSSQLSERVER" LoginType = "WindowsUser" - DependsOn = "[ADUser]CreateSPSetupAccount" + DependsOn = "[SqlServiceAccount]SetSqlInstanceServiceAccount" } - SqlRole GrantSQLRoleSysadmin - { + SqlRole GrantSQLRoleSysadmin { ServerRoleName = "sysadmin" MembersToInclude = @("${DomainNetbiosName}\$($DomainAdminCreds.UserName)") ServerName = $ComputerName @@ -229,20 +243,18 @@ configuration ConfigureSQLVM DependsOn = "[SqlLogin]AddDomainAdminLogin" } - SqlRole GrantSQLRoleSecurityAdmin - { + SqlRole GrantSQLRoleSecurityAdmin { ServerRoleName = "securityadmin" - MembersToInclude = @("${DomainNetbiosName}\$($SPSetupCreds.UserName)") + MembersToInclude = @($SPSetupUserNameQualified) ServerName = $ComputerName InstanceName = "MSSQLSERVER" Ensure = "Present" DependsOn = "[SqlLogin]AddSPSetupLogin" } - SqlRole GrantSQLRoleDBCreator - { + SqlRole GrantSQLRoleDBCreator { ServerRoleName = "dbcreator" - MembersToInclude = @("${DomainNetbiosName}\$($SPSetupCreds.UserName)") + MembersToInclude = @($SPSetupUserNameQualified) ServerName = $ComputerName InstanceName = "MSSQLSERVER" Ensure = "Present" @@ -251,86 +263,74 @@ configuration ConfigureSQLVM # Since SharePointDsc 4.4.0, SPFarm "Switched from creating a Lock database to a Lock table in the TempDB. This to allow the use of precreated databases." # But for this to work, the SPSetup account needs specific permissions on both the tempdb and the dbo schema - SqlDatabaseUser AddSPSetupUserToTempdb - { - ServerName = $ComputerName - InstanceName = "MSSQLSERVER" - DatabaseName = "tempdb" - UserType = 'Login' - Name = "${DomainNetbiosName}\$($SPSetupCreds.UserName)" - LoginName = "${DomainNetbiosName}\$($SPSetupCreds.UserName)" - DependsOn = "[SqlLogin]AddSPSetupLogin" + SqlDatabaseUser AddSPSetupUserToTempdb { + ServerName = $ComputerName + InstanceName = "MSSQLSERVER" + DatabaseName = "tempdb" + UserType = 'Login' + Name = $SPSetupUserNameQualified + LoginName = $SPSetupUserNameQualified + DependsOn = "[SqlLogin]AddSPSetupLogin" } # Reference: https://learn.microsoft.com/en-us/sql/t-sql/statements/grant-schema-permissions-transact-sql?view=sql-server-ver16 - SqlDatabasePermission GrantPermissionssToTempdb - { - Name = "${DomainNetbiosName}\$($SPSetupCreds.UserName)" - ServerName = $ComputerName - InstanceName = "MSSQLSERVER" - DatabaseName = "tempdb" + SqlDatabasePermission GrantPermissionssToTempdb { + Name = $SPSetupUserNameQualified + ServerName = $ComputerName + InstanceName = "MSSQLSERVER" + DatabaseName = "tempdb" Permission = @( - DatabasePermission - { + DatabasePermission { State = 'Grant' Permission = @('Select', 'CreateTable', 'Execute', 'DELETE', 'INSERT', 'UPDATE') } - DatabasePermission - { + DatabasePermission { State = 'GrantWithGrant' Permission = @() } - DatabasePermission - { + DatabasePermission { State = 'Deny' Permission = @() } ) - DependsOn = "[SqlDatabaseUser]AddSPSetupUserToTempdb" + DependsOn = "[SqlDatabaseUser]AddSPSetupUserToTempdb" } - SqlDatabaseObjectPermission GrantPermissionssToDboSchema - { - Name = "${DomainNetbiosName}\$($SPSetupCreds.UserName)" - ServerName = $ComputerName - InstanceName = "MSSQLSERVER" - DatabaseName = "tempdb" - SchemaName = "dbo" - ObjectName = "" - ObjectType = "Schema" - Permission = @( - DSC_DatabaseObjectPermission - { + SqlDatabaseObjectPermission GrantPermissionssToDboSchema { + Name = $SPSetupUserNameQualified + ServerName = $ComputerName + InstanceName = "MSSQLSERVER" + DatabaseName = "tempdb" + SchemaName = "dbo" + ObjectName = "" + ObjectType = "Schema" + Permission = @( + DSC_DatabaseObjectPermission { State = "Grant" Permission = "Select" } - DSC_DatabaseObjectPermission - { + DSC_DatabaseObjectPermission { State = "Grant" Permission = "Update" } - DSC_DatabaseObjectPermission - { + DSC_DatabaseObjectPermission { State = "Grant" Permission = "Insert" } - DSC_DatabaseObjectPermission - { + DSC_DatabaseObjectPermission { State = "Grant" Permission = "Execute" } - DSC_DatabaseObjectPermission - { + DSC_DatabaseObjectPermission { State = "Grant" Permission = "Control" } - DSC_DatabaseObjectPermission - { + DSC_DatabaseObjectPermission { State = "Grant" Permission = "References" } ) - DependsOn = "[SqlDatabaseUser]AddSPSetupUserToTempdb" + DependsOn = "[SqlDatabaseUser]AddSPSetupUserToTempdb" } # SqlDatabaseRole 'GrantPermissionsToTempdb' @@ -340,14 +340,12 @@ configuration ConfigureSQLVM # DatabaseName = "tempdb" # Name = "db_owner" # Ensure = "Present" - # MembersToInclude = @("${DomainNetbiosName}\$($SPSetupCreds.UserName)") + # MembersToInclude = @($SPSetupUserNameQualified) # PsDscRunAsCredential = $SqlAdministratorCredential # DependsOn = "[SqlLogin]AddSPSetupLogin" # } - # Update GPO to ensure the root certificate of the CA is present in "cert:\LocalMachine\Root\", otherwise certificate request will fail - # $DCServerName = Get-ADDomainController | Select-Object -First 1 -Expand Name - $DCServerName = "DC" + # Update GPOs to ensure the root certificate of the CA is present in "cert:\LocalMachine\Root\", otherwise certificate request will fail Script UpdateGPOToTrustRootCACert { SetScript = { @@ -388,12 +386,11 @@ configuration ConfigureSQLVM DependsOn = '[Script]UpdateGPOToTrustRootCACert' } - $sqlsvcUserName = $SQLCredsQualified.UserName Script GrantSqlsvcFullControlToPrivateKey { SetScript = { $subjectName = "CN=$($using:ComputerName).$($using:DomainFQDN)" - $sqlsvcUserName = $using:sqlsvcUserName + $sqlSvcUserNameQualified = $using:SqlSvcUserNameQualified # Grant access to the certificate private key. $cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -eq $subjectName } @@ -401,7 +398,7 @@ configuration ConfigureSQLVM $fileName = $rsaCert.key.UniqueName $path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$fileName" $permissions = Get-Acl -Path $path - $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($sqlsvcUserName, 'FullControl', 'None', 'None', 'Allow') + $access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule($sqlSvcUserNameQualified, 'FullControl', 'None', 'None', 'Allow') $permissions.AddAccessRule($access_rule) Set-Acl -Path $path -AclObject $permissions } @@ -421,10 +418,9 @@ configuration ConfigureSQLVM # $subjectName = "CN=SQL.contoso.local" # $sqlServerEncryptionCertThumbprint = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=$ComputerName.$DomainFQDN" } | Select-Object -Expand Thumbprint - SqlSecureConnection EnableSecureConnection - { + SqlSecureConnection EnableSecureConnection { InstanceName = 'MSSQLSERVER' - Thumbprint = "CN=SQL.contoso.local" + Thumbprint = "CN=$ComputerName.$DomainFQDN" ForceEncryption = $false Ensure = 'Present' ServiceAccount = $SqlSvcCreds.UserName @@ -433,8 +429,7 @@ configuration ConfigureSQLVM } # Open port on the firewall only when everything is ready, as SharePoint DSC is testing it to start creating the farm - Firewall AddDatabaseEngineFirewallRule - { + Firewall AddDatabaseEngineFirewallRule { Direction = "Inbound" Name = "SQL-Server-Database-Engine-TCP-In" DisplayName = "SQL Server Database Engine (TCP-In)" @@ -448,23 +443,22 @@ configuration ConfigureSQLVM } } -function Get-NetBIOSName -{ +function Get-NetBIOSName { [OutputType([string])] param( [string]$DomainFQDN ) if ($DomainFQDN.Contains('.')) { - $length=$DomainFQDN.IndexOf('.') + $length = $DomainFQDN.IndexOf('.') if ( $length -ge 16) { - $length=15 + $length = 15 } - return $DomainFQDN.Substring(0,$length) + return $DomainFQDN.Substring(0, $length) } else { if ($DomainFQDN.Length -gt 15) { - return $DomainFQDN.Substring(0,15) + return $DomainFQDN.Substring(0, 15) } else { return $DomainFQDN @@ -472,35 +466,34 @@ function Get-NetBIOSName } } -function WaitForSqlSetup -{ +function WaitForSqlSetup { # Wait for SQL Server Setup to finish before proceeding. - while ($true) - { - try - { - Get-ScheduledTaskInfo "\ConfigureSqlImageTasks\RunConfigureImage" -ErrorAction Stop + $maxAttempts = 20 + $attemptsCounter = 0 + while ($attemptsCounter -lt $maxAttempts) { + try { + $attemptsCounter++ + $taskResult = Get-ScheduledTaskInfo "\ConfigureSqlImageTasks\RunConfigureImage" -ErrorAction Stop + Write-Verbose -Verbose -Message "Attempt $($attemptsCounter): ScheduledTaskInfo results: LastRunTime: $($taskResult.LastRunTime); LastTaskResult: $($taskResult.LastTaskResult); NextRunTime: $($taskResult.NextRunTime); NumberOfMissedRuns: $($taskResult.NumberOfMissedRuns);" Start-Sleep -Seconds 5 } - catch - { + catch { break } } } - - <# $password = ConvertTo-SecureString -String "mytopsecurepassword" -AsPlainText -Force $DomainAdminCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "yvand", $password $SqlSvcCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "sqlsvc", $password -$SPSetupCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "spsetup", $password -$DNSServerIP = "10.1.1.4" +$DNSServerIP = "10.1.1.100" $DomainFQDN = "contoso.local" +$SPSetupUserName = "spsetup" -$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\ConfigureSQLVM.0\ConfigureSQLVM" -ConfigureSQLVM -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DomainAdminCreds $DomainAdminCreds -SqlSvcCreds $SqlSvcCreds -SPSetupCreds $SPSetupCreds -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +$outputPath = "C:\Packages\Plugins\Microsoft.Powershell.DSC\2.83.5\DSCWork\dsc-sql.0\ConfigSql" +ConfigSql -DNSServerIP $DNSServerIP -DomainFQDN $DomainFQDN -DomainAdminCreds $DomainAdminCreds -SqlSvcCreds $SqlSvcCreds -SPSetupUserName $SPSetupUserName -ConfigurationData @{AllNodes=@(@{ NodeName="localhost"; PSDscAllowPlainTextPassword=$true })} -OutputPath $outputPath +Set-DscLocalConfigurationManager -Path $outputPath Start-DscConfiguration -Path $outputPath -Wait -Verbose -Force #> diff --git a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSQLVM.zip b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-sql.zip similarity index 51% rename from application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSQLVM.zip rename to application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-sql.zip index 05a27bca236e..e34890e90d68 100644 Binary files a/application-workloads/sharepoint/sharepoint-adfs/dsc/ConfigureSQLVM.zip and b/application-workloads/sharepoint/sharepoint-adfs/dsc/dsc-sql.zip differ diff --git a/application-workloads/sharepoint/sharepoint-adfs/firewall.bicep b/application-workloads/sharepoint/sharepoint-adfs/firewall.bicep index 1cd14711958e..93858d46b3f3 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/firewall.bicep +++ b/application-workloads/sharepoint/sharepoint-adfs/firewall.bicep @@ -8,12 +8,12 @@ param https_port int = 8443 @description('Tags to apply on the resources.') param tags object -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-10-01' existing = { +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2025-05-01' existing = { scope: resourceGroup() name: virtualNetworkName } -resource bastion_subnet 'Microsoft.Network/virtualNetworks/subnets@2024-10-01' = { +resource bastion_subnet 'Microsoft.Network/virtualNetworks/subnets@2025-05-01' = { parent: virtualNetwork name: 'AzureFirewallSubnet' properties: { @@ -22,7 +22,7 @@ resource bastion_subnet 'Microsoft.Network/virtualNetworks/subnets@2024-10-01' = } } -resource firewall_policy_proxy 'Microsoft.Network/firewallPolicies@2024-10-01' = { +resource firewall_policy_proxy 'Microsoft.Network/firewallPolicies@2025-05-01' = { name: 'firewall-policy-proxy' location: location tags: tags @@ -40,7 +40,7 @@ resource firewall_policy_proxy 'Microsoft.Network/firewallPolicies@2024-10-01' = } } -resource firewall_proxy_rules 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2024-10-01' = { +resource firewall_proxy_rules 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2025-05-01' = { name: 'rules' parent: firewall_policy_proxy properties: { diff --git a/application-workloads/sharepoint/sharepoint-adfs/main.bicep b/application-workloads/sharepoint/sharepoint-adfs/main.bicep index f2ac127a6c96..4a0cf324201e 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/main.bicep +++ b/application-workloads/sharepoint/sharepoint-adfs/main.bicep @@ -21,11 +21,19 @@ param location string = resourceGroup().location ]) param sharePointVersion string = 'Subscription-Latest' -@description('FQDN of the Active Directory forest.') -@minLength(5) -param domainFqdn string = 'contoso.local' +@description('Level of configuration to apply on the SharePoint farm.') +@allowed([ + 'Minimum' + 'Light' + 'Medium' + 'Full' +]) +param sharePointConfigurationLevel string = 'Light' + +@description('Set to true if the default zone of the main web application must use HTTPS protocol.') +param defaultZoneMustBeHttps bool = false -@description('Number of servers with MinRole Front-end to add to the farm.') +@description('Number of additional servers with the MinRole front-end to add to the farm.') @allowed([ 0 1 @@ -35,6 +43,10 @@ param domainFqdn string = 'contoso.local' ]) param frontEndServersCount int = 0 +@description('FQDN of the Active Directory forest.') +@minLength(5) +param domainFqdn string = 'contoso.local' + @description('Name of the Active Directory and SharePoint administrator. "admin" and "administrator" are not allowed.') @minLength(1) param adminUsername string @@ -68,7 +80,7 @@ IMPORTANT: With AzureFirewallProxy, you need to either enable Azure Bastion, or ]) param outboundAccessMethod string = 'PublicIPAddress' -@description('Set if the Public IP addresses of virtual machines should have a name label.') +@description('If a virtual machine has a public IP address, specify if it should also have a public DNS name.') @allowed([ 'No' 'SharePointVMsOnly' @@ -76,13 +88,13 @@ param outboundAccessMethod string = 'PublicIPAddress' ]) param addNameToPublicIpAddresses string = 'SharePointVMsOnly' -@description('Specify if Azure Bastion should be provisioned. See https://azure.microsoft.com/en-us/services/azure-bastion for more information.') +@description('Specify if Azure Bastion Developer should be provisioned. See https://go.microsoft.com/fwlink/?linkid=2249215 for more information.') param enableAzureBastion bool = false @description('Enable the Azure Hybrid Benefit on virtual machines, to use your on-premises Windows Server licenses and reduce cost. See https://docs.microsoft.com/en-us/azure/virtual-machines/windows/hybrid-use-benefit-licensing for more information.') param enableHybridBenefitServerLicenses bool = false -@description('Time zone of the virtual machines. Type "[TimeZoneInfo]::GetSystemTimeZones().Id" in PowerShell to get the list.') +@description('Time zone of the virtual machines (also used for the parameter autoShutdownTime).') @minLength(2) @allowed([ 'Dateline Standard Time' @@ -277,8 +289,8 @@ param vmSharePointSize string = 'Standard_B4as_v2' ]) param vmSharePointStorage string = 'StandardSSD_LRS' -@description('The base URI where artifacts required by this template are located including a trailing \'/\'') -param _artifactsLocation string = deployment().properties.templateLink.uri +@description('The base URI where artifacts required by this template are located.') +param _artifactsLocation string = uri(deployment().properties.templateLink.uri, 'dsc/') @secure() @description('The sasToken required to access _artifactsLocation. When the template is deployed using the accompanying scripts, a sasToken will be automatically generated. Use the defaultValue if the staging location is not secured.') @@ -310,7 +322,7 @@ var sharePointSettings = { } sharePointSubscriptionBits: [ { - Label: 'RTM' + Label: 'SPRTM' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/3/f/5/3f5f8a7e-462b-41ff-a5b2-04bdf5821ceb/OfficeServer.iso' @@ -320,7 +332,7 @@ var sharePointSettings = { ] } { - Label: '22H2' + Label: 'SP22H2' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/8/d/f/8dfcb515-6e49-42e5-b20f-5ebdfd19d8e7/wssloc-subscription-kb5002270-fullfile-x64-glb.exe' @@ -335,7 +347,7 @@ var sharePointSettings = { ] } { - Label: '23H1' + Label: 'SP23H1' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/c/6/a/c6a17105-3d86-42ad-888d-49b22383bfa1/uber-subscription-kb5002355-fullfile-x64-glb.exe' @@ -343,7 +355,7 @@ var sharePointSettings = { ] } { - Label: '23H2' + Label: 'SP23H2' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/f/5/5/f5559e3f-8b24-419f-b238-b09cf986e927/uber-subscription-kb5002474-fullfile-x64-glb.exe' @@ -351,7 +363,7 @@ var sharePointSettings = { ] } { - Label: '24H1' + Label: 'SP24H1' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/b/a/b/bab0c7cc-0454-474b-8538-7927f75e6486/uber-subscription-kb5002564-fullfile-x64-glb.exe' @@ -359,7 +371,7 @@ var sharePointSettings = { ] } { - Label: '24H2' + Label: 'SP24H2' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/6/6/a/66a0057f-79af-4307-8263-103ee75ef5c6/uber-subscription-kb5002640-fullfile-x64-glb.exe' @@ -367,7 +379,7 @@ var sharePointSettings = { ] } { - Label: '25H1' + Label: 'SP25H1' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/0b131072-7ee6-41ea-b33a-b3410865f3a0/uber-subscription-kb5002698-fullfile-x64-glb.exe' @@ -375,7 +387,7 @@ var sharePointSettings = { ] } { - Label: '25H2' + Label: 'SP25H2' Packages: [ { DownloadUrl: 'https://download.microsoft.com/download/0ae39b29-890d-428c-bcee-c93eeca2053b/uber-subscription-kb5002784-fullfile-x64-glb.exe' @@ -383,10 +395,10 @@ var sharePointSettings = { ] } { - Label: 'Latest' + Label: 'SPLatest' Packages: [ { - DownloadUrl: 'https://download.microsoft.com/download/2ac104e1-555a-4186-9f83-ffca3ec88258/uber-subscription-kb5002800-fullfile-x64-glb.exe' + DownloadUrl: 'https://download.microsoft.com/download/f839c57c-7b4e-4213-b03b-2c1508e13588/uber-subscription-kb5002853-fullfile-x64-glb.exe' } ] } @@ -414,9 +426,12 @@ var environmentSettings = { dcPrivateIPAddress: '10.1.1.100' sharePointSitesAuthority: 'spsites' sharePointCentralAdminPort: 5000 - sharePointBitsSelected: (sharePointSettings.isSharePointSubscription + sharePointBitsDsc: (sharePointSettings.isSharePointSubscription ? sharePointSettings.sharePointSubscriptionBits : '') + sharePointVersion: (sharePointSettings.isSharePointSubscription + ? 'SP${split(sharePointVersion, '-')[1]}' + : sharePointVersion) localAdminUserName: 'l-${uniqueString(subscription().subscriptionId)}' enableAnalysis: false applyBrowserPolicies: true @@ -503,9 +518,9 @@ var baseVirtualMachines = [ dscSettings: { wmfVersion: 'latest' configuration: { - url: uri(_artifactsLocation, 'dsc/ConfigureDCVM.zip${_artifactsLocationSasToken}') - script: 'ConfigureDCVM.ps1' - function: 'ConfigureDCVM' + url: uri(_artifactsLocation, 'dsc-dc.zip${_artifactsLocationSasToken}') + script: 'dsc-dc.ps1' + function: 'ConfigDc' } configurationArguments: { domainFQDN: domainFqdn @@ -529,6 +544,14 @@ var baseVirtualMachines = [ UserName: environmentSettings.adfsSvcUserName Password: otherAccountsPassword } + SqlSvcCreds: { + UserName: environmentSettings.sqlSvcUserName + Password: otherAccountsPassword + } + SPSetupCreds: { + UserName: environmentSettings.spSetupUserName + Password: otherAccountsPassword + } } } } @@ -565,13 +588,14 @@ var baseVirtualMachines = [ dscSettings: { wmfVersion: 'latest' configuration: { - url: uri(_artifactsLocation, 'dsc/ConfigureSQLVM.zip${_artifactsLocationSasToken}') - script: 'ConfigureSQLVM.ps1' - function: 'ConfigureSQLVM' + url: uri(_artifactsLocation, 'dsc-sql.zip${_artifactsLocationSasToken}') + script: 'dsc-sql.ps1' + function: 'ConfigSql' } configurationArguments: { DNSServerIP: environmentSettings.dcPrivateIPAddress DomainFQDN: domainFqdn + SPSetupUserName: environmentSettings.spSetupUserName } privacy: { dataCollection: 'enable' @@ -587,10 +611,6 @@ var baseVirtualMachines = [ UserName: environmentSettings.sqlSvcUserName Password: otherAccountsPassword } - SPSetupCreds: { - UserName: environmentSettings.spSetupUserName - Password: otherAccountsPassword - } } } } @@ -629,10 +649,10 @@ var baseVirtualMachines = [ configuration: { url: uri( _artifactsLocation, - '${sharePointSettings.isSharePointSubscription ? 'dsc/ConfigureSPSE.zip' : 'dsc/ConfigureSPLegacy.zip'}${_artifactsLocationSasToken}' + '${sharePointSettings.isSharePointSubscription ? 'dsc-spse-main.zip' : 'dsc-splegacy-main.zip'}${_artifactsLocationSasToken}' ) - script: (sharePointSettings.isSharePointSubscription ? 'ConfigureSPSE.ps1' : 'ConfigureSPLegacy.ps1') - function: 'ConfigureSPVM' + script: (sharePointSettings.isSharePointSubscription ? 'dsc-spse-main.ps1' : 'dsc-splegacy-main.ps1') + function: 'ConfigSpMain' } configurationArguments: { DNSServerIP: environmentSettings.dcPrivateIPAddress @@ -640,11 +660,13 @@ var baseVirtualMachines = [ DCServerName: templateSettings.vmDCName SQLServerName: templateSettings.vmSQLName SQLAlias: environmentSettings.sqlAlias - SharePointVersion: sharePointVersion + SharePointVersion: environmentSettings.sharePointVersion SharePointSitesAuthority: environmentSettings.sharePointSitesAuthority SharePointCentralAdminPort: environmentSettings.sharePointCentralAdminPort EnableAnalysis: environmentSettings.enableAnalysis - SharePointBits: environmentSettings.sharePointBitsSelected + SharePointBits: environmentSettings.sharePointBitsDsc + DefaultZoneMustBeHttps: defaultZoneMustBeHttps + ConfigurationLevel: sharePointConfigurationLevel } privacy: { dataCollection: 'enable' @@ -714,10 +736,10 @@ var frontendVirtualMachinesSettings = { configuration: { url: uri( _artifactsLocation, - '${(sharePointSettings.isSharePointSubscription ? 'dsc/ConfigureFESE.zip' : 'dsc/ConfigureFELegacy.zip')}${_artifactsLocationSasToken}' + '${(sharePointSettings.isSharePointSubscription ? 'dsc-spse-frontend.zip' : 'dsc-splegacy-frontend.zip')}${_artifactsLocationSasToken}' ) - script: (sharePointSettings.isSharePointSubscription ? 'ConfigureFESE.ps1' : 'ConfigureFELegacy.ps1') - function: 'ConfigureFEVM' + script: (sharePointSettings.isSharePointSubscription ? 'dsc-spse-frontend.ps1' : 'dsc-splegacy-frontend.ps1') + function: 'ConfigSpFrontend' } configurationArguments: { DNSServerIP: environmentSettings.dcPrivateIPAddress @@ -725,10 +747,12 @@ var frontendVirtualMachinesSettings = { DCServerName: templateSettings.vmDCName SQLServerName: templateSettings.vmSQLName SQLAlias: environmentSettings.sqlAlias - SharePointVersion: sharePointVersion + SharePointVersion: environmentSettings.sharePointVersion SharePointSitesAuthority: environmentSettings.sharePointSitesAuthority EnableAnalysis: environmentSettings.enableAnalysis - SharePointBits: environmentSettings.sharePointBitsSelected + SharePointBits: environmentSettings.sharePointBitsDsc + DefaultZoneMustBeHttps: defaultZoneMustBeHttps + ConfigurationLevel: sharePointConfigurationLevel } privacy: { dataCollection: 'enable' diff --git a/application-workloads/sharepoint/sharepoint-adfs/metadata.json b/application-workloads/sharepoint/sharepoint-adfs/metadata.json index 98cbe30c64bd..5234ab427e06 100644 --- a/application-workloads/sharepoint/sharepoint-adfs/metadata.json +++ b/application-workloads/sharepoint/sharepoint-adfs/metadata.json @@ -5,5 +5,5 @@ "description": "Create a DC, a SQL Server 2025, and from 1 to 5 server(s) hosting a SharePoint Subscription / 2019 / 2016 farm with an extensive configuration, including trusted authentication, user profiles with personal sites, an OAuth trust (using a certificate), a dedicated IIS site for hosting high-trust add-ins, etc... The latest version of key softwares (including Fiddler, vscode, np++, 7zip, ULS Viewer) is installed. SharePoint machines have additional fine-tuning to make them immediately usable (remote administration tools, custom policies for Edge and Chrome, shortcuts, etc...).", "summary": "Create a SharePoint Server farm with an extensive configuration. Key softwares (inc. Fiddler, vscode, np++, 7zip and ULS Viewer) and fine tuning make the farm immediately useful for most scenarios", "githubUsername": "Yvand", - "dateUpdated": "2025-12-05" + "dateUpdated": "2026-04-24" } \ No newline at end of file