Chapter 12Lesson 05225–300 min

Checkpoint Lab — Conan, Ansible Galaxy, Terraform, Swift, and Emerging Repository Formats

Evaluate Ansible Galaxy and Terraform as two current emerging formats, stand up disposable repositories, publish synthetic content, configure isolated clients/fixtures, record evidence and limitations, and produce an operator adoption checklist without paid features.

Checkpoint labAnsible GalaxyTerraformAdoption checklistSafe cleanup

Checkpoint outcomes

  • Produce a current adoption matrix for Ansible Galaxy and Terraform on Nexus 3.95.2 Community Edition.
  • Stand up disposable native repositories and publish one synthetic collection/module.
  • Configure isolated client/registry state without exposing credentials in command history.
  • Verify repository, component/asset, checksum, authentication, and client-routing evidence.
  • Document limitations, recovery dependencies, revalidation triggers, and cleanup.

Checkpoint safety. Use only a disposable local Nexus instance and synthetic namespaces. The checkpoint does not require public package publication, cloud accounts, Pro features, real CI secrets, production identity systems, or manual blob/database changes. If a command prerequisite is absent, use the documented fixture path and record that it was simulated.

1. Scenario and acceptance criteria

You operate a small engineering platform. The automation team wants Ansible collections; the platform team wants Terraform modules. Both want Nexus as the controlled boundary. Your task is to prove whether the current 3.95.2 Community instance can support the required workflow, not merely to make two uploads succeed.

Evidence Pass condition
Feature matrix Version, edition, client/protocol, proxy/hosted/group, auth and operational gaps recorded.
Ansible publication Synthetic collection appears in hosted repository and can be addressed through the group/client fixture.
Terraform publication Synthetic module uploaded through Terraform registry path and visible as repository assets.
Identity Collection namespace/name/version and Terraform module namespace/name/provider/version are explicit; checksums captured.
Security No real secrets, no credentials in command arguments or committed files; temp config is permission-restricted.
No bypass Client configs point to Nexus; public upstream is not an alternate source for the internal namespace.
Cleanup Disposable repositories and temp files removed through supported operations.

2. Preflight and prediction

export NX_URL="http://127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch12-checkpoint"
rm -rf "$LAB"
mkdir -p "$LAB"/{evidence,ansible-src,ansible-home,terraform-src,terraform-home}
chmod 700 "$LAB"

curl -fsS "$NX_URL/service/rest/v1/status" > "$LAB/evidence/status.txt"
curl -fsS "$NX_URL/service/rest/v1/repositories" > "$LAB/evidence/repos-before.json"

Predict before acting:

  1. Publishing the Ansible collection should create component/asset metadata plus blob content in the hosted repository; the group should reference the member rather than duplicate an authoritative hosted copy.
  2. Uploading the Terraform module under /v1/modules/... should create format-specific module assets and metadata; a checksum of the source archive should remain stable when the same bytes are downloaded.
  3. Activating a token realm changes security configuration, not package content.

3. Record the feature matrix you are actually testing

reviewed_at: 2026-08-26
nexus:
  version: 3.95.2
  edition: Community
formats:
  ansible:
    introduced: 3.93.0
    proxy: true
    hosted: true
    group: true
    api: Galaxy v3
    auth: Ansible Galaxy Bearer Token Realm when protected
  terraform:
    proxy_since: 3.88.0
    hosted_since: 3.89.0
    group_since: 3.90.0
    opentofu_coder_since: 3.94.0
    non_url_auth_since: 3.95.0
operations:
  export_assets: "not mandatory; current self-hosted feature matrix marks Pro"
  import_external_files: "not mandatory; current self-hosted feature matrix marks Pro"
  backup_restore: "required production concern; separate from repository export"

4. Create the disposable repositories

Using the Nexus UI, create:

Repository Recipe Configuration
academy-ch12cp-ansible-hosted ansiblegalaxy (hosted) Synthetic collection origin.
academy-ch12cp-ansible-group ansiblegalaxy (group) Member: hosted only. This intentionally proves no public fallback for the internal namespace.
academy-ch12cp-terraform-hosted terraform (hosted) Synthetic module origin.
academy-ch12cp-terraform-group terraform (group) Member: hosted only. Optional client read endpoint; no public fallback.

Create one disposable lab identity with only the repository read/browse/add privileges needed for these hosted/group paths. If using native token endpoints, enable only the required Ansible/Terraform token realms and record that change.

5. Create the temporary authentication boundary

read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
export NX_AUTH_FILE="$LAB/nexus.netrc"
umask 077
printf 'machine 127.0.0.1 login %s password %s\n' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS

6. Build and publish the Ansible collection

