marie-wilson-cooking

I’ve run out of jokes today. So, minimal blabbering this time.  I’ll just post the code, with a few, dry-as-hell-techie comments, and you can post comments if you want/need more information or spot a bug or just want to say “you suck, your shit is stupid” whatever.

Much of the inner code stuff was derived from Niall Brady’s work, which you can find here and in the download ZIP he provides at the bottom.  I simply pasted in chunks of it, drank some coffee, had a conversation with my dog and cat, added some duct tape PowerShell, and tested it about 42 times.

What does it do?

  • Applies updates
  • Installs Roles and Features for SCCM (except for WSUS, which is later)
  • Installs ADK Windows 10 1607
  • Installs MDT 8443
  • Installs SQL Server 2016
  • Configures SQL min/max memory allocation
  • Installs SQL Server Management Studio 2016
  • Installs WSUS role and points to SQL DB
  • Installs WDS role
  • Installs Configuration Manager 1606
  • Installs ConfigMgr Toolkit 2012 R2
  • Installs ConfigMgr PowerShell cmdlet update

Test Scenario:

  • Hyper-V guest VM built with the following
    • 12 GB memory
    • 1 vCPU
    • 1 boot disk (100 GB, dynamic)
    • 2 secondary disks, SCSI, 200 GB each **
    • Installed with Windows Server 2016, named, static IPv4, and domain-joined
  • Log in as Administrator
  • Open PowerShell console
  • Type start http://boxstarter.org/package/nr/url? (followed by the URL to the code txt file***)

Lab Setup

  • Domain = contoso.com
  • File Server = FS1
    • Share: Apps
      • Contains installation source media for ADK, MDT, SQL, SCCM and related tools
      • Contains SXS for Windows Server 2016
    • Share: Scripts
      • Contains INI and XML files used for unattended tasks
  • SCCM site server = CM01
  • SCCM site = (configured in script)

** prepared using another BoxStarter script, 64k unit allocation, E: is “APPS”, F: is “DATA”

*** for GitHub Gist reference, click “Raw”, then highlight and copy the entire URL to paste onto the end of the BoxStarter base URL.  Press Enter.  Answer prompts to allow download and execution.  Drink coffee.

NOTE: You should also be able to dump all of the scripts, SXS and  install media on a USB drive and call it all from there, but I haven’t tested that scenario yet.

Known Limitations (in this version):

  1. Domain Accounts for SQL services (and passwords, and local service rights, SPN registration in AD, etc.)
  2. Extend AD schema by invoking extadsch.exe
  3. Invoking the CM powershell cmdlets to provision the CM site (currently in testing)
<#
.SYNOPSIS
  BoxStarter/Chocolatey-ish script to configure an SCCM 16xx Site Server

