Let’s forget all about my previous step 3. For the sake of argument, step 3 is dead, long live step 3.1! Any script should be a living, breathing document which gets updated as needed. In this case, it was needed. Why would I burn such a good script to the ground? Well, to be blunt, I’ve found a better way to do this, with better ultimate performance.
If you need a refresher on the previous steps, I have Step 1 for Hyper-V, Step 1 for VMware, and Step 2.
Let start with the basics again – variable declaration.
Variable Declaration
The only variables that I’m defining here are the drives on which the various parts of the system lives. If you’ve followed my step 1 & 2, then you are using these drive designations. If not, then you should change this for your own needs.
$ProgramsDrive = "E:\"
$WebDrive = "F:\"
$LogDrive = "G:\"
Near the end of the previous post, I redirected a bunch of folders to the other drives, but I decided that I wanted to do the same thing for IIS before I installed it.
Redirect IIS
This is a big block, but basically it says that if the IIS folder doesn’t already exist (it shouldn’t since you didn’t install it yet, right?), then create it and create one on the “Web” drive as well, then redirect the C:\
drive to the “Web” drive. This was originally based on the mklink
executable, but I am now using the New-Item
cmdlet.
if ( -not ( Test-Path -Path "C:\inetpub" -ErrorAction SilentlyContinue ) )
{
$Redirections = @()
$Redirections += New-Object -TypeName PSObject -Property ( [ordered]@{ Order = [int]6; SourcePath = "C:\inetpub"; TargetDrive = $WebDrive } )
$Redirections | Add-Member -MemberType ScriptProperty -Name TargetPath -Value { $this.SourcePath.Replace("C:\", $this.TargetDrive ) } -Force
ForEach ( $Redirection in $Redirections | Sort-Object -Property Order )
{
# Check to see if the target path exists - if not, create the target path
if ( -not ( Test-Path -Path $Redirection.TargetPath -ErrorAction SilentlyContinue ) )
{
Write-Host "Creating Path for Redirection [$( $Redirection.TargetPath )]" -ForegroundColor Yellow
New-Item -ItemType Directory -Path $Redirection.TargetPath | Out-Null
}
# Execute it
Write-Host "Creating Junction point from $( $Redirection.SourcePath ) --> $( $Redirection.TargetPath ) " -NoNewline
New-Item -ItemType Junction -Path $Redirection.SourcePath -Value $Redirection.TargetPath | Out-Null
Write-Host "[COMPLETED]" -ForegroundColor Green
}
}
Install the missing Windows Features
This is pretty cut and dry. We need these features, so instead of letting the Orion installer do it for me, I do it in advance. The Add-WindowsFeature
is one of my favorite script commands.
If you are installing the Orion Platform products version 2020.2.6 or later, then you don’t need the MSMQ (Message Queue) features. If that’s your situation, run the below.
# this is a list of the Windows Features that we'll need (omitting MSMQ for Orion Platform 2020.2.6+)
# it's being filtered for those which are not already installed
$Features = Get-WindowsFeature -Name FileAndStorage-Services, File-Services, FS-FileServer, FS-Data-Deduplication, Storage-Services, Web-Server, Web-WebServer, Web-Common-Http, Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content, Web-Health, Web-Http-Logging, Web-Log-Libraries, Web-Request-Monitor, Web-Performance, Web-Stat-Compression, Web-Dyn-Compression, Web-Security, Web-Filtering, Web-Windows-Auth, Web-App-Dev, Web-Net-Ext45, Web-Asp-Net45, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Mgmt-Tools, Web-Mgmt-Console, Web-Mgmt-Compat, Web-Metabase, NET-Framework-45-Features, NET-Framework-45-Core, NET-Framework-45-ASPNET, NET-WCF-Services45, NET-WCF-TCP-PortSharing45, FS-SMB1, Windows-Defender-Features, Windows-Defender, Windows-Defender-Gui, PowerShellRoot, PowerShell, PowerShell-ISE, WoW64-Support, XPS-Viewer | Where-Object { -not $_.Installed }
$Features | Add-WindowsFeature
If you are installing Orion 2020.2.5 or earlier, then you’ll need the MSMQ (Message Queue) features, so run this script instead.
# this is a list of the Windows Features that we'll need
# it's being filtered for those which are not already installed
$Features = Get-WindowsFeature -Name FileAndStorage-Services, File-Services, FS-FileServer, FS-Data-Deduplication, Storage-Services, Web-Server, Web-WebServer, Web-Common-Http, Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content, Web-Health, Web-Http-Logging, Web-Log-Libraries, Web-Request-Monitor, Web-Performance, Web-Stat-Compression, Web-Dyn-Compression, Web-Security, Web-Filtering, Web-Windows-Auth, Web-App-Dev, Web-Net-Ext45, Web-Asp-Net45, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Mgmt-Tools, Web-Mgmt-Console, Web-Mgmt-Compat, Web-Metabase, NET-Framework-45-Features, NET-Framework-45-Core, NET-Framework-45-ASPNET, NET-WCF-Services45, NET-WCF-TCP-PortSharing45, MSMQ, MSMQ-Services, MSMQ-Server, FS-SMB1, Windows-Defender-Features, Windows-Defender, Windows-Defender-Gui, PowerShellRoot, PowerShell, PowerShell-ISE, WoW64-Support, XPS-Viewer | Where-Object { -not $_.Installed }
$Features | Add-WindowsFeature
Enabling Disk Performance Metrics in Task Manager
Yeah, yeah, I know – this is a crutch for me, but I like having the Disk Metrics in the Task Manager.
Start-Process -FilePath "C:\Windows\System32\diskperf.exe" -ArgumentList "-Y" -Wait
Deduplicating the Logs
Deduplicating the log files saves a bunch of disk space. If you use a back-end storage solution that deduplicates, then you don’t need to do this, but it doesn’t really hurt to do it here as well AFAIK.
if ( Get-WindowsFeature -Name FS-Data-Deduplication | Where-Object { $_.Installed } )
{
Enable-DedupVolume -Volume ( $LogDrive.Replace("\", "") )
Get-DedupVolume -Volume ( $LogDrive.Replace("\", "") ) | Set-DedupVolume -OptimizeInUseFiles -OptimizePartialFiles -MinimumFileAgeDays 0 -Verify $true
}
Clean up Default IIS
Out of the box, when you install IIS, several things get created. I see no problem with this, but I like to free up as much memory and processor as possible for other uses, so I delete them.
Get-WebSite -Name "Default Web Site" | Remove-WebSite -Confirm:$false
Remove-WebAppPool -Name ".NET v2.0" -Confirm:$false -ErrorAction SilentlyContinue
Remove-WebAppPool -Name ".NET v2.0 Classic" -Confirm:$false -ErrorAction SilentlyContinue
Remove-WebAppPool -Name ".NET v4.5" -Confirm:$false -ErrorAction SilentlyContinue
Remove-WebAppPool -Name ".NET v4.5 Classic" -Confirm:$false -ErrorAction SilentlyContinue
Remove-WebAppPool -Name "Classic .NET AppPool" -Confirm:$false -ErrorAction SilentlyContinue
Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -ErrorAction SilentlyContinue
Changing IIS Configuration Files
This is where it gets hairy. There are many settings that I want to change. Specifically, I want to change the logging location, type, fields, and period. I also change the compressed file location and two disk space settings. Before I touch any of this though, I make a backup copy of the config file.
# XML Object that will be used for processing
$ConfigFile = New-Object -TypeName System.Xml.XmlDocument
# Change the Application Host settings
$ConfigFilePath = "C:\Windows\System32\inetsrv\config\applicationHost.config"
# Load the Configuration File
$ConfigFile.Load($ConfigFilePath)
# Save a backup if one doesn't already exist
if ( -not ( Test-Path -Path "$ConfigFilePath.orig" -ErrorAction SilentlyContinue ) )
{
Write-Host "Making Backup of $ConfigFilePath with '.orig' extension added" -ForegroundColor Yellow
$ConfigFile.Save("$ConfigFilePath.orig")
}
# change the settings (create if missing, update if existing)
$ConfigFile.configuration.'system.applicationHost'.log.centralBinaryLogFile.SetAttribute("directory", [string]( Join-Path -Path $LogDrive -ChildPath "inetpub\logs\LogFiles" ) )
$ConfigFile.configuration.'system.applicationHost'.log.centralW3CLogFile.SetAttribute("directory", [string]( Join-Path -Path $LogDrive -ChildPath "inetpub\logs\LogFiles" ) )
$ConfigFile.configuration.'system.applicationHost'.sites.siteDefaults.logfile.SetAttribute("directory", [string]( Join-Path -Path $LogDrive -ChildPath "inetpub\logs\LogFiles" ) )
$ConfigFile.configuration.'system.applicationHost'.sites.siteDefaults.logfile.SetAttribute("logFormat", "W3C" )
$ConfigFile.configuration.'system.applicationHost'.sites.siteDefaults.logfile.SetAttribute("logExtFileFlags", "Date, Time, ClientIP, UserName, SiteName, ComputerName, ServerIP, Method, UriStem, UriQuery, HttpStatus, Win32Status, BytesSent, BytesRecv, TimeTaken, ServerPort, UserAgent, Cookie, Referer, ProtocolVersion, Host, HttpSubStatus" )
$ConfigFile.configuration.'system.applicationHost'.sites.siteDefaults.logfile.SetAttribute("period", "Hourly")
$ConfigFile.configuration.'system.applicationHost'.sites.siteDefaults.traceFailedRequestsLogging.SetAttribute("directory", [string]( Join-Path -Path $LogDrive -ChildPath "inetpub\logs\FailedReqLogFiles" ) )
$ConfigFile.configuration.'system.webServer'.httpCompression.SetAttribute("directory", [string]( Join-Path -Path $WebDrive -ChildPath "inetpub\temp\IIS Temporary Compressed Files" ) )
$ConfigFile.configuration.'system.webServer'.httpCompression.SetAttribute("maxDiskSpaceUsage", "2048" )
$ConfigFile.configuration.'system.webServer'.httpCompression.SetAttribute("minFileSizeForComp", "5120" )
# Save the file
$ConfigFile.Save($ConfigFilePath)
Remove-Variable -Name ConfigFile -ErrorAction SilentlyContinue
I do pretty much the same things for the file that controls the compilation of IIS files.
# XML Object that will be used for processing
$ConfigFile = New-Object -TypeName System.Xml.XmlDocument
# Change the Compilation settings in teh ASP.NET Web Config
$ConfigFilePath = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config"
Write-Host "Editing [$ConfigFilePath]" -ForegroundColor Yellow
# Load the Configuration File
$ConfigFile.Load($ConfigFilePath)
# Save a backup if one doesn't already exist
if ( -not ( Test-Path -Path "$ConfigFilePath.orig" -ErrorAction SilentlyContinue ) )
{
Write-Host "Making Backup of $ConfigFilePath with '.orig' extension added" -ForegroundColor Yellow
$ConfigFile.Save("$ConfigFilePath.orig")
}
# change the settings (create if missing, update if existing)
$ConfigFile.configuration.'system.web'.compilation.SetAttribute("tempDirectory", [string]( Join-Path -Path $WebDrive -ChildPath "inetpub\temp") )
$ConfigFile.configuration.'system.web'.compilation.SetAttribute("maxConcurrentCompilations", "16")
$ConfigFile.configuration.'system.web'.compilation.SetAttribute("optimizeCompilations", "true")
Write-Host "Saving [$ConfigFilePath]" -ForegroundColor Yellow
$ConfigFile.Save($ConfigFilePath)
# Save the file
$ConfigFile.Save($ConfigFilePath)
Remove-Variable -Name ConfigFile -ErrorAction SilentlyContinue
Redirecting More Folders
I’m going to leverage the New-Item -ItemType Junction
command to create symbolic links like I did at the beginning for IIS specifically.
Here I’m applying it to a bunch of SolarWinds specific folders.
$Redirections = @()
$Redirections += New-Object -TypeName PSObject -Property ( [ordered]@{ Order = [int]1; SourcePath = "C:\ProgramData\SolarWinds"; TargetDrive = $ProgramsDrive } )
$Redirections += New-Object -TypeName PSObject -Property ( [ordered]@{ Order = [int]2; SourcePath = "C:\ProgramData\SolarWindsAgentInstall"; TargetDrive = $ProgramsDrive } )
$Redirections += New-Object -TypeName PSObject -Property ( [ordered]@{ Order = [int]3; SourcePath = "C:\Program Files (x86)\SolarWinds"; TargetDrive = $ProgramsDrive } )
$Redirections += New-Object -TypeName PSObject -Property ( [ordered]@{ Order = [int]4; SourcePath = "C:\Program Files (x86)\Common Files\SolarWinds"; TargetDrive = $ProgramsDrive } )
$Redirections += New-Object -TypeName PSObject -Property ( [ordered]@{ Order = [int]5; SourcePath = "C:\ProgramData\SolarWinds\Logs"; TargetDrive = $LogDrive } )
$Redirections | Add-Member -MemberType ScriptProperty -Name TargetPath -Value { $this.SourcePath.Replace("C:\", $this.TargetDrive ) } -Force
ForEach ( $Redirection in $Redirections | Sort-Object -Property Order )
{
# Check to see if the target path exists - if not, create the target path
if ( -not ( Test-Path -Path $Redirection.TargetPath -ErrorAction SilentlyContinue ) )
{
Write-Host "Creating Path for Redirection [$( $Redirection.TargetPath )]" -ForegroundColor Yellow
New-Item -ItemType Directory -Path $Redirection.TargetPath | Out-Null
}
# Execute it
Write-Host "Creating Junction point from $( $Redirection.SourcePath ) --> $( $Redirection.TargetPath ) " -NoNewline
New-Item -ItemType Junction -Path $Redirection.SourcePath -Value $Redirection.TargetPath | Out-Null
Write-Host "[COMPLETED]" -ForegroundColor Green
}
I also do a few more things in my personal environment like install Notepad++, some utilities, disabling the expiration of the local admin account, and importing a certificate so that I can run using secure web. But if you don’t want to do any of that stuff, then you are done and ready to install any of the Orion products.
Importing my Wildcard SSL Certificate
#region Import Certificate
$CertName = "WildcardCert_demo.lab"
$CertPath = "\\Demo.Lab\Files\Data\Certificates\"
$PfxFile = Get-ChildItem -Path $CertPath -Filter "$CertName.pfx"
$PfxPass = ConvertTo-SecureString -String ( Get-ChildItem -Path $CertPath -Filter "$CertName.password.txt" | Get-Content -Raw ) -AsPlainText -Force
Import-PfxCertificate -FilePath $PfxFile.FullName -Password $PfxPass -CertStoreLocation "Cert:\LocalMachine\My"
#endregion
3 thoughts on “Building my Orion Server [Scripting Edition] – Step 3.1”