cd "$LAB/ansible-src"
if command -v ansible-galaxy >/dev/null 2>&1; then
  ansible-galaxy collection init learner_example.adoption_probe
  cd learner_example/adoption_probe
  printf '# Nexus Chapter 12 checkpoint\n' > README.md
  ansible-galaxy collection build --output-path "$LAB/ansible-src/dist"     | tee "$LAB/evidence/ansible-build.txt"
  sha256sum "$LAB/ansible-src/dist/"*.tar.gz     | tee "$LAB/evidence/ansible-sha256.txt"
else
  printf 'ansible-galaxy unavailable: use synthetic collection fixture/UI walkthrough
'     > "$LAB/evidence/ansible-client-fixture.txt"
fi

Upload the generated collection archive to academy-ch12cp-ansible-hosted through the Nexus upload UI. Then verify supported repository state:

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/components?repository=academy-ch12cp-ansible-hosted"   | tee "$LAB/evidence/ansible-components.json"
curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch12cp-ansible-hosted"   | tee "$LAB/evidence/ansible-assets.json"

7. Configure a clean Ansible client without public fallback

The group has only the hosted repository, so the internal namespace cannot fall through to Galaxy. If authentication is required, put the reversible Base64 token in a temporary file only:

export ANSIBLE_CONFIG="$LAB/ansible-home/ansible.cfg"
export ANSIBLE_COLLECTIONS_PATH="$LAB/ansible-home/collections"
mkdir -p "$ANSIBLE_COLLECTIONS_PATH"
read -r -p 'Disposable Nexus username: ' A_USER
read -r -s -p 'Disposable Nexus password: ' A_PASS; echo
A_TOKEN="$(printf '%s:%s' "$A_USER" "$A_PASS" | base64 | tr -d '\n')"
umask 077
cat > "$ANSIBLE_CONFIG" <<EOF
[defaults]
collections_path = $ANSIBLE_COLLECTIONS_PATH
[galaxy]
server_list = nexus_internal
[galaxy_server.nexus_internal]
url = $NX_URL/repository/academy-ch12cp-ansible-group/
token = $A_TOKEN
EOF
cat > "$LAB/ansible-home/requirements.yml" <<EOF
---
collections:
  - name: learner_example.adoption_probe
    version: "1.0.0"
    source: http://127.0.0.1:8081/repository/academy-ch12cp-ansible-group/
    token: "$A_TOKEN"
EOF
chmod 600 "$ANSIBLE_CONFIG" "$LAB/ansible-home/requirements.yml"
unset A_USER A_PASS A_TOKEN
if command -v ansible-galaxy >/dev/null 2>&1; then
  ansible-galaxy collection install -r "$LAB/ansible-home/requirements.yml"     | tee "$LAB/evidence/ansible-install.txt"
fi

8. Build and publish a Terraform module

mkdir -p "$LAB/terraform-src/module"
cat > "$LAB/terraform-src/module/main.tf" <<'EOF'
variable "checkpoint" { type = string; default = "chapter12" }
output "checkpoint" { value = var.checkpoint }
EOF
cat > "$LAB/terraform-src/module/versions.tf" <<'EOF'
terraform { required_version = ">= 1.0" }
EOF
(cd "$LAB/terraform-src/module" && zip -q -r "$LAB/terraform-src/adoption-probe-1.0.0.zip" .)
sha256sum "$LAB/terraform-src/adoption-probe-1.0.0.zip"   | tee "$LAB/evidence/terraform-sha256-before.txt"
curl --fail --silent --show-error   --netrc-file "$NX_AUTH_FILE"   -X PUT   "$NX_URL/repository/academy-ch12cp-terraform-hosted/v1/modules/learner-example/adoption-probe/local/1.0.0/adoption-probe-1.0.0.zip"   -H 'Content-Type: application/zip'   --data-binary "@$LAB/terraform-src/adoption-probe-1.0.0.zip"   -o /dev/null -w 'HTTP %{http_code}\n'   | tee "$LAB/evidence/terraform-upload.txt"

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/assets?repository=academy-ch12cp-terraform-hosted"   | tee "$LAB/evidence/terraform-assets.json"

Do not rebuild the archive and call the result “the same module.” The SHA-256 you captured is an exact-byte identity for this lab artifact; it is not a signature or provenance attestation.

9. Exercise or simulate current Terraform authentication

If the Terraform Token Realm is enabled, request a 3.95+ bearer token into a protected file and generate an isolated CLI config. Do not print the token:

export TF_CLI_CONFIG_FILE="$LAB/terraform-home/terraform.rc"
curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/repository/academy-ch12cp-terraform-group/v1/api/token"   > "$LAB/terraform-home/token.json"
chmod 600 "$LAB/terraform-home/token.json"
python - <<'PYI'
from pathlib import Path
import json, os
lab=Path(os.environ['LAB'])
token=json.loads((lab/'terraform-home/token.json').read_text())['token']
text="""credentials "127.0.0.1:8081" {
  token = "__TOKEN__"
}

host "academy-ch12.local" {
  services = {
    modules.v1 = "http://127.0.0.1:8081/repository/academy-ch12cp-terraform-group/v1/modules/"
  }
}
"""
text=text.replace("__TOKEN__", token)
p=lab/'terraform-home/terraform.rc'
p.write_text(text); p.chmod(0o600)
PYI
rm -f "$LAB/terraform-home/token.json"

