DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Trending

  • The End of “Good Enough Agile”
  • Agile’s Quarter-Century Crisis
  • Creating a Web Project: Caching for Performance Optimization
  • Modern Test Automation With AI (LLM) and Playwright MCP

Windows Management With Chocolatey

This article is a guide and tutorial for DevOps, Operations, IT and Tools Team on how to enable On-Demand Windows Installation using open-source Chocolatey.

By 
Deeptesh Bhattacharya user avatar
Deeptesh Bhattacharya
·
Aug. 12, 21 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
7.8K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

There is always a trade-off between a centralized IT function and a federated IT function where the latter gives the developers flexibility around tools and technologies. However, as part of the centralized IT function, how can you enable federation and still manage the primary control?

This article takes you through the approach of managing Federated IT systems while you are adopting your DevOps journey towards increased automation and efficiency. 

A lot of time is wasted across when you have to enable developers on projects which are spread across different technologies. The developers always needs some tools to be installed on to their systems based on project requirements. This increases hierarchical red-tape and overall MTTF (Mean-Time-To-Fulfillment)

In general, the process will involve something like this:

Consumers vs Producers


The process can be further made efficient and automated by enabling federation using tools like CI/CD and a repository manager. The overall process can be hooked up to Jira Service Desk, Jenkins CI/CD and Nexus Antifactory.

Enabling federation tools


However, to further achieve more federation and enabling the developers to manage their own installs on-demand. This can be enabled using a package management tool. 

The workflow will somewhat look similar to the diagram below:

Package management tool

However, in order to achieve full federation and support on-demand installation the team can further leverage PowerShell and Chocolatey. 

Chocolatey supports installations across Windows and can pull artifacts from Nexus repository for installations. These packages can be auto generated using PowerShell and converted into Nuget Packages, as chocolatey uses a Nuget Package for installation from a Nuget Repository. 

Using Jenkins and Powershell, the tested packages can be converted into Nuget Packages with in the integrated pipelines during build and release and made available for self hosted proprietary private NuGet repository under as strict access control using Nexus RBACs and Security Realms.

For internal packages, the above process can used to create and push new packages whenever application teams release a new version for other teams to consume it on demand.

Here’s is an example of a Powershell Automation script which is used to automate the package conversion to Nuget Package. The package can comprise of any executable. 

PowerShell automation script

It collects the package variables from the variables defined during the build process and further integrates that to generate a Choco package using the chocolatey templating engine.

Generation of a Choco package

Note: This is a very generic script to give you a glimpse of what can be achieved.  

The entire process can be broken down into 5 pieces as key baby steps to achieve such automation.

  1. Define your own chocolatey Templates or use an existing one —
  • How do I create packages? See https://chocolatey.org/docs/create-packages
  • https://chocolatey.org/docs/how-to-create-custom-package-templates
  • Consider making this package an automatic package, for the best maintainability over time. Read up at https://chocolatey.org/docs/automatic-packages
  • You can further create extensions — https://chocolatey.org/docs/how-to-create-extensions

For eg. if you have to write your own chocolatey package you can use the below templating framework

PHP
 
