Skip to content

Commit 5c48718

Browse files
Merge pull request #1 from chocolatey/testing-actual
Initial Playbooks, Roles, Etc
2 parents 18c806a + 6c93641 commit 5c48718

37 files changed

+4687
-1
lines changed

.devcontainer/devcontainer.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "Ansible",
3+
"dockerFile": "dockerfile",
4+
// "mounts": [
5+
// "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
6+
// ],
7+
"forwardPorts": [],
8+
"customizations": {
9+
"vscode": {
10+
"extensions": [
11+
"redhat.ansible"
12+
]
13+
}
14+
}
15+
}

.devcontainer/dockerfile

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#-------------------------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
4+
#-------------------------------------------------------------------------------------------------------------
5+
FROM ubuntu:latest
6+
# This Dockerfile adds a non-root user with sudo access. Use the "remoteUser"
7+
# property in devcontainer.json to use it. On Linux, the container user's GID/UIDs
8+
# will be updated to match your local UID/GID (when using the dockerFile property).
9+
# See https://aka.ms/vscode-remote/containers/non-root-user for details.
10+
ARG USERNAME=vscode
11+
ARG USER_UID=1000
12+
ARG USER_GID=$USER_UID
13+
14+
# To ensure filesystem encoding is proper, add these.
15+
# See: https://github.com/pypa/pip/issues/10219#issuecomment-887337037
16+
ENV LANG C.UTF-8
17+
ENV LC_ALL C.UTF-8
18+
19+
# To make it easier for build and release pipelines to run apt-get,
20+
# configure apt to not require confirmation (assume the -y argument by default)
21+
ENV DEBIAN_FRONTEND noninteractive
22+
RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes
23+
24+
RUN apt-get update \
25+
&& apt-get install -y --no-install-recommends \
26+
apt-utils \
27+
software-properties-common \
28+
build-essential \
29+
jq \
30+
git \
31+
python3 \
32+
python3-dev \
33+
python3-pip && \
34+
rm -rf /var/lib/apt/lists/*
35+
36+
RUN add-apt-repository universe \
37+
&& add-apt-repository multiverse \
38+
&& apt update
39+
40+
RUN pip3 install --upgrade \
41+
setuptools \
42+
pip
43+
44+
ADD requirements.txt /requirements.txt
45+
46+
RUN pip3 install --upgrade -r /requirements.txt
47+
48+
RUN ln -s /usr/bin/python3 /usr/bin/python \
49+
# Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user.
50+
&& groupadd --gid $USER_GID $USERNAME \
51+
&& useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
52+
# [Optional] Add sudo support for the non-root user
53+
&& apt-get install sudo \
54+
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
55+
&& chmod 0440 /etc/sudoers.d/$USERNAME \
56+
#
57+
# Clean up
58+
&& apt-get autoremove \
59+
&& apt-get clean \
60+
&& rm -rf /var/lib/apt/lists/*
61+
62+
RUN ansible-galaxy collection install chocolatey.chocolatey --upgrade

.devcontainer/requirements.txt

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ansible>=2.8.0
2+
pywinrm>=0.2.2
3+
pip>=19.1
4+
psutil
5+
netaddr
6+
ansible-lint
7+
pylint
8+
jsonschema
9+
molecule
10+
jmespath
11+
dnspython

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/credentials
2+
/files/download/
3+
/files/*.nupkg
4+
/files/*.zip
5+
__pycache__
6+
chocolatey.license.xml
7+
*.pfx
8+
*.nupkg

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"ansible.python.interpreterPath": "/bin/python3"
3+
}

OfflineInstallPreparation.ps1

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<#
2+
.Synopsis
3+
Prepares the repository for an offline deployment.
4+
5+
.Description
6+
These playbooks 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+
$true
28+
})]
29+
[string]$LicensePath = "C:\ProgramData\chocolatey\license\chocolatey.license.xml",
30+
31+
[ValidateScript({
32+
if (-not (Test-Path (Convert-Path $_))) {
33+
throw "Certificate file does not exist at '$($_)'. Please provide a valid -CertificatePath"
34+
}
35+
$true
36+
})]
37+
[Parameter(Mandatory)]
38+
[string]$CertificatePath,
39+
40+
[Parameter(Mandatory)]
41+
[securestring]$CertificatePassword,
42+
43+
[string]$WorkingDirectory = $(Join-Path $env:Temp "choco-offline")
44+
)
45+
$ErrorActionPreference = "Stop"
46+
$ProgressPreference = "SilentlyContinue"
47+
$LicensePath = Convert-Path $LicensePath
48+
$CertificatePath = Convert-Path $CertificatePath
49+
50+
# Validate License
51+
try {
52+
[xml]$License = Get-Content $LicensePath
53+
$Expiry = Get-Date $License.license.expiration
54+
if (-not $Expiry -or $Expiry -lt (Get-Date)) {throw}
55+
} catch {
56+
throw "License '$($LicensePath)' is not valid.$(if ($Expiry) {" It expired at '$($Expiry)'."})"
57+
}
58+
59+
# Validate Certificate and Password
60+
try {
61+
$null = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
62+
$CertificatePath,
63+
$CertificatePassword,
64+
"EphemeralKeySet"
65+
)
66+
} catch {
67+
throw "Certificate '$($CertificatePath)' failed to import with the provided CertificatePassword. Please ensure the Certificate Path and Password are correct."
68+
}
69+
70+
if (-not (Get-Command choco.exe)) {
71+
Get-Content $PSScriptRoot\templates\ChocolateyInstall.ps1.j2 | Invoke-Expression
72+
}
73+
74+
# Initialize environment, ensure Chocolatey For Business, etc.
75+
$Licensed = ($($(choco)[0] -match "^Chocolatey (?<Version>\S+)\s*(?<LicenseType>Business)?$") -and $Matches.LicenseType)
76+
$InstalledLicensePath = "$env:ChocolateyInstall\license\chocolatey.license.xml"
77+
if (-not $Licensed) {
78+
if (-not (Test-Path $InstalledLicensePath)) {
79+
if (-not (Test-Path $env:ChocolateyInstall\license)) {
80+
$null = New-Item $env:ChocolateyInstall\license -ItemType Directory
81+
}
82+
Copy-Item $LicensePath $InstalledLicensePath -Force
83+
}
84+
choco install chocolatey.extension --source https://licensedpackages.chocolatey.org/api/v2/ --confirm
85+
}
86+
87+
# Download each set of packages to the output directories
88+
$PackageWorkingDirectory = Join-Path $WorkingDirectory "Packages"
89+
if (-not (Test-Path $PackageWorkingDirectory)) {
90+
$null = New-Item -Path $PackageWorkingDirectory -ItemType Directory -Force
91+
}
92+
foreach ($Package in (Get-Content $PSScriptRoot\files\chocolatey.json | ConvertFrom-Json).packages) {
93+
$ChocoArgs = @(
94+
"download", "$($Package.Name)"
95+
"--output-directory", $PackageWorkingDirectory
96+
)
97+
$ChocoArgs += switch ($Package.Keys) {
98+
"Version" { "--version", $Package.Version }
99+
"Args" { $Package.Args }
100+
}
101+
if ($Package.Internalize -or $Package.PSObject.Properties.Name -notcontains "Internalize") {
102+
$ChocoArgs += "--internalize" # Default to internalizing
103+
}
104+
105+
try {
106+
if (-not (Test-Path "$($PackageWorkingDirectory)\$($Package.Name)*.nupkg") -and -not (Test-Path "$PSScriptRoot\files\$($Package.Name)*.nupkg")) {
107+
Write-Verbose "Downloading '$($Package.Name)'"
108+
$Output = choco @ChocoArgs
109+
if ($LASTEXITCODE -ne 0) {
110+
$Output
111+
}
112+
}
113+
} catch {
114+
throw $_
115+
}
116+
}
117+
Move-Item -Path $PackageWorkingDirectory\*.nupkg -Destination $PSScriptRoot\files\
118+
119+
# Jenkins Plugins
120+
$PluginsWorkingDirectory = Join-Path $WorkingDirectory "JenkinsPlugins"
121+
if (-not (Test-Path $PluginsWorkingDirectory)) {
122+
$null = New-Item -Path $PluginsWorkingDirectory -ItemType Directory -Force
123+
}
124+
$ProgressPreference = "Ignore"
125+
foreach ($Plugin in (Get-Content $PSScriptRoot\files\jenkins.json | ConvertFrom-Json).plugins) {
126+
$RestArgs = @{
127+
Uri = "https://updates.jenkins-ci.org/latest/$($Plugin.Name).hpi"
128+
OutFile = Join-Path $PluginsWorkingDirectory "$($Plugin.Name).hpi"
129+
}
130+
if ($Plugin.Version -and $Plugin.Version -ne 'latest') {
131+
$RestArgs.Uri = "https://updates.jenkins-ci.org/download/plugins/$($Plugin.Name)/$($Plugin.Version)/$($Plugin.Name).hpi"
132+
}
133+
if (-not (Test-Path $RestArgs.OutFile)) {
134+
Invoke-WebRequest @RestArgs -UseBasicParsing
135+
}
136+
}
137+
Compress-Archive -Path $PluginsWorkingDirectory\* -Destination $PSScriptRoot\files\JenkinsPlugins.zip -Force
138+
139+
# License and Certificate
140+
Copy-Item -Path (Convert-Path $LicensePath) -Destination $PSScriptRoot\files\chocolatey.license.xml
141+
Copy-Item -Path (Convert-Path $CertificatePath) -Destination $PSScriptRoot\files\certificate.pfx

README.md

+75-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,75 @@
1-
# repository-template
1+
# Chocolatey for Business Ansible Environment
2+
3+
## Deployment
4+
5+
To deploy the Chocolatey for Business Ansible Environment, first, clone the repository to your chosen Ansible environment, make the following modifications, and deploy the `c4b-environment.yml` playbook.
6+
7+
Create hosts. Depending on your chosen configuration, you may not need to create all of them:
8+
9+
| Group | Purpose |
10+
| --------------- | ---------------------------------------------------------------------------- |
11+
| ccm_server | Runs the Chocolatey Central Management service and administration interface. |
12+
| nexus_server | Runs Sonatype Nexus Repository, to store and distribute packages. |
13+
| jenkins_server | Runs Jenkins, to run jobs to updating packages in the Nexus repository. |
14+
| database_server | Runs SQL Server Express, to store information from the CCM service. |
15+
16+
By default, any non-specified service will be installed on the ccm_server host.
17+
18+
You should provide the following arguments:
19+
20+
| Argument | Purpose |
21+
| -------------------------- | -------------------------------------------------- |
22+
| license_path | Your Chocolatey for Business license file. |
23+
| certificate_path | The PFX certificate to use for all HTTPS services. |
24+
| certificate_password | The password for the PFX certificate. |
25+
26+
Finally, you can deploy the playbook as follows (using the example hosts file):
27+
28+
`ansible-playbook ./c4b-environment.yml -i ./hosts.yml`
29+
30+
You will be prompted for any values you have not provided in `--extra-vars` or another fashion. An example of passing a variable on the command-line is as follows:
31+
32+
`ansible-playbook ./c4b-environment --extra-vars "license_path=/path/to/chocolatey.license.xml certificate_path=/path/to/certificate.pfx"`
33+
34+
You can also define variables in AWX, or within a file. For further details, see [Defining variables at runtime](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#passing-variables-on-the-command-line).
35+
36+
For further information on deploying this environment, see [the docs page](https://docs.chocolatey.org/en-us/c4b-environments/ansible/).
37+
38+
## Hardware Recommendations
39+
40+
We recommend the following configuration if deploying to a single Ansible host:
41+
42+
- Windows Server 2019+
43+
- 4+ CPU cores
44+
- 16 GB+ RAM (8GB as a bare minimum; 4GB of RAM is reserved specifically for Nexus)
45+
- 500 GB+ of free space for local NuGet package artifact storage
46+
47+
If deploying to multiple hosts, please refer to the recommended specifications for:
48+
49+
- [Chocolatey Central Management](https://docs.chocolatey.org/en-us/central-management/setup/#high-level-requirements)
50+
- [Sonatype Nexus Repository](https://help.sonatype.com/repomanager3/product-information/sonatype-nexus-repository-system-requirements)
51+
- [Jenkins](https://www.jenkins.io/doc/book/installing/windows/#prerequisites)
52+
- [SQL Server Express](https://www.microsoft.com/en-us/download/details.aspx?id=104781)
53+
54+
## Offline Installation
55+
56+
To install in an air-gapped environment, you can download this repository to a local machine and run the `OfflineInstallPreparation.ps1` script.
57+
58+
This script downloads all the required files and packages to ensure a successful installation. Please note that you will require a Windows machine with a licensed copy of Chocolatey, as it utilises the Package Internalizer feature.
59+
60+
After the script has run, copy the directory to your Ansible environment and deploy it.
61+
62+
## Storing Secrets
63+
64+
After the playbook has run, various secrets will have been created and stored in the `/credentials` directory. To keep these secure, you should use [Ansible Vault](https://docs.ansible.com/ansible/latest/vault_guide/index.html) or something similar to store and inject them instead of the password lookup files, as `lookup('ansible.builtin.password'` does not support encryption or Ansible Vault. To do so, follow these steps for each secret (using `ccm_client_salt` as the example):
65+
66+
- In a terminal on your Ansible machine, run `ansible-vault encrypt /path/to/repository/credentials/ccm_client_salt`.
67+
- Open the `/path/to/repository/credentials/ccm_client_salt` file and copy the new contents of the file.
68+
- Open the `./group_vars/all.yml` file and overwrite the value of `ccm_client_salt` with the vaulted value.
69+
70+
This will result in re-deployment of the environment using this secret, going forward.
71+
72+
If you want to re-deploy the environment having changed your passwords, or initially deploy it using your own generated values, you can use `ansible-vault` and the `all.yml` file to deploy using those values.
73+
74+
- In a terminal on your Ansible machine, run `ansible-vault encrypt_string 'some-secure-password-here' --name 'ccm_client_salt'`.
75+
- Open the `./group_vars/all.yml` file and overwrite the line beginning `ccm_client_salt:` with the output of the `ansible-vault` command.

0 commit comments

Comments
 (0)