Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"[powershell]": {
"debug.saveBeforeStart": "nonUntitledEditorsInActiveGroup",
"editor.semanticHighlighting.enabled": false,
"editor.wordSeparators": "`~!@#$%^&*()=+[{]}\\|;:'\",.<>/?",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "modificationsIfAvailable",
},
"diffEditor.experimental.showMoves": true,
"diffEditor.experimental.useTrueInlineView": true,
"powershell.codeFormatting.autoCorrectAliases": true,
"powershell.codeFormatting.avoidSemicolonsAsLineTerminators": true,
"powershell.codeFormatting.newLineAfterCloseBrace": false,
"powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline",
"powershell.codeFormatting.preset": "OTBS",
"powershell.codeFormatting.trimWhitespaceAroundPipe": true,
"powershell.codeFormatting.useCorrectCasing": true,
"powershell.codeFormatting.whitespaceAfterSeparator": true,
"powershell.codeFormatting.whitespaceBetweenParameters": false,
"powershell.debugging.executeMode": "DotSource",
"powershell.pester.useLegacyCodeLens": false
}
39 changes: 26 additions & 13 deletions Initialize-C4bSetup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,19 @@ param(
}
),

# Selects which automation platform to install and use.
[Parameter()]
[ValidateSet("Jenkins", "PowerShellUniversal")]
[string]$AutomationPlatform = "PowerShellUniversal",

# Selects which repository platform to use.
[Parameter()]
[ValidateSet("Nexus")] # , "ProGet")]
[string]$RepositoryPlatform = $(if ($PSBoundParameters.ContainsKey('ProGetLicense')) { "ProGet" } else { "Nexus" }),

# If provided, licenses Inedo ProGet non-interactively.
[string]$ProGetLicense,

# If provided, shows all Chocolatey output. Otherwise, blissful quiet.
[switch]
$ShowChocoOutput,
Expand Down Expand Up @@ -149,15 +162,6 @@ try {
}
}

# Add the Module Path and Import Helper Functions
if (-not (Get-Module C4B-Environment -ListAvailable)) {
if ($env:PSModulePath.Split(';') -notcontains "$FilesDir\modules") {
[Environment]::SetEnvironmentVariable("PSModulePath", "$env:PSModulePath;$FilesDir\modules" , "Machine")
$env:PSModulePath = [Environment]::GetEnvironmentVariables("Machine").PSModulePath
}
}
Import-Module C4B-Environment -Verbose:$false

# Downloading all CCM setup packages below
Write-Host "Downloading missing nupkg files to $($PkgsDir)." -ForegroundColor Green
Write-Host "This will take some time. Feel free to get a tea or coffee." -ForegroundColor Green
Expand All @@ -180,7 +184,7 @@ try {
# Collect current certificate configuration
$Certificate = Get-Certificate -Thumbprint $Thumbprint
Copy-CertToStore -Certificate $Certificate

$null = Test-CertificateDomain -Thumbprint $Thumbprint
} elseif ($PSScriptRoot) {
# We're going to be using a self-signed certificate
Expand Down Expand Up @@ -215,7 +219,7 @@ try {
# Set Choco Server Chocolatey Configuration
Invoke-Choco feature enable --name="'excludeChocolateyPackagesDuringUpgradeAll'"
Invoke-Choco feature enable --name="'usePackageHashValidation'"

# Convert license to a "choco-license" package, and install it locally to test
Write-Host "Creating a 'chocolatey-license' package, and testing install." -ForegroundColor Green
Set-Location $FilesDir
Expand All @@ -226,9 +230,18 @@ try {
if ($Thumbprint) { $Certificate.Thumbprint = $Thumbprint }

Set-Location "$env:SystemDrive\choco-setup\files"
.\Start-C4BNexusSetup.ps1 @Certificate

switch ($RepositoryPlatform) {
"Nexus" { .\Start-C4bNexusSetup.ps1 @Certificate }
# "ProGet" { .\Start-C4BProgetSetup.ps1 @Certificate -License $ProGetLicense }
}

.\Start-C4bCcmSetup.ps1 @Certificate -DatabaseCredential $DatabaseCredential
.\Start-C4bJenkinsSetup.ps1 @Certificate

switch ($AutomationPlatform) {
"Jenkins" { .\StartC4bJenkinsSetup.ps1 @Certificate }
"PowerShellUniversal" { .\Start-C4bPsuSetup.ps1 @Certificate }
}

Complete-C4bSetup -SkipBrowserLaunch:$SkipBrowserLaunch
}
Expand Down
55 changes: 30 additions & 25 deletions OfflineInstallPreparation.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ param(
}
),

# Selects which automation platform to install and use.
[Parameter()]
[ValidateSet("Jenkins", "PowerShellUniversal")]
[string]$AutomationPlatform = "PowerShellUniversal",

[string]$WorkingDirectory = $(Join-Path $env:Temp "choco-offline")
)
$ErrorActionPreference = "Stop"
Expand All @@ -78,8 +83,6 @@ if ($Signature.Status -eq 'Valid' -and $Signature.SignerCertificate.Subject -eq
Write-Error "ChocolateyInstall.ps1 script signature is not valid. Please investigate." -ErrorAction Stop
}

