Miscellaneous SCCM Configuration Stuff using PowerShell with Fries and a Coke

Rather than trying to build some Frankenstein stack of horrors, I decided to piecemeal this instead. What I mean is that in the past I would approach everything like I did back in my app-dev life, and try to make everything an API stack. But more often, for my needs anyway, I don’t need a giant roll-around tool case with built-in workbench. I just need a toolbox with a select group of tools to fit my project tasks.  This makes it easier to cherry-pick useful portions and ignore, or laugh at the rest, as you see fit.  Anyhow, hopefully some of it is useful to others.

  • Version 1.0 – 06/05/2018 – initial post
  • Version 1.1 – 06/08/2018 – added more crappy examples to bore you to death

Purpose:  Why not?

Intent: Automate some or all of the tasks with installing Configuration Manager on a modern Windows platform using PowerShell.

Caveats: You might have better alternatives to each of these snippets.  That’s cool.

Assumptions:  Most examples are intended for processing on the primary site server or CAS, rather than from a remote workstation.  However, considering the author, they can easily be improved upon.

Disclaimer: Provided “as-is” without warranties, test before using in production, blah blah blah.

Example Code Snippets

Set SQL Server Memory Allocation

Note:  Neither dbatools or sqlps provide a direct means for configuring minimum allocated memory for SQL Server instances.  For the the max-only example, I’m using dbatools for simplicity.  For the min and max example, I’m using SMO, because SMO contains “MO”, and “MO” is used for phrases like “mo money!” and “mo coffee!”

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="SQL Host Name")]
  [string] $SqlInstance = "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)",
  [parameter(Mandatory=$False, HelpMessage="Mo Memory. Mo Memory!")]
  [int32] $MaxMemMB = 25600
)
# following line is optional unless you've already finished off that bottle of wine
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Install-Module dbatools -AllowClobber -SkipPublisherCheck -Force
Import-Module dbatools
Set-DbaMaxMemory -SqlInstance $SqlInstance -MaxMB $MaxMemMB

Using SMO, because it has “mo” in the name…

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="SQL Host Name")]
  [string] $SqlInstance = "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)",
  [parameter(Mandatory=$False, HelpMessage="Mo Memory. Mo Memory!")]
  [int32] $MaxMemMB = 25600
)
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null
$srv = New-Object Microsoft.SQLServer.Management.Smo.Server($SQLInstanceName)
if ($srv.status) {
  $srv.Configuration.MaxServerMemory.ConfigValue = $MaxMemMB
  $srv.Configuration.MinServerMemory.ConfigValue = 8192 
  $srv.Configuration.Alter()
}

Set CM Database Recovery Model to Simple

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Server Name")]
  [string] $SqlInstance = "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)",
  [parameter(Mandatory=$False, HelpMessage="Site Code")]
  [string] $SiteCode = "P01"
)
Import-Module dbatools
Set-DbaDbRecoveryModel -SqlInstance $SqlInstance -Database "CM_$SiteCode" -RecoveryModel SIMPLE

Set CM Database Service Principal Name (SPN)

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="SQL Host Name")]
  [string] $SqlInstance = "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)",
  [parameter(Mandatory=$False, HelpMessage="SQL Instance Name")]
  [string] $InstanceName = "MSSQLSvc",
  [parameter(Mandatory=$False, HelpMessage="SQL Server Account")]
  [string] $SqlAccount = "$($env:USERDOMAIN)\cm-sql"
)
$SpnShort = $SqlInstance.split('.')[0]
if ((Test-DbaSpn -ComputerName $SqlInstance).InstanceServiceAccount[0] -ne $SqlAccount) {
  $Spn1 = "$InstanceName/$SpnShort:1433"
  $Spn2 = "$InstanceName/$SqlInstance:1433"
  try {
    Set-DbaSpn -SPN $Spn1 -ServiceAccount $SqlAccount -Credential (Get-Credential)
    Set-DbaSpn -SPN $Spn2 -ServiceAccount $SqlAccount -Credential (Get-Credential)
  }
  catch {
    Write-Error $_.Exception.Message
  }
}
else {
  Write-Warning "SPN is already configured.  Go back to sleep"
}

