Skip to content

Commit 6498db7

Browse files
Merge pull request #246 from chocolatey/offlineBootstrap
(#86) Adds Offline Preparation Script
2 parents f23d10f + 8b4666e commit 6498db7

19 files changed

+668
-323
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.nupkg
2+
*.zip
3+
/scripts/ChocolateyInstall.ps1
4+
Chocolatey.License.xml
5+
*/bcrypt.net.0.1.0/*

CONTRIBUTING.md

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,62 @@ This document outlines how to help with development of the Chocolatey Quickstart
22

33
## Development
44

5-
When looking to make a change ensure any working branch is taken from `develop`. You can do this with the following:
5+
When looking to make a change ensure any working branch is taken from the tip of `main`. You can do this with the following:
66

77
```powershell
8-
git checkout develop
9-
git fetch upstream
10-
git rebase upstream/develop
11-
git push origin
12-
git checkout -b $NewBranchName
8+
$ChocolateyUpstream = ((git remote -v) -match "github.com/chocolatey/choco-quickstart-scripts.git \(fetch\)$" -split "\t")[0]
9+
git fetch $ChocolateyUpstream
10+
git checkout -b $NewBranchName $ChocolateyUpstream/main
1311
```
1412

15-
## Testing
13+
### Development Testing
1614

17-
Test your changes before raising a Pull Request to merge your changes. In order to set things up for testing do the following:
15+
You must test your changes before submitting a PR.
1816

19-
1. Set `$env:CHOCO_QSG_DEVELOP = $true`
20-
1. The first step of the Guide will need amended to fetch from the `develop` branch:
17+
You should test on a clean, supported operating system.
18+
19+
> NB: To save time in repeated testing from a clean environment, you can run the OfflineInstallPreparation script in your repository and copy the files directory before copying.
20+
21+
To test the quickstart environment:
22+
23+
1. Copy the repository directory over to `C:\choco-setup\files\` on the test machine. You do not need to copy the `.git` directory.
24+
1. Open an elevated Windows PowerShell console.
25+
1. Run `C:\choco-setup\files\Start-C4bSetup.ps1`, and continue through the guide steps as detailed in `README.md`.
26+
1. Run `C:\choco-setup\files\Start-C4bVerification.ps1` and check that all tests pass.
27+
28+
## Testing a PR
29+
30+
Changes in a PR must be tested before merging. In order to set things up for testing do the following in an elevated Windows PowerShell terminal:
31+
32+
1. Set `$env:CHOCO_QSG_BRANCH` to the PR ID or Branch Name to download.
33+
1. Run Quickstart Guide as documented, in the same session.
34+
35+
Example:
2136

2237
```powershell
38+
$env:CHOCO_QSG_BRANCH = "< Insert PR ID or Upstream BranchName Here >"
39+
2340
Set-ExecutionPolicy Bypass -Scope Process -Force
2441
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::tls12
25-
$QuickStart = 'https://raw.githubusercontent.com/chocolatey/choco-quickstart-scripts/develop/Start-C4bSetup.ps1'
26-
$Script = [System.Net.Webclient]::new().DownloadString($QuickStart)
27-
$sb = [ScriptBlock]::Create($Script)
28-
& $sb
42+
Invoke-RestMethod "https://ch0.co/qsg-go" | Invoke-Expression
2943
```
3044

3145
1. Perform each step of the Quickstart Guide, and make sure the changes you have attempted to make work appropriately.
46+
1. Run `Start-C4bVerification.ps1` and check that all tests pass.
3247
1. If everything looks OK, push your branch and create your Pull Request.
3348

34-
## SSL Certificates for testing
49+
## Creating a PR
50+
51+
Push your branch to a fork or repository, and create a new PR [here](https://github.com/chocolatey/choco-quickstart-scripts/compare).
52+
53+
You should fill out the issue template as much as possible.
3554

36-
Reach out to Stephen, and he can generate a Let's Encrypt certificate for you.
55+
### Rebasing Your Branch
56+
57+
If something else has been merged since you created your branch, you should rebase your branch onto the new tip of `main`. If you'd already pushed the branch, you may need to force-push the new history over the upstream version. You can do this as follows:
58+
59+
```powershell
60+
$ChocolateyUpstream = ((git remote -v) -match "github.com/chocolatey/choco-quickstart-scripts.git \(fetch\)$" -split "\t")[0]
61+
git fetch $ChocolateyUpstream
62+
git rebase $ChocolateyUpstream\main --autostash
63+
```

OfflineInstallPreparation.ps1

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<#
2+
.Synopsis
3+
Prepares the repository for an offline deployment.
4+
5+
.Description
6+
These scripts can be run from a network without access to the internet,
7+
but it needs to prepare packages to be run offline.
8+
This script downloads and internalizes packages for such usage.
9+
10+
.Notes
11+
This must be run on a Windows system with access to the internet because
12+
it uses Chocolatey for Business' Package Internalizer.
13+
14+
.Notes
15+
Instead of using this script, you can internalize all required packages manually,
16+
zip them, and drop them in the files directory as shown below.
17+
18+
.Example
19+
.\OfflineInstallPreparation.ps1 -LicensePath C:\ProgramData\chocolatey\license\chocolatey.license.xml
20+
#>
21+
[CmdletBinding()]
22+
param(
23+
[ValidateScript({
24+
if (-not (Test-Path (Convert-Path $_))) {
25+
throw "License file does not exist at '$($_)'. Please provide a valid -LicensePath"
26+
}
27+
try {
28+
[xml]$License = Get-Content $_
29+
$Expiry = Get-Date $License.license.expiration
30+
if (-not $Expiry -or $Expiry -lt (Get-Date)) {throw}
31+
} catch {
32+
throw "License '$($_)' is not valid.$(if ($Expiry) {" It expired at '$($Expiry)'."})"
33+
}
34+
$true
35+
})]
36+
[string]$LicensePath = "C:\ProgramData\chocolatey\license\chocolatey.license.xml",
37+
38+
[string]$WorkingDirectory = $(Join-Path $env:Temp "choco-offline")
39+
)
40+
$ErrorActionPreference = "Stop"
41+
$ProgressPreference = "SilentlyContinue"
42+
$LicensePath = Convert-Path $LicensePath
43+
44+
. $PSScriptRoot\scripts\Get-Helpers.ps1
45+
46+
$ChocoInstallScript = Join-Path $PSScriptRoot "scripts\ChocolateyInstall.ps1"
47+
if (-not (Test-Path $ChocoInstallScript)) {
48+
Invoke-WebRequest -Uri 'https://chocolatey.org/install.ps1' -OutFile $ChocoInstallScript
49+
}
50+
51+
$Signature = Get-AuthenticodeSignature -FilePath $ChocoInstallScript
52+
53+
if ($Signature.Status -eq 'Valid' -and $Signature.SignerCertificate.Subject -eq 'CN="Chocolatey Software, Inc.", O="Chocolatey Software, Inc.", L=Topeka, S=Kansas, C=US') {
54+
if (-not (Get-Command choco.exe -ErrorAction SilentlyContinue)) {
55+
if (Test-Path $PSScriptRoot\files\chocolatey.*.nupkg) {
56+
$env:ChocolateyDownloadUrl = (Convert-Path $PSScriptRoot\files\chocolatey.*.nupkg)[0]
57+
}
58+
& $ChocoInstallScript
59+
}
60+
} else {
61+
Write-Error "ChocolateyInstall.ps1 script signature is not valid. Please investigate." -ErrorAction Stop
62+
}
63+
64+
# Initialize environment, ensure Chocolatey For Business, etc.
65+
$Licensed = ($($(choco.exe)[0] -match "^Chocolatey (?<Version>\S+)\s*(?<LicenseType>Business)?$") -and $Matches.LicenseType)
66+
$InstalledLicensePath = "$env:ChocolateyInstall\license\chocolatey.license.xml"
67+
if (-not $Licensed) {
68+
if (-not (Test-Path $InstalledLicensePath)) {
69+
if (-not (Test-Path $env:ChocolateyInstall\license)) {
70+
$null = New-Item $env:ChocolateyInstall\license -ItemType Directory
71+
}
72+
Copy-Item $LicensePath $InstalledLicensePath -Force
73+
}
74+
$ExtensionSource = if (Test-Path $PSScriptRoot\files\chocolatey.extension.*.nupkg) {
75+
Convert-Path $PSScriptRoot\files\
76+
} else {
77+
'https://licensedpackages.chocolatey.org/api/v2/'
78+
}
79+
choco install chocolatey.extension --source $ExtensionSource --params="'/NoContextMenu'" --confirm
80+
}
81+
82+
# Download each set of packages to the output directories
83+
$PackageWorkingDirectory = Join-Path $WorkingDirectory "Packages"
84+
if (-not (Test-Path $PackageWorkingDirectory)) {
85+
$null = New-Item -Path $PackageWorkingDirectory -ItemType Directory -Force
86+
}
87+
foreach ($Package in (Get-Content $PSScriptRoot\files\chocolatey.json | ConvertFrom-Json).packages) {
88+
$ChocoArgs = @(
89+
"download", "$($Package.Name)"
90+
"--output-directory", $PackageWorkingDirectory
91+
"--ignore-dependencies"
92+
)
93+
$ChocoArgs += switch ($Package.psobject.properties.name) {
94+
"Version" { "--version=$($Package.Version)" }
95+
"Args" { $Package.Args }
96+
}
97+
if ($Package.Internalize -or $Package.PSObject.Properties.Name -notcontains "Internalize") {
98+
$ChocoArgs += "--internalize" # Default to internalizing
99+
}
100+
101+
try {
102+
if (-not (Get-ChocolateyPackageMetadata -Path $PackageWorkingDirectory -Id $Package.Name) -and -not (Get-ChocolateyPackageMetadata -Path "$PSScriptRoot\files\" -Id $Package.Name)) {
103+
Write-Host "Downloading '$($Package.Name)'"
104+
105+
while ((Get-ChildItem $PackageWorkingDirectory -Filter *.nupkg).Where{$_.CreationTime -gt (Get-Date).AddMinutes(-1)}.Count -gt 5) {
106+
Write-Verbose "Slowing down for a minute, in order to not trigger rate-limiting..."
107+
Start-Sleep -Seconds 5
108+
}
109+
110+
choco @ChocoArgs
111+
}
112+
} catch {
113+
throw $_
114+
}
115+
}
116+
Move-Item -Path $PackageWorkingDirectory\*.nupkg -Destination $PSScriptRoot\files\
117+
118+
# Jenkins Plugins
119+
$PluginsWorkingDirectory = Join-Path $WorkingDirectory "JenkinsPlugins"
120+
if (-not (Test-Path $PluginsWorkingDirectory)) {
121+
$null = New-Item -Path $PluginsWorkingDirectory -ItemType Directory -Force
122+
}
123+
if (Test-Path $PSScriptRoot\files\JenkinsPlugins.zip) {
124+
Expand-Archive -Path $PSScriptRoot\files\JenkinsPlugins.zip -DestinationPath $PluginsWorkingDirectory -Force
125+
}
126+
$ProgressPreference = "Ignore"
127+
foreach ($Plugin in (Get-Content $PSScriptRoot\files\jenkins.json | ConvertFrom-Json).plugins) {
128+
$RestArgs = @{
129+
Uri = "https://updates.jenkins-ci.org/latest/$($Plugin.Name).hpi"
130+
OutFile = Join-Path $PluginsWorkingDirectory "$($Plugin.Name).hpi"
131+
}
132+
if ($Plugin.Version -and $Plugin.Version -ne 'latest') {
133+
$RestArgs.Uri = "https://updates.jenkins-ci.org/download/plugins/$($Plugin.Name)/$($Plugin.Version)/$($Plugin.Name).hpi"
134+
}
135+
if (-not (Test-Path $RestArgs.OutFile)) {
136+
Invoke-WebRequest @RestArgs -UseBasicParsing
137+
}
138+
}
139+
Compress-Archive -Path $PluginsWorkingDirectory\* -Destination $PSScriptRoot\files\JenkinsPlugins.zip -Force
140+
141+
# BCryptDll
142+
$null = Get-BcryptDll
143+
144+
# License
145+
if ($LicensePath -ne "$PSScriptRoot\files\chocolatey.license.xml") {
146+
Copy-Item -Path (Convert-Path $LicensePath) -Destination $PSScriptRoot\files\chocolatey.license.xml
147+
}

README.md

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,18 @@ Below are the minimum requirements for setting up your C4B server via this guide
7070

7171
> :warning:**DISCLAIMER**: This guide utilizes code from a GitHub repository, namely: [choco-quickstart-scripts](https://github.com/chocolatey/choco-quickstart-scripts). Though we explain what each script does in drop-down boxes, please do your due diligence to review this code and ensure it meets your Organizational requirements.
7272
73+
> :memo:**Offline Install**: If your C4B server does not have unrestricted access to the internet, you can download the `choco-quickstart-scripts` repository to a Windows machine that is connected to the internet and run `OfflineInstallPreparation.ps1`. This will use Chocolatey to save all of the required assets into the repository folder, which can then be transferred to the target C4B server.
74+
7375
### Step 1: Begin C4B Setup
7476

75-
> :exclamation:**[IMPORTANT]** All commands should be run from an **elevated** PowerShell window (and **not ISE**), by opening your PowerShell console with the `Run as Administrator` option.
77+
> :exclamation:**[IMPORTANT]** All commands must be run from an **elevated** Windows PowerShell window (and **not ISE**), by opening your PowerShell console with the `Run as Administrator` option.
7678
77-
1. Open a PowerShell console with the `Run as Administrator` option, and paste and run the following code:
79+
1. Open a Windows PowerShell console with the `Run as Administrator` option, and paste and run the following code:
7880

7981
```powershell
8082
Set-ExecutionPolicy Bypass -Scope Process -Force
8183
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::tls12
82-
$QuickStart = 'https://raw.githubusercontent.com/chocolatey/choco-quickstart-scripts/main/Start-C4bSetup.ps1'
83-
$Script = [System.Net.Webclient]::new().DownloadString($QuickStart)
84-
$sb = [ScriptBlock]::Create($Script)
85-
& $sb
84+
Invoke-RestMethod https://ch0.co/qsg-go | Invoke-Expression
8685
```
8786
8887
> <details>
@@ -97,9 +96,11 @@ Below are the minimum requirements for setting up your C4B server via this guide
9796
> </ul>
9897
> </details>
9998
99+
> :memo:**Offline Install**: You can now copy the `C:\choco-setup\` directory to any computer to continue the installation. To zip up that directory, run `Compress-Archive -Path C:\choco-setup\files\* -DestinationPath C:\choco-setup\C4B-Files.zip`. Move the archive to your new machine, and run `Expand-Archive -Path /path/to/C4B-Files.zip -DestinationPath C:\choco-setup\files -Force`. You should then run `Set-Location "$env:SystemDrive\choco-setup\files"; .\Start-C4bSetup.ps1`, and continue with the guide.
100+
100101
### Step 2: Nexus Setup
101102
102-
1. In the same **elevated** PowerShell console as above, paste and run the following code:
103+
1. In the same **elevated** Windows PowerShell console as above, paste and run the following code:
103104
104105
```powershell
105106
Set-Location "$env:SystemDrive\choco-setup\files"
@@ -121,7 +122,7 @@ Below are the minimum requirements for setting up your C4B server via this guide
121122
> </ul>
122123
> </details>
123124
124-
### Step 3: CCM Setup
125+
### Step 3: Chocolatey Central Management Setup
125126
126127
1. In the same PowerShell Administrator console as above, paste and run the following code:
127128
@@ -135,7 +136,7 @@ Below are the minimum requirements for setting up your C4B server via this guide
135136
> <ul class="list-style-type-disc">
136137
> <li>Installs MS SQL Express and SQL Server Management Studio (SSMS)</li>
137138
> <li>Creates "ChocolateyManagement" database, and adds appropriate `ChocoUser` permissions</li>
138-
> <li>Installs all 3 CCM packages (database, service, web), with correct parameters</li>
139+
> <li>Installs all 3 Chocolatey Central Management packages (database, service, web), with correct parameters</li>
139140
> <li>Outputs data to a JSON file to pass between scripts</li>
140141
> </ul>
141142
> </details>
@@ -208,7 +209,31 @@ Below are the minimum requirements for setting up your C4B server via this guide
208209
209210
> :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.
210211
211-
### Step 6: Setting up Endpoints
212+
### Step 6: Verification
213+
214+
1. In the same **elevated** PowerShell console as above, paste and run the following code:
215+
216+
```powershell
217+
Set-Location "$env:SystemDrive\choco-setup\files"
218+
.\Start-C4bVerification.ps1 -Fqdn '<Your expected fqdn here>'
219+
```
220+
221+
If you expect services to be available at `chocoserver.yourcompany.com`, then your command would look like: `.\Start-C4bVerification.ps1 -Fqdn 'chocoserver.yourcompany.com'`
222+
223+
> <details>
224+
> <summary><strong>What does this script do? (click to expand)</strong></summary>
225+
> <ul class="list-style-type-disc">
226+
> <li>Verifies Nexus Repository installation</li>
227+
> <li>Verifies Central Management installation</li>
228+
> <li>Verifies Jenkins installation</li>
229+
> <li>Ensures system firewall is configured</li>
230+
> <li>Ensures Windows Features are installed</li>
231+
> <li>Ensures services are correctly configured</li>
232+
> <li>Ensured README is created</li>
233+
> </ul>
234+
> </details>
235+
236+
### Step 7: Setting up Endpoints
212237
213238
1. Find the `Register-C4bEndpoint.ps1` script in the `choco-setup\files\scripts\` directory on your C4B Server. Copy this script to your client endpoint.
214239

Set-SslSecurity.ps1

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,19 @@ process {
8888
}
8989

9090
if (-not $CertificateDnsName) {
91-
$matcher = 'CN\s?=\s?[^,\s]+'
91+
$matcher = 'CN\s?=\s?(?<Subject>[^,\s]+)'
9292
$null = $Certificate.Subject -match $matcher
93-
$SubjectWithoutCn = $matches[0] -replace 'CN=', ''
94-
}
93+
$SubjectWithoutCn = if ($Matches.Subject.StartsWith('*')) {
94+
# This is a wildcard cert, we need to prompt for the intended CertificateDnsName
95+
while ($CertificateDnsName -notlike $Matches.Subject) {
96+
$CertificateDnsName = Read-Host -Prompt "$(if ($CertificateDnsName) {"'$($CertificateDnsName)' is not a subdomain of '$($Matches.Subject)'. "})Please provide an FQDN to use with the certificate '$($Matches.Subject)'"
97+
}
98+
$CertificateDnsName
99+
}
100+
else {
101+
$Matches.Subject
102+
}
103+
}
95104
else {
96105
$SubjectWithoutCn = $CertificateDnsName
97106
}
@@ -111,15 +120,15 @@ process {
111120
Copy-CertToStore -Certificate $Certificate
112121

113122
# Generate Nexus keystore
114-
New-NexusCert -Thumbprint $Certificate.Thumbprint
123+
$null = New-NexusCert -Thumbprint $Certificate.Thumbprint
115124

116125
# Add firewall rule for Nexus
117126
netsh advfirewall firewall add rule name="Nexus-8443" dir=in action=allow protocol=tcp localport=8443
118127

119128
Write-Verbose "Starting up Nexus"
120129
Start-Service nexus
121130

122-
Write-Warning "Waiting to give Nexus time to start up"
131+
Write-Warning "Waiting to give Nexus time to start up on 'https://${SubjectWithoutCn}:8443'"
123132
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::tls12
124133
do {
125134
$response = try {
@@ -202,6 +211,9 @@ process {
202211
$chocoArgs = @('apikey', "--source='$RepositoryUrl'", "--api-key='$NuGetApiKey'")
203212
& choco @chocoArgs
204213

214+
# Reset the NuGet v3 cache, such that it doesn't capture localhost as the FQDN
215+
Remove-NexusRepositoryFolder -RepositoryName ChocolateyInternal -Name v3
216+
205217
Update-JsonFile -Path "$env:SystemDrive\choco-setup\logs\nexus.json" -Properties @{
206218
NexusUri = "https://$($SubjectWithoutCn):8443"
207219
NexusRepo = $RepositoryUrl
@@ -329,10 +341,6 @@ Invoke-Expression (`$downloader.DownloadString("http://`$(`$HostName):80/Import-
329341
}
330342

331343
end {
332-
333-
# Hand back the created/found certificate to the caller.
334-
$Certificate
335-
336344
Write-Host 'Writing README to Desktop; this file contains login information for all C4B services.'
337345
New-QuickstartReadme
338346

0 commit comments

Comments
 (0)