Import-Module $PSScriptRoot\modules\C4B-Environment -Force

# Initialize environment, ensure Chocolatey For Business, etc.
$Licensed = ($($(choco.exe)[0] -match "^Chocolatey (?<Version>\S+)\s*(?<LicenseType>Business)?$") -and $Matches.LicenseType)
$InstalledLicensePath = "$env:ChocolateyInstall\license\chocolatey.license.xml"
Expand All @@ -95,9 +98,12 @@ if (-not $Licensed) {
} else {
'https://licensedpackages.chocolatey.org/api/v2/'
}
Invoke-Choco install chocolatey.extension --source $ExtensionSource --params="'/NoContextMenu'" --confirm
$null = choco install chocolatey.extension --source $ExtensionSource --params="'/NoContextMenu'" --confirm --no-progress
}

# Install the required C4B Environment module
$null = choco install c4b-environment.powershell --source $ExtensionSource --confirm --no-progress

# Download each set of packages to the output directories
$PackageWorkingDirectory = Join-Path $WorkingDirectory "Packages"
if (-not (Test-Path $PackageWorkingDirectory)) {
Expand All @@ -122,7 +128,7 @@ foreach ($Package in (Get-Content $PSScriptRoot\files\chocolatey.json | ConvertF
if (-not (Get-ChocolateyPackageMetadata -Path $PackageWorkingDirectory -Id $Package.Name) -and -not (Get-ChocolateyPackageMetadata -Path "$PSScriptRoot\files\" -Id $Package.Name)) {
Write-Host "Downloading '$($Package.Name)'"

while ((Get-ChildItem $PackageWorkingDirectory -Filter *.nupkg).Where{$_.CreationTime -gt (Get-Date).AddMinutes(-1)}.Count -gt 5) {
while ((Get-ChildItem $PackageWorkingDirectory -Filter *.nupkg).Where{ $_.CreationTime -gt (Get-Date).AddMinutes(-1) }.Count -gt 5) {
Write-Verbose "Slowing down for a minute, in order to not trigger rate-limiting..."
Start-Sleep -Seconds 5
}
Expand All @@ -135,31 +141,30 @@ foreach ($Package in (Get-Content $PSScriptRoot\files\chocolatey.json | ConvertF
}
Move-Item -Path $PackageWorkingDirectory\*.nupkg -Destination $PSScriptRoot\files\

# Jenkins Plugins
$PluginsWorkingDirectory = Join-Path $WorkingDirectory "JenkinsPlugins"
if (-not (Test-Path $PluginsWorkingDirectory)) {
$null = New-Item -Path $PluginsWorkingDirectory -ItemType Directory -Force
}
if (Test-Path $PSScriptRoot\files\JenkinsPlugins.zip) {
Expand-Archive -Path $PSScriptRoot\files\JenkinsPlugins.zip -DestinationPath $PluginsWorkingDirectory -Force
}
$ProgressPreference = "Ignore"
foreach ($Plugin in (Get-Content $PSScriptRoot\files\jenkins.json | ConvertFrom-Json).plugins) {
$RestArgs = @{
Uri = "https://updates.jenkins-ci.org/latest/$($Plugin.Name).hpi"
OutFile = Join-Path $PluginsWorkingDirectory "$($Plugin.Name).hpi"
if ($AutomationPlatform -eq 'Jenkins') {
# Jenkins Plugins
$PluginsWorkingDirectory = Join-Path $WorkingDirectory "JenkinsPlugins"
if (-not (Test-Path $PluginsWorkingDirectory)) {
$null = New-Item -Path $PluginsWorkingDirectory -ItemType Directory -Force
}
if ($Plugin.Version -and $Plugin.Version -ne 'latest') {
$RestArgs.Uri = "https://updates.jenkins.io/download/plugins/$($Plugin.Name)/$($Plugin.Version)/$($Plugin.Name).hpi"
if (Test-Path $PSScriptRoot\files\JenkinsPlugins.zip) {
Expand-Archive -Path $PSScriptRoot\files\JenkinsPlugins.zip -DestinationPath $PluginsWorkingDirectory -Force
}
if (-not (Test-Path $RestArgs.OutFile)) {
Invoke-WebRequest @RestArgs -UseBasicParsing
$ProgressPreference = "Ignore"
foreach ($Plugin in (Get-Content $PSScriptRoot\files\jenkins.json | ConvertFrom-Json).plugins) {
$RestArgs = @{
Uri = "https://updates.jenkins-ci.org/latest/$($Plugin.Name).hpi"
OutFile = Join-Path $PluginsWorkingDirectory "$($Plugin.Name).hpi"
}
if ($Plugin.Version -and $Plugin.Version -ne 'latest') {
$RestArgs.Uri = "https://updates.jenkins.io/download/plugins/$($Plugin.Name)/$($Plugin.Version)/$($Plugin.Name).hpi"
}
if (-not (Test-Path $RestArgs.OutFile)) {
Invoke-WebRequest @RestArgs -UseBasicParsing
}
}
Compress-Archive -Path $PluginsWorkingDirectory\* -Destination $PSScriptRoot\files\JenkinsPlugins.zip -Force
}
Compress-Archive -Path $PluginsWorkingDirectory\* -Destination $PSScriptRoot\files\JenkinsPlugins.zip -Force

# BCryptDll
$null = Get-BcryptDll

# License
if ($LicensePath -ne "$PSScriptRoot\files\chocolatey.license.xml") {
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Welcome to the Chocolatey for Business (C4B) Quick-Start Guide! This guide will
- The Chocolatey Licensed components
- A NuGet V3 Repository (Nexus)
- Chocolatey Central Management (CCM)
- An Automation Pipeline (Jenkins)
- An Automation Pipeline (PowerShell Universal)

> :memo: **NOTE**
>
Expand Down Expand Up @@ -41,7 +41,7 @@ As illustrated in the diagram above, there are four main components to a Chocola

1. **Chocolatey Central Management (CCM)**: CCM is the Web UI portal for your entire Chocolatey environment. Your endpoints check-in to CCM to report their package status. This includes the Chocolatey packages they have installed, and whether any of these packages are outdated. And now, with CCM Deployments, you can also deploy packages or package updates to groups of endpoints, as well as ad-hoc PowerShell commands. CCM is backed by an MS SQL Database. This guide will set up MS SQL Express for you.

1. **Automation Pipeline (Jenkins)**: A pipeline tool will help you automate repetitive tasks, such checking for updates to a set of Chocolatey Packages from the Chocolatey Community Repository (CCR). If updates exist, the pipeline task will auto-internalize your list of packages, and push them into your NuGet repository for you. This guide will help you set up Jenkins as your automation pipeline.
1. **Automation Pipeline (PowerShell Universal)**: A pipeline tool will help you automate repetitive tasks, such checking for updates to a set of Chocolatey Packages from the Chocolatey Community Repository (CCR). If updates exist, the pipeline task will auto-internalize your list of packages, and push them into your NuGet repository for you. This guide will help you set up PowerShell Universal as your automation pipeline.

## Requirements

Expand Down Expand Up @@ -171,17 +171,17 @@ As part of the C4B setup, we install and configure Chocolatey Central Management
> </ul>
> </details>

#### Script: Jenkins Setup
#### Script: PowerShell Universal Setup

As part of the C4B setup, we install and configure Jenkins as a method for running Package Internalizer and other jobs:
As part of the C4B setup, we install and configure PowerShell Universal as a method for running Package Internalizer and other jobs:

> <details>
> <summary><strong>What does this script do? (click to expand)</strong></summary>
> <ul class="list-style-type-disc">
> <li>Installs Jenkins package</li>
> <li>Updates Jenkins plugins</li>
> <li>Configures pre-downloaded Jenkins scripts for Package Internalizer automation</li>
> <li>Sets up pre-defined Jenkins jobs for the scripts above</li>
> <li>Installs PowerShell Universal package</li>
> <li>Installs a module containing the Chocolatey for Business environment for PSU</li>
> <li>Configures scripts for Package Internalizer automation</li>
> <li>Sets up a Chocolatey for Business dashboard</li>
> </ul>
> </details>

Expand All @@ -194,11 +194,11 @@ As part of the C4B setup, we create a readme and install the Chocolatey Agent on
> <ul class="list-style-type-disc">
> <li>Sets up Chocolatey Agent on this system</li>
> <li>Writes a Readme.html file to the Public Desktop with account information for C4B services</li>
> <li>Auto-opens README, CCM, Nexus, and Jenkins in your web browser</li>
> <li>Auto-opens README, CCM, Nexus, and PowerShell Universal in your web browser</li>
> </ul>
> </details>

> :mag: **FYI**: A `Readme.html` file will now be generated on your desktop. This file contains login information for all 3 web portals (CCM, Nexus, and Jenkins). This `Readme.html`, along with all 3 web portals, will automatically be opened in your browser.
> :mag: **FYI**: A `Readme.html` file will now be generated on your desktop. This file contains login information for all 3 web portals (CCM, Nexus, and PowerShell Universal). This `Readme.html`, along with all 3 web portals, will automatically be opened in your browser.

### Step 2: Setting up Endpoints

Expand Down
10 changes: 8 additions & 2 deletions Start-C4bCcmSetup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -193,17 +193,23 @@ try {
New-CcmBinding -Thumbprint $Thumbprint
Start-CcmService

# If the user provided certificate has multiple SANs, they may need to select one with params
if (-not (Get-ChocoEnvironmentProperty CertSubject)) {
$CertSubject = (Get-Item Cert:\LocalMachine\TrustedPeople\$Thumbprint).Subject -replace '^CN='
Set-ChocoEnvironmentProperty CertSubject $CertSubject
}

$CcmEndpoint = "https://$(Get-ChocoEnvironmentProperty CertSubject)"
}
choco config set centralManagementServiceUrl "$($CcmEndpoint):24020/ChocolateyManagementService"

#Generate CCM Salt Values
if (-not (Get-ChocoEnvironmentProperty ClientSalt)) {
if (-not ($ClientSaltValue = Get-ChocoEnvironmentProperty ClientSalt)) {
$ClientSaltValue = New-ServicePassword
Set-ChocoEnvironmentProperty ClientSalt $ClientSaltValue
}

if (-not (Get-ChocoEnvironmentProperty ServiceSalt)) {
if (-not ($ServiceSaltValue = Get-ChocoEnvironmentProperty ServiceSalt)) {
$ServiceSaltValue = New-ServicePassword
Set-ChocoEnvironmentProperty ServiceSalt $ServiceSaltValue
}
Expand Down
40 changes: 21 additions & 19 deletions Start-C4bNexusSetup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ C4B Quick-Start Guide Nexus setup script
- Setup of firewall rule for repository access
#>
[CmdletBinding()]
param(
param(
# The certificate thumbprint that identifies the target SSL certificate in
# the local machine certificate stores.
[Parameter()]
[ArgumentCompleter({
Get-ChildItem Cert:\LocalMachine\TrustedPeople | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_.Thumbprint,
$_.Thumbprint,
"ParameterValue",
($_.Subject -replace "^CN=(?<FQDN>.+),?.*$",'${FQDN}')
)
}
})]
[ValidateScript({Test-CertificateDomain -Thumbprint $_})]
Get-ChildItem Cert:\LocalMachine\TrustedPeople | ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_.Thumbprint,
$_.Thumbprint,
"ParameterValue",
($_.Subject -replace "^CN=(?<FQDN>.+),?.*$", '${FQDN}')
)
}
})]
[ValidateScript({ Test-CertificateDomain -Thumbprint $_ })]
[string]
$Thumbprint = $(
if ((Test-Path C:\choco-setup\clixml\chocolatey-for-business.xml) -and (Import-Clixml C:\choco-setup\clixml\chocolatey-for-business.xml).CertThumbprint) {
Expand All @@ -51,17 +51,17 @@ process {

# Install base nexus-repository package
Write-Host "Installing Sonatype Nexus Repository"
$chocoArgs = @('install', 'nexus-repository', '-y' ,'--no-progress', "--package-parameters='/Fqdn:localhost'")
$chocoArgs = @('install', 'nexus-repository', '-y' , '--no-progress', "--package-parameters='/Fqdn:localhost'")
& Invoke-Choco @chocoArgs

$chocoArgs = @('install', 'nexushell', '-y' ,'--no-progress')
$chocoArgs = @('install', 'nexushell', '-y' , '--no-progress')
& Invoke-Choco @chocoArgs

if ($Thumbprint) {
$NexusPort = 8443

$null = Set-NexusCert -Thumbprint $Thumbprint -Port $NexusPort

if ($CertificateDnsName = Get-ChocoEnvironmentProperty CertSubject) {
# Override the domain, so we don't get prompted for wildcard certificates
Get-NexusLocalServiceUri -HostnameOverride $CertificateDnsName | Write-Verbose
Expand All @@ -71,10 +71,10 @@ process {
# Add Nexus port access via firewall
$FwRuleParams = @{
DisplayName = "Nexus Repository access on TCP $NexusPort"
Direction = 'Inbound'
LocalPort = $NexusPort
Protocol = 'TCP'
Action = 'Allow'
Direction = 'Inbound'
LocalPort = $NexusPort
Protocol = 'TCP'
Action = 'Allow'
}
$null = New-NetFirewallRule @FwRuleParams

Expand Down Expand Up @@ -216,6 +216,7 @@ process {
} else {
$NuGetApiKey = (Get-NexusNuGetApiKey -Credential $UploadUser).apiKey
Set-ChocoEnvironmentProperty -Name PackageApiKey -Value $NuGetApiKey
Set-ChocoEnvironmentProperty PackageUploaderApiKey -Value $NuGetApiKey
}

# Push latest ChocolateyInstall.ps1 to raw repo
Expand Down Expand Up @@ -249,6 +250,7 @@ process {
# Add ChocolateyTest as a source repository, to enable authenticated pushing
Invoke-Choco source add -n 'ChocolateyTest' -s "$((Get-NexusRepository -Name 'ChocolateyTest').url)/index.json" -u="$($UploadUser.UserName)" -p="$($UploadUser.GetNetworkCredential().Password)"
Invoke-Choco source disable -n 'ChocolateyTest'
Set-ChocoEnvironmentProperty PackageUploaderRepo "$((Get-NexusRepository -Name 'ChocolateyTest').url)/index.json"

# Push all packages from previous steps to NuGet repo
Write-Host "Pushing C4B Environment Packages to ChocolateyInternal"
Expand All @@ -262,7 +264,7 @@ process {
# Remove Local Chocolatey Setup Source
$chocoArgs = @('source', 'remove', '--name="LocalChocolateySetup"')
& Invoke-Choco @chocoArgs

# Install a non-IE browser for browsing the Nexus web portal.
if (-not (Test-Path 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe')) {
Write-Host "Installing Microsoft Edge, to allow viewing the Nexus site"
Expand Down
Loading
Loading