Add CM SQL Service Account to “Log on as a Service” Rights

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Service Account Name")]
  [string] $AccountName = "$($env:USERDOMAIN)\cm-sql"
)
Install-Module carbon -SkipPublisherCheck -AllowClobber -Force
if ((Get-Privilege -Identity $AccountName) -ne SeServiceLogonRight) {
  try {
    Grant-Privilege -Identity $AccountName -Privilege SeServiceLogonRight
  }
  catch {
    Write-Error $_.Exception.Message
  }
}
else {
  Write-Warning "Already granted service logon rights. Continue drinking"
}

Set WSUS IIS Application Pool properties

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Queue Length")]
  [int32] $QueueLength = 2000,
  [parameter(Mandatory=$False, HelpMessage="Private Memory Limit")]
  [int32] $PrivateMemoryLimit = 7372800
)
Import-Module WebAdministration -DisableNameChecking
try {
  Set-ItemProperty IIS:\AppPool\WsusPool -Name queueLength -Value $QueueLength
  Set-ItemProperty IIS:\AppPool\WsusPool -Name recycling.periodicRestart.privateMemory -Value $PrivateMemoryLimit
}
catch {
  Write-Error $_.Exception.Message
}

Move WSUS SQL Database Files

[CmdletBinding()]
param (
    [parameter(Mandatory=$False, HelpMessage="New Database Files Path")]
    [string] $NewFolderPath = "G:\Database"
)
$ServerName = $env:COMPUTERNAME
$DatabaseName = "SUSDB"
$ServiceName = "WsusService"
$AppPool = "WsusPool"

if (!(Test-Path $NewFolderPath)) { mkdir $NewFolderPath -Force }
if (!(Test-Path $NewFolderPath)) {
  Write-Error "Your request died a horrible flaming death."
  break
}
Import-Module WebAdministration
Write-Verbose "stopping WSUS application pool"
Stop-WebAppPool -Name $AppPool
Write-Verbose "stopping WSUS service"
Get-Service -Name $ServiceName | Stop-Service

Import-Module SQLPS -DisableNameChecking
$ServerSource = New-Object "Microsoft.SqlServer.Management.Smo.Server" $ServerName

Write-Verbose "detaching WSUS SUSDB database"
$Db = $ServerSource.Databases | Where-Object {$_.Name -eq $DatabaseName}
$CurrentPath = $Db.PrimaryFilePath
$ServerSource.DetachDatabase($DatabaseName, $True, $True)
$files = Get-ChildItem -Path $CurrentPath -Filter "$DatabaseName*.??f"
Write-Verbose "moving database files to $NewFolderPath"
$files | Move-Item -Destination $NewFolderPath
$files = (Get-ChildItem -Path $NewFolderPath -Filter "$DatabaseName*.??f") | Select-Object -ExpandProperty FullName
Write-Verbose "attaching database files"
# hard-coded 'sa' as the DB owner because I'm lazy AF
$ServerSource.AttachDatabase($DatabaseName, $files, 'sa')

Write-Verbose "starting WSUS service"
Get-Service -Name $ServiceName | Start-Service

Write-Verbose "starting WSUS app pool"
Start-WebAppPool -Name $AppPool

Write-Host "WSUS database files have been moved to $NewFolderPath"

Create System Management AD Container

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Domain Suffix")]
  [string] $DomainSuffix = "DC=contoso,DC=local"
)
if (!(Get-Module -ListAvailable | Where-Object {$_.Name -eq 'ActiveDirectory'})) {
  Install-WindowsFeature RSAT-AD-Tools -IncludeAllSubFeature -IncludeManagementTools
}
Import-Module ServerManager
Import-Module ActiveDirectory

if (!(Get-ADObject -Identity 'CN=System Management,CN=System,'+$DomainSuffix)) {
  New-ADObject -Name 'System Management' -Path 'CN=System,'+$DomainSuffix -Type container |
    Set-ADObject -ProtectedFromAccidentalDeletion:$True -Confirm:$False
}