# IMPORTANT: Before releasing this package, copy/paste the next 2 lines into PowerShell to remove all comments from this file: #   $f='c:\path\to\thisFile.ps1' #   gc $f | ? {$_ -notmatch "^\s*#"} | % {$_ -replace '(^.*?)\s*?[^``]#.*','$1'} | Out-File $f+".~" -en utf8; mv -fo $f+".~" $f # 1. See the _TODO.md that is generated top level and read through that # 2. Follow the documentation below to learn how to create a package for the package type you are creating. # 3. In Chocolatey scripts, ALWAYS use absolute paths - $toolsDir gets you to the package's tools directory. $ErrorActionPreference = 'Stop'; # stop on all errors $toolsDir   = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" # Internal packages (organizations) or software that has redistribution rights (community repo) # - Use `Install-ChocolateyInstallPackage` instead of `Install-ChocolateyPackage` #   and put the binaries directly into the tools folder (we call it embedding) #$fileLocation = Join-Path $toolsDir 'NAME_OF_EMBEDDED_INSTALLER_FILE' # If embedding binaries increase total nupkg size to over 1GB, use share location or download from urls #$fileLocation = '\\SHARE_LOCATION\to\INSTALLER_FILE' # Community Repo: Use official urls for non-redist binaries or redist where total package size is over 200MB # Internal/Organization: Download from internal location (internet sources are unreliable) $url        = '' # download url, HTTPS preferred $url64      = '' # 64bit URL here (HTTPS preferred) or remove - if installer contains both (very rare), use $url $packageArgs = @{ packageName   = $env:ChocolateyPackageName unzipLocation = $toolsDir fileType      = 'EXE_MSI_OR_MSU' #only one of these: exe, msi, msu url           = $url url64bit      = $url64 #file         = $fileLocation softwareName  = 'myapptemplate.template*' #part or all of the Display Name as you see it in Programs and Features. It should be enough to be unique # Checksums are now required as of 0.10.0. # To determine checksums, you can get that from the original site if provided. # You can also use checksum.exe (choco install checksum) and use it # e.g. checksum -t sha256 -f path\to\file checksum      = '' checksumType  = 'sha256' #default is md5, can also be sha1, sha256 or sha512 checksum64    = '' checksumType64= 'sha256' #default is checksumType # MSI silentArgs    = "/qn /norestart /l*v `"$($env:TEMP)\$($packageName).$($env:chocolateyPackageVersion).MsiInstall.log`"" # ALLUSERS=1 DISABLEDESKTOPSHORTCUT=1 ADDDESKTOPICON=0 ADDSTARTMENU=0 validExitCodes= @(0, 3010, 1641) # OTHERS # Uncomment matching EXE type (sorted by most to least common) #silentArgs   = '/S'           # NSIS #silentArgs   = '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-' # Inno Setup #silentArgs   = '/s'           # InstallShield #silentArgs   = '/s /v"/qn"'   # InstallShield with MSI #silentArgs   = '/s'           # Wise InstallMaster #silentArgs   = '-s'           # Squirrel #silentArgs   = '-q'           # Install4j #silentArgs   = '-s'           # Ghost # Note that some installers, in addition to the silentArgs above, may also need assistance of AHK to achieve silence. #silentArgs   = ''             # none; make silent with input macro script like AutoHotKey (AHK) #       https://chocolatey.org/packages/autohotkey.portable #validExitCodes= @(0) #please insert other valid exit codes here }
Install-ChocolateyPackage @packageArgs # https://chocolatey.org/docs/helpers-install-chocolatey-package #Install-ChocolateyZipPackage @packageArgs # https://chocolatey.org/docs/helpers-install-chocolatey-zip-package ## If you are making your own internal packages (organizations), you can embed the installer or ## put on internal file share and use the following instead (you'll need to add $file to the above) #Install-ChocolateyInstallPackage @packageArgs # https://chocolatey.org/docs/helpers-install-chocolatey-install-package ## Main helper functions - these have error handling tucked into them already ## see https://chocolatey.org/docs/helpers-reference ## Install an application, will assert administrative rights ## - https://chocolatey.org/docs/helpers-install-chocolatey-package ## - https://chocolatey.org/docs/helpers-install-chocolatey-install-package ## add additional optional arguments as necessary ##Install-ChocolateyPackage $packageName $fileType $silentArgs $url [$url64 -validExitCodes $validExitCodes -checksum $checksum -checksumType $checksumType -checksum64 $checksum64 -checksumType64 $checksumType64] ## Download and unpack a zip file - https://chocolatey.org/docs/helpers-install-chocolatey-zip-package ##Install-ChocolateyZipPackage $packageName $url $toolsDir [$url64 -checksum $checksum -checksumType $checksumType -checksum64 $checksum64 -checksumType64 $checksumType64] ## Install Visual Studio Package - https://chocolatey.org/docs/helpers-install-chocolatey-vsix-package #Install-ChocolateyVsixPackage $packageName $url [$vsVersion] [-checksum $checksum -checksumType $checksumType] #Install-ChocolateyVsixPackage @packageArgs ## see the full list at https://chocolatey.org/docs/helpers-reference ## downloader that the main helpers use to download items ## if removing $url64, please remove from here ## - https://chocolatey.org/docs/helpers-get-chocolatey-web-file #Get-ChocolateyWebFile $packageName 'DOWNLOAD_TO_FILE_FULL_PATH' $url $url64 ## Installer, will assert administrative rights - used by Install-ChocolateyPackage ## use this for embedding installers in the package when not going to community feed or when you have distribution rights ## - https://chocolatey.org/docs/helpers-install-chocolatey-install-package #Install-ChocolateyInstallPackage $packageName $fileType $silentArgs '_FULLFILEPATH_' -validExitCodes $validExitCodes ## Unzips a file to the specified location - auto overwrites existing content ## - https://chocolatey.org/docs/helpers-get-chocolatey-unzip #Get-ChocolateyUnzip "FULL_LOCATION_TO_ZIP.zip" $toolsDir ## Runs processes asserting UAC, will assert administrative rights - used by Install-ChocolateyInstallPackage ## - https://chocolatey.org/docs/helpers-start-chocolatey-process-as-admin #Start-ChocolateyProcessAsAdmin 'STATEMENTS_TO_RUN' 'Optional_Application_If_Not_PowerShell' -validExitCodes $validExitCodes ## To avoid quoting issues, you can also assemble your -Statements in another variable and pass it in #$appPath = "$env:ProgramFiles\appname" ##Will resolve to C:\Program Files\appname #$statementsToRun = "/C `"$appPath\bin\installservice.bat`"" #Start-ChocolateyProcessAsAdmin $statementsToRun cmd -validExitCodes $validExitCodes ## add specific folders to the path - any executables found in the chocolatey package ## folder will already be on the path. This is used in addition to that or for cases ## when a native installer doesn't add things to the path. ## - https://chocolatey.org/docs/helpers-install-chocolatey-path #Install-ChocolateyPath 'LOCATION_TO_ADD_TO_PATH' 'User_OR_Machine' # Machine will assert administrative rights ## Add specific files as shortcuts to the desktop ## - https://chocolatey.org/docs/helpers-install-chocolatey-shortcut #$target = Join-Path $toolsDir "$($packageName).exe" # Install-ChocolateyShortcut -shortcutFilePath "<path>" -targetPath "<path>" [-workDirectory "C:\" -arguments "C:\test.txt" -iconLocation "C:\test.ico" -description "This is the description"] ## Outputs the bitness of the OS (either "32" or "64") ## - https://chocolatey.org/docs/helpers-get-o-s-architecture-width #$osBitness = Get-ProcessorBits ## Set persistent Environment variables ## - https://chocolatey.org/docs/helpers-install-chocolatey-environment-variable #Install-ChocolateyEnvironmentVariable -variableName "SOMEVAR" -variableValue "value" [-variableType = 'Machine' #Defaults to 'User'] ## Set up a file association ## - https://chocolatey.org/docs/helpers-install-chocolatey-file-association #Install-ChocolateyFileAssociation ## Adding a shim when not automatically found - Cocolatey automatically shims exe files found in package directory. ## - https://chocolatey.org/docs/helpers-install-bin-file ## - https://chocolatey.org/docs/create-packages#how-do-i-exclude-executables-from-getting-shims #Install-BinFile ##PORTABLE EXAMPLE #$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" # despite the name "Install-ChocolateyZipPackage" this also works with 7z archives #Install-ChocolateyZipPackage $packageName $url $toolsDir $url64 ## END PORTABLE EXAMPLE ## [DEPRECATING] PORTABLE EXAMPLE #$binRoot = Get-BinRoot #$installDir = Join-Path $binRoot "$packageName" #Write-Host "Adding `'$installDir`' to the path and the current shell path" #Install-ChocolateyPath "$installDir" #$env:Path = "$($env:Path);$installDir" # if removing $url64, please remove from here # despite the name "Install-ChocolateyZipPackage" this also works with 7z archives #Install-ChocolateyZipPackage "$packageName" "$url" "$installDir" "$url64" ## END PORTABLE EXAMPLE


