NTS.Tools.Application.psm1
function Start-C2RSetup { <# .Description this function can be used to install Microsoft 365 Apps .Parameter ConfigXMLPath defines the path to xml config file, cannot be used with UseDefaultConfigXML or UseSilentDefaultConfigXML .Parameter WorkingDir defines to folder where installation files should be saved .Parameter CleanUpInstallFiles if specified, the folder used in the parameter workdir will be deleted afterwards .Parameter UseDefaultConfigXML if specified, it will create config with "Outlook", "Word", "Excel", "Teams" and install those apps .Parameter UseSilentDefaultConfigXML same as parameter UseDefaultConfigXML, but silent .Parameter OfficeEdition define the office edition which should be installed with the default config .Example # this will install Microsoft 365 Apps for Business and deletes the setup files afterwards Install-M365Apps -UseDefaultConfigXML -OfficeEdition O365BusinessRetail -CleanUpInstallFiles .NOTES https://github.com/mallockey/Install-Office365Suite/blob/master/Install-Office365Suite.ps1 #> [CmdletBinding(DefaultParameterSetName = 'XMLFile')] [Alias("Install-M365Apps", "Install-OfficeLTSC")] param( [Parameter(Mandatory = $true, ParameterSetName = "XMLFile")] [string] $ConfigXMLPath, [Parameter(Mandatory = $false)] [string] $WorkingDir = "$($env:ProgramData)\NTS\Office", [Parameter(Mandatory = $false)] [switch] $CleanUpInstallFiles, [Parameter(Mandatory = $false, ParameterSetName = "DefaultXML")] [switch] $UseDefaultConfigXML, [Parameter(Mandatory = $false, ParameterSetName = "SilentDefaultXML")] [switch] $UseSilentDefaultConfigXML, [Parameter(Mandatory = $false, ParameterSetName = "DefaultXML")] [Parameter(Mandatory = $false, ParameterSetName = "SilentDefaultXML")] [ValidateSet( "O365ProPlusRetail", "O365BusinessRetail", "ProPlus2021Volume", "ProPlus2019Volume" )] [string] $OfficeEdition ) $ErrorActionPreference = "Stop" # check admin access $CurrentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (!($CurrentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) { Write-Warning "$($env:COMPUTERNAME): Script is not running as Administrator" Write-Warning "$($env:COMPUTERNAME): Please rerun this script as Administrator" exit } if (!(Test-Path $WorkingDir)) { New-Item -Path $WorkingDir -ItemType Directory | Out-Null } if ($UseDefaultConfigXML) { Write-Output "$($env:COMPUTERNAME): creating xml config file for $($OfficeEdition)" New-C2RConfigXML -ConfigXMLPath $WorkingDir -ConfigXMLFileName "config.xml" -OfficeEdition $OfficeEdition -IncludeApps ("Outlook", "Word", "Excel", "Teams") $ConfigXMLPath = "$($WorkingDir)\config.xml" } elseif ($UseSilentDefaultConfigXML) { Write-Output "$($env:COMPUTERNAME): creating xml config file for $($OfficeEdition)" New-C2RConfigXML -ConfigXMLPath $WorkingDir -ConfigXMLFileName "config.xml" -OfficeEdition $OfficeEdition -IncludeApps ("Outlook", "Word", "Excel", "Teams") -DisplayLevel None $ConfigXMLPath = "$($WorkingDir)\config.xml" } # verify config xml Test-OfficeConfiguration -ConfigurationXMLFilePath $ConfigXMLPath # Get Setup.exe Get-C2RSetup -OutPath $WorkingDir -FileName "Setup.exe" # Run the setup [xml]$OfficeConfig = Get-Content -Path $ConfigXMLPath Write-Output "$($env:COMPUTERNAME): start installation of $($OfficeConfig.Configuration.Add.Product.ID)" $Process = Start-Process "$($WorkingDir)\Setup.exe" -ArgumentList "/configure `"$ConfigXMLPath`"" -Wait -PassThru -WindowStyle Hidden if ($Process.ExitCode -ne 0) { throw "there was an error installing office - $($PSItem.Exception.Message)" } #Check if suite was installed correctly $RegLocations = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ) $ItemsFound = ((Get-ChildItem -Path $RegLocations).Name -match "$($OfficeConfig.Configuration.Add.Product.ID)")[0].ToString().Replace("HKEY_LOCAL_MACHINE\","") if($null -ne $ItemsFound) { $OfficeVersionInstalled = (Get-ItemProperty -Path "HKLM:\$($ItemsFound)").Displayname Write-Output "$($env:COMPUTERNAME): $($OfficeVersionInstalled) was successfully installed" } else { Write-Warning "$($env:COMPUTERNAME): $($OfficeConfig.Configuration.Add.Product.ID) was not detected after the install ran" } if ($CleanUpInstallFiles) { Start-FolderCleanUp -FolderToRemove $WorkingDir } } function New-C2RConfigXML { <# .Description this function can be used generate a microsoft 365 apps config file .Parameter ConfigXMLPath defines the folder where the config file will be saved .Parameter ConfigXMLFileName name of the config file .Parameter OfficeEdition define the office edtion proplus or business for example .Parameter ExcludeApps apps that should not be installed .Parameter IncludeApps apps that should be installed .Parameter LanguageIDs languages that should be installed, defaults to matchos .Parameter DisplayLevel installation display level .Parameter OfficeArch architecture of the microsoft 365 suite .Parameter AcceptEULA should the eula be accepted or not .Parameter EnableUpdates enabled updates for the suite .Parameter Channel update channel .Parameter IncludeProject include project in the installation .Parameter IncludeVisio include visio in the installation .Parameter SharedComputerLicensing activate shared computer licensing .Parameter RemoveMSI unininstall msi version of office before starting the installation .Parameter SetFileFormat sets the default file format for word, excel, powerpoint .Example # this will create the config xml under $WorkDir to install onyl the specified apps New-C2RConfigXML -ConfigXMLPath $WorkingDir -ConfigXMLFileName "config.xml" -IncludeApps "Outlook", "Word", "Excel", "Teams" .NOTES https://github.com/mallockey/Install-Office365Suite/blob/master/Install-Office365Suite.ps1 #> [CmdletBinding(DefaultParameterSetName = 'ExcludeApps')] param( [Parameter(Mandatory = $false)] [string] $ConfigXMLPath = "$($env:ProgramData)\NTS\Office\", [Parameter(Mandatory = $false)] [string] $ConfigXMLFileName = "config.xml", [Parameter(Mandatory = $false)] [ValidateSet( "O365ProPlusRetail", "O365BusinessRetail", "ProPlus2021Volume", "ProPlus2019Volume" )] [string] $OfficeEdition = 'O365ProPlusRetail', [Parameter(Mandatory = $false, ParameterSetName = "ExcludeApps")] [ValidateSet( 'Groove', 'Outlook', 'OneNote', 'Access', 'OneDrive', 'Publisher', 'Word', 'Excel', 'PowerPoint', 'Teams', 'Lync' )] [Array] $ExcludeApps, [Parameter(Mandatory = $false, ParameterSetName = "IncludeApps")] [ValidateSet( 'Groove', 'Outlook', 'OneNote', 'Access', 'OneDrive', 'Publisher', 'Word', 'Excel', 'PowerPoint', 'Teams', 'Lync' )] [Array] $IncludeApps, [Parameter(Mandatory = $false)] [Array] $LanguageIDs, [Parameter(Mandatory = $false)] [ValidateSet("None", "Full")] [string] $DisplayLevel = "Full", [Parameter(Mandatory = $false)] [ValidateSet('64', '32')] [string] $OfficeArch = '64', [Parameter(Mandatory = $false)] [ValidateSet("TRUE", "FALSE")] [string] $AcceptEULA = "TRUE", [Parameter(Mandatory = $false)] [ValidateSet("TRUE", "FALSE")] [string] $EnableUpdates = "TRUE", [Parameter(Mandatory = $false)] [ValidateSet('SemiAnnualPreview', 'SemiAnnual', 'MonthlyEnterprise', 'CurrentPreview', 'Current')] [string] $Channel = 'Current', [Parameter(Mandatory = $false)] [Switch] $IncludeProject, [Parameter(Mandatory = $false)] [Switch] $IncludeVisio, [Parameter(Mandatory = $false)] [ValidateSet(0, 1)] [string] $SharedComputerLicensing = '0', [Parameter(Mandatory = $false)] [bool] $RemoveMSI = $true, [Parameter(Mandatory = $false)] [bool] $SetFileFormat = $true ) $ErrorActionPreference = 'Stop' $ValidApps = ( 'Groove', 'Outlook', 'OneNote', 'Access', 'OneDrive', 'Publisher', 'Word', 'Excel', 'PowerPoint', 'Teams', 'Lync' ) if ($ExcludeApps -and $IncludeApps) { throw "you can use 'ExcludeApps' or 'IncludeApps' not both" } elseif ($ExcludeApps) { $ExcludeApps | ForEach-Object { $ExcludeAppsString += "<ExcludeApp ID =`"$_`" />" } } elseif ($IncludeApps) { $ValidApps | Where-Object { $PSItem -notin $IncludeApps } | ForEach-Object { $ExcludeAppsString += "<ExcludeApp ID =`"$_`" />" } } if ($LanguageIDs) { $LanguageIDs | ForEach-Object { $LanguageString += "<Language ID =`"$_`" />" } } else { $LanguageString = "<Language ID=`"MatchOS`" />" } if ($OfficeArch) { $OfficeArchString = "`"$OfficeArch`"" } if ($RemoveMSI) { $RemoveMSIString = '<RemoveMSI />' } else { $RemoveMSIString = $Null } if ($SetFileFormat) { $AppSettingsString = '<AppSettings> <User Key="software\microsoft\office\16.0\excel\options" Name="defaultformat" Value="51" Type="REG_DWORD" App="excel16" Id="L_SaveExcelfilesas" /> <User Key="software\microsoft\office\16.0\powerpoint\options" Name="defaultformat" Value="27" Type="REG_DWORD" App="ppt16" Id="L_SavePowerPointfilesas" /> <User Key="software\microsoft\office\16.0\word\options" Name="defaultformat" Value="" Type="REG_SZ" App="word16" Id="L_SaveWordfilesas" /> </AppSettings>' } else { $AppSettingsString = $Null } if ($OfficeEdition -eq "ProPlus2021Volume") { $ChannelString = "Channel=`"PerpetualVL2021`"" } elseif ($OfficeEdition -eq "ProPlus2019Volume") { $ChannelString = "Channel=`"PerpetualVL2019`"" } elseif ($Channel) { $ChannelString = "Channel=`"$Channel`"" } else { $ChannelString = $Null } if ($IncludeProject) { $ProjectString = "<Product ID=`"ProjectProRetail`"`>$ExcludeAppsString $LanguageString</Product>" } else { $ProjectString = $Null } if ($IncludeVisio) { $VisioString = "<Product ID=`"VisioProRetail`"`>$ExcludeAppsString $LanguageString</Product>" } else { $VisioString = $Null } $OfficeXML = [XML]@" <Configuration> <Add OfficeClientEdition=$($OfficeArchString) $($ChannelString) $($SourcePathString) > <Product ID="$($OfficeEdition)"> $($LanguageString) $($ExcludeAppsString) </Product> $($ProjectString) $($VisioString) </Add> <Property Name="SharedComputerLicensing" Value="$($SharedComputerlicensing)" /> <Display Level="$($DisplayLevel)" AcceptEULA="$($AcceptEULA)" /> <Updates Enabled="$($EnableUpdates)" /> $($AppSettingsString) $($RemoveMSIString) </Configuration> "@ try { if (!(Test-Path $ConfigXMLPath)) { New-Item -Path $ConfigXMLPath -ItemType Directory | Out-Null } Write-Output "$($env:COMPUTERNAME): XML Config file will be saved to $($ConfigXMLPath)\$($ConfigXMLFileName)" $OfficeXML.Save("$($ConfigXMLPath)\$($ConfigXMLFileName)") } catch { throw "could not create config xml at $($ConfigXMLPath) - $($PSItem.Exception.Message)" } } function Get-C2RSetup { <# .Description this function downloads office deployment tool kit and extract it, the setup.exe file be keept .Parameter OutPath folder pat where the setup file should be placed .Parameter FileName name of the setup.exe file .Parameter KeepODTFiles if specified, the folder containing the office deployment tool kit files will not be removed .Example # this will install download the setup.exe file to the folder in $Workdir Get-C2RSetup -OutPath $WorkingDir -FileName "Setup.exe" .NOTES https://github.com/mallockey/Install-Office365Suite/blob/master/Install-Office365Suite.ps1 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $OutPath = "$($env:ProgramData)\NTS\Office\Setup", [Parameter(Mandatory = $false)] [string] $FileName = "setup.exe", [Parameter(Mandatory = $false)] [switch] $KeepODTFiles ) $ODTFolder = "$($OutPath)\ODT" $ODTSetupFilePath = "$($ODTFolder)\ODTSetup.exe" # get odt try { if (!(Test-Path $OutPath)) { New-Item -Path $OutPath -ItemType Directory | Out-Null } if (!(Test-Path $ODTFolder)) { New-Item -Path $ODTFolder -ItemType Directory | Out-Null } if (Test-Path -Path $ODTSetupFilePath) { throw "$($ODTSetupFilePath) already exits" } else { try { [String]$MSWebPage = Invoke-RestMethod "https://www.microsoft.com/en-us/download/confirmation.aspx?id=49117" $ODTDownloadURL = $MSWebPage | ForEach-Object { if ($PSItem -match 'url=(https://.*officedeploymenttool.*\.exe)') { $matches[1] } } } catch { throw "could not find odt download url - $($PSItem.Exception.Message)" } Invoke-WebRequest -Uri $ODTDownloadURL -OutFile $ODTSetupFilePath } } catch { throw "could not download ODTSetup - $($PSItem.Exception.Message)" } # get setup try { Write-Output "$($env:COMPUTERNAME): extracting office deployment tool kit" Start-Process -FilePath $ODTSetupFilePath -ArgumentList "/quiet /extract:`"$($ODTFolder)`"" -Wait Write-Output "$($env:COMPUTERNAME): Copy Setup.exe to $($OutPath)" Copy-Item -Path "$($ODTFolder)\setup.exe" -Destination "$($OutPath)\$($FileName)" -Force if (!($KeepODTFiles)) { Start-FolderCleanUp -FolderToRemove $ODTFolder } } catch { Write-Warning "$($env:COMPUTERNAME): Error running the Office Deployment Tool - $($PSItem.Exception.Message)" } } function Test-OfficeConfiguration { <# .Description this function verifies a office config xml file against microsofts service .Parameter ConfigurationXMLFilePath path to the xml file .Example # this will verify the xml file at $ConfigXMLPath Test-OfficeConfiguration -ConfigurationXMLFilePath $ConfigXMLPath .NOTES https://github.com/mallockey/Install-Office365Suite/blob/master/Install-Office365Suite.ps1 #> param( [Parameter(Mandatory = $true)] [String] $ConfigurationXMLFilePath ) $ErrorActionPreference = 'Stop' try { $OfficeXML = Get-Content -Path $ConfigurationXMLFilePath } catch { throw "There was an error generating the XML config file" } try { Write-Output "$($env:COMPUTERNAME): Uploading XML config file to clients.config.office.net to verify syntax" Invoke-RestMethod -Uri 'https://clients.config.office.net/intents/v1.0/DeploymentSettings/ImportConfiguration' ` -Method Post ` -Body $OfficeXML ` -ContentType 'text/xml' | Out-Null Write-Output "$($env:COMPUTERNAME): XML config file was successfully verified" } catch { throw "The XML is not formatted correctly" } } |