Grant Permissions on System Management Container (added in 1.1)

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Your Domain Suffix")]
  [string] $DomainSuffix = "DC=contoso,DC=local",
  [parameter(Mandatory=$False, HelpMessage="Site Server Name")]
  [string] $SiteServer = "CM01"
)
$AdObj = [ADSI]("LDAP://CN=System Management,CN=System,$DomainSuffix")
try {
  $computer = Get-ADComputer $SiteServer
  $sid = [System.Security.Principal.SecurityIdentifier] $computer.SID
  $identity = [System.Security.Principal.IdentityReference] $SID
  $privs = [System.DirectoryServices.ActiveDirectoryRights] "GenericAll"
  $type = [System.Security.AccessControl.AccessControlType] "Allow"
  $inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "All"
  $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity, $privs, $type, $inheritanceType
  $AdObj.psbase.ObjectSecurity.AddAccessRule($ACE)
  $AdObj.psbase.commitchanges()
}
catch {
  Write-Error $_.Exception.Message
}

Import Windows 10 OS Image (added in 1.1)

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="OS Source Root Location")]
  [string] $ImageSource = "\\foo\sources\osimages\w10-1803",
  [parameter(Mandatory=$False, HelpMessage="Name to Assign")]
  [string] $OSName = "Windows 10 x64 1803"
)
$Source = "$ImageSource\sources\install.wim"
if (!(Test-Path $Source)) {
  Write-Error "Boom!  And just like that your code ate itself."
  break
}
try {
  New-CMOperatingSystemImage -Name $OSName -Path $Source -Description $OSName -ErrorAction Stop
}
catch {
  Write-Error $_.Exception.Message
}

Import Windows 10 OS Upgrade Package (added in 1.1)

[CmdletBinding()]
param (
 [parameter(Mandatory=$False, HelpMessage="OS Source Root Location")]
 [string] $ImageSource = "\\foo\sources\osimages\w10-1803",
 [parameter(Mandatory=$False, HelpMessage="Name to Assign")]
 [string] $OSName = "Windows 10 x64 1803"
)
if (!(Test-Path $ImageSource)) {
  Write-Error "I bet Jimmy deleted your source folder. You know what to do next."
  break
}
try {
  New-CMOperatingSystemInstaller -Name $OSName -Path $ImageSource -Description $OSName -ErrorAction Stop
}
catch {
  Write-Error $_.Exception.Message
}

Create a Console Folder (added in 1.1)

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Site Code")]
  [string] $SiteCode = "P01",
  [parameter(Mandatory=$False, HelpMessage="Folder Name")]
  [string] $FolderName = "Windows Client",
  [parameter(Mandatory=$False, HelpMessage="Parent Folder")]
  [ValidateSet('Application','BootImage','ConfigurationBaseline','ConfigurationItem','DeviceCollection','Driver','DriverPackage','OperatingSystemImage','OperatingSystemInstaller','Package','Query','SoftwareMetering','SoftwareUpdate','TaskSequence','UserCollection','UserStateMigration','VirtualHardDisk')]
  [string] $ParentFolder = "OperatingSystemImage"
)
Set-Location "$($SiteCode):"
try {
  New-Item -Path "$SiteCode`:\$ParentFolder" -Name $FolderName -ErrorAction Stop
}
catch {
  Write-Error $_.Exception.Message
}

Move a Console Item into a Custom Folder (added in 1.1)

$OsImage = "Windows 10 x64 1803"
$Folder = "\OperatingSystemImage\Windows Client"
try {
  Get-CMOperatingSystemImage -Name $OsImage |
    Move-CMObject -FolderPath $Folder
}
catch {
  Write-Error $_.Exception.Message
}

Semi-Bonus: Create a Device Collection for each OS in AD