The fixture proves the safer credential model without requiring local DNS or a full Terraform infrastructure project. The dedicated Terraform course should own deeper provider/module client behavior.

10. Build the evidence packet

curl -fsS --netrc-file "$NX_AUTH_FILE"   "$NX_URL/service/rest/v1/repositories" > "$LAB/evidence/repos-after.json"

python - <<'PYI'
from pathlib import Path
import os, json
lab=Path(os.environ['LAB'])
summary={
  'nexus':'3.95.2 baseline; record actual server evidence separately',
  'ansible_repo':'academy-ch12cp-ansible-hosted -> academy-ch12cp-ansible-group',
  'terraform_repo':'academy-ch12cp-terraform-hosted -> academy-ch12cp-terraform-group',
  'public_fallback_for_internal_namespace':False,
  'manual_blob_or_database_edits':False,
  'pro_only_export_import_used':False,
}
(lab/'evidence/adoption-summary.json').write_text(json.dumps(summary, indent=2)+'\n')
PYI

find "$LAB/evidence" -maxdepth 1 -type f -printf '%f\n' 2>/dev/null   | sort > "$LAB/evidence/files.txt" || true
cat "$LAB/evidence/files.txt"

Review evidence files before sharing. Do not include nexus.netrc, ansible.cfg, or terraform.rc in a support bundle because they can contain credentials.

11. Verification checklist

  • The server version/edition and client versions are recorded.
  • Ansible hosted/group recipes exist on the pinned build and the synthetic collection is visible.
  • The Ansible group contains only hosted for the internal namespace; no accidental Galaxy fallback is required.
  • The Terraform module was uploaded under a documented /v1/modules path and its assets are visible.
  • Source archive checksum is captured and no claim confuses checksum with signature/provenance.
  • Any token realm activation is documented as security configuration.
  • No credential is printed, embedded in a repository URL, committed, or included in evidence.
  • Export/import is not treated as a Community prerequisite.

12. Cleanup through supported operations

Delete the four disposable repositories through the Nexus UI after reviewing their names carefully. Do not delete blob-store files or database rows manually. Remove only the local lab directory after repository deletion is verified.

rm -f "$LAB/nexus.netrc"       "$LAB/ansible-home/ansible.cfg"       "$LAB/ansible-home/requirements.yml"       "$LAB/terraform-home/terraform.rc"
unset NX_AUTH_FILE ANSIBLE_CONFIG ANSIBLE_COLLECTIONS_PATH TF_CLI_CONFIG_FILE
rm -rf "$LAB"

13. Operator adoption checklist

Question Ansible result Terraform result
Protocol/client minimum Galaxy v3; ansible-galaxy 2.9+ documented Terraform CLI 0.13+; 1.x supported in current docs
Hosted/proxy/group All three from 3.93 Proxy 3.88 / hosted 3.89 / group 3.90
Current auth Ansible bearer realm; protected Base64 token field Terraform Token Realm; non-URL bearer auth from 3.95
Internal publication Hosted collection archive Hosted module/provider registry path
Public fallback control Internal-only group for private namespace Internal-only group/host service for private namespace
Migration caveat Do not assume CE export/import Do not assume CE export/import
Revalidate Nexus/Ansible upgrades and realm changes Nexus/Terraform/OpenTofu/upstream changes

14. Knowledge check

Why does the checkpoint use hosted-only groups for the internal namespaces?

What state changes when a token realm is activated?

Why is the Terraform checksum useful but insufficient for trust?

Why are Export Assets and Import External Files absent from the mandatory workflow?

What makes the checkpoint complete rather than just two successful uploads?

15. Chapter summary and bridge to Chapter 13

You now have a repeatable way to adopt a format whose Nexus support is new or evolving: identify the protocol, pin versions, verify recipes and edition, isolate client/auth state, test synthetic content, record operational gaps, and revalidate after changes.

Chapter 13 narrows the focus from format adoption to proxy behavior itself: cache ages, negative cache, remote health, routing rules, repository health, and failure semantics.

Official references and version notes

Version-sensitive statements were rechecked on 2026-08-26. The mandatory lab pins Nexus Repository 3.95.2, which Sonatype's current download page lists as the latest downloadable self-hosted release and whose release notes date it to 2026-08-21. Java 21 remains the current Nexus runtime requirement. The mandatory path uses Community-compatible Ansible Galaxy and Terraform behavior and avoids depending on Pro-only export/import, user-token, staging, HA, or content-replication features.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.