added docs
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
# AIM Changelog
|
||||||
|
|
||||||
|
## 2.1.x
|
||||||
|
|
||||||
|
### 2.1.2
|
||||||
|
|
||||||
|
- Removed the experimental ASCII shield.
|
||||||
|
- Retained bitformer orange `#ff7a00` accent.
|
||||||
|
- Added compact `bitformer · AIM · Ansible Inventory Manager` header.
|
||||||
|
- Preserved `p / n` pagination convention.
|
||||||
|
- No intended Ansible behavior changes.
|
||||||
|
|
||||||
|
### 2.1.1
|
||||||
|
|
||||||
|
- Changed the primary UI accent to bitformer orange.
|
||||||
|
- Introduced temporary ASCII branding experiment.
|
||||||
|
- Standardized pagination on `p / n`.
|
||||||
|
|
||||||
|
### 2.1.0
|
||||||
|
|
||||||
|
- Major operator-console UI/UX refactor.
|
||||||
|
- Split presentation into focused UI modules.
|
||||||
|
- Added customer overview/dashboard.
|
||||||
|
- Unified selectors, review screens, result presentation, pagination
|
||||||
|
and filtering.
|
||||||
|
- Kept interactive commands live where password/editor interaction is
|
||||||
|
required.
|
||||||
|
|
||||||
|
## 2.0.x
|
||||||
|
|
||||||
|
- Added domain WinRM GPO rollout.
|
||||||
|
- Added malformed-YAML handling and multiple GPO/AD/GPP reliability
|
||||||
|
hotfixes.
|
||||||
|
- Corrected Windows local-account credential handling to be
|
||||||
|
host-specific.
|
||||||
|
- Corrected new-customer domain UPN defaults.
|
||||||
|
- Final 2.0.7 GPP Scheduled Task XML fix removed the invalid inner
|
||||||
|
`LogonType` element and added immediate registration
|
||||||
|
execution/retries.
|
||||||
|
|
||||||
|
## 1.9.x
|
||||||
|
|
||||||
|
- Added semantic Git-style template/Vault validation.
|
||||||
|
- Added non-destructive template consolidation.
|
||||||
|
- Consolidated playbook categories and generic multi-select behavior.
|
||||||
|
|
||||||
|
## 1.8.x
|
||||||
|
|
||||||
|
- Added structured Vault template creation.
|
||||||
|
- Added Windows credential models.
|
||||||
|
- Added SSH-agent/private-key-passphrase integration.
|
||||||
|
- Added direct multi-select workflows.
|
||||||
|
|
||||||
|
## 1.7.x
|
||||||
|
|
||||||
|
- Added customer domain/network defaults.
|
||||||
|
- Added batch Windows member-server domain access.
|
||||||
|
|
||||||
|
## 1.6.x
|
||||||
|
|
||||||
|
- Added/expanded automated Sophos configuration.
|
||||||
|
- Improved lock cleanup and Sophos menu organization.
|
||||||
|
|
||||||
|
## 1.5.x
|
||||||
|
|
||||||
|
- Added Sophos defaults, host variables and customer-specific playbook
|
||||||
|
support.
|
||||||
|
|
||||||
|
## 1.4.x
|
||||||
|
|
||||||
|
- Split Windows domain-account and member-server access workflows.
|
||||||
|
|
||||||
|
## 1.3.x
|
||||||
|
|
||||||
|
- Added local/domain Windows account flows and safer credential
|
||||||
|
transport.
|
||||||
|
|
||||||
|
## 1.2.x
|
||||||
|
|
||||||
|
- Added Windows WinRM service-account bootstrap and localized
|
||||||
|
Administrators handling.
|
||||||
|
|
||||||
|
## 1.1.x
|
||||||
|
|
||||||
|
- Added curated playbooks, routine local-only inventory validation,
|
||||||
|
empty default host-vars files and Enter=`0` navigation behavior.
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Initial consolidated Python AIM base.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# AIM Development Guide
|
||||||
|
|
||||||
|
## Project root
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/scripts/
|
||||||
|
├── pyproject.toml
|
||||||
|
└── src/
|
||||||
|
└── aim/
|
||||||
|
```
|
||||||
|
|
||||||
|
The environment is installed editable:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
/etc/ansible/.venv/bin/python -m pip install -e /etc/ansible/scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Backend areas include inventory loading/writing/validation, customer
|
||||||
|
management, backup/session recovery, locking, permissions, playbooks,
|
||||||
|
SSH, Vault, Sophos and WinRM.
|
||||||
|
|
||||||
|
The 2.1 UI is split into focused modules under `src/aim/ui/`, including
|
||||||
|
shared components, selection, execution, customers, hosts, access,
|
||||||
|
playbooks, administration and target selection.
|
||||||
|
|
||||||
|
## UI conventions
|
||||||
|
|
||||||
|
- Orange `#ff7a00` is the AIM/bitformer accent.
|
||||||
|
- Green = success.
|
||||||
|
- Yellow = warning.
|
||||||
|
- Red = failure.
|
||||||
|
- No ASCII logo.
|
||||||
|
- Header identity: `bitformer · AIM · Ansible Inventory Manager`.
|
||||||
|
- Pagination: `p / n`.
|
||||||
|
- Numbered navigation: Enter/`0` = Back or Cancel.
|
||||||
|
- Multi-select: number toggles; Enter reviews; `0` cancels.
|
||||||
|
|
||||||
|
Opening menus should not unexpectedly run Ansible, contact hosts,
|
||||||
|
decrypt Vaults or prompt for passwords.
|
||||||
|
|
||||||
|
## Inventory invariants
|
||||||
|
|
||||||
|
`hosts.yml` is authoritative. Preserve arbitrary valid YAML/custom
|
||||||
|
keys/comments where possible.
|
||||||
|
|
||||||
|
Writes should be validated and atomic, with stale-write/concurrency
|
||||||
|
protection and a session recovery backup.
|
||||||
|
|
||||||
|
Do not change unrelated Ansible connection/authentication parameters as
|
||||||
|
part of feature work.
|
||||||
|
|
||||||
|
## Packaging
|
||||||
|
|
||||||
|
Production packages should contain runtime source and metadata, without
|
||||||
|
caches, `.pyc`, test artifacts, legacy Bash implementations or developer
|
||||||
|
notes unless explicitly requested.
|
||||||
|
|
||||||
|
A release archive should expose `pyproject.toml` and `src/` at its root
|
||||||
|
rather than adding an extra wrapper directory.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Use a patch release for contained fixes and a feature/minor release for
|
||||||
|
larger functional changes. Update package metadata and the changelog
|
||||||
|
together.
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# AIM Inventory Model
|
||||||
|
|
||||||
|
## Source of truth
|
||||||
|
|
||||||
|
The authoritative inventory is:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/inventories/<customer>/hosts.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
AIM does not use `.hosts.tsv` as a secondary database and does not
|
||||||
|
reverse-sync TSV data into YAML.
|
||||||
|
|
||||||
|
AIM uses round-trip YAML handling so valid manually maintained
|
||||||
|
structures/comments can be preserved where possible.
|
||||||
|
|
||||||
|
## Platform groups
|
||||||
|
|
||||||
|
Default top-level platform groups:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
linux
|
||||||
|
windows
|
||||||
|
sophosxgs
|
||||||
|
pfsense
|
||||||
|
```
|
||||||
|
|
||||||
|
Platform remains top-level because it determines connection semantics
|
||||||
|
such as SSH, WinRM or HTTPAPI.
|
||||||
|
|
||||||
|
Hosts may have multiple memberships and optional one-level functional
|
||||||
|
subgroups.
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
Linux group variables normally include the SSH connection and service
|
||||||
|
account. The customer SSH key directory is:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
group_vars/linux/.ssh/
|
||||||
|
```
|
||||||
|
|
||||||
|
not:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
group_vars/linux/files/.ssh/
|
||||||
|
```
|
||||||
|
|
||||||
|
`ansible_ssh_pass` may remain configured as a legacy
|
||||||
|
remote-login-password fallback. A private-key passphrase is a separate
|
||||||
|
secret.
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
Domain-joined Windows hosts normally inherit the group-level service
|
||||||
|
identity/password.
|
||||||
|
|
||||||
|
A local-account host can override credentials in:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
host_vars/<fqdn>/main.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Shared local example:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
ansible_user: svc_bf-ansible
|
||||||
|
ansible_password: "{{ vault_windows_local_ansible_password }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Host-specific example:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
ansible_user: svc_bf-ansible
|
||||||
|
ansible_password: "{{ vault_ansible_password_server01_example_lan }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Host vars
|
||||||
|
|
||||||
|
Every newly managed host has:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
host_vars/<fqdn>/main.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing host-vars content is not blindly overwritten.
|
||||||
|
|
||||||
|
## Customer defaults
|
||||||
|
|
||||||
|
AIM customer defaults live in:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/inventories/<customer>/.aim.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
domain_suffix: bfmiglabor.lan
|
||||||
|
network_address: 10.20.30.0
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
ad_dns_domain: intra.company.de
|
||||||
|
ad_netbios_domain: COMPANY
|
||||||
|
```
|
||||||
|
|
||||||
|
The AD DNS domain is used for service-account UPNs. NetBIOS remains
|
||||||
|
metadata/legacy naming information.
|
||||||
|
|
||||||
|
## Safe writes
|
||||||
|
|
||||||
|
Inventory mutations use the conceptual sequence:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
candidate temp file
|
||||||
|
→ local YAML validation
|
||||||
|
→ compare
|
||||||
|
→ session backup
|
||||||
|
→ atomic replace
|
||||||
|
```
|
||||||
|
|
||||||
|
AIM also protects against stale/concurrent writes and uses an inventory
|
||||||
|
lock.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# AIM Operations Guide
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
AIM is organized around a customer context:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
Customer
|
||||||
|
├── Hosts Management
|
||||||
|
├── Access Management
|
||||||
|
├── Vault Management
|
||||||
|
├── Group Variables
|
||||||
|
├── Host Variables
|
||||||
|
├── Playbooks
|
||||||
|
└── Administration
|
||||||
|
```
|
||||||
|
|
||||||
|
For numbered navigation menus, Enter or `0` means Back/Cancel; at the
|
||||||
|
main menu it means Exit. Single selectors use Enter/`0` to cancel.
|
||||||
|
Multi-select uses numbers to toggle, Enter to review, and `0` to cancel.
|
||||||
|
Paginated views use `p / n` for Previous / Next.
|
||||||
|
|
||||||
|
## Customer overview
|
||||||
|
|
||||||
|
Opening a customer should provide local information without unexpectedly
|
||||||
|
contacting hosts, running Ansible or decrypting Vaults. The dashboard
|
||||||
|
includes inventory YAML state, Vault presence, host/platform counts and
|
||||||
|
available customer defaults.
|
||||||
|
|
||||||
|
## Hosts
|
||||||
|
|
||||||
|
`hosts.yml` is the source of truth.
|
||||||
|
|
||||||
|
Creating a host also ensures:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
host_vars/<fqdn>/main.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
For ordinary non-Sophos hosts this file may be empty. Existing host
|
||||||
|
variable files are not overwritten. Removing a host also removes its
|
||||||
|
corresponding host-vars directory.
|
||||||
|
|
||||||
|
Routine add/update/remove operations perform local YAML validation and
|
||||||
|
do not request the Vault password.
|
||||||
|
|
||||||
|
## Access Management
|
||||||
|
|
||||||
|
Linux access manages SSH keys/service-user access.
|
||||||
|
|
||||||
|
Windows access includes temporary WinRM testing, local account creation,
|
||||||
|
domain account creation/repair, member-server domain access, domain
|
||||||
|
WinRM GPO rollout and configured-service-user testing.
|
||||||
|
|
||||||
|
See `WINDOWS.md` for the Windows model.
|
||||||
|
|
||||||
|
## Vault Management
|
||||||
|
|
||||||
|
Vault operations include information, create, edit and delete.
|
||||||
|
|
||||||
|
A new Vault is populated as plaintext with mode `0600`, YAML-validated,
|
||||||
|
then encrypted using:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
ansible-vault encrypt --vault-id <customer>@prompt vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
A failed encryption must not leave populated plaintext secrets behind.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Group and host variable views are generally operator-readable without
|
||||||
|
AIM rewriting arbitrary custom configuration. AIM only changes values in
|
||||||
|
workflows explicitly designed to do so.
|
||||||
|
|
||||||
|
Template consolidation is explicit and non-destructive: missing AIM
|
||||||
|
defaults/comments can be added, while existing non-empty values and
|
||||||
|
custom keys are retained.
|
||||||
|
|
||||||
|
## Playbooks
|
||||||
|
|
||||||
|
Curated categories include CheckMK, Debug, Maintenance and Sophos XGS.
|
||||||
|
Compatible host/group selection is used to build the Ansible `--limit`.
|
||||||
|
|
||||||
|
Interactive playbooks and operations that require password/Vault prompts
|
||||||
|
retain live terminal access.
|
||||||
|
|
||||||
|
## Administration
|
||||||
|
|
||||||
|
Administration includes inventory validation, template consolidation,
|
||||||
|
recovery and customer defaults.
|
||||||
|
|
||||||
|
Explicit inventory validation performs YAML parsing and
|
||||||
|
`ansible-inventory`. When a customer Vault exists, validation uses the
|
||||||
|
customer's Vault identity and may prompt for its password.
|
||||||
|
|
||||||
|
AIM maintains one pre-change inventory recovery backup per inventory per
|
||||||
|
AIM process/session:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
hosts.aim-session.bak.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Restores are explicit and YAML-validated.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# AIM Recovery Guide
|
||||||
|
|
||||||
|
## Principle
|
||||||
|
|
||||||
|
Git protects source code, not AIM's ignored runtime secrets.
|
||||||
|
|
||||||
|
A `git reset --hard` or fresh checkout cannot restore ignored data such
|
||||||
|
as inventory Vaults, SSH keys or a Python virtual environment.
|
||||||
|
|
||||||
|
## Critical persistent data
|
||||||
|
|
||||||
|
Back up separately:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/inventories/<customer>/group_vars/all/vault.yml
|
||||||
|
/etc/ansible/inventories/<customer>/group_vars/linux/.ssh/
|
||||||
|
```
|
||||||
|
|
||||||
|
Other customer inventory YAML should also be covered by the system
|
||||||
|
backup policy.
|
||||||
|
|
||||||
|
## Rebuildable data
|
||||||
|
|
||||||
|
Do not recover `.venv` from Git. Rebuild it:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo rm -rf /etc/ansible/.venv
|
||||||
|
sudo python3 -m venv /etc/ansible/.venv
|
||||||
|
sudo /etc/ansible/.venv/bin/python -m pip install --upgrade pip setuptools wheel
|
||||||
|
sudo /etc/ansible/.venv/bin/python -m pip install -e /etc/ansible/scripts
|
||||||
|
sudo ln -sfn /etc/ansible/.venv/bin/aim /usr/local/bin/aim
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recover Vaults and SSH keys from a filesystem backup
|
||||||
|
|
||||||
|
When restoring from a backup tree, copy only files that do not already
|
||||||
|
exist in the active inventory. Never overwrite surviving current secrets
|
||||||
|
as part of a bulk recovery.
|
||||||
|
|
||||||
|
See `../install.md` for the current dry-run and restore commands.
|
||||||
|
|
||||||
|
## AIM session inventory backup
|
||||||
|
|
||||||
|
AIM can create:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
hosts.aim-session.bak.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a pre-change recovery aid, not the primary backup strategy for
|
||||||
|
customer secrets.
|
||||||
|
|
||||||
|
Restore from it only through an explicit recovery decision after
|
||||||
|
validating the relevant inventory state.
|
||||||
|
|
||||||
|
## Post-recovery validation
|
||||||
|
|
||||||
|
Before deleting the backup source:
|
||||||
|
|
||||||
|
1. Confirm Vault files exist for expected customers.
|
||||||
|
2. Confirm SSH private/public key files and permissions.
|
||||||
|
3. Test Vault decryption/access for representative customers.
|
||||||
|
4. Test Linux SSH authentication.
|
||||||
|
5. Test Windows WinRM for representative domain/local credential
|
||||||
|
models.
|
||||||
|
6. Test any other customer-specific access that depends on recovered
|
||||||
|
secrets.
|
||||||
|
|
||||||
|
Keep the filesystem backup until these checks pass.
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# AIM Sensitive Data and Security Notes
|
||||||
|
|
||||||
|
## Sensitive files
|
||||||
|
|
||||||
|
AIM deliberately keeps secrets outside Git.
|
||||||
|
|
||||||
|
Important locations include:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/inventories/<customer>/group_vars/all/vault.yml
|
||||||
|
/etc/ansible/inventories/<customer>/group_vars/linux/.ssh/
|
||||||
|
```
|
||||||
|
|
||||||
|
Vault files may contain Windows and Linux authentication material.
|
||||||
|
`.ssh` directories may contain private keys that cannot be regenerated
|
||||||
|
without coordinating key rotation on managed systems.
|
||||||
|
|
||||||
|
## Backup requirement
|
||||||
|
|
||||||
|
Git is not a backup for ignored secrets.
|
||||||
|
|
||||||
|
System backup procedures must include customer Vaults and SSH key
|
||||||
|
material. Recovery should preserve existing current files and restore
|
||||||
|
only missing data unless an operator explicitly chooses otherwise.
|
||||||
|
|
||||||
|
## Vault handling
|
||||||
|
|
||||||
|
AIM encrypts newly created Vaults with `ansible-vault`. Plaintext
|
||||||
|
populated Vault data should not remain on disk after a failed encryption
|
||||||
|
attempt.
|
||||||
|
|
||||||
|
Semantic Vault comparison must not print secret values. It should
|
||||||
|
compare key existence/state rather than exposing plaintext.
|
||||||
|
|
||||||
|
## SSH key handling
|
||||||
|
|
||||||
|
Private-key passphrases and remote SSH passwords are separate concepts.
|
||||||
|
|
||||||
|
The Linux private key location is:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
group_vars/linux/.ssh/svc_bf-ansible
|
||||||
|
```
|
||||||
|
|
||||||
|
Private keys should have restrictive filesystem permissions.
|
||||||
|
|
||||||
|
## Authorization
|
||||||
|
|
||||||
|
AIM authorization is based on membership in a configured group resolved
|
||||||
|
through NSS/SSSD/winbind/local group services. The configured group name
|
||||||
|
is authoritative; numeric GIDs may differ across hosts.
|
||||||
|
|
||||||
|
Operators should verify effective membership with:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
getent group '<required-group>'
|
||||||
|
id
|
||||||
|
```
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
# Windows, WinRM and Active Directory
|
||||||
|
|
||||||
|
## Service account
|
||||||
|
|
||||||
|
The standard service account is:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
svc_bf-ansible
|
||||||
|
```
|
||||||
|
|
||||||
|
For an AD domain, the configured identity is normally the UPN:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
svc_bf-ansible@<ad_dns_domain>
|
||||||
|
```
|
||||||
|
|
||||||
|
The normal domain-account Vault variable is:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
vault_windows_ansible_password: "..."
|
||||||
|
```
|
||||||
|
|
||||||
|
The shared-local-account Vault variable is:
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
vault_windows_local_ansible_password: "..."
|
||||||
|
```
|
||||||
|
|
||||||
|
## Windows credential models
|
||||||
|
|
||||||
|
New Windows hosts can use:
|
||||||
|
|
||||||
|
1. Domain service account
|
||||||
|
2. Shared local service account
|
||||||
|
3. Host-specific local service account
|
||||||
|
|
||||||
|
Local credential choices are host-specific overrides and must not
|
||||||
|
rewrite `group_vars/windows/main.yml` for every Windows machine.
|
||||||
|
|
||||||
|
## Domain account creation
|
||||||
|
|
||||||
|
AIM can create/repair the domain service identity and add it to the
|
||||||
|
appropriate built-in Administrators context used by the current design.
|
||||||
|
It does not make the account a Domain Admin.
|
||||||
|
|
||||||
|
Administrative bootstrap credentials should only be requested where
|
||||||
|
genuinely required.
|
||||||
|
|
||||||
|
## Member-server access
|
||||||
|
|
||||||
|
AIM can grant the existing domain service identity local Administrators
|
||||||
|
membership on selected member servers. Domain Controllers are
|
||||||
|
rejected/skipped for this workflow.
|
||||||
|
|
||||||
|
## Domain WinRM GPO rollout
|
||||||
|
|
||||||
|
The rollout uses one prepared, already-manageable Domain Controller as
|
||||||
|
its administration point.
|
||||||
|
|
||||||
|
The managed objects are:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
AD group: GG_bitformer_Ansible_Admins
|
||||||
|
GPO: bitformer - Ansible WinRM
|
||||||
|
Task: bitformer - Configure Ansible WinRM
|
||||||
|
```
|
||||||
|
|
||||||
|
The GPO configures the local Administrators membership, deploys the
|
||||||
|
WinRM setup payload/scheduled task, configures HTTPS WinRM and firewall
|
||||||
|
access, and verifies the resulting state.
|
||||||
|
|
||||||
|
### Multiple target OUs
|
||||||
|
|
||||||
|
A single GPO should be linked to multiple selected OUs rather than
|
||||||
|
creating a separate GPO for Servers, Clients, etc.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
bitformer - Ansible WinRM
|
||||||
|
├── OU=Servers,DC=intra,DC=company,DC=de
|
||||||
|
└── OU=Clients,DC=intra,DC=company,DC=de
|
||||||
|
```
|
||||||
|
|
||||||
|
The OU selector should therefore support multi-selection.
|
||||||
|
|
||||||
|
GPO link management is **additive and idempotent**:
|
||||||
|
|
||||||
|
- Selected OU already linked: keep/repair as appropriate.
|
||||||
|
- Selected OU not linked: create the link.
|
||||||
|
- Unselected OU: do nothing.
|
||||||
|
|
||||||
|
Selecting only `Servers` on a later run must **not** imply that an
|
||||||
|
existing `Clients` link should be removed.
|
||||||
|
|
||||||
|
Link removal should be an explicit operation if/when AIM implements it.
|
||||||
|
|
||||||
|
### Child OUs
|
||||||
|
|
||||||
|
AIM links the GPO to the selected OU. It should not create redundant
|
||||||
|
links on every descendant OU merely to emulate inheritance. Normal Group
|
||||||
|
Policy inheritance handles descendants unless AD policy configuration
|
||||||
|
changes that behavior.
|
||||||
|
|
||||||
|
### Domain Controllers
|
||||||
|
|
||||||
|
The Domain Controllers OU must remain unavailable/rejected for the
|
||||||
|
normal member-machine WinRM rollout.
|
||||||
|
|
||||||
|
## WinRM payload
|
||||||
|
|
||||||
|
The payload ensures WinRM is running, configures/reuses a suitable
|
||||||
|
certificate or creates a self-signed Server Authentication certificate,
|
||||||
|
creates the HTTPS listener, allows TCP/5986 and verifies the final
|
||||||
|
state.
|
||||||
|
|
||||||
|
The scheduled task runs immediately after registration and can retry
|
||||||
|
periodically. After successful verification it disables itself.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
Useful client-side checks include:
|
||||||
|
|
||||||
|
``` powershell
|
||||||
|
gpupdate /force
|
||||||
|
gpresult /h C:\Temp\gpresult.html
|
||||||
|
Get-Service WinRM
|
||||||
|
winrm enumerate winrm/config/listener
|
||||||
|
Get-ScheduledTask -TaskName "bitformer - Configure Ansible WinRM"
|
||||||
|
```
|
||||||
|
|
||||||
|
Also inspect Group Policy operational logs and Task Scheduler events
|
||||||
|
when Group Policy Preferences reports a task import failure.
|
||||||
+286
@@ -0,0 +1,286 @@
|
|||||||
|
# AIM Installation Guide
|
||||||
|
|
||||||
|
This guide installs AIM with `/etc/ansible/scripts` as the project root.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/scripts/
|
||||||
|
├── pyproject.toml
|
||||||
|
└── src/
|
||||||
|
└── aim/
|
||||||
|
|
||||||
|
/etc/ansible/.venv/
|
||||||
|
/usr/local/bin/aim -> /etc/ansible/.venv/bin/aim
|
||||||
|
```
|
||||||
|
|
||||||
|
The virtual environment is intentionally not stored in Git.
|
||||||
|
|
||||||
|
## 1. Prerequisites
|
||||||
|
|
||||||
|
On Debian/Ubuntu:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y python3 python3-venv python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Rebuild the virtual environment
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo rm -rf /etc/ansible/.venv
|
||||||
|
sudo python3 -m venv /etc/ansible/.venv
|
||||||
|
sudo /etc/ansible/.venv/bin/python -m pip install --upgrade pip setuptools wheel
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Install AIM
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo /etc/ansible/.venv/bin/python -m pip install -e /etc/ansible/scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
ls -l /etc/ansible/.venv/bin/aim
|
||||||
|
/etc/ansible/.venv/bin/python -m pip check
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Restore the system-wide command
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo ln -sfn /etc/ansible/.venv/bin/aim /usr/local/bin/aim
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
ls -l /usr/local/bin/aim
|
||||||
|
aim --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Authorization group / using another GID
|
||||||
|
|
||||||
|
AIM authorization is group-based rather than root-based. The default
|
||||||
|
required group is:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
srv_debsansible01_admins@bitformer.lan
|
||||||
|
```
|
||||||
|
|
||||||
|
AIM resolves the configured **group name** through the operating
|
||||||
|
system's NSS layer (for example local groups, SSSD or winbind). The
|
||||||
|
operator must have that group active as a primary or supplementary
|
||||||
|
group.
|
||||||
|
|
||||||
|
Do not hard-code a numeric GID into AIM merely because a particular
|
||||||
|
server uses a different GID. Numeric GIDs can differ between systems;
|
||||||
|
configure the appropriate group name and let NSS resolve its GID.
|
||||||
|
|
||||||
|
Inspect the default group and its resolved GID:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
getent group 'srv_debsansible01_admins@bitformer.lan'
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the current user's active groups/GIDs:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
id
|
||||||
|
id -G
|
||||||
|
id -Gn
|
||||||
|
```
|
||||||
|
|
||||||
|
If another authorization group should be used, change AIM's **Required
|
||||||
|
Group** through:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
Global Config
|
||||||
|
└── Required Group
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, if the local/AD-backed group is:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
ansible_operators
|
||||||
|
```
|
||||||
|
|
||||||
|
verify it first:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
getent group ansible_operators
|
||||||
|
```
|
||||||
|
|
||||||
|
Then configure `ansible_operators` as AIM's Required Group. AIM will use
|
||||||
|
the GID returned by NSS for that group.
|
||||||
|
|
||||||
|
After adding a user to a group, the login session may need to be renewed
|
||||||
|
before the supplementary group becomes active. Verify with `id` before
|
||||||
|
troubleshooting AIM authorization.
|
||||||
|
|
||||||
|
### Same group name, different GID
|
||||||
|
|
||||||
|
This is supported. For example, one server may resolve:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
ansible_operators:x:1200:...
|
||||||
|
```
|
||||||
|
|
||||||
|
and another may resolve:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
ansible_operators:x:48001:...
|
||||||
|
```
|
||||||
|
|
||||||
|
AIM should be configured with `ansible_operators`, not `1200` or
|
||||||
|
`48001`.
|
||||||
|
|
||||||
|
### Different group name
|
||||||
|
|
||||||
|
Configure the alternative group name in AIM Global Config and confirm:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
getent group '<group-name>'
|
||||||
|
id
|
||||||
|
```
|
||||||
|
|
||||||
|
If `getent` cannot resolve the group, fix NSS/SSSD/winbind/local group
|
||||||
|
resolution first.
|
||||||
|
|
||||||
|
## 6. Git-ignored runtime data
|
||||||
|
|
||||||
|
Important ignored data includes:
|
||||||
|
|
||||||
|
``` gitignore
|
||||||
|
.vscode/*
|
||||||
|
.venv/*
|
||||||
|
.ansible/*
|
||||||
|
secure/*
|
||||||
|
secure/keys/*
|
||||||
|
vault.yml
|
||||||
|
.ssh
|
||||||
|
*.msi
|
||||||
|
*.deb
|
||||||
|
*.rpm
|
||||||
|
**/.hosts.tsv
|
||||||
|
**/hosts.yml.aim-session.bak
|
||||||
|
**/hosts.aim-session.bak.yml
|
||||||
|
*.bak
|
||||||
|
```
|
||||||
|
|
||||||
|
For AIM inventory recovery, the critical persistent data is `vault.yml`
|
||||||
|
and inventory `.ssh/` content. Do not restore `.venv`; rebuild it.
|
||||||
|
|
||||||
|
## 7. Recover missing Vaults and SSH keys
|
||||||
|
|
||||||
|
Example restored backup:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/inventories_RESTORED_20260915_152127
|
||||||
|
```
|
||||||
|
|
||||||
|
Active inventories:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
/etc/ansible/inventories
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dry run
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
BACKUP="/etc/ansible/inventories_RESTORED_20260915_152127"
|
||||||
|
TARGET="/etc/ansible/inventories"
|
||||||
|
|
||||||
|
echo "=== vault.yml ==="
|
||||||
|
sudo find "$BACKUP" -type f -name 'vault.yml' -print0 |
|
||||||
|
while IFS= read -r -d '' src; do
|
||||||
|
rel="${src#"$BACKUP"/}"
|
||||||
|
dst="$TARGET/$rel"
|
||||||
|
if [ -e "$dst" ]; then
|
||||||
|
echo "KEEP $dst"
|
||||||
|
else
|
||||||
|
echo "RESTORE $src -> $dst"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== .ssh files ==="
|
||||||
|
sudo find "$BACKUP" -path '*/.ssh/*' -type f -print0 |
|
||||||
|
while IFS= read -r -d '' src; do
|
||||||
|
rel="${src#"$BACKUP"/}"
|
||||||
|
dst="$TARGET/$rel"
|
||||||
|
if [ -e "$dst" ]; then
|
||||||
|
echo "KEEP $dst"
|
||||||
|
else
|
||||||
|
echo "RESTORE $src -> $dst"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
BACKUP="/etc/ansible/inventories_RESTORED_20260915_152127"
|
||||||
|
TARGET="/etc/ansible/inventories"
|
||||||
|
|
||||||
|
sudo find "$BACKUP" -type f -name 'vault.yml' -print0 |
|
||||||
|
while IFS= read -r -d '' src; do
|
||||||
|
rel="${src#"$BACKUP"/}"
|
||||||
|
dst="$TARGET/$rel"
|
||||||
|
if [ -e "$dst" ]; then
|
||||||
|
echo "KEEP $dst"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
sudo mkdir -p "$(dirname "$dst")"
|
||||||
|
sudo cp -a "$src" "$dst"
|
||||||
|
echo "RESTORED $dst"
|
||||||
|
done
|
||||||
|
|
||||||
|
sudo find "$BACKUP" -path '*/.ssh/*' -type f -print0 |
|
||||||
|
while IFS= read -r -d '' src; do
|
||||||
|
rel="${src#"$BACKUP"/}"
|
||||||
|
dst="$TARGET/$rel"
|
||||||
|
if [ -e "$dst" ]; then
|
||||||
|
echo "KEEP $dst"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
sudo mkdir -p "$(dirname "$dst")"
|
||||||
|
sudo cp -a "$src" "$dst"
|
||||||
|
echo "RESTORED $dst"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
`cp -a` preserves ownership, permissions and timestamps. Existing active
|
||||||
|
files are never overwritten.
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo find /etc/ansible/inventories -type f -name 'vault.yml' -print | sort
|
||||||
|
sudo find /etc/ansible/inventories -path '*/.ssh/*' -type f -print | sort
|
||||||
|
sudo find /etc/ansible/inventories -path '*/.ssh/*' -type f -exec ls -l {} \;
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the restored backup until Vault and SSH access have been tested.
|
||||||
|
|
||||||
|
## 8. Final verification
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
aim --version
|
||||||
|
/etc/ansible/.venv/bin/python -m pip check
|
||||||
|
aim
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating AIM
|
||||||
|
|
||||||
|
AIM is installed editable from `/etc/ansible/scripts`.
|
||||||
|
|
||||||
|
If its entry point needs recreation:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo /etc/ansible/.venv/bin/python -m pip install -e /etc/ansible/scripts
|
||||||
|
sudo ln -sfn /etc/ansible/.venv/bin/aim /usr/local/bin/aim
|
||||||
|
```
|
||||||
|
|
||||||
|
Customer data under `/etc/ansible/inventories` is separate from the AIM
|
||||||
|
source installation.
|
||||||
Reference in New Issue
Block a user