[CmdletBinding()]
param (
  [parameter(Mandatory=$False, HelpMessage="Site Code")]
  [string] $SiteCode = "P01"
)
Import-Module ActiveDirectory
$osnames = Get-ADComputer -Filter * -Properties "operatingSystem" | Select-Object -ExpandProperty operatingSystem -Unique
$key = "HKLM:\SOFTWARE\Microsoft\SMS\Setup"
$val = "UI Installation Directory"
$uiPath = (Get-Item -Path $key).GetValue($val)
$modulePath = "$uiPath\bin\ConfigurationManager.psd1"
if (!(Test-Path $modulePath)) {
  Write-Error "Sudden implosion of planetary system.  The end. Roll the credits and dont forget to drop your 3D glasses in the barrel outside."
  break
}
Import-Module $modulePath
Set-Location "$($SiteCode):"
foreach ($os in $osnames) {
  $collname = "Devices - $os"
  try {
    $sched = New-CMSchedule -DurationInterval Days -DurationCount 7 -RecurCount 1 -RecurInterval 7
    New-CMCollection -Name $collname -CollectionType Device -LimitingCollectionName "All Systems" -RefreshType Both -RefreshSchedule $sched -ErrorAction SilentlyContinue
    $query = 'select distinct SMS_R_System.ResourceId, SMS_R_System.ResourceType, SMS_R_System.Name, SMS_R_System.SMSUniqueIdentifier, SMS_R_System.ResourceDomainORWorkgroup, SMS_R_System.Client from SMS_R_System where SMS_R_System.OperatingSystemNameandVersion="'+$os+'"'
    Add-CMDeviceCollectionQueryMembershipRule -CollectionName $collname -RuleName "1" -QueryExpression $query
    Write-Host "collection created: $collname"
  }
  catch {
    Write-Error $_.Exception.Message
  }
}

 

Advertisements

Things that make me nervous

  1. Cops following me for a long time
  2. Doctor says “I think we need to do more tests”
  3. Dentist (looks at x-ray and just shakes head)
  4. Someone pointing a gun at my face
  5. Only one open restroom and it’s out of toilet tissue on taco lunch day
  6. Driving in Boston around the holidays
  7. A spider walking on my face
  8. A TSA person who stares at me without blinking while meticulously stretching latex gloves
  9. Something running around inside the refrigerator
  10. Seeing “Jason Sandys has replied to your post”

The Most Basic of Basics

Some of the less-common issues I’ve run across in the past few months on the road.  They all tie back to being careful to read the documentation and following the guidelines properly.  These are all 100% true.  This is a stream-of-semi-conciousness post, so it’s going to ramble a bit.

  • Client Push Installation Not Working
    • Client Push Installation Accounts weren’t configured to use the appropriate accounts or the accounts didn’t have permissions
    • AntiVirus was waiting around the corner to stab the client installation files in the neck and steal it’s lunch (exclusions)
  • Clients Not Functioning
    • Site Boundaries were missing
    • DNS is fucked worse than a construction site toilet
    • DHCP is waiting outside the construction site toilet after having eaten 15 burritos and a Coke
    • Network team hates the AD team who hates the SCCM team
    • AD team refuses to hear any complaints about their “perfect” DNS environment
    • Boundary Group systems weren’t configured properly
  • OSD deployments not working
    • Didn’t visit DeploymentResearch.com or any other pertinent web sites / blogs / conferences / book stores / random homeless people wearing a “OSD is cool AF” t-shirt / etc.
    • Didn’t watch any YouTube videos of Johan or Mikael, or anyone else that does this every day
    • Didn’t install the correct ADK version
  • PXE not working
    • PXE wasn’t actually installed
    • DHCP options AND IP Helpers were in conflict (should only use IP helpers)
    • Forgot to distribute boot images where needed
    • Forgot to come to work awake
  • Backups Not Working
    • Backup targets were moved without telling anyone
    • GPO settings were stopping the backup service
    • Backup target was out of space
  • SQL database connectivity issues
    • DBA team moved the database without telling the SCCM admins
    • SCCM admins urinated on DBA cars in the parking lot
    • Unsupported SQL configuration (with regards to SCCM)
  • Slow console and slow inventory and status processing
    • Database was more fragmented than the teeth of an old English town drunk
    • DBA’s never heard of Ola Hallengren or Steve Thompson
    • DBA’s heard more than they wanted from me about Ola Hallengren and Steve Thompson
  • Apps and Updates not deploying
    • Maintenance windows were set to impossible periods of availability
    • Collections were not properly aligned with maintenance windows
    • Collections were not named to clarify the use (and configuration) of maintenance windows
  • Couldn’t use “Run Scripts” feature
    • Forgot to check the “pre-release” features option (earlier builds)
    • Forgot to check the ‘Run Scripts’ feature / enable it (earlier builds)
    • Forgot to uncheck the “Script authors require additional script approver” option
    • Forgot to approve the scripts
    • Forgot to actually import any scripts (they thought it made its own with hot water and a spoon)
    • Forgot to upgrade site from 1702
  • Software Center not working / Apps not showing up
    • Using a GPO to place a shortcut to Software Center but referring to the wrong SCClient.exe
    • Nothing was actually deployed to the device or the user trying to use it
  • Software Updates not working
    • WSUS is hosed
    • WSUS is still hosed
    • IIS is hosed too, but over-hosed by WSUS at the moment
    • Never learned to read logs like WCM.log, WSUSCtrl.log, wsyncmgr.log, SUPSetup.log
    • Never bothers looking at any logs
    • Forgot to actually make groups and packages and deploy them to anything
    • Still using GPO settings that conflict with ConfigMgr policies
  • Slow Deployments
    • Poor boundaries and boundary groups
    • Poor poor poor boundary groups
    • Sad little boundary groups, all neglected and lonely
    • BranchCache? WTF is that?
    • Our Network guys are throttling my shit all the time and don’t need to?! What?
  • Too many admins logging into the site server all day / every day
    • No local console installations
    • No understanding of RBA configurations (where needed)
  • More coffee, back to work