.DESCRIPTION
  runs with boxstarter command (e.g. http://boxstarter.org/package/nr/url?<gist_url>)
  click RAW button. Copy the URL from the address bar, and paste into
  powershell console in target machine after the ...url? string shown above.

.NOTES
  Spewed forth by: David Stein
  Created........: 12/17/2016
  Modified.......: 12/19/2016
 
  Assumptions: 
    1. The computer is already assigned the intended name
    2. The computer is assigned a static IP, DNS and gateway
    3. Secondary disks are allocated
    4. The computer is joined to the target domain
    5. The server is Windows Server 2012 R2 or 2016
    6. The apps are SQL 2016, CM 1606, MDT 8443, ADK 1607
 
  Custom Files (see ZIP file under sccm-ps repo)...
    1. ServerRoles.xml (in scripts folder)
    2. RolesWsus.xml (in scripts folder) (not used currently)
    3. SqlConfig.ini (in scripts folder) (not used currently)
    4. SSMS-Setup-ENU.exe (in SQL source folder) (download latest)
    5. CmConfig.ini (in scripts folder) (not used currently)
    6. ConfigMgrTools.msi (in SCCM source folder)
    7. ConfigMgr2012PowerShellCmdlets.msi (in SCCM source folder)
#>

#------------------------------------------------------------
# global parameters
#------------------------------------------------------------
$ServerName = "CM01"
$DomainName = "CONTOSO"
$DomainSuffix = "contoso.com"
$scriptsPath = "\\FS1\Scripts\CM"
$sharedSource = "\\FS1\Apps\Sources\2016\SXS"
$sqlSource = "\\FS1\Apps\MS\SQL2016"
$mdtSource = "\\FS1\Apps\MS\MDT8443"
$adkSource = "\\FS1\Apps\MS\ADK1607"
$cmSource = "\\FS1\Apps\MS\CM1606"
$WSUSFolder = "E:\wsus"
$SqlMemMin = 8192
$SqlMemMax = 8192
$tmpFolder = $env:TEMP
$ScriptVersion = "2016.12.19.02"
#------------------------------------------------------------
# SQL configuration file
# ref: https://msdn.microsoft.com/en-us/library/ms144259.aspx
#------------------------------------------------------------
# next line sets user as a SQL sysadmin
$yourusername="$DomainName\sccmadmin"
# path to the SQL media
$SQLsource="$sqlSource\"
$ACTION="Install"
$ASCOLLATION="Latin1_General_CI_AS"
$ErrorReporting="False"
$SUPPRESSPRIVACYSTATEMENTNOTICE="False"
$IACCEPTROPENLICENSETERMS="False"
$ENU="True"
$QUIET="True"
$QUIETSIMPLE="False"
$UpdateEnabled="True"
$USEMICROSOFTUPDATE="False"
$FEATURES="SQLENGINE,RS,CONN,IS,BC,SDK,BOL"
$UpdateSource="MU"
$HELP="False"
$INDICATEPROGRESS="False"
$X86="False"
$INSTANCENAME="MSSQLSERVER"
$INSTALLSHAREDDIR="C:\Program Files\Microsoft SQL Server"
$INSTALLSHAREDWOWDIR="C:\Program Files (x86)\Microsoft SQL Server"
$INSTANCEID="MSSQLSERVER"
$SQLBACKUPDIR="F:\SQLBCK"
$SQLUSERDBDIR="E:\SQLDB"
$SQLUSERDBLOGDIR="E:\SQLLOG"
$SQLTEMPDBDIR="E:\SQLTMP"
$RSINSTALLMODE="DefaultNativeMode"
$SQLTELSVCACCT="NT Service\SQLTELEMETRY"
$SQLTELSVCSTARTUPTYPE="Automatic"
$ISTELSVCSTARTUPTYPE="Automatic"
$ISTELSVCACCT="NT Service\SSISTELEMETRY130"
$INSTANCEDIR="C:\Program Files\Microsoft SQL Server"
$AGTSVCACCOUNT="NT AUTHORITY\SYSTEM"
$AGTSVCSTARTUPTYPE="Automatic"
$ISSVCSTARTUPTYPE="Disabled"
$ISSVCACCOUNT="NT AUTHORITY\System"
$COMMFABRICPORT="0"
$COMMFABRICNETWORKLEVEL="0"
$COMMFABRICENCRYPTION="0"
$MATRIXCMBRICKCOMMPORT="0"
$SQLSVCSTARTUPTYPE="Automatic"
$FILESTREAMLEVEL="0"
$ENABLERANU="False"
$SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"
$SQLSVCACCOUNT="NT AUTHORITY\System"
$SQLSVCINSTANTFILEINIT="False"
$SQLSYSADMINACCOUNTS="$yourusername"
$SQLTEMPDBFILECOUNT="1"
$SQLTEMPDBFILESIZE="8"
$SQLTEMPDBFILEGROWTH="64"
$SQLTEMPDBLOGFILESIZE="8"
$SQLTEMPDBLOGFILEGROWTH="64"
$ADDCURRENTUSERASSQLADMIN="True"
$TCPENABLED="1"
$SqlSvcPwd=""
$NPENABLED="1"
$BROWSERSVCSTARTUPTYPE="Disabled"
$RSSVCACCOUNT="NT AUTHORITY\System"
$RSSVCSTARTUPTYPE="Automatic"
$IAcceptSQLServerLicenseTerms="True"
#------------------------------------------------------------
# SCCM configuration file
#------------------------------------------------------------
$Action="InstallPrimarySite"
$ProductID="EVAL"
$SiteCode="PS1"
$Sitename="Primary Site 1"
$SMSInstallDir="E:\ConfigMgr"
$SDKServer="$ServerName.$DomainSuffix"
$RoleCommunicationProtocol="HTTPorHTTPS"
$ClientsUsePKICertificate="0"
$PrerequisiteComp="1"
$PrerequisitePath="E:\CMPreReqs"
$ManagementPoint="$ServerName.$DomainSuffix"
$ManagementPointProtocol="HTTP"
$DistributionPoint="$ServerName.$DomainSuffix"
$DistributionPointProtocol="HTTP"
$DistributionPointInstallIIS="0"
$AdminConsole="1"
$JoinCEIP="0"
$SQLServerName="$ServerName.$DomainSuffix"
$DatabaseName="CM_PS1"
$SQLSSBPort="4022"
$CloudConnector="1"
$CloudConnectorServer="$ServerName.$DomainSuffix"
$UseProxy="0"
$ProxyName=""
$ProxyPort=""
$SysCenterId=""
$SAExpDate="2016-12-18 00:00:00.000"
$SAActive="1"
$CurrentBranch="1"
#------------------------------------------------------------

$Boxstarter.RebootOk = $true
write-host "info: script version $ScriptVersion" -ForegroundColor Cyan

#------------------------------------------------------------
# functions
#------------------------------------------------------------

function Test-AppInstalled {
 param($DisplayName)
 $test = Get-WmiObject -Class Win32_Product | ? {$_.Caption -eq $DisplayName}
 (!($test -eq $null))
}

function Test-Feature {
 param($FeatureName)
 $(Get-WindowsFeature $FeatureName).Installed
}

function Disable-ServerManager {
 write-output "info: disabling server manager at startup..."
 New-ItemProperty -Path HKCU:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value "0x1" -Force | Out-Null
}

function Install-MsiPackage {
  param($ProductName, $Inst, $Options)
  if (Test-AppInstalled "$ProductName") {
    write-output "info: $ProductName is already installed"
  }
  else {
    if (Test-Path "$appInst") {
      write-output "info: installing $ProductName..."
      write-output "info: type...... msi"
      write-output "info: package... $Inst"
      write-output "info: options... $Options"
      $Arg2 = "/i ""$inst"" /qb! /norestart REBOOT=ReallySuppress"
      if ($Options -ne "") {
        $Arg2 += " ""$Options"""
      }
      try {
        $res = (Start-Process -FilePath "msiexec.exe" -ArgumentList $Arg2 -Wait -Passthru).ExitCode
        if ($res -ne 0) {
          write-output "error: exit code is $res"
          $errmsg = [ComponentModel.Win32Exception] $res
          write-output "error: $errmsg"
          $Boxstarter.RebootOk = $False
          break
        }
        else {
          write-output "info: exit code is $res"
        }
      }
      catch {
        $Boxstarter.RebootOk = $False
        write-output "error: installation failed!"
        break
      }
      write-output "info: installation successful"
      if (Test-PendingReboot) { Invoke-Reboot }
    }
    else {
      write-output "error: unable to locate $appInst"
      break
    }
  }
}

function Install-ExePackage {
  param($ProductName, $Inst, $Options)
  if (Test-AppInstalled "$ProductName") {
    write-output "info: $ProductName is already installed."
  }
  else {
    write-output "info: installing $ProductName..."
    write-output "info: type...... exe"
    write-output "info: source.... $Inst"
    write-output "info: options... $Options"
    try {
      $res = (Start-Process -FilePath "$Inst" -ArgumentList "$Options" -Wait -PassThru).ExitCode
      if ($res -ne 0) {
        write-output "error: exit code is $res"
        $Boxstarter.RebootOk = $False
        break
      }
      else {
        write-output "info: exit code is $res"
      }
    }
    catch {
      $ErrorMessage = $_.Exception.Message
      $FailedItem = $_.Exception.ItemName
      $Boxstarter.RebootOk = $False
      write-output "error: installation failed... $ErrorMessage"
      break
    }
    write-output "info: installation successful"
    if (Test-PendingReboot) { Invoke-Reboot }
  }
}

function Install-Roles {
  param(
    [parameter(Mandatory=$False)] $XmlFile = "", 
    [parameter(Mandatory=$False)] $RoleNames = ""
  )
  if ($xmlFile -ne "") {
    if (Test-Path $xmlFile) {
      write-output "info: installing server roles and features from config file..."
      Install-WindowsFeature -ConfigurationFilePath $xmlFile -Source $sharedSource
      write-output "info: roles and features installation completed."
    }
    else {
      write-output "error: unable to locate configuration file: $xmlFile"
      $Boxstarter.RebootOk = $False
      break
    }
  }
  else {
    write-output "info: installing server roles and features..."
    try {
      Install-WindowsFeature -Name $RoleNames.Split(",") -IncludeManagementTools -Source $sharedSource -ErrorAction Stop
    }
    catch {
      $ErrorMessage = $_.Exception.Message
      $FailedItem = $_.Exception.ItemName
      $Boxstarter.RebootOk = $False
      write-output "error: role names...... $RoleNames"
      write-output "error: config file..... $XmlFile"
      write-output "error: installation failed... $ErrorMessage"
      break
    }
    Start-Sleep -s 10
  }
}

#------------------------------------------------------------
# preliminary setup stuff
#------------------------------------------------------------

Disable-UAC
Disable-MicrosoftUpdate
Set-WindowsExplorerOptions -EnableShowHiddenFilesFoldersDrives -EnableShowProtectedOSFiles -EnableShowFileExtension
Disable-InternetExplorerESC
Disable-ServerManager

#------------------------------------------------------------
# windows roles and features
# Note: get xml file from windowsnoob.com
#------------------------------------------------------------

Install-Roles -XmlFile "$scriptsPath\ServerRoles.xml"
Install-Roles -RoleName "WDS"
Install-WindowsUpdate

#------------------------------------------------------------
# install ADK
#------------------------------------------------------------

$prod    = "Windows Deployment Tools"
$appInst = "$adkSource\adksetup.exe"
$argList = " /Features OptionId.DeploymentTools OptionId.WindowsPreinstallationEnvironment OptionId.ImagingAndConfigurationDesigner OptionId.UserStateMigrationTool /norestart /quiet /ceip off"
Install-ExePackage $prod $appInst $argList

Install-MsiPackage "Microsoft Deployment Toolkit (6.3.8443.1000)" "$mdtSource\MicrosoftDeploymentToolkit_x64.msi" ""

#------------------------------------------------------------
# install SQL Server 2016
#------------------------------------------------------------

if (!(Test-AppInstalled "SQL Server 2016 Database Engine Services")) {
  write-output "info: preparing unattend config file for SQL installation..."

$cfgData= @"
[OPTIONS]
Action="$ACTION"
ErrorReporting="$ERRORREPORTING"
Quiet="$Quiet"
Features="$FEATURES"
InstanceName="$INSTANCENAME"
InstanceDir="$INSTANCEDIR"
SQLBACKUPDIR="$SQLBACKUPDIR"
SQLUSERDBDIR="$SQLUSERDBDIR"
SQLUSERDBLOGDIR="$SQLUSERDBLOGDIR"
SQLTEMPDBDIR="$SQLTEMPDBDIR"
SQLSVCAccount="$SQLSVCACCOUNT"
SQLSysAdminAccounts="$SQLSYSADMINACCOUNTS"
SQLSVCStartupType="$SQLSVCSTARTUPTYPE"
AGTSVCACCOUNT="$AGTSVCACCOUNT"
AGTSVCSTARTUPTYPE="$AGTSVCSTARTUPTYPE"
RSSVCACCOUNT="$RSSVCACCOUNT"
RSSVCSTARTUPTYPE="$RSSVCSTARTUPTYPE"
ISSVCACCOUNT="$ISSVCACCOUNT" 
ISSVCSTARTUPTYPE="$ISSVCSTARTUPTYPE"
ASCOLLATION="$ASCOLLATION"
SQLCOLLATION="$SQLCOLLATION"
TCPENABLED="$TCPENABLED"
NPENABLED="$NPENABLED"
SQLSVCPASSWORD="$SqlSvcPwd"
AGTSVCPASSWORD="$SqlSvcPwd"
ASSVCPASSWORD="$SqlSvcPwd"
ISSVCPASSWORD="$SqlSvcPwd"
RSSVCPASSWORD="$SqlSvcPwd"
IAcceptSQLServerLicenseTerms="$IAcceptSQLServerLicenseTerms"
"@

  $cfgFile = "$scriptsPath\SqlConfigNew.ini"

  if (Test-Path "$cfgFile") {
    write-output "info: '$cfgFile' already exists, removing..."
    Remove-Item -Path "$cfgFile" -Force
    write-host "info: creating '$cfgFile'..."
    New-Item -Path "$cfgFile" -ItemType File -Value $cfgData
  } 
  else {
    write-host "info: creating '$cfgFile'..."
    New-Item -Path "$cfgFile" -ItemType File -Value $cfgData
  }

  $flist = @($SQLBACKUPDIR, $SQLUSERDBDIR, $SQLUSERDBLOGDIR, $SQLTEMPDBDIR)
  foreach ($n in $flist) {
    if (!(Test-Path $n)) {
      write-output "info: creating SQL folder: $n"
      New-Item -Path $n -ItemType Directory
    }
  }

  $prod = "SQL Server 2016 Database Engine Services"
  $appInst = "$SQLsource\setup.exe"
  $argList = "/CONFIGURATIONFILE=$cfgFile"
  Install-ExePackage $prod $appInst $argList
}

#------------------------------------------------------------
# configure SQL memory limits
#------------------------------------------------------------

write-output "info: configuring SQL server memory limits..."
write-output "info: minimum = $SQLMemMin"
write-output "info: maximum = $SQLMemMax"
try {
  [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
  [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
  $SQLMemory = New-Object ('Microsoft.SqlServer.Management.Smo.Server') ("(local)")
  $SQLMemory.Configuration.MinServerMemory.ConfigValue = $SQLMemMin
  $SQLMemory.Configuration.MaxServerMemory.ConfigValue = $SQLMemMax
  $SQLMemory.Configuration.Alter()
  write-output "info: SQL memory limits have been configured."
}
catch {
  write-output "error: failed to modify SQL memory limits. Continuing..."
}

#------------------------------------------------------------
# install SSMS
#------------------------------------------------------------

$prod = "SQL Server 2016 Management Studio"
$appInst = "$sqlSource\SSMS-Setup-ENU.exe"
$argList = "/install /quiet /norestart"
Install-ExePackage $prod $appInst $argList

#------------------------------------------------------------
# install WSUS role
#------------------------------------------------------------

if (!(Test-Path $WSUSFolder)) {
  write-output "info: creating wsus target folder..."
  New-Item -Path $WSUSFolder -ItemType Directory -Force
}

Install-Roles -RoleNames "UpdateServices-Services,UpdateServices-DB,UpdateServices-RSAT"

write-output "info: invoking wsus post installation setup..."
write-output "info: wsus SQL_INSTANCE_NAME=$ServerName"
write-output "info: wsus CONTENT_DIR=$WSUSFolder"
& 'C:\Program Files\Update Services\Tools\WsusUtil.exe' postinstall SQL_INSTANCE_NAME=$ServerName CONTENT_DIR=$WSUSFolder | Out-Null

#------------------------------------------------------------
# Create firewall rule
#------------------------------------------------------------

if (!(Get-NetFirewallRule -DisplayName "SQL Server (TCP 1433) Inbound" -ErrorAction SilentlyContinue)) {
  write-output "info: creating network firewall rule..."
  New-NetFirewallRule -DisplayName "SQL Server (TCP 1433) Inbound" -Action Allow -Direction Inbound -LocalPort 1433 -Protocol TCP
}
else {
  write-output "info: firewall rule is already created."
}

#------------------------------------------------------------
# install SCCM 1606
#------------------------------------------------------------

if (!(Test-AppInstalled "ConfigMgr Management Point")) {
  write-output "info: preparing unattend config file for SCCM install..."

$cfgData= @"
[Identification]
Action="$Action"

[Options]
ProductID="$ProductID"
SiteCode="$SiteCode"
SiteName="$SiteName"
SMSInstallDir="$SMSInstallDir"
SDKServer="$ServerName"
RoleCommunicationProtocol="$RoleCommunicationProtocol"
ClientsUsePKICertificate="$ClientsUsePKICertificate"
PrerequisiteComp="$PrerequisiteComp"
PrerequisitePath="$PrerequisitePath"
ManagementPoint="$ManagementPoint"
ManagementPointProtocol="$ManagementPointProtocol"
DistributionPoint="$DistributionPoint"
DistributionPointProtocol="$DistributionPointProtocol"
DistributionPointInstallIIS="$DistributionPointInstallIIS"
AdminConsole="$AdminConsole"
JoinCEIP="$JoinCEIP"

[SQLConfigOptions]
SQLServerName="$SQLServerName"
DatabaseName="$DatabaseName"
SQLSSBPort="$SQLSSBPort"
SQLDataFilePath="$SQLUSERDBDIR"
SQLLogFilePath="$SQLUSERDBLOGDIR"

[CloudConnectorOptions]
CloudConnector="$CloudConnector"
CloudConnectorServer="$CloudConnectorServer"
UseProxy="$UseProxy"
ProxyName="$ProxyName"
ProxyPort="$ProxyPort"

[SystemCenterOptions]
SysCenterId="$SysCenterId"

[SABranchOptions]
SAActive="$SAActive"
CurrentBranch="$CurrentBranch"
SAExpiration="$SAExpDate"

[HierarchyExpansionOption]
"@

  if (!(Test-Path $SMSInstallDir)) {
    New-Item -Path $SMSInstallDir -ItemType Directory -Force
  }
  if (!(Test-Path $PrerequisitePath)) {
    New-Item -Path $PrerequisitePath -ItemType Directory -Force
  }
  $cfgFile = "$scriptsPath\CmConfigNew.ini"

  if (Test-Path "$cfgFile") {
    write-output "info: '$cfgFile' already exists, removing..."
    Remove-Item -Path "$cfgFile" -Force
    write-host "info: creating '$cfgFile'..."
    New-Item -Path "$cfgFile" -ItemType File -Value $cfgData
  } 
  else {
    write-host "info: creating '$cfgFile'..."
    New-Item -Path "$cfgFile" -ItemType File -Value $cfgData
  }
  $prod = "ConfigMgr Management Point"
  $appInst = "$cmSource\SMSSETUP\bin\x64\setup.exe"
  $argList = "/script $cfgFile"
  Install-ExePackage $prod $appInst $argList
}

#------------------------------------------------------------
# install CM 2012 R2 Toolkit
#------------------------------------------------------------

$prod = "ConfigMgr 2012 Toolkit R2"
$appInst = "$cmSource\ConfigMgrTools.msi"
$argList = ""
Install-MsiPackage $prod $appInst $argList

# install CM powershell cmdlet library
$prod = "System Center Configuration Manager Cmdlet Library"
$appInst = "$cmSource\ConfigMgr2012PowerShellCmdlets.msi"
$argList = ""
Install-MsiPackage $prod $appInst $argList

write-output "info: cleaning up and finishing..."

$Boxstarter.RebootOk = $False
Enable-WindowsUpdate
Enable-UAC

write-output "info: finished!"

 

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s