Or Write your own installation script. The script below is used to install Nexus Repository.

PHP
 
# Nexus Download Link: http://www.sonatype.org/downloads/nexus-latest-bundle.zip Clear-Host # URL Parameter $WebURL = "http://www.sonatype.org/downloads/nexus-latest-bundle.zip" # Directory Parameter $FileDirectory = "$($env:USERPROFILE)$("\downloads\")" #Write-Output $FileDirectory # If directory doesn't exist create the directory if((Test-Path $FileDirectory) -eq 0) {
mkdir $FileDirectory; }
# We assume the file you download is named what you want it to be on your computer $FileName = [System.IO.Path]::GetFileName($WebURL) # Concatenate the two values to prepare the download $FullFilePath = "$($FileDirectory)$($FileName)" #Write-Output $FullFilePath function Get-FileDownload([String] $WebURL, [String] $FullFilePath) {
# Give a basic message to the user to let them know what we are doing Write-Output "Downloading '$WebURL' to '$FullFilePath'" $uri = New-Object "System.Uri" "$WebURL" $request = [System.Net.HttpWebRequest]::Create($uri) $request.set_Timeout(30000) #15 second timeout $response = $request.GetResponse() $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024) $responseStream = $response.GetResponseStream() $targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $FullFilePath, Create $buffer = new-object byte[] 10KB $count = $responseStream.Read($buffer,0,$buffer.length) $downloadedBytes = $count while ($count -gt 0) {
[System.Console]::Write("`r`nDownloaded {0}K of {1}K", [System.Math]::Floor($downloadedBytes/1024), $totalLength) $targetStream.Write($buffer, 0, $count) $count = $responseStream.Read($buffer,0,$buffer.length) $downloadedBytes = $downloadedBytes + $count }
$targetStream.Flush() $targetStream.Close() $targetStream.Dispose() $responseStream.Dispose() # Give a basic message to the user to let them know we are done Write-Output "`r`nDownload complete" }
function Expand-ZipFile([string]$File, [string]$Destination) #The targets to run. {
# If directory doesn't exist create the directory if((Test-Path $Destination) -eq 0) {
mkdir $Destination; }
$Shell = new-object -com shell.application # Get the name of the Zip file $Zip = $Shell.NameSpace($File) #Expand/Extract each file from the zip file foreach($Item in $Zip.items()) {
$Shell.Namespace($Destination).copyhere($Item) }
} Get-FileDownload $WebURL  $FullFilePath Expand-ZipFile $FullFilePath c:\Nexus Set-Location C:\Nexus $NexusFolder = (Get-ChildItem nexus* | Select-Object Name).Name # Create System Variable [Environment]::SetEnvironmentVariable("NEXUS_HOME", "C:\Nexus\$NexusFolder", "Machine") Set-Location "C:\Nexus\$NexusFolder" # Configure C:\Nexus\nexus-2.12.0-01\conf\nexus.properties #     Set Port Number if you want something other than 8081 Set-Location bin # Nexus Download Link: http://www.sonatype.org/downloads/nexus-latest-bundle.zip Clear-Host # URL Parameter $WebURL = "http://www.sonatype.org/downloads/nexus-latest-bundle.zip" # Directory Parameter $FileDirectory = "$($env:USERPROFILE)$("\downloads\")" #Write-Output $FileDirectory # If directory doesn't exist create the directory if((Test-Path $FileDirectory) -eq 0) {
mkdir $FileDirectory; }
# We assume the file you download is named what you want it to be on your computer $FileName = [System.IO.Path]::GetFileName($WebURL) # Concatenate the two values to prepare the download $FullFilePath = "$($FileDirectory)$($FileName)" #Write-Output $FullFilePath function Get-FileDownload([String] $WebURL, [String] $FullFilePath) {
# Give a basic message to the user to let them know what we are doing Write-Output "Downloading '$WebURL' to '$FullFilePath'" $uri = New-Object "System.Uri" "$WebURL" $request = [System.Net.HttpWebRequest]::Create($uri) $request.set_Timeout(30000) #15 second timeout $response = $request.GetResponse() $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024) $responseStream = $response.GetResponseStream() $targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $FullFilePath, Create $buffer = new-object byte[] 10KB $count = $responseStream.Read($buffer,0,$buffer.length) $downloadedBytes = $count while ($count -gt 0) {
[System.Console]::Write("`r`nDownloaded {0}K of {1}K", [System.Math]::Floor($downloadedBytes/1024), $totalLength) $targetStream.Write($buffer, 0, $count) $count = $responseStream.Read($buffer,0,$buffer.length) $downloadedBytes = $downloadedBytes + $count }
$targetStream.Flush() $targetStream.Close() $targetStream.Dispose() $responseStream.Dispose() # Give a basic message to the user to let them know we are done Write-Output "`r`nDownload complete" }
function Expand-ZipFile([string]$File, [string]$Destination) #The targets to run. {
# If directory doesn't exist create the directory if((Test-Path $Destination) -eq 0) {
mkdir $Destination; }
$Shell = new-object -com shell.application # Get the name of the Zip file $Zip = $Shell.NameSpace($File) #Expand/Extract each file from the zip file foreach($Item in $Zip.items()) {
$Shell.Namespace($Destination).copyhere($Item) }
} Get-FileDownload $WebURL  $FullFilePath Expand-ZipFile $FullFilePath c:\Nexus Set-Location C:\Nexus $NexusFolder = (Get-ChildItem nexus* | Select-Object Name).Name # Create System Variable [Environment]::SetEnvironmentVariable("NEXUS_HOME", "C:\Nexus\$NexusFolder", "Machine") Set-Location "C:\Nexus\$NexusFolder" # Configure C:\Nexus\nexus-2.12.0-01\conf\nexus.properties #     Set Port Number if you want something other than 8081 Set-Location bin Start-Process nexus.bat install -Wait Start-Process nexus.bat start -Wait Start-Process 'http://localhost:8081/nexus'


Use the Built-In Functions

https://chocolatey.org/docs/helpers-reference

Write a Nuget Package conversion tool using PowerShell for auto generation of packages. You can use the code reference from example above.
  • https://docs.microsoft.com/en-us/nuget/reference/nuspec
  • https://docs.microsoft.com/en-us/dotnet/api/System.Management.Automation?view=powershellsdk-7.0.0

Embed this tool into your existing CI/CD pipeline.

Enable download of chocolatey from the Self Service Portal and run initial scripts for configuring chocolatey repos.

Optional — Enable all of this as part of your IT/DevOps self service portal.

I hope this article helps bring your Developers and Operations team closer and can support your OKR’s for the year.

Chocolatey

Opinions expressed by DZone contributors are their own.

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!