This Week in Nerdy Stuff

While I’m still sorting out some personal issues and trying to grasp how it is so many American-English words that begin with “con” or “pro” or “in” don’t have corollary words that make any damned sense whatsoever, and still lamenting walking away from a trip to MMS/MOA this year to start a new job, well, here’s what flew past my dust-covered radar this week, and might have missed your smartphone as well.

A Hammer to Turn Screws

20161120_160743

Script: Check-Readiness.ps1

Purpose: Keeps me busy and away from drinking too much coffee.  Okay, seriously, it’s just another flavor of “check for Windows 10 upgrade readiness using PowerShell”. It can be used within SCCM or while standing naked in a WalMart, your choice.

<#
.DESCRIPTION
.PARAMETER SourcePath
Why didn't they use PARAMETRE, hmm? So American. Anyhow, SourcePath is wherever the Windows 10 media is located.
.PARAMETER OutputPath
If not "" then it will dump a file named <computer>-<result>.txt in that location.
.NOTES
I was only half awake when I wrote this. Use at your own risk.
#>
[CmdletBinding()]
param (
    [parameter(Mandatory=$True, HelpMessage="Path to setup.exe")]
    [ValidateNotNullOrEmpty()]
    [string] $SourcePath,
    [parameter(Mandatory=$False, HelpMessage="Path to dump output data")]
    [string] $OutputPath = ""
)
$setup = Join-Path -Path $SourcePath -ChildPath "setup.exe"
Write-Verbose "setup source is $setup"
if (!(Test-Path $setup)) {Write-Error "$setup not found"; break}
Write-Verbose "starting assessment"
$p = Start-Process -FilePath $setup -Wait -ArgumentList "/Auto Upgrade /Quiet /NoReboot /Compat ScanOnly" -PassThru
Write-Verbose "exit code: $($p.ExitCode)"
switch ($p.ExitCode) {
    -1047526896 { $result = "UpgradeReady"; break } 
    -1047526912 { $result = "SysReqtsFault"; break } 
    -1047526904 { $result = "CompatIssues"; break } 
    -1047526898 { $result = "InsuffDiskSpace"; break } 
    default { $result = "InvalidProductSku"; break }
}
if ($OutputPath -ne "") { 
    $filename = "$($env:COMPUTERNAME)-$result.txt"
    $filepath = Join-Path -Path $OutputPath -ChildPath $filename 
    $result | Out-File -FilePath $filepath -Append -NoClobber -Encoding Default
}
Write-Output $result

Shove it out as a Package, or make it a Script object you can spray all over helpless devices via Run Script.  Or run it directly (just add hot water and PSRemoting), or print it out, hang it on the wall, and laugh hysterically at it.  No matter what, it beats whatever your next staff meeting has to offer.

Cheers!

Dr. Skatterbrainz Answers Reader Mail

Warning: The following text may contain adult-ish offensive language which may cause unwanted side effects.  Read at your own risk.

“I was just wondering what you think about going into IT consulting for someone who’s never done it before, but who’s been working in IT direct for about ten years?” – Brad

It’s not for everyone, but some really enjoy it.  It also depends on whether you’re working from home/remote or on-site mostly.  If you’re used to being in a room full of people and a bustling office environment, then switch to being alone all the time, even with online communication tools, it’s sometimes lonely.  If you have pets or someone at home to talk to it helps, otherwise you should get outside frequently and mix with other people.  Coffee shop, park, etc.  It also requires you to impose your own control over scheduling, sleep, eating, exercise, etc.

If you prefer keeping hands-on with things after you build them, it might be tough letting go of each project and moving on to the next.  If you like having a steady office environment, that too may be a tough adjustment.  If you don’t like traveling a lot, or meeting strangers and getting used to strange places, accents, rules, customs, and so on, it may be a tough adjustment.

Other things to consider are how well you adjust to working alone, or with different teams from one day/week/month to the next, as opposed to being with the same group of people for months/years.

I would suggest that if you’re really curious/interested in consulting to give it a try.  You will be exposed to more variety and more ideas than you typically get with a steady office role.  But, no matter how it turns out, it will still be more experience, and more experience is good no matter what (as long as it doesn’t kill you or leave you brain-damaged). And you may get to rack up lots of flyer miles and hotel rewards points.

“Why do CIOs so often turn down requests from their own IT staff to improve tools and processes?” – Jim

Because most technical people suck at communicating things in terms of money.  Remember that old book “Women are from Venus. Men are from Mars”?.  CxO’s like numbers and charts.  The more colors and spiffiness (I made that term up) the better.  The situations where I’ve seen (or done by myself) a proposal laid out in terms of what it will provide in terms of the following, it got a positive result:

  • Cost savings
  • New revenue (not always welcome, unless it’s in a core competency)
  • Added capability (e.g. competitive advantage)

Any idea you have to improve things needs to be distilled down to what “improve” really means.  Improves what?  How?  For whom?  Keep walking that question back until it comes to a dollar figure.  And one other aspect I find that helps is to focus more on the repeat financial benefit, rather than a one-time benefit.  A simple one-time cost-savings doesn’t usually get them excited enough to set down the Martini and whip out the credit card, but a pay-off that keeps getting better every quarter/year is hard to ignore.  In the end, if you have to fluff the numbers to make it work, you need to ask yourself if you really have the best idea.  If it really makes sense you shouldn’t need to oversell it, but make sure you present it in the language the suit-clad folks really love to hear.

“Some of my siblings and cousins have a condescending view of the IT profession.  They’re all lawyers and marketing people, but somehow think IT is like dishwashing.  What’s the best thing I can do for that?” – Charles

Hi Charles.  I can completely sympathize.  I have a few of those people in my family as well.  You can either hold a grudge, or let it go. I prefer to let it go.  Time is your most valuable asset.  Don’t waste it.  The time you would spend on debating them could be better used on learning new skills or finding more projects to grow your experience.  Changing someone’s mind about things is almost impossible without proper firearms and pharmaceuticals.

“I’ve seen you pick on Microsoft Access a few times.  What do you hate about it?” – Chris

I don’t hate Access itself.  It’s a great product, especially for small scale needs.  But it’s not built for large-scale, shared use, and it couples the application (forms, reports, logic) with the data (tables, views, etc.) which doesn’t scale or lend itself to flexible maintenance.  It’s also very dependent on the version of Office installed, so upgrading the rest of Office then becomes hostage to it.

The other issue is that shared-use problems often lead to proliferation of multiple, standalone copies throughout the enterprise.  Maintaining consistency and centralized reporting becomes increasingly difficult.

Then when IT wants (or needs) to roll out a new version of Office, it turns into “Now hold on a minute!  That’ll break our precious Access DB ‘application’!”   The longer the Access app remains in production, the more intrinsic it becomes to business operations, making it more sensitive to disruption.  And the longer is remains in production, the more likely staff will have quit/retired/died/joined a cult, whatever, and now nobody is left who knows how to maintain or modify the code.

This often leads to the following discussion playbook scenarios, complete with the eye-rolling, mumbling, and drooling package…

Version 1

IT: “You guys in Finance need to upgrade this Access thing of yours so it works with 2016!”

Finance guys: “The guy who wrote it left months/years ago and we don’t have anyone who can update it”

IT: “Not our problem. Make it happen.”

Finance guys: “Then fuck you.”

Version 2

IT: “You guys in Finance need to upgrade this Access thing of yours so it works with 2016!”

Finance guys: “Okay, but since this is YOUR requirement, then IT should pay for all that work.”

IT: “No!”

Finance guys: “Then fuck you.”

Version 3

IT: “You guys in Finance need to upgrade this Access thing of yours so it works with 2016!”

Finance guys: “Okay, but we don’t have anyone who can do it due to schedules and other work.  Can IT do it for us?”

IT: “No!”

Finance guys: “Then fuck you.”

Version 4

IT: “You guys in Finance need to…”

Finance guys: “Just fuck you.”

Then we break for lunch, listen to IT complain about how <insert department name here> are a bunch of a-holes to work with.  We come back to the office, fighting off the carb-coma sleep monster, and repeat the same discussions again.  Someone will suggest the usual workarounds…

  • “Let’s install the Access runtime for 2007 or 2010 along with Access 2016!”
    • “Adding more complexity to our environment is not the answer.”
  • “Move it to Citrix or RDS!”
    • Citrix/RDS guy: “No!  I need funding to buy more hardware.”
  • “Let’s App-V or ThinApp it!”
    • “Carl – Do YOU know how to sequence that?”
      • “Ummm no.  But I hear it’s really cool.”
    • “You thought Justin Bieber was cool.”
      • “What’s wrong with Justin?!”
    • (continues on until some smacks the table to break it up)
  • “Let’s rent an unmarked van and kidnap that old guy that wrote this!”
    • “I have $53 on me.  Can we rent one that cheap?”
      • “I have duct tape, maybe a gift card too, hold on…”

In the end, the most expensive, least efficient and most painful “solution” will be chosen and everyone will be unhappy.  After a few months they will have left for other jobs and a new staff will be looking at it, wash rinse and repeat.  Or, in some cases, they hire someone to rewrite the app using modern tools that support shared use, are easy to maintain and even move to the cloud.

“I’d like to use Chocolatey at my company, but management won’t allow it and won’t pay for the business version.  Can I still leverage pieces of it somehow?” – Larry

There are several things you can “leverage” without using the public repository, or buying the business license features.

  • Set up an internal repository.  If there’s no objection to internal sourcing of packages.
  • Crack open Chocolatey packages for the silent installation and configuration syntax to use elsewhere.  Scripts, SCCM, etc.
  • Apply for a new job elsewhere.

Option 1 – Setting up an internal repo…

  1. Read this – https://chocolatey.org/docs/how-to-host-feed
  2. Test, test, and test some more
  3. Pilot deployment
  4. Production domination and ultimate anihilation

Option 2 – Cracking open a warm one…

  1. Locate the desired package (e.g. Microsoft Teams desktop app)
  2. Click on the “Package source” link along the left (opens the Github repo)
  3. Inspect the .nuspec file for some general details
  4. Inspect the xxxinstall.ps1 file for the code and tasty stuff
  5. Copy / adapt when you can into whatever else you’re using

Notes:

  • If you intend to “keep up” with the latest releases of a given package, you may need to repeat the above steps, or monitor the vendor source location(s) to react as they post new versions.
  • If you want to expand on this, you can post your own packages with internal modification requirements (icacls, registry hacks, etc.) as needed, or adapt them into your own deployment scripts or task sequences.

Cheers!

Send more questions via Twitter DM.  If you follow me, and your account doesn’t smell like a bot, or a weird cats-for-kids black market thing, then I will usually follow you back.