33 Commits

Author SHA1 Message Date
matt
cdd140f513 add some small fixes 2024-06-25 12:39:05 -06:00
Matthew Fernandez
40807f1eab Add OCP-CNV patching demo (#140) 2024-06-11 15:23:56 -06:00
willtome
65936930c0 Add state exists for credentials (#150) 2024-06-10 08:37:23 -04:00
Dale Lemons
c98170d5f7 variablize chart version via host_vars (#151) 2024-06-06 12:43:34 -04:00
Chris Edillon
c6c3231234 updated requirements to match product-demos-ee (#145) 2024-06-06 10:17:09 -04:00
willtome
f554bc0ee1 Revert "add state exists to credentials"
This reverts commit 88b171bb48.
2024-06-06 09:45:37 -04:00
willtome
88b171bb48 add state exists to credentials 2024-06-06 09:38:57 -04:00
Chris Edillon
16553210bd Add deployment ID for bucket naming (#149) 2024-05-20 15:10:02 -04:00
Chris Edillon
4f0df3c8db Change injector raw formatting (#146) 2024-05-13 15:19:38 -04:00
Chris Edillon
e990f39c60 switch to infra.controller_configuration.dispatch (#147) 2024-04-29 10:36:22 -04:00
Todd Ruch
9cd49892c6 Updated README.md to provide details on using the new product-demos EE (#139) 2024-04-08 11:19:05 -04:00
Matthew Fernandez
3468d1c443 add cjis to compliance demo (#134) 2024-03-03 14:46:19 -05:00
Leo
10f0bb4641 Feature/changelog release (#131) 2024-01-15 15:20:57 -05:00
willtome
018c006e3b Update gitlab version (#128)
Co-authored-by: youtous <contact@youtous.me>
2024-01-14 14:02:31 -05:00
MKletz
1af584b4ea Workaround for #109 (#123)
Co-authored-by: willtome <wtome@redhat.com>
2024-01-08 10:08:52 -05:00
Zach LeBlanc
d60e0c7ca6 Update COLLECTIONS_PATHS config (#127)
Co-authored-by: willtome <wtome@redhat.com>
2024-01-05 10:05:33 -05:00
willtome
c198780d72 More Windows in Workflow (#126) 2024-01-05 10:05:02 -05:00
Leo
1832bb6199 fix missing comment in win_scan_packages (#125) 2023-12-15 16:21:02 -05:00
Leo
2447d0d511 Feature/improve pre commit (#119) 2023-12-14 15:29:28 -05:00
willtome
c0cd993c69 Random Bug fixes (#103)
Co-authored-by: youtous <contact@youtous.me>
2023-12-11 15:27:14 -05:00
MKletz
d5093fa544 #113 solution - Windows AD domain reboots (#114) 2023-11-13 11:12:18 -05:00
Dale Lemons
dd1de852b6 fix playbook paths for Cloud setup (#112) 2023-11-09 14:38:25 -05:00
Dale Lemons
e958164cb6 Gitlab url fix (#106) 2023-10-23 15:40:22 -04:00
Dale Lemons
98416fcc3c gitlab first pass (#104)
Co-authored-by: willtome <wtome@redhat.com>
2023-10-16 15:58:30 -04:00
Matthew Fernandez
5f8bd8929e Setup multiple (selectable) demos (#102) 2023-10-16 15:49:50 -04:00
Chris Edillon
2ee334f6b3 added pre-commit configuration for ansible-lint (#93)
Co-authored-by: willtome <wtome@redhat.com>
2023-09-25 15:56:11 -04:00
willtome
d7e9ad637b Update ansible-lint.yml 2023-09-25 15:40:43 -04:00
Chris Edillon
a5aa9564f5 Multi-profile compliance (#87)
Co-authored-by: willtome <wtome@redhat.com>
2023-09-25 15:13:15 -04:00
willtome
44585bf1b9 Update Docs (#63) 2023-09-18 14:19:56 -05:00
Matthew Fernandez
2cd3ec6f72 Extend create vm job template (#97) 2023-09-13 08:09:34 -06:00
Zach LeBlanc
7e4399eac2 Patch EC2 Workflow (#75)
Co-authored-by: zjleblanc <zjleblanc3@gmail.com>
Co-authored-by: willtome <wtome@redhat.com>
2023-09-11 16:00:17 -04:00
willtome
a78e74e782 OpenShift Dev Spaces (#64) 2023-08-28 15:57:19 -04:00
willtome
ddb4c09157 Move to Demo Creds and Inventory (#88) 2023-08-22 09:03:34 -04:00
176 changed files with 4729 additions and 1629 deletions

View File

@@ -1,4 +1,12 @@
--- ---
profile: production
offline: false
skip_list:
- "galaxy[no-changelog]"
exclude_paths: exclude_paths:
# would be better to move the roles here to the top-level roles directory
- collections/ansible_collections/demo/compliance/roles/ - collections/ansible_collections/demo/compliance/roles/
- roles/redhatofficial.*
- .github/ - .github/

13
.devfile.yaml Normal file
View File

@@ -0,0 +1,13 @@
---
schemaVersion: 2.2.0
metadata:
name: product-demos
components:
- name: product-demos-ee
container:
image: quay.io/mloriedo/ansible-creator-ee:latest # workaround for https://github.com/eclipse/che/issues/21778
memoryRequest: 256M
memoryLimit: 5Gi
cpuRequest: 250m
cpuLimit: 2000m
args: ['tail', '-f', '/dev/null']

BIN
.github/images/project-architecture.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@@ -1,25 +0,0 @@
---
name: Ansible Lint
on:
- push
- pull_request
env:
ANSIBLE_GALAXY_SERVER_LIST: ah,galaxy
ANSIBLE_GALAXY_SERVER_AH_URL: https://console.redhat.com/api/automation-hub/
ANSIBLE_GALAXY_SERVER_AH_AUTH_URL: https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
ANSIBLE_GALAXY_SERVER_AH_TOKEN: ${{ secrets.ANSIBLE_GALAXY_SERVER_AH_TOKEN }}
ANSIBLE_GALAXY_SERVER_GALAXY_URL: https://galaxy.ansible.com/
jobs:
build:
runs-on: ubuntu-latest
steps:
# Important: This sets up your GITHUB_WORKSPACE environment variable
- uses: actions/checkout@v3
with:
fetch-depth: 0 # needed for progressive mode to work
- name: Run ansible-lint
uses: ansible/ansible-lint-action@v6.11.0

View File

@@ -1,50 +0,0 @@
---
###########################
###########################
## Linter GitHub Actions ##
###########################
###########################
name: Lint Code Base
#
# Documentation:
# https://help.github.com/en/articles/workflow-syntax-for-github-actions
#
#############################
# Start the job on all push #
#############################
on: [push, pull_request]
###############
# Set the Job #
###############
jobs:
build:
# Name the Job
name: Lint Code Base
# Set the agent to run on
runs-on: ubuntu-latest
##################
# Load all steps #
##################
steps:
##########################
# Checkout the code base #
##########################
- name: Checkout Code
uses: actions/checkout@v2
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
################################
# Run Linter against code base #
################################
- name: Lint Code Base
uses: github/super-linter@v4
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

17
.github/workflows/pre-commit.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
---
name: pre-commit
on:
- push
- pull_request_target
env:
ANSIBLE_GALAXY_SERVER_AH_TOKEN: ${{ secrets.ANSIBLE_GALAXY_SERVER_AH_TOKEN }}
jobs:
pre-commit:
name: pre-commit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.0

41
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
---
name: release
on:
push:
branches:
- main
tags:
- "v*.*.*"
workflow_run:
workflows: ["pre-commit"]
types:
- completed
jobs:
release:
name: Release Job
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install go (required for Changelog parsing)
uses: actions/setup-go@v4
- name: Parse CHANGELOG.md
run: |
GO111MODULE=on go install github.com/rcmachado/changelog@0.7.0
changelog show "$GITHUB_REF_NAME" > ${{ github.workspace }}-CHANGELOG.txt
echo "Release note for $GITHUB_REF_NAME :"
cat ${{ github.workspace }}-CHANGELOG.txt
- name: Release
uses: softprops/action-gh-release@v1
with:
body_path: ${{ github.workspace }}-CHANGELOG.txt
files: |
LICENSE
CHANGELOG.md

7
.gitignore vendored
View File

@@ -1,4 +1,4 @@
ansible-navigator.log
sean_login_info.yml sean_login_info.yml
.DS_Store .DS_Store
choose_demo.yml choose_demo.yml
@@ -6,4 +6,7 @@ choose_demo_example_azure.yml
choose_demo_example_aws.yml choose_demo_example_aws.yml
.ansible.cfg .ansible.cfg
*.gz *.gz
*artifact*.json
**/roles/*
!**/roles/requirements.yml
.deployment_id

29
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,29 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-yaml
exclude: \.j2.(yaml|yml)$|\.(yaml|yml).j2$
args: [--unsafe] # see https://github.com/pre-commit/pre-commit-hooks/issues/273
- id: check-toml
- id: check-json
- id: check-symlinks
- repo: https://github.com/ansible/ansible-lint.git
# get latest release tag from https://github.com/ansible/ansible-lint/releases/
rev: v6.20.3
hooks:
- id: ansible-lint
additional_dependencies:
- jmespath
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.11.0
hooks:
- id: black
...

7
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"redhat.vscode-yaml",
"redhat.ansible",
"ms-python.black-formatter"
]
}

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"editor.renderWhitespace": "all"
}

19
.yamllint Normal file
View File

@@ -0,0 +1,19 @@
---
extends: default
rules:
line-length: disable
trailing-spaces: enable
colons:
max-spaces-before: 0
max-spaces-after: -1
indentation:
level: error
indent-sequences: true # consistent with ansible-lint
truthy:
level: error
allowed-values:
- 'true'
- 'false'
...

12
CHANGELOG.md Normal file
View File

@@ -0,0 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v-0.0.1](https://github.com/ansible/product-demos/-/tree/v-0.0.1) - 2024-01-12
### Added
- Initial release ([1af584b4ea6d77812bfcb2f6474fee6ee1b13666](https://github.com/ansible/product-demos/-/commit/1af584b4ea6d77812bfcb2f6474fee6ee1b13666))

View File

@@ -1,12 +1,18 @@
# Contribution Guidelines # Contribution Guidelines
This document aims to outline the requirements for the various forms of contribution for this project. This document aims to outline the requirements for the various forms of contribution for this project.
**ALL** contributions are subject to review via pull request ## Project Architecture
![project-architecture](.github/images/project-architecture.png)
## Pull Requests ## Pull Requests
**ALL** contributions are subject to review via pull request
### Pull Requests
1) Ensure the "base repository" is set to "ansible/product-demos". 1) Ensure the "base repository" is set to "ansible/product-demos".
### Pull Request Guidelines #### Pull Request Guidelines
- PRs should include the playbook/demo and required entry in corresponding `<demo>/setup.yml`. - PRs should include the playbook/demo and required entry in corresponding `<demo>/setup.yml`.
- PRs should include documentation in corresponding `<demo>/README.md`. - PRs should include documentation in corresponding `<demo>/README.md`.
- PRs should be rebased against the `main` branch to avoid conflicts. - PRs should be rebased against the `main` branch to avoid conflicts.
@@ -19,13 +25,15 @@ This document aims to outline the requirements for the various forms of contribu
3) Make any changes needed to match the existing standards in the directory. 3) Make any changes needed to match the existing standards in the directory.
1) Ex: Parameterized hosts 1) Ex: Parameterized hosts
```ansible ```ansible
hosts: "{{ HOSTS | default('windows') }}" hosts: "{{ _hosts | default('windows') }}"
``` ```
4) Create an entry for your playbook in your subdirectories `setup.yml` 4) Create an entry for your playbook in your subdirectories `setup.yml`
1) You can copy paste an existing one and edit it. 1) You can copy paste an existing one and edit it.
2) Ensure you edit the name, playbook path, survey etc. 2) Ensure you edit the name, playbook path, survey etc.
5) Add any needed roles/collections to the [requirements.yml](/collections/requirements.yml) 5) Add any needed roles/collections to the [requirements.yml](/collections/requirements.yml)
6) Test via RHPDS, specify your branch name within the project configuration. 6) Test via [demo.redhat.com](https://demo.redhat.com/catalog?item=babylon-catalog-prod/sandboxes-gpte.aap-product-demos.prod&utm_source=webapp&utm_medium=share-link), specify your branch name within the project configuration.
> NOTE: demo.redhat.com is available to Red Hat Associates and Partners with a valid account.
## New Demo Section/Category ## New Demo Section/Category
1) Create a new subdirectory with no spaces 1) Create a new subdirectory with no spaces
@@ -44,3 +52,96 @@ This document aims to outline the requirements for the various forms of contribu
- `controller_components` can be any of the roles defined [here](https://github.com/redhat-cop/controller_configuration/tree/devel/roles) - `controller_components` can be any of the roles defined [here](https://github.com/redhat-cop/controller_configuration/tree/devel/roles)
- Add variables for each component listed - Add variables for each component listed
3) Include a README.md in the subdirectory 3) Include a README.md in the subdirectory
## Testing
We utilize pre-commit to handle Git hooks, initiating a pre-commit check with each commit, both locally and on CI.
To install pre-commit, use the following commands:
```bash
pip install pre-commit
pre-commit install
```
For further details, refer to the [pre-commit installation documentation](https://pre-commit.com/#installation).
To execute ansible-lint (whether within pre-commit or independently), you must configure an environment variable for the token required to connect to Automation Hub. Obtain the token [here](https://console.redhat.com/ansible/automation-hub/token).
Copy the token value and execute the following command:
```bash
export ANSIBLE_GALAXY_SERVER_AH_TOKEN=<token>
```
## Release Process
We follow a structured release process for this project. Here are the steps involved:
1. **Create a Release Branch:**
- Start by creating a new release branch from the `main` branch.
```bash
git checkout -b release/v-<version>
```
2. **Update Changelog:**
- Open the `CHANGELOG.md` file to manually add your change to the appropriate section.
- Our changelog follows the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format and includes the following categories of changes:
- `Added` for new features.
- `Changed` for changes in existing functionality.
- `Deprecated` for features that will be removed in upcoming releases.
- `Fixed` for bug fixes.
- `Removed` for deprecated features that were removed.
- `Security` for security-related changes.
- Add a new entry under the relevant category. Include a brief summary of the change and the merge request commit tag.
```markdown
## [Unreleased]
### Added
- New feature or enhancement ([Merge Request Commit](https://github.com/ansible/product-demos/-/commit/<commit-hash>))
```
- Replace `<commit-hash>` with the actual commit hash from the merge request.
3. **Commit Changes:**
- Commit the changes made to the `CHANGELOG.md` file.
```bash
git add CHANGELOG.md
git commit -m "Update CHANGELOG for release <version>"
```
4. **Create a Pull Request:**
- Open a pull request from the release branch to the `main` branch.
5. **Review and Merge:**
- Review the pull request and merge it into the `main` branch.
6. **Tag the Release:**
- Once the pull request is merged, tag the release with the version number.
```bash
git tag -a v-<version> -m "Release <version>"
git push origin v-<version>
```
7. **Publish the Release:**
- After the successful completion of the pull request and merging into the `main` branch, an automatic GitHub Action will be triggered to publish the release.
The GitHub Action will perform the following steps:
- Parse the `CHANGELOG.md` file.
- Generate a release note based on the changes.
- Attach relevant files (such as `LICENSE`, `CHANGELOG.md`, and the generated `CHANGELOG.txt`) to the GitHub Release.
No manual intervention is required for this step; the GitHub Action will handle the release process automatically.
8. **Cleanup:**
- Delete the release branch.
```bash
git branch -d release/v-<version>
```

View File

@@ -1,6 +1,9 @@
[![Lab](https://img.shields.io/badge/Try%20Me-EE0000?style=for-the-badge&logo=redhat&logoColor=white)](https://red.ht/aap-product-demos)
[![Dev Spaces](https://img.shields.io/badge/Customize%20Here-0078d7.svg?style=for-the-badge&logo=visual-studio-code&logoColor=white)](https://workspaces.openshift.com/f?url=https://github.com/ansible/product-demos)
# Official Ansible Product Demos # Official Ansible Product Demos
This is a centralized location for all Ansible Product Demos going forward. This is a centralized location for Ansible Product Demos. This project is a collection of use cases implemented with Ansible for use with the Ansible Automation Platform.
| Demo Name | Description | | Demo Name | Description |
|-----------|-------------| |-----------|-------------|
@@ -8,6 +11,7 @@ This is a centralized location for all Ansible Product Demos going forward.
| [Windows](windows/README.md) | Repository of demos for Windows Server automation | | [Windows](windows/README.md) | Repository of demos for Windows Server automation |
| [Cloud](cloud/README.md) | Demo for infrastructure and cloud provisioning automation | | [Cloud](cloud/README.md) | Demo for infrastructure and cloud provisioning automation |
| [Network](network/README.md) | Ansible Network automation demos | | [Network](network/README.md) | Ansible Network automation demos |
| [Satellite](satellite/README.md) | Demos of automation with Red Hat Satellite Server |
## Contributions ## Contributions
@@ -15,25 +19,49 @@ If you would like to contribute to this project please refer to [contribution gu
## Using this project ## Using this project
> This project is tested for compatibility with AAP2 Linux Automation Workshop available to Red Hat Employees and Partners. To use with other Ansible Controller installations, review the [pre-requisite documentation](https://github.com/RedHatGov/ansible-tower-samples/tree/product-demos). This project is tested for compatibility with the [demo.redhat.com Product Demos Sandbox]([red.ht/aap-product-demos](https://demo.redhat.com/catalog?item=babylon-catalog-prod/sandboxes-gpte.aap-product-demos.prod&utm_source=webapp&utm_medium=share-link)) lab environment. To use with other Ansible Controller installations, review the [prerequisite documentation](https://github.com/RedHatGov/ansible-tower-samples).
> NOTE: demo.redhat.com is available to Red Hat Associates and Partners with a valid account.
1. First you must create a credential for [Automation Hub](https://console.redhat.com/ansible/automation-hub/) to successfully sync collections used by this project. 1. First you must create a credential for [Automation Hub](https://console.redhat.com/ansible/automation-hub/) to successfully sync collections used by this project.
1. In the Credentials section of the Controller UI, add a new Credential called `Automation Hub` with the type `Ansible Galaxy/Automation Hub API Token` 1. In the Credentials section of the Controller UI, add a new Credential called `Automation Hub` with the type `Ansible Galaxy/Automation Hub API Token`
2. You can obtain a token [here](https://console.redhat.com/ansible/automation-hub/token). This page will also provide the Server URL and Auth Server URL. 2. You can obtain a token [here](https://console.redhat.com/ansible/automation-hub/token). This page will also provide the Server URL and Auth Server URL.
3. Next, click on Organizations and edit the `Default` organization. Add your `Automation Hub` credential to the `Galaxy Credentials` section. Don't forget to click Save!! 3. Next, click on Organizations and edit the `Default` organization. Add your `Automation Hub` credential to the `Galaxy Credentials` section. Don't forget to click **Save**!!
2. If it has not been created for you, add a Project called `Ansible official demo project` with this repo as a source. NOTE: if you are using a fork, be sure that you have the correct URL. Update the project. > You can also use an execution environment for disconnected environments. To do this, you must disable collection downloads in the Controller. This can be done in `Settings` > `Job Settings`. This setting prevents the controller from downloading collections listed in the [collections/requirements.yml](collections/requirements.yml) file.
3. Finally, Create a Job Template called `Setup` with the following configuration:
2. If it is not already created for you, add an Execution Environment called `product-demos`
- Name: product-demos
- Image: quay.io/acme_corp/product-demos-ee:latest
- Pull: Only pull the image if not present before running
3. If it is not already created for you, create a Project called `Ansible official demo project` with this repo as a source. NOTE: if you are using a fork, be sure that you have the correct URL. Update the project.
4. Finally, Create a Job Template called `Setup` with the following configuration:
- Name: Setup - Name: Setup
- Inventory: Demo Inventory - Inventory: Demo Inventory
- Exec Env: Control Plane EE - Exec Env: product-demos
- Playbook: setup_demo.yml - Playbook: setup_demo.yml
- Credentials: - Credentials:
- Type: Red Hat Ansible Automation Platform - Type: Red Hat Ansible Automation Platform
- Name: Controller Credential - Name: Controller Credential
- Extra vars: - Extra vars:
demo: <linux or windows or cloud or network> demo: <linux or windows or cloud or network>
## Bring Your Own Demo
Can't find what you're looking for? Customize this repo to make it your own.
1. Create a fork of this repo.
2. Update the URL of the `Ansible official demo project` in the Controller.
3. Make changes as needed and run the **Setup** job
See the [contribution guide](CONTRIBUTING.md) for more details on how to customize the project.
---
[Privacy statement](https://www.redhat.com/en/about/privacy-policy) | [Terms of use](https://www.redhat.com/en/about/terms-use) | [Security disclosure](https://www.ansible.com/security?hsLang=en-us) | [All policies and guidelines](https://www.redhat.com/en/about/all-policies-guidelines)

View File

@@ -1,3 +1,16 @@
[defaults] [defaults]
collections_paths=./collections collections_path=./collections
roles_path=./roles roles_path=./roles
[galaxy]
server_list = ah,galaxy
[galaxy_server.ah]
# Grab a token at https://console.redhat.com/ansible/automation-hub/token
# Then define it using ANSIBLE_GALAXY_SERVER_AH_TOKEN=""
url=https://console.redhat.com/api/automation-hub/content/published/
auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
[galaxy_server.galaxy]
url=https://galaxy.ansible.com/

View File

@@ -10,7 +10,7 @@
- [Configure Credentials](#configure-credentials) - [Configure Credentials](#configure-credentials)
- [Add Workshop Credential Password](#add-workshop-credential-password) - [Add Workshop Credential Password](#add-workshop-credential-password)
- [Remove Inventory Variables](#remove-inventory-variables) - [Remove Inventory Variables](#remove-inventory-variables)
- [Getting your Puiblic Key for Create Infra Job](#getting-your-puiblic-key-for-create-infra-job) - [Getting your Puiblic Key for Create Keypair Job](#getting-your-puiblic-key-for-create-keypair-job)
- [Suggested Usage](#suggested-usage) - [Suggested Usage](#suggested-usage)
- [Known Issues](#known-issues) - [Known Issues](#known-issues)
@@ -20,8 +20,11 @@ This category of demos shows examples of multi-cloud provisioning and management
### Jobs ### Jobs
- [**Cloud / Create Infra**](create_infra.yml) - Creates a VPC with required routing and firewall rules for provisioning VMs - [**Cloud / Create Infra**](create_infra.yml) - Creates a VPC with required routing and firewall rules for provisioning VMs
- [**Cloud / Create Keypair**](aws_key.yml) - Creates a keypair for connecting to EC2 instances
- [**Cloud / Create VM**](create_vm.yml) - Create a VM based on a [blueprint](blueprints/) in the selected cloud provider - [**Cloud / Create VM**](create_vm.yml) - Create a VM based on a [blueprint](blueprints/) in the selected cloud provider
- [**Cloud / Destroy VM**](destroy_vm.yml) - Destroy a VM that has been created in a cloud provider. VM must be imported into dynamic inventory to be deleted. - [**Cloud / Destroy VM**](destroy_vm.yml) - Destroy a VM that has been created in a cloud provider. VM must be imported into dynamic inventory to be deleted.
- [**Cloud / Snapshot EC2**](snapshot_ec2.yml) - Snapshot a VM that has been created in a cloud provider. VM must be imported into dynamic inventory to be snapshot.
- [**Cloud / Restore EC2 from Snapshot**](snapshot_ec2.yml) - Restore a VM that has been created in a cloud provider. By default, volumes will be restored from their latest snapshot. VM must be imported into dynamic inventory to be patched.
### Inventory ### Inventory
@@ -46,7 +49,7 @@ After running the setup job template, there are a few steps required to make the
1) Remove Workshop Inventory variables on the Details page of the inventory. Required until [RFE](https://github.com/ansible/workshops/issues/1597]) is complete 1) Remove Workshop Inventory variables on the Details page of the inventory. Required until [RFE](https://github.com/ansible/workshops/issues/1597]) is complete
### Getting your Puiblic Key for Create Infra Job ### Getting your Puiblic Key for Create Keypair Job
1) Connect to the command line of your Controller server. This is easiest to do by opening the VS Code Web Editor from the landing page where you found the Controller login details. 1) Connect to the command line of your Controller server. This is easiest to do by opening the VS Code Web Editor from the landing page where you found the Controller login details.
2) Open a Terminal Window in the VS Code Web Editor. 2) Open a Terminal Window in the VS Code Web Editor.
@@ -56,9 +59,11 @@ After running the setup job template, there are a few steps required to make the
## Suggested Usage ## Suggested Usage
**Cloud / Create Infra** -The Create Infra job builds cloud infrastructure based on the provider definition in the included `demo.cloud` collection. **Cloud / Create Keypair** - The Create Keypair job creates an EC2 keypair which can be used when creating EC2 instances to enable SSH access.
**Cloud / Create VM** - The Create VM job builds a VM in the given provider based on the included `demo.cloud` collection. VM [blueprints](blueprints/) define variables for each provider that override the defaults in the collection. When creating VMs it is recommended to follow naming conventions that can be used as host patterns. (eg. VM names: `win1`, `win2`, `win3`. Host Pattern: `win*` ) **Cloud / Create VM** - The Create VM job builds a VM in the given provider based on the included `demo.cloud` collection. VM [blueprints](blueprints/) define variables for each provider that override the defaults in the collection. When creating VMs it is recommended to follow naming conventions that can be used as host patterns. (eg. VM names: `win1`, `win2`, `win3`. Host Pattern: `win*` )
**Cloud / AWS / Patch EC2 Workflow** - Create a VPC and one or more linux VM(s) in AWS using the `Cloud / Create VPC` and `Cloud / Create VM` templates. Run the workflow and observe the instance snapshots followed by patching operation. Optionally, use the survey to force a patch failure in order to demonstrate the restore path. At this time, the workflow does not support patching Windows instances.
## Known Issues ## Known Issues
Azure does not work without a custom execution environment that includes the Azure dependencies. Azure does not work without a custom execution environment that includes the Azure dependencies.

View File

@@ -10,7 +10,7 @@
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- aws_key_name is defined - aws_key_name is defined
- aws_region is defined - create_vm_aws_region is defined
- aws_public_key is defined - aws_public_key is defined
- aws_keypair_owner is defined - aws_keypair_owner is defined
fail_msg: "Required variables not set" fail_msg: "Required variables not set"
@@ -18,7 +18,7 @@
- name: Create AWS keypair - name: Create AWS keypair
amazon.aws.ec2_key: amazon.aws.ec2_key:
name: "{{ aws_key_name }}" name: "{{ aws_key_name }}"
region: "{{ aws_region }}" region: "{{ create_vm_aws_region }}"
key_material: "{{ aws_public_key }}" key_material: "{{ aws_public_key }}"
state: present state: present
tags: tags:

View File

@@ -9,7 +9,6 @@
aws_tenancy: default aws_tenancy: default
aws_vpc_cidr_block: 10.0.0.0/16 aws_vpc_cidr_block: 10.0.0.0/16
aws_subnet_cidr: 10.0.1.0/24 aws_subnet_cidr: 10.0.1.0/24
aws_region: us-east-1
aws_sg_name: aws-test-sg aws_sg_name: aws-test-sg
aws_subnet_name: aws-test-subnet aws_subnet_name: aws-test-subnet
aws_rt_name: aws-test-rt aws_rt_name: aws-test-rt
@@ -21,7 +20,7 @@
name: "{{ aws_vpc_name }}" name: "{{ aws_vpc_name }}"
cidr_block: "{{ aws_vpc_cidr_block }}" cidr_block: "{{ aws_vpc_cidr_block }}"
tenancy: "{{ aws_tenancy }}" tenancy: "{{ aws_tenancy }}"
region: "{{ aws_region }}" region: "{{ create_vm_aws_region }}"
tags: tags:
owner: "{{ aws_owner_tag }}" owner: "{{ aws_owner_tag }}"
purpose: "{{ aws_purpose_tag }}" purpose: "{{ aws_purpose_tag }}"
@@ -31,7 +30,7 @@
amazon.aws.ec2_vpc_igw: amazon.aws.ec2_vpc_igw:
state: present state: present
vpc_id: "{{ aws_vpc.vpc.id }}" vpc_id: "{{ aws_vpc.vpc.id }}"
region: "{{ aws_region }}" region: "{{ create_vm_aws_region }}"
tags: tags:
Name: "{{ aws_vpc_name }}" Name: "{{ aws_vpc_name }}"
owner: "{{ aws_owner_tag }}" owner: "{{ aws_owner_tag }}"
@@ -42,17 +41,17 @@
amazon.aws.ec2_security_group: amazon.aws.ec2_security_group:
state: present state: present
name: "{{ aws_sg_name }}" name: "{{ aws_sg_name }}"
region: "{{ aws_region }}" region: "{{ create_vm_aws_region }}"
description: Inbound WinRM and RDP, http for demo servers and internal AD ports description: Inbound WinRM and RDP, http for demo servers and internal AD ports
rules: rules:
- proto: tcp - proto: tcp
ports: ports:
- 80 # HTTP - 80 # HTTP
- 443 # HTTPS - 443 # HTTPS
- 22 # SSH - 22 # SSH
- 5986 # WinRM - 5986 # WinRM
- 3389 # RDP - 3389 # RDP
- 9090 # Cockpit - 9090 # Cockpit
cidr_ip: 0.0.0.0/0 cidr_ip: 0.0.0.0/0
- proto: icmp - proto: icmp
to_port: -1 to_port: -1
@@ -60,32 +59,32 @@
cidr_ip: 0.0.0.0/0 cidr_ip: 0.0.0.0/0
- proto: tcp - proto: tcp
ports: ports:
- 80 # HTTP - 80 # HTTP
- 5986 # WinRM - 5986 # WinRM
- 3389 # RDP - 3389 # RDP
- 53 # DNS - 53 # DNS
- 88 # Kerberos Authentication - 88 # Kerberos Authentication
- 135 # RPC - 135 # RPC
- 139 # Netlogon - 139 # Netlogon
- 389 # LDAP - 389 # LDAP
- 445 # SMB - 445 # SMB
- 464 # Kerberos Authentication - 464 # Kerberos Authentication
- 5432 # PostgreSQL - 5432 # PostgreSQL
- 636 # LDAPS (LDAP over TLS) - 636 # LDAPS (LDAP over TLS)
- 873 # Rsync - 873 # Rsync
- 3268-3269 # Global Catalog - 3268-3269 # Global Catalog
- 1024-65535 # Ephemeral RPC ports - 1024-65535 # Ephemeral RPC ports
cidr_ip: "{{ aws_vpc_cidr_block }}" cidr_ip: "{{ aws_vpc_cidr_block }}"
- proto: udp - proto: udp
ports: ports:
- 53 # DNS - 53 # DNS
- 88 # Kerberos Authentication - 88 # Kerberos Authentication
- 123 # NTP - 123 # NTP
- 137-138 # Netlogon - 137-138 # Netlogon
- 389 # LDAP - 389 # LDAP
- 445 # SMB - 445 # SMB
- 464 # Kerberos Authentication - 464 # Kerberos Authentication
- 1024-65535 # Ephemeral RPC ports - 1024-65535 # Ephemeral RPC ports
cidr_ip: "{{ aws_vpc_cidr_block }}" cidr_ip: "{{ aws_vpc_cidr_block }}"
rules_egress: rules_egress:
- proto: -1 - proto: -1
@@ -101,7 +100,7 @@
state: present state: present
vpc_id: "{{ aws_vpc.vpc.id }}" vpc_id: "{{ aws_vpc.vpc.id }}"
cidr: "{{ aws_subnet_cidr }}" cidr: "{{ aws_subnet_cidr }}"
region: "{{ aws_region }}" region: "{{ create_vm_aws_region }}"
map_public: true map_public: true
tags: tags:
Name: "{{ aws_subnet_name }}" Name: "{{ aws_subnet_name }}"
@@ -113,7 +112,7 @@
amazon.aws.ec2_vpc_route_table: amazon.aws.ec2_vpc_route_table:
state: present state: present
vpc_id: "{{ aws_vpc.vpc.id }}" vpc_id: "{{ aws_vpc.vpc.id }}"
region: "{{ aws_region }}" region: "{{ create_vm_aws_region }}"
subnets: subnets:
- "{{ aws_subnet.subnet.id }}" - "{{ aws_subnet.subnet.id }}"
routes: routes:

10
cloud/restore_ec2.yml Normal file
View File

@@ -0,0 +1,10 @@
---
- name: Restore ec2 instance from snapshot
hosts: "{{ _hosts | default(omit) }}"
gather_facts: false
tasks:
- name: Include restore from snapshot role
ansible.builtin.include_role:
name: "demo.cloud.aws"
tasks_from: restore_vm

View File

@@ -1,14 +1,7 @@
--- ---
user_message: _deployment_id: "{{ lookup('file', playbook_dir + '/.deployment_id') }}"
controller_components: user_message:
- execution_environments
- projects
- credentials
- inventory_sources
- groups
- job_templates
- workflow_job_templates
controller_execution_environments: controller_execution_environments:
- name: Cloud Services Execution Environment - name: Cloud Services Execution Environment
@@ -27,16 +20,17 @@ controller_credentials:
credential_type: Amazon Web Services credential_type: Amazon Web Services
organization: Default organization: Default
update_secrets: false update_secrets: false
state: exists
inputs: inputs:
username: REPLACEME username: REPLACEME
password: REPLACEME password: REPLACEME
# - name: Azure # - name: Azure
# credential_type: Microsoft Azure Resource Manager # credential_type: Microsoft Azure Resource Manager
# organization: Default # organization: Default
# update_secrets: false # update_secrets: false
# inputs: # inputs:
# subscription: REPLACEME # subscription: REPLACEME
controller_inventory_sources: controller_inventory_sources:
- name: AWS Inventory - name: AWS Inventory
@@ -62,22 +56,22 @@ controller_inventory_sources:
- key: tags.owner - key: tags.owner
prefix: owner prefix: owner
# - name: Azure Inventory # - name: Azure Inventory
# organization: Default # organization: Default
# source: azure_rm # source: azure_rm
# inventory: Demo Inventory # inventory: Demo Inventory
# credential: Azure # credential: Azure
# execution_environment: Ansible Engine 2.9 execution environment # execution_environment: Ansible Engine 2.9 execution environment
# overwrite: true # overwrite: true
# source_vars: # source_vars:
# hostnames: # hostnames:
# - tags.Name # - tags.Name
# - default # - default
# keyed_groups: # keyed_groups:
# - key: os_profile.system # - key: os_profile.system
# prefix: os # prefix: os
# conditional_groups: # conditional_groups:
# cloud_azure: true # cloud_azure: true
controller_groups: controller_groups:
- name: cloud_aws - name: cloud_aws
@@ -92,7 +86,7 @@ controller_templates:
credentials: credentials:
- AWS - AWS
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_create_peer_network.yml playbook: playbooks/create_peer_network.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
@@ -108,7 +102,7 @@ controller_templates:
credentials: credentials:
- AWS - AWS
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_delete_peer_network.yml playbook: playbooks/delete_peer_network.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
@@ -122,7 +116,7 @@ controller_templates:
credentials: credentials:
- AWS - AWS
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_create_transit_network.yml playbook: playbooks/create_transit_network.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
@@ -138,7 +132,7 @@ controller_templates:
credentials: credentials:
- AWS - AWS
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_delete_transit_network.yml playbook: playbooks/delete_transit_network.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
@@ -164,7 +158,7 @@ controller_templates:
spec: spec:
- question_name: AWS Region - question_name: AWS Region
type: multiplechoice type: multiplechoice
variable: aws_region variable: create_vm_aws_region
required: true required: true
choices: choices:
- us-east-1 - us-east-1
@@ -183,7 +177,7 @@ controller_templates:
- AWS - AWS
- Demo Credential - Demo Credential
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_create_vm.yml playbook: playbooks/create_vm.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
@@ -249,6 +243,14 @@ controller_templates:
variable: create_vm_aws_keypair_name variable: create_vm_aws_keypair_name
required: true required: true
default: aws-test-key default: aws-test-key
- question_name: AWS Instance Type (defaults to blueprint value)
type: text
variable: create_vm_aws_instance_size
required: false
- question_name: AWS Image Filter (defaults to blueprint value)
type: text
variable: create_vm_aws_image_filter
required: false
- name: Cloud / AWS / Delete VM - name: Cloud / AWS / Delete VM
job_type: run job_type: run
@@ -257,14 +259,12 @@ controller_templates:
- AWS - AWS
- Demo Credential - Demo Credential
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_delete_inventory_vm.yml playbook: playbooks/delete_inventory_vm.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
notification_templates_error: Telemetry notification_templates_error: Telemetry
survey_enabled: true survey_enabled: true
extra_vars:
aws_region: us-east-1
survey: survey:
name: '' name: ''
description: '' description: ''
@@ -280,28 +280,14 @@ controller_templates:
credentials: credentials:
- AWS - AWS
project: Ansible Cloud Content Lab - AWS project: Ansible Cloud Content Lab - AWS
playbook: playbook_create_reports.yml playbook: playbooks/create_reports.yml
inventory: Demo Inventory inventory: Demo Inventory
notification_templates_started: Telemetry notification_templates_started: Telemetry
notification_templates_success: Telemetry notification_templates_success: Telemetry
notification_templates_error: Telemetry notification_templates_error: Telemetry
extra_vars: extra_vars:
aws_region: us-east-1
aws_report: vpc aws_report: vpc
reports_aws_bucket_name: reports-pd-{{ _deployment_id }}
- name: Cloud / AWS / Tags Report
job_type: run
organization: Default
credentials:
- AWS
project: Ansible Cloud Content Lab - AWS
playbook: playbook_create_reports.yml
inventory: Demo Inventory
notification_templates_started: Telemetry
notification_templates_success: Telemetry
notification_templates_error: Telemetry
extra_vars:
aws_report: tags
survey_enabled: true survey_enabled: true
survey: survey:
name: '' name: ''
@@ -309,7 +295,36 @@ controller_templates:
spec: spec:
- question_name: AWS Region - question_name: AWS Region
type: multiplechoice type: multiplechoice
variable: aws_region variable: create_vm_aws_region
required: true
choices:
- us-east-1
- us-east-2
- us-west-1
- us-west-2
- name: Cloud / AWS / Tags Report
job_type: run
organization: Default
credentials:
- AWS
project: Ansible Cloud Content Lab - AWS
playbook: playbooks/create_reports.yml
inventory: Demo Inventory
notification_templates_started: Telemetry
notification_templates_success: Telemetry
notification_templates_error: Telemetry
extra_vars:
aws_report: tags
reports_aws_bucket_name: reports-pd-{{ _deployment_id }}
survey_enabled: true
survey:
name: ''
description: ''
spec:
- question_name: AWS Region
type: multiplechoice
variable: create_vm_aws_region
required: true required: true
choices: choices:
- us-east-1 - us-east-1
@@ -335,7 +350,7 @@ controller_templates:
spec: spec:
- question_name: AWS Region - question_name: AWS Region
type: multiplechoice type: multiplechoice
variable: aws_region variable: create_vm_aws_region
required: true required: true
choices: choices:
- us-east-1 - us-east-1
@@ -356,6 +371,91 @@ controller_templates:
variable: aws_keypair_owner variable: aws_keypair_owner
required: true required: true
- name: Cloud / AWS / Snapshot EC2
job_type: run
organization: Default
credentials:
- AWS
project: Ansible official demo project
playbook: cloud/snapshot_ec2.yml
inventory: Demo Inventory
notification_templates_started: Telemetry
notification_templates_success: Telemetry
notification_templates_error: Telemetry
survey_enabled: true
survey:
name: ''
description: ''
spec:
- question_name: AWS Region
type: multiplechoice
variable: aws_region
required: true
default: us-east-1
choices:
- us-east-1
- us-east-2
- us-west-1
- us-west-2
- question_name: Specify target hosts
type: text
variable: _hosts
required: false
- name: Cloud / AWS / Restore EC2 from Snapshot
job_type: run
organization: Default
credentials:
- AWS
project: Ansible official demo project
playbook: cloud/restore_ec2.yml
inventory: Demo Inventory
notification_templates_started: Telemetry
notification_templates_success: Telemetry
notification_templates_error: Telemetry
survey_enabled: true
survey:
name: ''
description: ''
spec:
- question_name: AWS Region
type: multiplechoice
variable: aws_region
required: true
default: us-east-1
choices:
- us-east-1
- us-east-2
- us-west-1
- us-west-2
- question_name: Specify target hosts
type: text
variable: _hosts
required: false
- name: "LINUX / Patching"
job_type: check
inventory: "Demo Inventory"
project: "Ansible official demo project"
playbook: "linux/patching.yml"
execution_environment: Default execution environment
notification_templates_started: Telemetry
notification_templates_success: Telemetry
notification_templates_error: Telemetry
use_fact_cache: true
ask_job_type_on_launch: true
credentials:
- "Demo Credential"
survey_enabled: true
survey:
name: ''
description: ''
spec:
- question_name: Server Name or Pattern
type: text
variable: _hosts
required: true
controller_workflows: controller_workflows:
- name: Deploy Cloud Stack in AWS - name: Deploy Cloud Stack in AWS
description: A workflow to deploy a cloud stack description: A workflow to deploy a cloud stack
@@ -372,7 +472,7 @@ controller_workflows:
spec: spec:
- question_name: AWS Region - question_name: AWS Region
type: multiplechoice type: multiplechoice
variable: aws_region variable: create_vm_aws_region
required: true required: true
choices: choices:
- us-east-1 - us-east-1
@@ -381,7 +481,7 @@ controller_workflows:
- us-west-2 - us-west-2
- question_name: Owner - question_name: Owner
type: text type: text
variable: aws_owner_tag variable: create_vm_aws_owner_tag
required: true required: true
- question_name: Environment - question_name: Environment
type: multiplechoice type: multiplechoice
@@ -402,8 +502,6 @@ controller_workflows:
simplified_workflow_nodes: simplified_workflow_nodes:
- identifier: Create Keypair - identifier: Create Keypair
unified_job_template: Cloud / AWS / Create Keypair unified_job_template: Cloud / AWS / Create Keypair
extra_data:
aws_keypair_owner: !unsafe "{{ aws_owner_tag }}"
success_nodes: success_nodes:
- VPC Report - VPC Report
failure_nodes: failure_nodes:
@@ -421,16 +519,25 @@ controller_workflows:
- identifier: VPC Report - identifier: VPC Report
unified_job_template: Cloud / AWS / VPC Report unified_job_template: Cloud / AWS / VPC Report
all_parents_must_converge: true all_parents_must_converge: true
success_nodes: always_nodes:
- Deploy Windows Blueprint - Deploy Windows GUI Blueprint
- Deploy RHEL8 Blueprint - Deploy RHEL8 Blueprint
- Deploy RHEL9 Blueprint - Deploy RHEL9 Blueprint
- identifier: Deploy Windows Blueprint - Deploy Windows Core Blueprint
- identifier: Deploy Windows GUI Blueprint
unified_job_template: Cloud / AWS / Create VM unified_job_template: Cloud / AWS / Create VM
extra_data: extra_data:
vm_name: aws_win create_vm_vm_name: aws_dc
vm_blueprint: windows_full vm_blueprint: windows_full
vm_owner: !unsafe "{{ aws_owner_tag }}" success_nodes:
- Update Inventory
failure_nodes:
- Ticket - Instance Failed
- identifier: Deploy Windows Core Blueprint
unified_job_template: Cloud / AWS / Create VM
extra_data:
create_vm_vm_name: aws_win1
vm_blueprint: windows_core
success_nodes: success_nodes:
- Update Inventory - Update Inventory
failure_nodes: failure_nodes:
@@ -438,9 +545,8 @@ controller_workflows:
- identifier: Deploy RHEL8 Blueprint - identifier: Deploy RHEL8 Blueprint
unified_job_template: Cloud / AWS / Create VM unified_job_template: Cloud / AWS / Create VM
extra_data: extra_data:
vm_name: aws_rhel8 create_vm_vm_name: aws_rhel8
vm_blueprint: rhel8 vm_blueprint: rhel8
vm_owner: !unsafe "{{ aws_owner_tag }}"
success_nodes: success_nodes:
- Update Inventory - Update Inventory
failure_nodes: failure_nodes:
@@ -448,9 +554,8 @@ controller_workflows:
- identifier: Deploy RHEL9 Blueprint - identifier: Deploy RHEL9 Blueprint
unified_job_template: Cloud / AWS / Create VM unified_job_template: Cloud / AWS / Create VM
extra_data: extra_data:
vm_name: aws_rhel9 create_vm_vm_name: aws_rhel9
vm_blueprint: rhel9 vm_blueprint: rhel9
vm_owner: !unsafe "{{ aws_owner_tag }}"
success_nodes: success_nodes:
- Update Inventory - Update Inventory
failure_nodes: failure_nodes:
@@ -469,3 +574,56 @@ controller_workflows:
feedback: Failed to create AWS instance feedback: Failed to create AWS instance
- identifier: Tag Report - identifier: Tag Report
unified_job_template: Cloud / AWS / Tags Report unified_job_template: Cloud / AWS / Tags Report
- name: Cloud / AWS / Patch EC2 Workflow
description: A workflow to patch ec2 instances with snapshot and restore on failure.
organization: Default
notification_templates_started: Telemetry
notification_templates_success: Telemetry
notification_templates_error: Telemetry
survey_enabled: true
survey:
name: ''
description: ''
spec:
- question_name: AWS Region
type: multiplechoice
variable: aws_region
required: true
default: us-east-1
choices:
- us-east-1
- us-east-2
- us-west-1
- us-west-2
- question_name: Specify target hosts
type: text
variable: _hosts
required: true
default: os_linux
simplified_workflow_nodes:
- identifier: Project Sync
unified_job_template: Ansible official demo project
success_nodes:
- Take Snapshot
- identifier: Inventory Sync
unified_job_template: AWS Inventory
success_nodes:
- Take Snapshot
- identifier: Take Snapshot
unified_job_template: Cloud / AWS / Snapshot EC2
success_nodes:
- Patch Instance
- identifier: Patch Instance
unified_job_template: LINUX / Patching
job_type: run
failure_nodes:
- Restore from Snapshot
- identifier: Restore from Snapshot
unified_job_template: Cloud / AWS / Restore EC2 from Snapshot
failure_nodes:
- Ticket - Restore Failed
- identifier: Ticket - Restore Failed
unified_job_template: 'SUBMIT FEEDBACK'
extra_data:
feedback: Cloud / AWS / Patch EC2 Workflow | Failed to restore ec2 from snapshot

10
cloud/snapshot_ec2.yml Normal file
View File

@@ -0,0 +1,10 @@
---
- name: Snapshot ec2 instance
hosts: "{{ _hosts | default(omit) }}"
gather_facts: false
tasks:
- name: Include snapshot role
ansible.builtin.include_role:
name: "demo.cloud.aws"
tasks_from: snapshot_vm

View File

@@ -21,3 +21,4 @@ aws_env_tag: prod
aws_purpose_tag: ansible_demo aws_purpose_tag: ansible_demo
aws_ansiblegroup_tag: cloud aws_ansiblegroup_tag: cloud
aws_ec2_wait: true aws_ec2_wait: true
aws_snapshots: {}

View File

@@ -31,11 +31,11 @@
rules: rules:
- proto: tcp - proto: tcp
ports: ports:
- 80 # HTTP - 80 # HTTP
- 443 # HTTPS - 443 # HTTPS
- 22 # SSH - 22 # SSH
- 5986 # WinRM - 5986 # WinRM
- 3389 # RDP - 3389 # RDP
cidr_ip: 0.0.0.0/0 cidr_ip: 0.0.0.0/0
- proto: icmp - proto: icmp
to_port: -1 to_port: -1
@@ -43,32 +43,32 @@
cidr_ip: 0.0.0.0/0 cidr_ip: 0.0.0.0/0
- proto: tcp - proto: tcp
ports: ports:
- 80 # HTTP - 80 # HTTP
- 5986 # WinRM - 5986 # WinRM
- 3389 # RDP - 3389 # RDP
- 53 # DNS - 53 # DNS
- 88 # Kerberos Authentication - 88 # Kerberos Authentication
- 135 # RPC - 135 # RPC
- 139 # Netlogon - 139 # Netlogon
- 389 # LDAP - 389 # LDAP
- 445 # SMB - 445 # SMB
- 464 # Kerberos Authentication - 464 # Kerberos Authentication
- 5432 # PostgreSQL - 5432 # PostgreSQL
- 636 # LDAPS (LDAP over TLS) - 636 # LDAPS (LDAP over TLS)
- 873 # Rsync - 873 # Rsync
- 3268-3269 # Global Catalog - 3268-3269 # Global Catalog
- 1024-65535 # Ephemeral RPC ports - 1024-65535 # Ephemeral RPC ports
cidr_ip: 10.0.0.0/16 cidr_ip: 10.0.0.0/16
- proto: udp - proto: udp
ports: ports:
- 53 # DNS - 53 # DNS
- 88 # Kerberos Authentication - 88 # Kerberos Authentication
- 123 # NTP - 123 # NTP
- 137-138 # Netlogon - 137-138 # Netlogon
- 389 # LDAP - 389 # LDAP
- 445 # SMB - 445 # SMB
- 464 # Kerberos Authentication - 464 # Kerberos Authentication
- 1024-65535 # Ephemeral RPC ports - 1024-65535 # Ephemeral RPC ports
cidr_ip: 10.0.0.0/16 cidr_ip: 10.0.0.0/16
rules_egress: rules_egress:
- proto: -1 - proto: -1

View File

@@ -0,0 +1,62 @@
---
- name: AWS | RESTORE VM
delegate_to: localhost
block:
- name: AWS | RESTORE VM | stop vm
amazon.aws.ec2_instance:
region: "{{ aws_region }}"
instance_ids: "{{ instance_id }}"
state: stopped
wait: true
- name: AWS | RESTORE VM | get volumes
register: r_vol_info
amazon.aws.ec2_vol_info:
region: "{{ aws_region }}"
filters:
attachment.instance-id: "{{ instance_id }}"
- name: AWS | RESTORE VM | detach volumes
loop: "{{ r_vol_info.volumes }}"
loop_control:
loop_var: volume
label: "{{ volume.id }}"
amazon.aws.ec2_vol:
region: "{{ aws_region }}"
id: "{{ volume.id }}"
instance: None
- name: AWS | RESTORE VM | attach snapshots from stat
when: inventory_hostname in aws_snapshots
loop: "{{ aws_snapshots[inventory_hostname] }}"
loop_control:
loop_var: snap
label: "{{ snap.snapshot_id }}"
amazon.aws.ec2_vol:
region: "{{ aws_region }}"
instance: "{{ instance_id }}"
snapshot: "{{ snap.snapshot_id }}"
device_name: "{{ snap.device }}"
- name: AWS | RESTORE VM | get all snapshots
when: inventory_hostname not in aws_snapshots
register: r_snapshots
amazon.aws.ec2_snapshot_info:
region: "{{ aws_region }}"
filters:
"tag:Name": "{{ inventory_hostname }}"
- name: AWS | RESTORE VM | create volume from latest snapshot
when: inventory_hostname not in aws_snapshots
amazon.aws.ec2_vol:
region: "{{ aws_region }}"
instance: "{{ instance_id }}"
snapshot: "{{ r_snapshots.snapshots[0].snapshot_id }}"
device_name: "/dev/sda1"
- name: AWS | RESTORE VM | start vm
amazon.aws.ec2_instance:
region: "{{ aws_region }}"
instance_ids: "{{ instance_id }}"
state: started
wait: true

View File

@@ -0,0 +1,42 @@
---
- name: AWS | SNAPSHOT VM
delegate_to: localhost
block:
- name: AWS | SNAPSHOT VM | assert id
ansible.builtin.assert:
that: instance_id is defined
fail_msg: "instance_id is required for snapshot operations"
- name: AWS | SNAPSHOT VM | include vars
ansible.builtin.include_vars:
file: snapshot_vm.yml
- name: AWS | SNAPSHOT VM | get volumes
register: r_vol_info
amazon.aws.ec2_vol_info:
region: "{{ aws_region }}"
filters:
attachment.instance-id: "{{ instance_id }}"
- name: AWS | SNAPSHOT VM | take snapshots
loop: "{{ r_vol_info.volumes }}"
loop_control:
loop_var: volume
label: "{{ volume.id }}"
register: r_snapshots
amazon.aws.ec2_snapshot:
region: "{{ aws_region }}"
volume_id: "{{ volume.id }}"
description: "Snapshot taken by Red Hat Product demos"
snapshot_tags: "{{ tags }}"
- name: AWS | SNAPSHOT VM | format snapshot stat
ansible.builtin.set_fact:
snapshot_stat:
- key: "{{ inventory_hostname }}"
value: "{{ r_snapshots.results | json_query(aws_ec2_snapshot_query) }}"
- name: AWS | SNAPSHOT VM | record snapshot with host key
ansible.builtin.set_stats:
data:
aws_snapshots: "{{ snapshot_stat | items2dict }}"

View File

@@ -0,0 +1,11 @@
---
# Set stat_snapshots with model:
# [
# {
# "snapshot_id": "snap-0e981f05704e19ffd",
# "vol_id": "vol-0bd55f313bb7bcdd8",
# "device": "/dev/sda1"
# },
# ...
# ]
aws_ec2_snapshot_query: "[].{snapshot_id: snapshot_id, vol_id: volume.id, device: volume.attachment_set[?instance_id=='{{ instance_id }}'].device | [0]}"

View File

@@ -1,4 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
@@ -14,61 +15,65 @@ import xml.dom.minidom
role = "iosxeSTIG" role = "iosxeSTIG"
class CallbackModule(CallbackBase): class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0 CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'xml' CALLBACK_TYPE = "xml"
CALLBACK_NAME = 'stig_xml' CALLBACK_NAME = "stig_xml"
CALLBACK_NEEDS_WHITELIST = True CALLBACK_NEEDS_WHITELIST = True
def __init__(self): def __init__(self):
super(CallbackModule, self).__init__() super(CallbackModule, self).__init__()
self.rules = {} self.rules = {}
self.stig_path = os.environ.get('STIG_PATH') self.stig_path = os.environ.get("STIG_PATH")
self.XML_path = os.environ.get('XML_PATH') self.XML_path = os.environ.get("XML_PATH")
if self.stig_path is None: if self.stig_path is None:
self.stig_path = os.path.join(os.getcwd(), "roles", role, "files") self.stig_path = os.path.join(os.getcwd(), "roles", role, "files")
self._display.display('Using STIG_PATH: {}'.format(self.stig_path)) self._display.display("Using STIG_PATH: {}".format(self.stig_path))
if self.XML_path is None: if self.XML_path is None:
self.XML_path = os.getcwd() self.XML_path = os.getcwd()
self._display.display('Using XML_PATH: {}'.format(self.XML_path)) self._display.display("Using XML_PATH: {}".format(self.XML_path))
print("Writing: {}".format(self.XML_path)) print("Writing: {}".format(self.XML_path))
STIG_name = os.path.basename(self.stig_path) STIG_name = os.path.basename(self.stig_path)
ET.register_namespace('cdf', 'http://checklists.nist.gov/xccdf/1.2') ET.register_namespace("cdf", "http://checklists.nist.gov/xccdf/1.2")
self.tr = ET.Element('{http://checklists.nist.gov/xccdf/1.2}TestResult') self.tr = ET.Element("{http://checklists.nist.gov/xccdf/1.2}TestResult")
self.tr.set('id', 'xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}'.format(STIG_name)) self.tr.set(
"id",
"xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}".format(STIG_name),
)
endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime()) endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime())
self.tr.set('end-time', endtime) self.tr.set("end-time", endtime)
tg = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}target') tg = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}target")
tg.text = platform.node() tg.text = platform.node()
def __get_rev(self, nid): def __get_rev(self, nid):
rev = '0' rev = "0"
# Check all files for the rule number. # Check all files for the rule number.
for file in os.listdir(self.stig_path): for file in os.listdir(self.stig_path):
with open(os.path.join(self.stig_path, file), 'r') as f: with open(os.path.join(self.stig_path, file), "r") as f:
r = 'SV-{}r(?P<rev>\d)_rule'.format(nid) r = "SV-{}r(?P<rev>\d)_rule".format(nid)
m = re.search(r, f.read()) m = re.search(r, f.read())
if m: if m:
rev = m.group('rev') rev = m.group("rev")
break break
return rev return rev
def v2_runner_on_ok(self, result): def v2_runner_on_ok(self, result):
name = result._task.get_name() name = result._task.get_name()
m = re.search('stigrule_(?P<id>\d+)', name) m = re.search("stigrule_(?P<id>\d+)", name)
if m: if m:
nid = m.group('id') nid = m.group("id")
else: else:
return return
rev = self.__get_rev(nid) rev = self.__get_rev(nid)
key = "{}r{}".format(nid, rev) key = "{}r{}".format(nid, rev)
if self.rules.get(key, 'Unknown') != False: if self.rules.get(key, "Unknown") != False:
self.rules[key] = result.is_changed() self.rules[key] = result.is_changed()
def __set_duplicates(self): def __set_duplicates(self):
with open(os.path.join(self.stig_path, 'duplicates.json')) as f: with open(os.path.join(self.stig_path, "duplicates.json")) as f:
dups = json.load(f) dups = json.load(f)
for d in dups: for d in dups:
dup_of = str(dups[d][0]) dup_of = str(dups[d][0])
@@ -82,17 +87,19 @@ class CallbackModule(CallbackBase):
def v2_playbook_on_stats(self, stats): def v2_playbook_on_stats(self, stats):
self.__set_duplicates() self.__set_duplicates()
for rule, changed in self.rules.items(): for rule, changed in self.rules.items():
state = 'fail' if changed else 'pass' state = "fail" if changed else "pass"
rr = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}rule-result') rr = ET.SubElement(
rr.set('idref', 'xccdf_mil.disa.stig_rule_SV-{}_rule'.format(rule)) self.tr, "{http://checklists.nist.gov/xccdf/1.2}rule-result"
rs = ET.SubElement(rr, '{http://checklists.nist.gov/xccdf/1.2}result') )
rr.set("idref", "xccdf_mil.disa.stig_rule_SV-{}_rule".format(rule))
rs = ET.SubElement(rr, "{http://checklists.nist.gov/xccdf/1.2}result")
rs.text = state rs.text = state
passing = len(self.rules) - sum(self.rules.values()) passing = len(self.rules) - sum(self.rules.values())
sc = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}score') sc = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}score")
sc.set('maximum', str(len(self.rules))) sc.set("maximum", str(len(self.rules)))
sc.set('system', 'urn:xccdf:scoring:flat-unweighted') sc.set("system", "urn:xccdf:scoring:flat-unweighted")
sc.text = str(passing) sc.text = str(passing)
with open(os.path.join(self.XML_path, "xccdf-results.xml"), 'w') as f: with open(os.path.join(self.XML_path, "xccdf-results.xml"), "w") as f:
out = ET.tostring(self.tr) out = ET.tostring(self.tr)
pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding='utf-8') pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding="utf-8")
f.write(pretty) f.write(pretty)

View File

@@ -1,4 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
@@ -11,76 +12,82 @@ import os
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import xml.dom.minidom import xml.dom.minidom
class CallbackModule(CallbackBase): class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0 CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'xml' CALLBACK_TYPE = "xml"
CALLBACK_NAME = 'stig_xml' CALLBACK_NAME = "stig_xml"
CALLBACK_NEEDS_WHITELIST = True CALLBACK_NEEDS_WHITELIST = True
def _get_STIG_path(self): def _get_STIG_path(self):
cwd = os.path.abspath('.') cwd = os.path.abspath(".")
for dirpath, dirs, files in os.walk(cwd): for dirpath, dirs, files in os.walk(cwd):
if os.path.sep + 'files' in dirpath and '.xml' in files[0]: if os.path.sep + "files" in dirpath and ".xml" in files[0]:
return os.path.join(cwd, dirpath, files[0]) return os.path.join(cwd, dirpath, files[0])
def __init__(self): def __init__(self):
super(CallbackModule, self).__init__() super(CallbackModule, self).__init__()
self.rules = {} self.rules = {}
self.stig_path = os.environ.get('STIG_PATH') self.stig_path = os.environ.get("STIG_PATH")
self.XML_path = os.environ.get('XML_PATH') self.XML_path = os.environ.get("XML_PATH")
if self.stig_path is None: if self.stig_path is None:
self.stig_path = self._get_STIG_path() self.stig_path = self._get_STIG_path()
self._display.display('Using STIG_PATH: {}'.format(self.stig_path)) self._display.display("Using STIG_PATH: {}".format(self.stig_path))
if self.XML_path is None: if self.XML_path is None:
self.XML_path = tempfile.mkdtemp() + "/xccdf-results.xml" self.XML_path = tempfile.mkdtemp() + "/xccdf-results.xml"
self._display.display('Using XML_PATH: {}'.format(self.XML_path)) self._display.display("Using XML_PATH: {}".format(self.XML_path))
print("Writing: {}".format(self.XML_path)) print("Writing: {}".format(self.XML_path))
STIG_name = os.path.basename(self.stig_path) STIG_name = os.path.basename(self.stig_path)
ET.register_namespace('cdf', 'http://checklists.nist.gov/xccdf/1.2') ET.register_namespace("cdf", "http://checklists.nist.gov/xccdf/1.2")
self.tr = ET.Element('{http://checklists.nist.gov/xccdf/1.2}TestResult') self.tr = ET.Element("{http://checklists.nist.gov/xccdf/1.2}TestResult")
self.tr.set('id', 'xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}'.format(STIG_name)) self.tr.set(
"id",
"xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}".format(STIG_name),
)
endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime()) endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime())
self.tr.set('end-time', endtime) self.tr.set("end-time", endtime)
tg = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}target') tg = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}target")
tg.text = platform.node() tg.text = platform.node()
def _get_rev(self, nid): def _get_rev(self, nid):
with open(self.stig_path, 'r') as f: with open(self.stig_path, "r") as f:
r = 'SV-{}r(?P<rev>\d+)_rule'.format(nid) r = "SV-{}r(?P<rev>\d+)_rule".format(nid)
m = re.search(r, f.read()) m = re.search(r, f.read())
if m: if m:
rev = m.group('rev') rev = m.group("rev")
else: else:
rev = '0' rev = "0"
return rev return rev
def v2_runner_on_ok(self, result): def v2_runner_on_ok(self, result):
name = result._task.get_name() name = result._task.get_name()
m = re.search('stigrule_(?P<id>\d+)', name) m = re.search("stigrule_(?P<id>\d+)", name)
if m: if m:
nid = m.group('id') nid = m.group("id")
else: else:
return return
rev = self._get_rev(nid) rev = self._get_rev(nid)
key = "{}r{}".format(nid, rev) key = "{}r{}".format(nid, rev)
if self.rules.get(key, 'Unknown') != False: if self.rules.get(key, "Unknown") != False:
self.rules[key] = result.is_changed() self.rules[key] = result.is_changed()
def v2_playbook_on_stats(self, stats): def v2_playbook_on_stats(self, stats):
for rule, changed in self.rules.items(): for rule, changed in self.rules.items():
state = 'fail' if changed else 'pass' state = "fail" if changed else "pass"
rr = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}rule-result') rr = ET.SubElement(
rr.set('idref', 'xccdf_mil.disa.stig_rule_SV-{}_rule'.format(rule)) self.tr, "{http://checklists.nist.gov/xccdf/1.2}rule-result"
rs = ET.SubElement(rr, '{http://checklists.nist.gov/xccdf/1.2}result') )
rr.set("idref", "xccdf_mil.disa.stig_rule_SV-{}_rule".format(rule))
rs = ET.SubElement(rr, "{http://checklists.nist.gov/xccdf/1.2}result")
rs.text = state rs.text = state
passing = len(self.rules) - sum(self.rules.values()) passing = len(self.rules) - sum(self.rules.values())
sc = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}score') sc = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}score")
sc.set('maximum', str(len(self.rules))) sc.set("maximum", str(len(self.rules)))
sc.set('system', 'urn:xccdf:scoring:flat-unweighted') sc.set("system", "urn:xccdf:scoring:flat-unweighted")
sc.text = str(passing) sc.text = str(passing)
with open(self.XML_path, 'wb') as f: with open(self.XML_path, "wb") as f:
out = ET.tostring(self.tr) out = ET.tostring(self.tr)
pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding='utf-8') pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding="utf-8")
f.write(pretty) f.write(pretty)

View File

@@ -1,4 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
@@ -11,76 +12,82 @@ import os
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import xml.dom.minidom import xml.dom.minidom
class CallbackModule(CallbackBase): class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0 CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'xml' CALLBACK_TYPE = "xml"
CALLBACK_NAME = 'stig_xml' CALLBACK_NAME = "stig_xml"
CALLBACK_NEEDS_WHITELIST = True CALLBACK_NEEDS_WHITELIST = True
def _get_STIG_path(self): def _get_STIG_path(self):
cwd = os.path.abspath('.') cwd = os.path.abspath(".")
for dirpath, dirs, files in os.walk(cwd): for dirpath, dirs, files in os.walk(cwd):
if os.path.sep + 'files' in dirpath and '.xml' in files[0]: if os.path.sep + "files" in dirpath and ".xml" in files[0]:
return os.path.join(cwd, dirpath, files[0]) return os.path.join(cwd, dirpath, files[0])
def __init__(self): def __init__(self):
super(CallbackModule, self).__init__() super(CallbackModule, self).__init__()
self.rules = {} self.rules = {}
self.stig_path = os.environ.get('STIG_PATH') self.stig_path = os.environ.get("STIG_PATH")
self.XML_path = os.environ.get('XML_PATH') self.XML_path = os.environ.get("XML_PATH")
if self.stig_path is None: if self.stig_path is None:
self.stig_path = self._get_STIG_path() self.stig_path = self._get_STIG_path()
self._display.display('Using STIG_PATH: {}'.format(self.stig_path)) self._display.display("Using STIG_PATH: {}".format(self.stig_path))
if self.XML_path is None: if self.XML_path is None:
self.XML_path = tempfile.mkdtemp() + "/xccdf-results.xml" self.XML_path = tempfile.mkdtemp() + "/xccdf-results.xml"
self._display.display('Using XML_PATH: {}'.format(self.XML_path)) self._display.display("Using XML_PATH: {}".format(self.XML_path))
print("Writing: {}".format(self.XML_path)) print("Writing: {}".format(self.XML_path))
STIG_name = os.path.basename(self.stig_path) STIG_name = os.path.basename(self.stig_path)
ET.register_namespace('cdf', 'http://checklists.nist.gov/xccdf/1.2') ET.register_namespace("cdf", "http://checklists.nist.gov/xccdf/1.2")
self.tr = ET.Element('{http://checklists.nist.gov/xccdf/1.2}TestResult') self.tr = ET.Element("{http://checklists.nist.gov/xccdf/1.2}TestResult")
self.tr.set('id', 'xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}'.format(STIG_name)) self.tr.set(
"id",
"xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}".format(STIG_name),
)
endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime()) endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime())
self.tr.set('end-time', endtime) self.tr.set("end-time", endtime)
tg = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}target') tg = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}target")
tg.text = platform.node() tg.text = platform.node()
def _get_rev(self, nid): def _get_rev(self, nid):
with open(self.stig_path, 'r') as f: with open(self.stig_path, "r") as f:
r = 'SV-{}r(?P<rev>\d+)_rule'.format(nid) r = "SV-{}r(?P<rev>\d+)_rule".format(nid)
m = re.search(r, f.read()) m = re.search(r, f.read())
if m: if m:
rev = m.group('rev') rev = m.group("rev")
else: else:
rev = '0' rev = "0"
return rev return rev
def v2_runner_on_ok(self, result): def v2_runner_on_ok(self, result):
name = result._task.get_name() name = result._task.get_name()
m = re.search('stigrule_(?P<id>\d+)', name) m = re.search("stigrule_(?P<id>\d+)", name)
if m: if m:
nid = m.group('id') nid = m.group("id")
else: else:
return return
rev = self._get_rev(nid) rev = self._get_rev(nid)
key = "{}r{}".format(nid, rev) key = "{}r{}".format(nid, rev)
if self.rules.get(key, 'Unknown') != False: if self.rules.get(key, "Unknown") != False:
self.rules[key] = result.is_changed() self.rules[key] = result.is_changed()
def v2_playbook_on_stats(self, stats): def v2_playbook_on_stats(self, stats):
for rule, changed in self.rules.items(): for rule, changed in self.rules.items():
state = 'fail' if changed else 'pass' state = "fail" if changed else "pass"
rr = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}rule-result') rr = ET.SubElement(
rr.set('idref', 'xccdf_mil.disa.stig_rule_SV-{}_rule'.format(rule)) self.tr, "{http://checklists.nist.gov/xccdf/1.2}rule-result"
rs = ET.SubElement(rr, '{http://checklists.nist.gov/xccdf/1.2}result') )
rr.set("idref", "xccdf_mil.disa.stig_rule_SV-{}_rule".format(rule))
rs = ET.SubElement(rr, "{http://checklists.nist.gov/xccdf/1.2}result")
rs.text = state rs.text = state
passing = len(self.rules) - sum(self.rules.values()) passing = len(self.rules) - sum(self.rules.values())
sc = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}score') sc = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}score")
sc.set('maximum', str(len(self.rules))) sc.set("maximum", str(len(self.rules)))
sc.set('system', 'urn:xccdf:scoring:flat-unweighted') sc.set("system", "urn:xccdf:scoring:flat-unweighted")
sc.text = str(passing) sc.text = str(passing)
with open(self.XML_path, 'wb') as f: with open(self.XML_path, "wb") as f:
out = ET.tostring(self.tr) out = ET.tostring(self.tr)
pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding='utf-8') pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding="utf-8")
f.write(pretty) f.write(pretty)

View File

@@ -1,4 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
@@ -11,76 +12,82 @@ import os
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import xml.dom.minidom import xml.dom.minidom
class CallbackModule(CallbackBase): class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0 CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'xml' CALLBACK_TYPE = "xml"
CALLBACK_NAME = 'stig_xml' CALLBACK_NAME = "stig_xml"
CALLBACK_NEEDS_WHITELIST = True CALLBACK_NEEDS_WHITELIST = True
def _get_STIG_path(self): def _get_STIG_path(self):
cwd = os.path.abspath('.') cwd = os.path.abspath(".")
for dirpath, dirs, files in os.walk(cwd): for dirpath, dirs, files in os.walk(cwd):
if os.path.sep + 'files' in dirpath and '.xml' in files[0]: if os.path.sep + "files" in dirpath and ".xml" in files[0]:
return os.path.join(cwd, dirpath, files[0]) return os.path.join(cwd, dirpath, files[0])
def __init__(self): def __init__(self):
super(CallbackModule, self).__init__() super(CallbackModule, self).__init__()
self.rules = {} self.rules = {}
self.stig_path = os.environ.get('STIG_PATH') self.stig_path = os.environ.get("STIG_PATH")
self.XML_path = os.environ.get('XML_PATH') self.XML_path = os.environ.get("XML_PATH")
if self.stig_path is None: if self.stig_path is None:
self.stig_path = self._get_STIG_path() self.stig_path = self._get_STIG_path()
self._display.display('Using STIG_PATH: {}'.format(self.stig_path)) self._display.display("Using STIG_PATH: {}".format(self.stig_path))
if self.XML_path is None: if self.XML_path is None:
self.XML_path = tempfile.mkdtemp() + "/xccdf-results.xml" self.XML_path = tempfile.mkdtemp() + "/xccdf-results.xml"
self._display.display('Using XML_PATH: {}'.format(self.XML_path)) self._display.display("Using XML_PATH: {}".format(self.XML_path))
print("Writing: {}".format(self.XML_path)) print("Writing: {}".format(self.XML_path))
STIG_name = os.path.basename(self.stig_path) STIG_name = os.path.basename(self.stig_path)
ET.register_namespace('cdf', 'http://checklists.nist.gov/xccdf/1.2') ET.register_namespace("cdf", "http://checklists.nist.gov/xccdf/1.2")
self.tr = ET.Element('{http://checklists.nist.gov/xccdf/1.2}TestResult') self.tr = ET.Element("{http://checklists.nist.gov/xccdf/1.2}TestResult")
self.tr.set('id', 'xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}'.format(STIG_name)) self.tr.set(
"id",
"xccdf_mil.disa.stig_testresult_scap_mil.disa_comp_{}".format(STIG_name),
)
endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime()) endtime = strftime("%Y-%m-%dT%H:%M:%S", gmtime())
self.tr.set('end-time', endtime) self.tr.set("end-time", endtime)
tg = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}target') tg = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}target")
tg.text = platform.node() tg.text = platform.node()
def _get_rev(self, nid): def _get_rev(self, nid):
with open(self.stig_path, 'r') as f: with open(self.stig_path, "r") as f:
r = 'SV-{}r(?P<rev>\d+)_rule'.format(nid) r = "SV-{}r(?P<rev>\d+)_rule".format(nid)
m = re.search(r, f.read()) m = re.search(r, f.read())
if m: if m:
rev = m.group('rev') rev = m.group("rev")
else: else:
rev = '0' rev = "0"
return rev return rev
def v2_runner_on_ok(self, result): def v2_runner_on_ok(self, result):
name = result._task.get_name() name = result._task.get_name()
m = re.search('stigrule_(?P<id>\d+)', name) m = re.search("stigrule_(?P<id>\d+)", name)
if m: if m:
nid = m.group('id') nid = m.group("id")
else: else:
return return
rev = self._get_rev(nid) rev = self._get_rev(nid)
key = "{}r{}".format(nid, rev) key = "{}r{}".format(nid, rev)
if self.rules.get(key, 'Unknown') != False: if self.rules.get(key, "Unknown") != False:
self.rules[key] = result.is_changed() self.rules[key] = result.is_changed()
def v2_playbook_on_stats(self, stats): def v2_playbook_on_stats(self, stats):
for rule, changed in self.rules.items(): for rule, changed in self.rules.items():
state = 'fail' if changed else 'pass' state = "fail" if changed else "pass"
rr = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}rule-result') rr = ET.SubElement(
rr.set('idref', 'xccdf_mil.disa.stig_rule_SV-{}_rule'.format(rule)) self.tr, "{http://checklists.nist.gov/xccdf/1.2}rule-result"
rs = ET.SubElement(rr, '{http://checklists.nist.gov/xccdf/1.2}result') )
rr.set("idref", "xccdf_mil.disa.stig_rule_SV-{}_rule".format(rule))
rs = ET.SubElement(rr, "{http://checklists.nist.gov/xccdf/1.2}result")
rs.text = state rs.text = state
passing = len(self.rules) - sum(self.rules.values()) passing = len(self.rules) - sum(self.rules.values())
sc = ET.SubElement(self.tr, '{http://checklists.nist.gov/xccdf/1.2}score') sc = ET.SubElement(self.tr, "{http://checklists.nist.gov/xccdf/1.2}score")
sc.set('maximum', str(len(self.rules))) sc.set("maximum", str(len(self.rules)))
sc.set('system', 'urn:xccdf:scoring:flat-unweighted') sc.set("system", "urn:xccdf:scoring:flat-unweighted")
sc.text = str(passing) sc.text = str(passing)
with open(self.XML_path, 'wb') as f: with open(self.XML_path, "wb") as f:
out = ET.tostring(self.tr) out = ET.tostring(self.tr)
pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding='utf-8') pretty = xml.dom.minidom.parseString(out).toprettyxml(encoding="utf-8")
f.write(pretty) f.write(pretty)

View File

@@ -0,0 +1,131 @@
Role Name
=========
This Ansible role helps configure Operators on the Openshift Cluster to support VM migrations. Tasks include
- Configure Catalog Sources to use mirroring repository for Operators
- Create and configure Operators
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
The task `operators/catalog_sources.yml` needs following variables:
- **Variable Name**: `cluster_config_catalog_sources`
- **Type**: List
- **Description**: A list of custom CatalogSources configurations used as loop variables to generate Kubernetes manifest files from the template `catalog_source.j2` for CatalogSource. If the variable is not available, no manifest is created.
- **Example**:
```yaml
cluster_config_catalog_sources:
- name: redhat-marketplace2
source_type: grpc
display_name: Mirror to Red Hat Marketplace
image_path: internal-registry.example.com/operator:v1
priority: '-300'
icon:
base64data: ''
mediatype: ''
publisher: redhat
address: ''
grpc_pod_config: |
nodeSelector:
kubernetes.io/os: linux
node-role.kubernetes.io/master: ''
priorityClassName: system-cluster-critical
securityContextConfig: restricted
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 120
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 120
registry_poll_interval: 10m
```
The task `operators/operator_config.yaml` needs following variables:
- **Variable Name**: `cluster_config_operators`
- **Type**: List
- **Description**: A list of operators to be installed on OCP cluster
- **Variable Name**: `cluster_config_[OPERATOR_NAME]`
- **Type**: Dict
- **Description**: Configuration specific to each operator listed in `cluster_config_operators`. Includes settings for namespace, operator group, subscription, and any extra resources
- **Example**: Assume the `cluster_config_operators` specifies these operators:
```yaml
cluster_config_operators:
- cnv
- oadp
```
then the corresponding `cluster_config_mtv` and `cluster_config_cnv` can be configured as following:
```yaml
cluster_config_cnv_namespace: openshift-cnv
cluster_config_cnv:
namespace:
name: "{{ cluster_config_cnv_namespace }}"
operator_group:
name: kubevirt-hyperconverged-group
target_namespaces:
- "{{ cluster_config_cnv_namespace }}"
subscription:
name: kubevirt-hyperconverged
starting_csv: kubevirt-hyperconverged-operator.v4.13.8
extra_resources:
- apiVersion: hco.kubevirt.io/v1beta1
kind: HyperConverged
metadata:
name: kubevirt-hyperconverged
namespace: "{{ cluster_config_cnv_namespace }}"
spec:
BareMetalPlatform: true
cluster_config_oadp_namespace: openshift-adp
cluster_config_oadp:
namespace:
name: "{{ cluster_config_oadp_namespace }}"
operator_group:
name: redhat-oadp-operator-group
target_namespaces:
- "{{ cluster_config_oadp_namespace }}"
subscription:
name: redhat-oadp-operator-subscription
spec_name: redhat-oadp-operator
```
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
An example of configuring a CatalogSource resource:
```
- name: Configure Catalog Sources for Operators
hosts: localhost
gather_facts: false
tasks:
- ansible.builtin.include_role:
name: cluster_config
tasks_from: operators/catalog_sources
```
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View File

@@ -0,0 +1,23 @@
---
# defaults file for cluster_config
cluster_config_operators:
- cnv
cluster_config_cnv:
checkplan: true
namespace:
name: &cluster_config_cnv_namespace openshift-cnv
operator_group:
name: kubevirt-hyperconverged-group
target_namespaces:
- *cluster_config_cnv_namespace
subscription:
name: kubevirt-hyperconverged
extra_resources:
- apiVersion: hco.kubevirt.io/v1beta1
kind: HyperConverged
metadata:
name: kubevirt-hyperconverged
namespace: *cluster_config_cnv_namespace
spec:
BareMetalPlatform: true

View File

@@ -0,0 +1,2 @@
---
# handlers file for cluster_config

View File

@@ -0,0 +1,3 @@
---
- name: Configure Operators
ansible.builtin.import_tasks: operators/operator_config.yml

View File

@@ -0,0 +1,37 @@
---
- name: Retrieve Operator name
ansible.builtin.set_fact:
_operator: "{{ vars['cluster_config_' + _operator_name] }}"
- name: Configure Operator {{ _operator_name }}
redhat.openshift.k8s:
state: present
template:
- operators/namespace.yml.j2
- operators/operator_group.yml.j2
- operators/subscription.yml.j2
- name: Query for install plan
kubernetes.core.k8s_info:
api_version: operators.coreos.com/v1alpha1
kind: InstallPlan
namespace: "{{ _operator.namespace.name }}"
register: r_install_plans
retries: 30
delay: 5
until:
- r_install_plans.resources | default([]) | length > 0
- r_install_plans.resources[0].status is defined
- r_install_plans.resources[0].status.phase == "Complete"
when:
- _operator.checkplan is defined
- _operator.checkplan | bool
- name: Configure extra resources for Operator {{ _operator_name }}
redhat.openshift.k8s:
state: present
definition: "{{ item }}"
register: creation_result
loop: "{{ _operator.extra_resources }}"
retries: 30
delay: 5
until: creation_result is success
when: _operator.extra_resources is defined

View File

@@ -0,0 +1,7 @@
---
- name: Configure custom CatalogSource for Operators
redhat.openshift.k8s:
state: present
template: operators/catalog_source.j2
loop: "{{ cluster_config_catalog_sources }}"
when: cluster_config_catalog_sources is defined

View File

@@ -0,0 +1,59 @@
---
- name: Create node-health-check operator namespace
redhat.openshift.k8s:
name: openshift-workload-availability
api_version: v1
kind: Namespace
state: present
- name: Create node-health-check operator group
redhat.openshift.k8s:
state: present
definition:
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
generateName: openshift-workload-availability-
annotations:
olm.providedAPIs: >-
NodeHealthCheck.v1alpha1.remediation.medik8s.io,SelfNodeRemediation.v1alpha1.self-node-remediation.medik8s.io,SelfNodeRemediationConfig.v1alpha1.self-node-remediation.medik8s.io,SelfNodeRemediationTemplate.v1alpha1.self-node-remediation.medik8s.io
namespace: openshift-workload-availability
spec:
upgradeStrategy: Default
- name: Create node-health-check operator subscription
redhat.openshift.k8s:
state: present
definition:
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
labels:
operators.coreos.com/node-healthcheck-operator.openshift-workload-availability: ''
name: node-health-check-operator
namespace: openshift-workload-availability
spec:
channel: stable
installPlanApproval: Automatic
name: node-healthcheck-operator
source: redhat-operators
sourceNamespace: openshift-marketplace
- name: Create Self Node Remediation subscription
redhat.openshift.k8s:
state: present
definition:
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: self-node-remediation-stable-redhat-operators-openshift-marketplace
namespace: openshift-workload-availability
labels:
operators.coreos.com/self-node-remediation.openshift-workload-availability: ''
spec:
channel: stable
installPlanApproval: Automatic
name: self-node-remediation
source: redhat-operators
sourceNamespace: openshift-marketplace
startingCSV: self-node-remediation.v0.8.0

View File

@@ -0,0 +1,6 @@
---
- name: Configure Operators
ansible.builtin.include_tasks: _operator_config_item.yml
loop: "{{ cluster_config_operators }}"
loop_control:
loop_var: _operator_name

View File

@@ -0,0 +1,34 @@
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
name: {{ item.name }}
namespace: openshift-marketplace
spec:
sourceType: {{ item.source_type | d('grpc',true) }}
image: {{ item.image_path }}
{% if item.display_name is defined -%}
displayName: {{ item.display_name }}
{% endif -%}
{% if item.priority is defined -%}
priority: {{ item.priority }}
{% endif -%}
{% if item.grpc_pod_config is defined -%}
grpcPodConfig:
{{ item.grpc_pod_config | indent(4) }}
{% endif -%}
{% if item.icon is defined -%}
icon:
base64data: '{{ item.icon.base64data or '' }}'
mediatype: '{{ item.icon.mediatype or '' }}'
{% endif -%}
{% if item.publisher is defined -%}
publisher: {{ item.publisher }}
{% endif -%}
{% if item.address is defined -%}
address: {{ item.address }}
{% endif -%}
{% if item.registry_poll_interval is defined -%}
updateStrategy:
registryPoll:
interval: {{ item.registry_poll_interval }}
{% endif -%}

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ _operator.namespace.name }}
{% if _operator.namespace.labels is defined %}
labels:
{% for key, value in _operator.namespace.labels.items() -%}
{{ key }}: "{{ value }}"
{% endfor -%}
{% endif -%}

View File

@@ -0,0 +1,12 @@
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: {{ _operator.operator_group.name }}
namespace: {{ _operator.operator_group.namespace | d(_operator.namespace.name, true) }}
spec:
{% if _operator.operator_group.target_namespaces is defined -%}
targetNamespaces:
{% for item in _operator.operator_group.target_namespaces %}
- {{ item }}
{% endfor %}
{% endif -%}

View File

@@ -0,0 +1,14 @@
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: {{ _operator.subscription.name }}
namespace: "{{ _operator.subscription.namespace | d(_operator.namespace.name, true) }}"
spec:
channel: {{ _operator.subscription.channel | d('stable', true) }}
installPlanApproval: {{ _operator.subscription.install_plan_approval | d('Automatic', true) }}
name: {{ _operator.subscription.spec_name | d(_operator.subscription.name, true) }}
source: {{ _operator.subscription.source | d('redhat-operators', true) }}
sourceNamespace: {{ _operator.subscription.source_namespace | d('openshift-marketplace', true) }}
{% if _operator.subscription.starting_csv is defined %}
startingCSV: {{ _operator.subscription.starting_csv }}
{% endif -%}

View File

@@ -0,0 +1,6 @@
---
- name: Include cluster_config role
hosts: localhost
remote_user: root
roles:
- cluster_config

View File

@@ -0,0 +1,2 @@
---
# vars file for cluster_config

View File

@@ -1,16 +1,16 @@
#!/usr/bin/env python #!/usr/bin/env python
from ansible.module_utils.basic import * # noqa from ansible.module_utils.basic import * # noqa
DOCUMENTATION = ''' DOCUMENTATION = """
--- ---
module: scan_packages module: scan_packages
short_description: Return installed packages information as fact data short_description: Return installed packages information as fact data
description: description:
- Return information about installed packages as fact data - Return information about installed packages as fact data
''' """
EXAMPLES = ''' EXAMPLES = """
# Example fact output: # Example fact output:
# host | success >> { # host | success >> {
# "ansible_facts": { # "ansible_facts": {
@@ -34,21 +34,23 @@ EXAMPLES = '''
# "name": "gcc-4.8-base" # "name": "gcc-4.8-base"
# } # }
# ] # ]
''' """
def rpm_package_list(): def rpm_package_list():
import rpm import rpm
trans_set = rpm.TransactionSet() trans_set = rpm.TransactionSet()
installed_packages = [] installed_packages = []
for package in trans_set.dbMatch(): for package in trans_set.dbMatch():
package_details = { package_details = {
'name':package[rpm.RPMTAG_NAME], "name": package[rpm.RPMTAG_NAME],
'version':package[rpm.RPMTAG_VERSION], "version": package[rpm.RPMTAG_VERSION],
'release':package[rpm.RPMTAG_RELEASE], "release": package[rpm.RPMTAG_RELEASE],
'epoch':package[rpm.RPMTAG_EPOCH], "epoch": package[rpm.RPMTAG_EPOCH],
'arch':package[rpm.RPMTAG_ARCH], "arch": package[rpm.RPMTAG_ARCH],
'source':'rpm' } "source": "rpm",
}
if installed_packages == []: if installed_packages == []:
installed_packages = [package_details] installed_packages = [package_details]
else: else:
@@ -58,16 +60,20 @@ def rpm_package_list():
def deb_package_list(): def deb_package_list():
import apt import apt
apt_cache = apt.Cache() apt_cache = apt.Cache()
installed_packages = [] installed_packages = []
apt_installed_packages = [pk for pk in apt_cache.keys() if apt_cache[pk].is_installed] apt_installed_packages = [
pk for pk in apt_cache.keys() if apt_cache[pk].is_installed
]
for package in apt_installed_packages: for package in apt_installed_packages:
ac_pkg = apt_cache[package].installed ac_pkg = apt_cache[package].installed
package_details = { package_details = {
'name':package, "name": package,
'version':ac_pkg.version, "version": ac_pkg.version,
'arch':ac_pkg.architecture, "arch": ac_pkg.architecture,
'source':'apt'} "source": "apt",
}
if installed_packages == []: if installed_packages == []:
installed_packages = [package_details] installed_packages = [package_details]
else: else:
@@ -76,13 +82,11 @@ def deb_package_list():
def main(): def main():
module = AnsibleModule( module = AnsibleModule(argument_spec=dict(os_family=dict(required=True)))
argument_spec = dict(os_family=dict(required=True)) ans_os = module.params["os_family"]
) if ans_os in ("RedHat", "Suse", "openSUSE Leap"):
ans_os = module.params['os_family']
if ans_os in ('RedHat', 'Suse', 'openSUSE Leap'):
packages = rpm_package_list() packages = rpm_package_list()
elif ans_os == 'Debian': elif ans_os == "Debian":
packages = deb_package_list() packages = deb_package_list()
else: else:
packages = None packages = None

View File

@@ -1,46 +1,47 @@
#!/usr/bin/env python #!/usr/bin/env python
import re import re
from ansible.module_utils.basic import * # noqa from ansible.module_utils.basic import * # noqa
DOCUMENTATION = ''' DOCUMENTATION = """
--- ---
module: scan_services module: scan_services
short_description: Return service state information as fact data short_description: Return service state information as fact data
description: description:
- Return service state information as fact data for various service management utilities - Return service state information as fact data for various service management utilities
''' """
EXAMPLES = ''' EXAMPLES = """
---
- monit: scan_services - monit: scan_services
# Example fact output: # Example fact output:
# host | success >> { # host | success >> {
# "ansible_facts": { # "ansible_facts": {
# "services": { # "services": {
# "network": { # "network": {
# "source": "sysv", # "source": "sysv",
# "state": "running", # "state": "running",
# "name": "network" # "name": "network"
# }, # },
# "arp-ethers.service": { # "arp-ethers.service": {
# "source": "systemd", # "source": "systemd",
# "state": "stopped", # "state": "stopped",
# "name": "arp-ethers.service" # "name": "arp-ethers.service"
# } # }
# } # }
# } # }
''' # }
"""
class BaseService(object): class BaseService(object):
def __init__(self, module): def __init__(self, module):
self.module = module self.module = module
self.incomplete_warning = False self.incomplete_warning = False
class ServiceScanService(BaseService): class ServiceScanService(BaseService):
def gather_services(self): def gather_services(self):
services = {} services = {}
service_path = self.module.get_bin_path("service") service_path = self.module.get_bin_path("service")
@@ -51,94 +52,125 @@ class ServiceScanService(BaseService):
# sysvinit # sysvinit
if service_path is not None and chkconfig_path is None: if service_path is not None and chkconfig_path is None:
rc, stdout, stderr = self.module.run_command("%s --status-all 2>&1 | grep -E \"\\[ (\\+|\\-) \\]\"" % service_path, use_unsafe_shell=True) rc, stdout, stderr = self.module.run_command(
'%s --status-all 2>&1 | grep -E "\\[ (\\+|\\-) \\]"' % service_path,
use_unsafe_shell=True,
)
for line in stdout.split("\n"): for line in stdout.split("\n"):
line_data = line.split() line_data = line.split()
if len(line_data) < 4: if len(line_data) < 4:
continue # Skipping because we expected more data continue # Skipping because we expected more data
service_name = " ".join(line_data[3:]) service_name = " ".join(line_data[3:])
if line_data[1] == "+": if line_data[1] == "+":
service_state = "running" service_state = "running"
else: else:
service_state = "stopped" service_state = "stopped"
services[service_name] = {"name": service_name, "state": service_state, "source": "sysv"} services[service_name] = {
"name": service_name,
"state": service_state,
"source": "sysv",
}
# Upstart # Upstart
if initctl_path is not None and chkconfig_path is None: if initctl_path is not None and chkconfig_path is None:
p = re.compile('^\s?(?P<name>.*)\s(?P<goal>\w+)\/(?P<state>\w+)(\,\sprocess\s(?P<pid>[0-9]+))?\s*$') p = re.compile(
"^\s?(?P<name>.*)\s(?P<goal>\w+)\/(?P<state>\w+)(\,\sprocess\s(?P<pid>[0-9]+))?\s*$"
)
rc, stdout, stderr = self.module.run_command("%s list" % initctl_path) rc, stdout, stderr = self.module.run_command("%s list" % initctl_path)
real_stdout = stdout.replace("\r","") real_stdout = stdout.replace("\r", "")
for line in real_stdout.split("\n"): for line in real_stdout.split("\n"):
m = p.match(line) m = p.match(line)
if not m: if not m:
continue continue
service_name = m.group('name') service_name = m.group("name")
service_goal = m.group('goal') service_goal = m.group("goal")
service_state = m.group('state') service_state = m.group("state")
if m.group('pid'): if m.group("pid"):
pid = m.group('pid') pid = m.group("pid")
else: else:
pid = None # NOQA pid = None # NOQA
payload = {"name": service_name, "state": service_state, "goal": service_goal, "source": "upstart"} payload = {
"name": service_name,
"state": service_state,
"goal": service_goal,
"source": "upstart",
}
services[service_name] = payload services[service_name] = payload
# RH sysvinit # RH sysvinit
elif chkconfig_path is not None: elif chkconfig_path is not None:
#print '%s --status-all | grep -E "is (running|stopped)"' % service_path # print '%s --status-all | grep -E "is (running|stopped)"' % service_path
p = re.compile( p = re.compile(
'(?P<service>.*?)\s+[0-9]:(?P<rl0>on|off)\s+[0-9]:(?P<rl1>on|off)\s+[0-9]:(?P<rl2>on|off)\s+' "(?P<service>.*?)\s+[0-9]:(?P<rl0>on|off)\s+[0-9]:(?P<rl1>on|off)\s+[0-9]:(?P<rl2>on|off)\s+"
'[0-9]:(?P<rl3>on|off)\s+[0-9]:(?P<rl4>on|off)\s+[0-9]:(?P<rl5>on|off)\s+[0-9]:(?P<rl6>on|off)') "[0-9]:(?P<rl3>on|off)\s+[0-9]:(?P<rl4>on|off)\s+[0-9]:(?P<rl5>on|off)\s+[0-9]:(?P<rl6>on|off)"
rc, stdout, stderr = self.module.run_command('%s' % chkconfig_path, use_unsafe_shell=True) )
rc, stdout, stderr = self.module.run_command(
"%s" % chkconfig_path, use_unsafe_shell=True
)
# Check for special cases where stdout does not fit pattern # Check for special cases where stdout does not fit pattern
match_any = False match_any = False
for line in stdout.split('\n'): for line in stdout.split("\n"):
if p.match(line): if p.match(line):
match_any = True match_any = True
if not match_any: if not match_any:
p_simple = re.compile('(?P<service>.*?)\s+(?P<rl0>on|off)') p_simple = re.compile("(?P<service>.*?)\s+(?P<rl0>on|off)")
match_any = False match_any = False
for line in stdout.split('\n'): for line in stdout.split("\n"):
if p_simple.match(line): if p_simple.match(line):
match_any = True match_any = True
if match_any: if match_any:
# Try extra flags " -l --allservices" needed for SLES11 # Try extra flags " -l --allservices" needed for SLES11
rc, stdout, stderr = self.module.run_command('%s -l --allservices' % chkconfig_path, use_unsafe_shell=True) rc, stdout, stderr = self.module.run_command(
elif '--list' in stderr: "%s -l --allservices" % chkconfig_path, use_unsafe_shell=True
)
elif "--list" in stderr:
# Extra flag needed for RHEL5 # Extra flag needed for RHEL5
rc, stdout, stderr = self.module.run_command('%s --list' % chkconfig_path, use_unsafe_shell=True) rc, stdout, stderr = self.module.run_command(
for line in stdout.split('\n'): "%s --list" % chkconfig_path, use_unsafe_shell=True
)
for line in stdout.split("\n"):
m = p.match(line) m = p.match(line)
if m: if m:
service_name = m.group('service') service_name = m.group("service")
service_state = 'stopped' service_state = "stopped"
if m.group('rl3') == 'on': if m.group("rl3") == "on":
rc, stdout, stderr = self.module.run_command('%s %s status' % (service_path, service_name), use_unsafe_shell=True) rc, stdout, stderr = self.module.run_command(
"%s %s status" % (service_path, service_name),
use_unsafe_shell=True,
)
service_state = rc service_state = rc
if rc in (0,): if rc in (0,):
service_state = 'running' service_state = "running"
#elif rc in (1,3): # elif rc in (1,3):
else: else:
if 'root' in stderr or 'permission' in stderr.lower() or 'not in sudoers' in stderr.lower(): if (
"root" in stderr
or "permission" in stderr.lower()
or "not in sudoers" in stderr.lower()
):
self.incomplete_warning = True self.incomplete_warning = True
continue continue
else: else:
service_state = 'stopped' service_state = "stopped"
service_data = {"name": service_name, "state": service_state, "source": "sysv"} service_data = {
"name": service_name,
"state": service_state,
"source": "sysv",
}
services[service_name] = service_data services[service_name] = service_data
return services return services
class SystemctlScanService(BaseService): class SystemctlScanService(BaseService):
def systemd_enabled(self): def systemd_enabled(self):
# Check if init is the systemd command, using comm as cmdline could be symlink # Check if init is the systemd command, using comm as cmdline could be symlink
try: try:
f = open('/proc/1/comm', 'r') f = open("/proc/1/comm", "r")
except IOError: except IOError:
# If comm doesn't exist, old kernel, no systemd # If comm doesn't exist, old kernel, no systemd
return False return False
for line in f: for line in f:
if 'systemd' in line: if "systemd" in line:
return True return True
return False return False
@@ -146,10 +178,16 @@ class SystemctlScanService(BaseService):
services = {} services = {}
if not self.systemd_enabled(): if not self.systemd_enabled():
return None return None
systemctl_path = self.module.get_bin_path("systemctl", opt_dirs=["/usr/bin", "/usr/local/bin"]) systemctl_path = self.module.get_bin_path(
"systemctl", opt_dirs=["/usr/bin", "/usr/local/bin"]
)
if systemctl_path is None: if systemctl_path is None:
return None return None
rc, stdout, stderr = self.module.run_command("%s list-unit-files --type=service | tail -n +2 | head -n -2" % systemctl_path, use_unsafe_shell=True) rc, stdout, stderr = self.module.run_command(
"%s list-unit-files --type=service | tail -n +2 | head -n -2"
% systemctl_path,
use_unsafe_shell=True,
)
for line in stdout.split("\n"): for line in stdout.split("\n"):
line_data = line.split() line_data = line.split()
if len(line_data) != 2: if len(line_data) != 2:
@@ -158,12 +196,16 @@ class SystemctlScanService(BaseService):
state_val = "running" state_val = "running"
else: else:
state_val = "stopped" state_val = "stopped"
services[line_data[0]] = {"name": line_data[0], "state": state_val, "source": "systemd"} services[line_data[0]] = {
"name": line_data[0],
"state": state_val,
"source": "systemd",
}
return services return services
def main(): def main():
module = AnsibleModule(argument_spec = dict()) module = AnsibleModule(argument_spec=dict())
service_modules = (ServiceScanService, SystemctlScanService) service_modules = (ServiceScanService, SystemctlScanService)
all_services = {} all_services = {}
incomplete_warning = False incomplete_warning = False
@@ -175,11 +217,16 @@ def main():
if svcmod.incomplete_warning: if svcmod.incomplete_warning:
incomplete_warning = True incomplete_warning = True
if len(all_services) == 0: if len(all_services) == 0:
results = dict(skipped=True, msg="Failed to find any services. Sometimes this is due to insufficient privileges.") results = dict(
skipped=True,
msg="Failed to find any services. Sometimes this is due to insufficient privileges.",
)
else: else:
results = dict(ansible_facts=dict(services=all_services)) results = dict(ansible_facts=dict(services=all_services))
if incomplete_warning: if incomplete_warning:
results['msg'] = "WARNING: Could not find status for all services. Sometimes this is due to insufficient privileges." results[
"msg"
] = "WARNING: Could not find status for all services. Sometimes this is due to insufficient privileges."
module.exit_json(**results) module.exit_json(**results)

View File

@@ -1,31 +1,34 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
DOCUMENTATION = ''' DOCUMENTATION = """
--- ---
module: win_scan_packages module: win_scan_packages
short_description: Return Package state information as fact data short_description: Return Package state information as fact data
description: description:
- Return Package state information as fact data for various Packages - Return Package state information as fact data for various Packages
''' """
EXAMPLES = ''' EXAMPLES = """
- monit: win_scan_packages - monit: win_scan_packages
# Example fact output:
# host | success >> { # Example fact output:
# "ansible_facts": { # host | success >> {
# "packages": [ # "ansible_facts": {
{ # "packages": [
"name": "Mozilla Firefox 76.0.1 (x64 en-US)", # {
"version": "76.0.1", # "name": "Mozilla Firefox 76.0.1 (x64 en-US)",
"publisher": "Mozilla", # "version": "76.0.1",
"arch": "Win64" # "publisher": "Mozilla",
}, # "arch": "Win64"
{ # },
"name": "Mozilla Maintenance Service", # {
"version": "76.0.1", # "name": "Mozilla Maintenance Service",
"publisher": "Mozilla", # "version": "76.0.1",
"arch": "Win64" # "publisher": "Mozilla",
}, # "arch": "Win64"
# }
# ]
# } # }
''' # }
"""

View File

@@ -1,34 +1,37 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
DOCUMENTATION = ''' DOCUMENTATION = """
--- ---
module: win_scan_services module: win_scan_services
short_description: Return service state information as fact data short_description: Return service state information as fact data
description: description:
- Return service state information as fact data for various service management utilities - Return service state information as fact data for various service management utilities
''' """
EXAMPLES = ''' EXAMPLES = """
- monit: win_scan_services - monit: win_scan_services
# Example fact output: # Example fact output:
# host | success >> { # host | success >> {
# "ansible_facts": { # "ansible_facts": {
# "services": [ # "services": [
{ # {
"name": "AllJoyn Router Service", # "name": "AllJoyn Router Service",
"win_svc_name": "AJRouter", # "win_svc_name": "AJRouter",
"state": "stopped" # "state": "stopped"
}, # },
{ # {
"name": "Application Layer Gateway Service", # "name": "Application Layer Gateway Service",
"win_svc_name": "ALG", # "win_svc_name": "ALG",
"state": "stopped" # "state": "stopped"
}, # },
{ # {
"name": "Application Host Helper Service", # "name": "Application Host Helper Service",
"win_svc_name": "AppHostSvc", # "win_svc_name": "AppHostSvc",
"state": "running" # "state": "running"
}, # }
# ]
# } # }
''' # }
"""

View File

@@ -1,3 +1,4 @@
---
- name: Create web directory if it does not exist - name: Create web directory if it does not exist
ansible.builtin.file: ansible.builtin.file:
path: "{{ file_path }}" path: "{{ file_path }}"

View File

@@ -1,11 +1,12 @@
file_path: "{{ web_path | default('/var/www/html/reports') }}" ---
vendor: file_path: "{{ web_path | default('/var/www/html/reports') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
vendor: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
ios: &my_value 'Cisco' ios: &my_value 'Cisco'
nxos: *my_value nxos: *my_value
iosxr: *my_value iosxr: *my_value
junos: "Juniper" junos: "Juniper"
eos: "Arista" eos: "Arista"
transport: transport: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
cliconf: "Network_CLI" cliconf: "Network_CLI"
netconf: "NETCONF" netconf: "NETCONF"
nxapi: "NX-API" nxapi: "NX-API"

View File

@@ -1,2 +1,2 @@
--- ---
detailedreport: true detailedreport: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
---
- name: Create HTML report - name: Create HTML report
ansible.builtin.template: ansible.builtin.template:
src: report.j2 src: report.j2

View File

@@ -1 +1,2 @@
file_path: /var/www/html ---
file_path: /var/www/html # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
email_from: tower@shadowman.dev ---
to_emails: alex@shadowman.dev,tower@shadowman.dev email_from: tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails_list: "{{ to_emails.split(',') }}" to_emails: alex@shadowman.dev,tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails_list: "{{ to_emails.split(',') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
---
- name: Create HTML report - name: Create HTML report
ansible.builtin.template: ansible.builtin.template:
src: report.j2 src: report.j2

View File

@@ -1 +1,2 @@
file_path: /var/www/html ---
file_path: /var/www/html # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,7 +1,7 @@
--- ---
exclude_packages: exclude_packages: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
- authselect - authselect
- authselect-compat - authselect-compat
- authselect-libs - authselect-libs
- fprintd-pam - fprintd-pam
allow_reboot: true allow_reboot: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -10,7 +10,7 @@
- name: Upgrade packages (yum) - name: Upgrade packages (yum)
ansible.builtin.yum: ansible.builtin.yum:
name: '*' name: '*'
state: latest # noqa: package-latest - Intended to update packages to latest state: latest # noqa: package-latest - Intended to update packages to latest
exclude: "{{ exclude_packages }}" exclude: "{{ exclude_packages }}"
when: ansible_pkg_mgr == "yum" when: ansible_pkg_mgr == "yum"
register: patchingresult_yum register: patchingresult_yum
@@ -18,7 +18,7 @@
- name: Upgrade packages (dnf) - name: Upgrade packages (dnf)
ansible.builtin.dnf: ansible.builtin.dnf:
name: '*' name: '*'
state: latest # noqa: package-latest - Intended to update packages to latest state: latest # noqa: package-latest - Intended to update packages to latest
exclude: "{{ exclude_packages }}" exclude: "{{ exclude_packages }}"
when: ansible_pkg_mgr == "dnf" when: ansible_pkg_mgr == "dnf"
register: patchingresult_dnf register: patchingresult_dnf

View File

@@ -1,5 +1,5 @@
--- ---
win_update_categories: win_update_categories: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
- Application - Application
- Connectors - Connectors
- CriticalUpdates - CriticalUpdates
@@ -11,4 +11,4 @@ win_update_categories:
- Tools - Tools
- UpdateRollups - UpdateRollups
- Updates - Updates
allow_reboot: true allow_reboot: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,2 +1,2 @@
--- ---
detailedreport: true detailedreport: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
---
- name: Create HTML report - name: Create HTML report
ansible.builtin.template: ansible.builtin.template:
src: report.j2 src: report.j2

View File

@@ -1 +1,2 @@
file_path: /var/www/html/reports ---
file_path: /var/www/html/reports # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
email_from: tower@shadowman.dev ---
to_emails: alex@shadowman.dev,tower@shadowman.dev email_from: tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails_list: "{{ to_emails.split(',') }}" to_emails: alex@shadowman.dev,tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails_list: "{{ to_emails.split(',') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
---
- name: Create HTML report - name: Create HTML report
ansible.builtin.template: ansible.builtin.template:
src: report.j2 src: report.j2

View File

@@ -1 +1,2 @@
file_path: /var/www/html/reports ---
file_path: /var/www/html/reports # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -0,0 +1,36 @@
build_report_linux_patch
========
Installs Apache and creates a report based on facts from Linux patching
Requirements
------------
Must run on Apache server
Role Variables / Configuration
--------------
N/A
Dependencies
------------
N/A
Example Playbook
----------------
The role can be used to create an html report on any number of Linux hosts using any number of Linux servers about their patching results(yum and dnf)
```
---
- hosts: all
tasks:
- name: Run Windows Report
import_role:
name: shadowman.reports.build_report_linux_patch
```

View File

@@ -0,0 +1,8 @@
---
email_from: tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails: alex@shadowman.dev,tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails_list: "{{ to_emails.split(',') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
detailedreport: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
reports: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
- linux.html
- linuxpatch.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,202 @@
p.hostname {
color: #000000;
font-weight: bolder;
font-size: large;
margin: auto;
width: 50%;
}
#subtable {
background: #ebebeb;
margin: 0px;
width: 100%;
}
#subtable tbody tr td {
padding: 5px 5px 5px 5px;
}
#subtable thead th {
padding: 5px;
}
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: "Open Sans", "Helvetica";
}
a {
color: #ffffff;
}
p {
color: #ffffff;
}
h1 {
text-align: center;
color: #ffffff;
}
body {
background:#353a40;
padding: 0px;
margin: 0px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
table {
border-collapse: separate;
background:#fff;
@include border-radius(5px);
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
}
.main_net_table {
margin:50px auto;
}
thead {
@include border-radius(5px);
}
thead th {
font-size:16px;
font-weight:400;
color:#fff;
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
text-align:left;
padding:20px;
border-top:1px solid #858d99;
background: #353a40;
&:first-child {
@include border-top-left-radius(5px);
}
&:last-child {
@include border-top-right-radius(5px);
}
}
tbody tr td {
font-weight:400;
color:#5f6062;
font-size:13px;
padding:20px 20px 20px 20px;
border-bottom:1px solid #e0e0e0;
}
tbody tr:nth-child(2n) {
background:#f0f3f5;
}
tbody tr:last-child td {
border-bottom:none;
&:first-child {
@include border-bottom-left-radius(5px);
}
&:last-child {
@include border-bottom-right-radius(5px);
}
}
td {
vertical-align: top;
}
span.highlight {
background-color: yellow;
}
.expandclass {
color: #5f6062;
}
.content{
display:none;
margin: 10px;
}
header {
width: 100%;
position: initial;
float: initial;
padding: 0;
margin: 0;
border-radius: 0;
height: 88px;
background-color: #171717;
}
.header-container {
margin: 0 auto;
width: 100%;
height: 100%;
max-width: 1170px;
padding: 0;
float: initial;
display: flex;
align-items: center;
}
.header-logo {
width: 137px;
border: 0;
margin: 0;
margin-left: 15px;
}
.header-link {
margin-left: 40px;
text-decoration: none;
cursor: pointer;
text-transform: uppercase;
font-size: 15px;
font-family: 'Red Hat Text';
font-weight: 500;
}
.header-link:hover {
text-shadow: 0 0 0.02px white;
text-decoration: none;
}
table.net_info td {
padding: 5px;
}
p.expandclass:hover {
text-decoration: underline;
color: #EE0000;
cursor: pointer;
}
.summary_info {
}
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
border: 1px solid #5F0000;
background: #EE0000;
}
div#net_content {
padding: 0px;
height: auto !important;
}
img.router_image {
vertical-align: middle;
padding: 0px 10px 10px 10px;
width: 50px;
}
table.net_info {
width: 100%;
}
p.internal_label {
color: #000000;
}

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Logos" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="930.2px" height="350px" viewBox="0 0 930.2 350" style="enable-background:new 0 0 930.2 350;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EE0000;}
</style>
<title>Logo-Red_Hat-Ansible_Automation_Platform-A-Reverse-RGB</title>
<path class="st0" d="M383.3,228.5h18.8L446,335.7h-17.5l-12.4-31.4h-48l-12.6,31.4h-16.7L383.3,228.5z M410.9,291l-18.7-47l-18.7,47
H410.9z"/>
<path class="st0" d="M455.2,257.7h15.3v7.8c6.2-6.2,14.7-9.6,23.5-9.3c17.9,0,30.5,12.4,30.5,30.5v49h-15.3v-46.5
c0-12.3-7.5-19.8-19.3-19.8c-7.8-0.3-15.1,3.6-19.3,10.1v56.1h-15.3V257.7z"/>
<path class="st0" d="M543,315.5c8.1,6.4,16.7,9.8,25.4,9.8c11,0,18.7-4.8,18.7-11.7c0-5.5-4-8.7-12.6-10l-14.1-2
c-15.5-2.3-23.3-9.5-23.3-21.6c0-14.1,12.3-23.6,30.5-23.6c11.3-0.1,22.3,3.4,31.5,9.9l-7.8,10.1c-8.6-5.7-16.4-8.1-24.7-8.1
c-9.3,0-15.6,4.3-15.6,10.6c0,5.7,3.7,8.4,12.9,9.8l14.1,2c15.5,2.3,23.6,9.7,23.6,21.7c0,14-14.1,24.5-32.6,24.5
c-13.5,0-25.6-4-34.2-11.5L543,315.5z"/>
<path class="st0" d="M611.6,235.6c0-5.2,4.1-9.4,9.3-9.5c0,0,0,0,0,0c5.2-0.2,9.7,3.9,9.9,9.1c0.2,5.2-3.9,9.7-9.1,9.9
c-0.2,0-0.5,0-0.7,0C615.8,245.1,611.6,240.9,611.6,235.6C611.6,235.7,611.6,235.7,611.6,235.6z M628.6,335.7h-15.3v-78h15.3V335.7z
"/>
<path class="st0" d="M685.5,336.9c-8.5,0-16.8-2.7-23.6-7.8v6.6h-15.2V228.5l15.3-3.4v40c6.6-5.6,15.1-8.7,23.7-8.6
c22.1,0,39.4,17.7,39.4,40.1C725.2,319.1,707.9,336.9,685.5,336.9z M662,279.2v35.2c4.9,5.7,13,9.2,21.8,9.2
c15,0,26.4-11.5,26.4-26.8c0-15.3-11.5-27-26.4-27C674.9,269.8,667.1,273.2,662,279.2z"/>
<path class="st0" d="M755,335.7h-15.3V228.5l15.3-3.4V335.7z"/>
<path class="st0" d="M810.5,337.1c-23,0-40.9-17.7-40.9-40.4c0-22.5,17.2-40.1,39.1-40.1c21.5,0,37.7,17.8,37.7,40.8v4.4h-61.6
c2,13,13.2,22.5,26.4,22.4c7.2,0.2,14.2-2.3,19.8-6.8l9.8,9.7C832.1,333.7,821.5,337.4,810.5,337.1z M784.9,290.2h46.3
c-2.3-11.9-11.5-20.8-22.8-20.8C796.5,269.4,787.2,277.8,784.9,290.2z"/>
<path class="st1" d="M202.8,137.5c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5l-11-47.7c-2.5-10.5-4.8-15.2-23.2-24.5
c-14.3-7.3-45.5-19.4-54.7-19.4c-8.6,0-11.1,11.1-21.3,11.1c-9.8,0-17.1-8.3-26.4-8.3c-8.8,0-14.6,6-19,18.4c0,0-12.4,34.9-14,40
c-0.3,0.9-0.4,1.9-0.4,2.9C77.6,92.9,131.1,137.5,202.8,137.5 M250.8,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1
c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8c-24.8,1.3-56.9,5.7-56.9,34c0,46.4,109.9,103.5,196.9,103.5
c66.7,0,83.5-30.2,83.5-54C296.1,154.6,279.9,133.4,250.8,120.7"/>
<path d="M250.7,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8
l5.4-13.3c-0.3,0.9-0.4,1.9-0.4,2.8c0,13.6,53.5,58.1,125.2,58.1c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5L250.7,120.7z"/>
<path class="st0" d="M869.1,151.2c0,17.5,10.5,26,29.7,26c5.9-0.1,11.8-1,17.5-2.5v-20.3c-3.7,1.2-7.5,1.7-11.3,1.7
c-7.9,0-10.8-2.5-10.8-9.9v-31.1h22.9V94.2h-22.9V67.7l-25,5.4v21.1h-16.6v20.9h16.6L869.1,151.2z M791,151.7
c0-5.4,5.4-8.1,13.6-8.1c5,0,10,0.7,14.9,1.9V156c-4.8,2.6-10.2,3.9-15.6,3.9C795.9,159.9,791.1,156.8,791,151.7 M798.7,177.5
c8.8,0,16-1.9,22.6-6.3v5h24.8v-52.5c0-20-13.5-30.9-35.9-30.9c-12.6,0-25,2.9-38.3,9l9,18.4c9.6-4,17.7-6.5,24.8-6.5
c10.3,0,15.6,4,15.6,12.2v4c-6.1-1.6-12.3-2.4-18.6-2.3c-21.1,0-33.8,8.8-33.8,24.6C768.9,166.6,780.4,177.6,798.7,177.5
M662.5,176.2h26.7v-42.5h44.6v42.5h26.7V67.7h-26.6v41.7h-44.6V67.7h-26.7L662.5,176.2z M561,135.1c0-11.8,9.3-20.8,21.5-20.8
c6.4-0.1,12.6,2.1,17.4,6.4v28.6c-4.7,4.4-10.9,6.7-17.4,6.5C570.5,155.8,561,146.8,561,135.1 M600.2,176.1H625V62.3l-25,5.4v30.8
c-6.4-3.6-13.6-5.5-20.9-5.4c-23.9,0-42.6,18.4-42.6,42c-0.3,23,18.1,41.9,41.1,42.2c0.2,0,0.5,0,0.7,0c7.9,0,15.6-2.5,22-7.1V176.1
z M486.5,113.2c7.9,0,14.6,5.1,17.2,13h-34.2C471.9,118,478.2,113.2,486.5,113.2 M444.2,135.2c0,23.9,19.5,42.5,44.6,42.5
c13.8,0,23.9-3.7,34.3-12.4l-16.6-14.7c-3.9,4-9.6,6.2-16.4,6.2c-8.8,0.2-16.8-4.9-20.2-13h58.4v-6.2c0-26-17.5-44.8-41.4-44.8
c-23.2-0.4-42.4,18.2-42.7,41.5C444.2,134.6,444.2,134.9,444.2,135.2 M400.9,90.5c8.8,0,13.8,5.6,13.8,12.2s-5,12.2-13.8,12.2h-26.3
V90.5H400.9z M347.9,176.2h26.7v-39.5h20.3l20.5,39.5h29.7l-23.9-43.4c12.4-5,20.5-17.1,20.4-30.5c0-19.5-15.3-34.5-38.3-34.5H348
L347.9,176.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -0,0 +1,22 @@
---
- name: Define namespace
redhat.openshift.k8s:
wait: true
state: present
api_version: v1
kind: Namespace
name: patching-report
- name: Define deployment resources
redhat.openshift.k8s:
wait: true
state: present
namespace: patching-report
definition: "{{ lookup('ansible.builtin.template', 'resources.yaml.j2') }}"
register: resources_output
- name: Display link to patching report
ansible.builtin.debug:
msg:
- "Patching report availbable at:"
- "{{ resources_output.result.results[3].result.spec.port.targetPort }}://{{ resources_output.result.results[3].result.spec.host }}"

View File

@@ -0,0 +1,15 @@
<div class="wrapper">
<header>
<div class="header-container">
<a href="https://ansible.com">
<img
class="header-logo"
src="redhat-ansible-logo.svg"
title="Red Hat Ansible"
alt="Red Hat Ansible"
/>
</a>
</div>
</header>

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title> Ansible Linux Automation Report </title>
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="new.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
</head>
<body>
<div class="wrapper">
{% include 'header.j2' %}
<section>
<center>
<h1>Ansible Automation Reports</h1>
<h3><input type="search" placeholder="Search..." class="form-control search-input" data-table="main_net_table"/>
</center>
<table class="table table-striped mt32 main_net_table">
<tbody>
{% for report in reports %}
<tr>
<td class="summary_info">
<div id="hostname">
<p class="hostname"> <img class="router_image" src="report.png"></p>
</div>
</td>
<td>
<a href="{{ report }}"> {{ report }} <a>
</td>
{% endfor %}
</tbody>
</table>
<center><p>Created with</p><br><img src="webpage_logo.png" width="300">
</center>
</section>
</div>
</body>
</html>

View File

@@ -0,0 +1,202 @@
p.hostname {
color: #000000;
font-weight: bolder;
font-size: large;
margin: auto;
width: 50%;
}
#subtable {
background: #ebebeb;
margin: 0px;
width: 100%;
}
#subtable tbody tr td {
padding: 5px 5px 5px 5px;
}
#subtable thead th {
padding: 5px;
}
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: "Open Sans", "Helvetica";
}
a {
color: #000000;
}
p {
color: #ffffff;
}
h1 {
text-align: center;
color: #ffffff;
}
body {
background:#353a40;
padding: 0px;
margin: 0px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
table {
border-collapse: separate;
background:#fff;
@include border-radius(5px);
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
}
.main_net_table {
margin:50px auto;
}
thead {
@include border-radius(5px);
}
thead th {
font-size:16px;
font-weight:400;
color:#fff;
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
text-align:left;
padding:20px;
border-top:1px solid #858d99;
background: #353a40;
&:first-child {
@include border-top-left-radius(5px);
}
&:last-child {
@include border-top-right-radius(5px);
}
}
tbody tr td {
font-weight:400;
color:#5f6062;
font-size:13px;
padding:20px 20px 20px 20px;
border-bottom:1px solid #e0e0e0;
}
tbody tr:nth-child(2n) {
background:#f0f3f5;
}
tbody tr:last-child td {
border-bottom:none;
&:first-child {
@include border-bottom-left-radius(5px);
}
&:last-child {
@include border-bottom-right-radius(5px);
}
}
td {
vertical-align: top;
}
span.highlight {
background-color: yellow;
}
.expandclass {
color: #5f6062;
}
.content{
display:none;
margin: 10px;
}
header {
width: 100%;
position: initial;
float: initial;
padding: 0;
margin: 0;
border-radius: 0;
height: 88px;
background-color: #171717;
}
.header-container {
margin: 0 auto;
width: 100%;
height: 100%;
max-width: 1170px;
padding: 0;
float: initial;
display: flex;
align-items: center;
}
.header-logo {
width: 137px;
border: 0;
margin: 0;
margin-left: 15px;
}
.header-link {
margin-left: 40px;
text-decoration: none;
cursor: pointer;
text-transform: uppercase;
font-size: 15px;
font-family: 'Red Hat Text';
font-weight: 500;
}
.header-link:hover {
text-shadow: 0 0 0.02px white;
text-decoration: none;
}
table.net_info td {
padding: 5px;
}
p.expandclass:hover {
text-decoration: underline;
color: #EE0000;
cursor: pointer;
}
.summary_info {
}
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
border: 1px solid #5F0000;
background: #EE0000;
}
div#net_content {
padding: 0px;
height: auto !important;
}
img.router_image {
vertical-align: middle;
padding: 0px 10px 10px 10px;
width: 50px;
}
table.net_info {
width: 100%;
}
p.internal_label {
color: #000000;
}

View File

@@ -0,0 +1,31 @@
<! INTERNAL TABLE FOR PACKAGES --!>
<div id="accordion">
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">Package Facts</h3>
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
<table id="subtable" class="sortable">
<thead>
<tr>
<th>Package Name</th>
<th>source</th>
<th>release</th>
<th>version</th>
</tr>
</thead>
<tbody>
{% if hostvars[linux_host]['packages'] is defined %}
{% for package in hostvars[linux_host]['packages'] %}
<tr>
<td>{{package['name']}}</td>
<td>{{package['source']}}</td>
<td>{{package['release']}}</td>
<td>{{package['version']}}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
<! END INTERNAL TABLE FOR PACKAGES --!>

View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<title> Linux Patch Report </title>
</head>
<body>
<center>
<h1>Ansible Linux Patching Report</h1>
<style>
@media print {
.noprint {
display: none !important;
}
}
</style>
<div class="noprint">
<button type="button" onclick="tableToCSV()">Download CSV</button>
<input type="button" value="Print" onClick="window.print()">
</div>
</center>
<table border = "1" cellpadding = "5" cellspacing = "5">
<thead>
<tr>
<th>Hostname</th>
<th>Operating System</th>
<th>Operating System Version</th>
<th>Required Updates</th>
</tr>
</thead>
<tbody>
{% for linux_host in ansible_play_hosts |sort %}
<tr>
<td>{{hostvars[linux_host]['inventory_hostname']}}</td>
<td>{{hostvars[linux_host]['ansible_os_family']|default("none")}}</td>
<td>{{hostvars[linux_host]['ansible_distribution_version']|default("none")}}</td>
<td>
<ul>
{% if hostvars[linux_host].patchingresult_yum.changed|default("false",true) == true %}
{% for packagename in hostvars[linux_host].patchingresult_yum.changes.updated|sort %}
<li> {{ packagename[0] }} - {{ packagename[1] }} </li>
{% endfor %}
{% elif hostvars[linux_host].patchingresult_dnf.changed|default("false",true) == true %}
{% for packagename in hostvars[linux_host].patchingresult_dnf.results|sort %}
<li> {{ packagename }} </li>
{% endfor %}
{% elif hostvars[linux_host].patchingresult_dnf.changed is undefined %}
<li> Patching Failed </li>
{% elif hostvars[linux_host].patchingresult_yum.changed is undefined %}
<li> Patching Failed </li>
{% else %}
<li> Compliant </li>
{% endif %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<center><p>Created with Ansible on {{hostvars[inventory_hostname].ansible_date_time.iso8601}}</p></center>
<script type="text/javascript">
function tableToCSV() {
// Variable to store the final csv data
var csv_data = [];
// Get each row data
var rows = document.getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++) {
// Get each column data
var cols = rows[i].querySelectorAll('td,th');
// Stores each csv row data
var csvrow = [];
for (var j = 0; j < (cols.length); j++) {
// Get the text data of each cell of
// a row and push it to csvrow
if ( j == cols.length-1 && i==0){}
else{
csvrow.push(cols[j].textContent.replace(/,/g, " "));
}
}
csv_data.push(csvrow.join(","));
}
// combine each row data with new line character
csv_data = csv_data.join('\n');
// Call this function to download csv file
downloadCSVFile(csv_data);
}
function downloadCSVFile(csv_data) {
// Create CSV file object and feed our
// csv_data into it
CSVFile = new Blob([csv_data], { type: "text/csv" });
// Create to temporary link to initiate
// download process
var temp_link = document.createElement('a');
var todayDate = new Date().toISOString().slice(0, 10);
// Download csv file
temp_link.download = "linuxpatching-" + todayDate + ".csv";
var url = window.URL.createObjectURL(CSVFile);
temp_link.href = url;
// This link should not be displayed
temp_link.style.display = "none";
document.body.appendChild(temp_link);
// Automatically click the link to trigger download
temp_link.click();
document.body.removeChild(temp_link);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html>
<head>
<title> Ansible Linux Automation Report </title>
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="new.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
<script>
$(function() {
$( "#accordion > div" ).accordion({
header: "h3",
active: false,
collapsible: true
});
});
</script>
<script>
(function(document) {
'use strict';
var TableFilter = (function(myArray) {
var search_input;
function _onInputSearch(e) {
search_input = e.target;
var tables = document.getElementsByClassName(search_input.getAttribute('data-table'));
myArray.forEach.call(tables, function(table) {
myArray.forEach.call(table.tBodies, function(tbody) {
myArray.forEach.call(tbody.rows, function(row) {
var text_content = row.textContent.toLowerCase();
var search_val = search_input.value.toLowerCase();
row.style.display = text_content.indexOf(search_val) > -1 ? '' : 'none';
});
});
});
}
return {
init: function() {
var inputs = document.getElementsByClassName('search-input');
myArray.forEach.call(inputs, function(input) {
input.oninput = _onInputSearch;
});
}
};
})(Array.prototype);
document.addEventListener('readystatechange', function() {
if (document.readyState === 'complete') {
TableFilter.init();
}
});
})(document);
</script>
</head>
<body>
<div class="wrapper">
{% include 'header.j2' %}
<section>
<center>
<h1>Ansible Linux Automation Report</h1>
<h3><input type="search" placeholder="Search..." class="form-control search-input" data-table="main_net_table"/>
</center>
<table class="table table-striped mt32 main_net_table">
<thead>
<tr>
<th>Linux Device</th>
<th>Package Manager</th>
<th>Operating System</th>
<th>Operating System Version</th>
<th>Operating System Kernel Version</th>
</tr>
</thead>
<tbody>
{% for linux_host in ansible_play_hosts |sort %}
<tr>
<td class="summary_info">
<div id="hostname">
<p class="hostname">
<img class="router_image" src="server.png"> {{ hostvars[linux_host]['inventory_hostname'].split('.')[0] }}</p>
</div>
{% if detailedreport == 'True' %}
{% include 'packages.j2' %}
{% include 'services.j2' %}
{% endif %}
</td>
<td>{{hostvars[linux_host]['ansible_pkg_mgr']|default("none")}}</td>
<td>{{hostvars[linux_host]['ansible_os_family']|default("none")}}</td>
<td>{{hostvars[linux_host]['ansible_distribution_version']|default("none")}}</td>
<td>{{hostvars[linux_host]['ansible_kernel']|default("none")}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<center><p>Created with</p><br><img src="webpage_logo.png" width="300">
</center>
</section>
</div>
</body>
</html>

View File

@@ -0,0 +1,94 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: linux-patching-report
labels:
app: linux-patching-report
data:
index.html: |
{% filter indent(width=4) %}
{%- include 'landing.j2' %}
{% endfilter %}
linux.html: |
{% filter indent(width=4) %}
{%- include 'report.j2' %}
{% endfilter %}
linuxpatch.html: |
{% filter indent(width=4) %}
{%- include 'patch.j2' %}
{% endfilter %}
new.css: |
{% filter indent(width=4) %}
{%- include 'new.css.j2' %}
{% endfilter %}
binaryData:
server.png: {{ lookup('ansible.builtin.file', 'server.png') | b64encode }}
report.png: {{ lookup('ansible.builtin.file', 'report.png') | b64encode }}
webpage_logo.png: {{ lookup('file', 'webpage_logo.png') | b64encode }}
redhat-ansible-logo.svg: {{ lookup('ansible.builtin.file', 'redhat-ansible-logo.svg') | b64encode }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: linux-patching-report
labels:
app: linux-patching-report
spec:
replicas: 1
selector:
matchLabels:
app: linux-patching-report
template:
metadata:
labels:
app: linux-patching-report
spec:
terminationGracePeriodSeconds: 1
containers:
- image: registry.redhat.io/rhel8/httpd-24
name: report-server
volumeMounts:
- name: html
mountPath: /var/www/html
volumes:
- name: html
configMap:
name: linux-patching-report
---
apiVersion: v1
kind: Service
metadata:
labels:
app: linux-patching-report
name: linux-patching-report
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: linux-patching-report
type: ClusterIP
---
kind: Route
apiVersion: route.openshift.io/v1
metadata:
labels:
app: linux-patching-report
name: linux-patching-report
spec:
to:
kind: Service
name: linux-patching-report
weight: 100
port:
targetPort: http
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect

View File

@@ -0,0 +1,30 @@
<! INTERNAL TABLE FOR SERVICES --!>
<div id="accordion">
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">Services Facts</h3>
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
<table id="subtable" class="sortable">
<thead>
<tr>
<th>Service Name</th>
<th>State</th>
<th>Source</th>
</tr>
</thead>
<tbody>
{% if hostvars[linux_host]['services'] is defined %}
{% for servicesname in hostvars[linux_host]['services']|sort %}
{% set service = hostvars[linux_host]['services'][servicesname] %}
<tr>
<td>{{service['name']}}</td>
<td>{{service['state']}}</td>
<td>{{service['source']}}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
<! END INTERNAL TABLE FOR SERVICES --!>

View File

@@ -1,3 +1,3 @@
--- ---
doc_root: /var/www/html doc_root: /var/www/html # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
reports_dir: reports reports_dir: reports # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,3 @@
--- ---
doc_root: C:\Inetpub\wwwroot doc_root: C:\Inetpub\wwwroot # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
reports_dir: reports reports_dir: reports # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,2 +1,2 @@
--- ---
detailedreport: true detailedreport: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,2 +1,2 @@
--- ---
file_path: C:\Inetpub\wwwroot\reports file_path: C:\Inetpub\wwwroot\reports # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,4 +1,4 @@
--- ---
email_from: tower@shadowman.dev email_from: tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails: alex@shadowman.dev,tower@shadowman.dev to_emails: alex@shadowman.dev,tower@shadowman.dev # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
to_emails_list: "{{ to_emails.split(',') }}" to_emails_list: "{{ to_emails.split(',') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,3 +1,4 @@
---
- name: Create HTML report - name: Create HTML report
ansible.windows.win_template: ansible.windows.win_template:
src: report.j2 src: report.j2

View File

@@ -1,2 +1,2 @@
--- ---
file_path: C:\Inetpub\wwwroot\reports file_path: C:\Inetpub\wwwroot\reports # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,5 +1,5 @@
--- ---
instance_name: "{{ inventory_hostname | regex_replace('_', '-') }}" instance_name: "{{ inventory_hostname | regex_replace('_', '-') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
activation_key: "{{ 'RHEL' + ansible_distribution_major_version + '_' + env }}" activation_key: "{{ 'RHEL' + ansible_distribution_major_version + '_' + env }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
rex_user: root # "{{ ansible_user }}" rex_user: root # "{{ ansible_user }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
force_register: true force_register: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,4 +1,4 @@
--- ---
rhsm_enabled_repos: rhsm_enabled_repos: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
- rhel-7-server-rpms - rhel-7-server-rpms
# - rhel-7-server-satellite-maintenance-6.11-rpms # - rhel-7-server-satellite-maintenance-6.11-rpms

View File

@@ -1,5 +1,5 @@
--- ---
rhsm_enabled_repos: rhsm_enabled_repos: # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
- rhel-8-for-x86_64-baseos-rpms - rhel-8-for-x86_64-baseos-rpms
- rhel-8-for-x86_64-appstream-rpms - rhel-8-for-x86_64-appstream-rpms
- satellite-client-6-for-rhel-8-x86_64-rpms - satellite-client-6-for-rhel-8-x86_64-rpms

View File

@@ -1,12 +1,13 @@
foreman_server_url: "{{ lookup('env', 'SATELLITE_SERVER') }}" ---
foreman_username: "{{ lookup('env', 'SATELLITE_USERNAME') }}" foreman_server_url: "{{ lookup('env', 'SATELLITE_SERVER') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
foreman_password: "{{ lookup('env', 'SATELLITE_PASSWORD') }}" foreman_username: "{{ lookup('env', 'SATELLITE_USERNAME') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
foreman_validate_certs: "{{ lookup('env', 'FOREMAN_VALIDATE_CERTS') | default(true) }}" foreman_password: "{{ lookup('env', 'SATELLITE_PASSWORD') }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
capsule_server: "{{ foreman_server_url }}" foreman_validate_certs: "{{ lookup('env', 'FOREMAN_VALIDATE_CERTS') | default(true) }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
capsule_port: '9090' capsule_server: "{{ foreman_server_url }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
policy_name: 'all' capsule_port: '9090' # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
policy_scan: "{{ policy_name }}" policy_name: 'all' # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
crontab_hour: 2 policy_scan: "{{ policy_name }}" # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
crontab_minute: 0 crontab_hour: 2 # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
crontab_weekdays: 0 crontab_minute: 0 # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
foreman_operations_scap_client_secure_logging: true crontab_weekdays: 0 # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way
foreman_operations_scap_client_secure_logging: true # noqa var-naming[no-role-prefix] - TODO : we should rework roles to use variable prefix, until scope is defined, silence is the way

View File

@@ -1,37 +1,53 @@
--- ---
# This file is mainly used by product-demos CI,
# See cloin/ee-builds/product-demos-ee/requirements.yml
# for configuring collections and collection versions.
collections: collections:
- name: ansible.controller - name: ansible.controller
version: 4.4.0 version: ">=4.5.5"
- name: infra.ah_configuration
version: ">=2.0.6"
- name: infra.controller_configuration
version: ">=2.7.1"
- name: redhat_cop.controller_configuration - name: redhat_cop.controller_configuration
version: 2.3.1 version: ">=2.3.1"
# linux # linux
- name: redhat.insights - name: ansible.posix
version: 1.0.7 version: ">=1.5.4"
- name: redhat.rhel_system_roles
version: 1.20.0
- name: community.general - name: community.general
version: 6.3.0 version: ">=8.0.0"
- name: containers.podman - name: containers.podman
version: ">=1.12.1"
- name: redhat.insights
version: ">=1.2.2"
- name: redhat.rhel_system_roles
version: ">=1.23.0"
# windows # windows
- name: chocolatey.chocolatey
- name: community.windows
version: 1.12.0
- name: ansible.windows - name: ansible.windows
version: 1.13.0 version: ">=2.3.0"
- name: chocolatey.chocolatey
version: ">=1.5.1"
- name: community.windows
version: ">=2.2.0"
# cloud # cloud
- name: azure.azcollection
version: 1.14.0
- name: amazon.aws - name: amazon.aws
version: 5.2.0 version: ">=7.5.0"
# satellite # satellite
- name: redhat.satellite - name: redhat.satellite
version: 3.8.0 version: ">=4.0.0"
# network # network
- name: cisco.ios
version: 4.4.0
- name: cisco.nxos
version: 4.1.0
- name: cisco.iosxr
version: 5.0.0
- name: ansible.netcommon - name: ansible.netcommon
version: 5.0.0 version: ">=6.0.0"
- name: cisco.ios
version: ">=7.0.0"
- name: cisco.iosxr
version: ">=8.0.0"
- name: cisco.nxos
version: ">=7.0.0"
# openshift
- name: kubernetes.core
version: ">=4.0.0"
- name: redhat.openshift
version: ">=3.0.1"
- name: redhat.openshift_virtualization
version: ">=1.4.0"

View File

@@ -26,8 +26,10 @@ This category of demos shows examples of linux operations and management with An
- [**Linux / Fact Scan**](https://github.com/ansible/awx-facts-playbooks/blob/master/scan_facts.yml) - Run a fact, package, and service scan against a system and store in fact cache - [**Linux / Fact Scan**](https://github.com/ansible/awx-facts-playbooks/blob/master/scan_facts.yml) - Run a fact, package, and service scan against a system and store in fact cache
- [**Linux / Podman Webserver**](podman.yml) - Install and run a Podman webserver with given text on the home page - [**Linux / Podman Webserver**](podman.yml) - Install and run a Podman webserver with given text on the home page
- [**Linux / System Roles**](system_roles.yml) - Apply Linux system roles to servers. Must provide variables and role names. - [**Linux / System Roles**](system_roles.yml) - Apply Linux system roles to servers. Must provide variables and role names.
- [**Linux / Compliance Enforce**](compliance.yml) - Apply remediation to meet the requirements of a compliance baseline - [**Linux / DISA STIG**](compliance.yml) - Apply the RHEL STIG supplemental content from DISA
- [**Linux / Insights Compliance Scan**](insights_compliance_scan.yml) - Run a Compliance scan based on the configuration in [Red Hat Insights][https://console.redhat.com] - [**Linux / Multi-profile compliance**](compliance-enforce.yml) - Apply remediation from [Compliance as Code](https://github.com/ComplianceAsCode/content) to enforce the requirements of a specified compliance profile
- [**Linux / Report Compliance**](compliance-report.yml) - Run an OpenSCAP report against a specified compliance profile
- [**Linux / Insights Compliance Scan**](insights_compliance_scan.yml) - Run a Compliance scan based on the configuration in [Red Hat Insights](https://console.redhat.com)
### Inventory ### Inventory
@@ -86,6 +88,10 @@ timesync_ntp_servers:
pool: yes pool: yes
iburst: yes iburst: yes
``` ```
**Linux / Compliance** - Apply compliance profile hardening configuration from [here](https://galaxy.ansible.com/RedHatOfficial). BE AWARE: this could have unintended results based on the current state of your machine. Always test on a single machine before distributing at scale. For example, AWS instances have NOPASSWD allowed for sudo. Running STIG compliance without adding `sudo_remove_nopasswd: false` to extra_vars on the job template will lock you out of the machine. This variable is configured on the job template by default for this reason. **Linux / DISA STIG** - Apply the RHEL STIG security hardening configuration using the [DISA Supplemental Automation Content](https://public.cyber.mil/stigs/supplemental-automation-content/). BE AWARE: this could have unintended results based on the current state of your machine. Always test on a single machine before distributing at scale. For example, AWS instances have NOPASSWD allowed for sudo. Running STIG compliance without adding `sudo_remove_nopasswd: false` to extra_vars on the job template will lock you out of the machine. This variable is configured on the job template by default for this reason.
**Linux / Multi-profile Compliance** - Apply security hardening configuration from a [supported compliance profile role](compliance_profiles.md). BE AWARE: this could have unintended results based on the current state of your machine. Always test on a single machine before distributing at scale. For example, AWS instances have NOPASSWD allowed for sudo. Applying certain compliance profiles without adding `sudo_remove_nopasswd: false` to extra_vars on the job template will lock you out of the machine. This variable is configured on the job template by default for this reason.
**Linux / Report Compliance** - Run this template before running the "**Linux / Multi-profile Compliance**" template and again afterwards to highlight the changes made by the enforcement template. By default, the reports are available by pointing a web browser to the system(s) where the report runs. By setting the `use_httpd` variable to "false" in the template survey the reports will instead be stored on the target node in the /tmp/oscap-reports directory.
**Linux / Insights Compliance Scan** - Scan the system according to the compliance profile configured via [Red Hat Insights](https://console.redhat.com). NOTE: This job will fail if the systems haven't been registered with Insights and associated with a relevant compliance profile. A survey when running the job will ask if you have configured all systems with a compliance profile, and effectively skip all tasks in the job template if the answer is "No". **Linux / Insights Compliance Scan** - Scan the system according to the compliance profile configured via [Red Hat Insights](https://console.redhat.com). NOTE: This job will fail if the systems haven't been registered with Insights and associated with a relevant compliance profile. A survey when running the job will ask if you have configured all systems with a compliance profile, and effectively skip all tasks in the job template if the answer is "No".

View File

@@ -0,0 +1,16 @@
---
- name: Apply compliance profile
hosts: "{{ _hosts | default(omit) }}"
become: true
vars:
compliance_profile: undef
tasks:
- name: Check os type
ansible.builtin.assert:
that: "ansible_os_family == 'RedHat'"
- name: Run Compliance Profile
ansible.builtin.include_role:
name: "redhatofficial.rhel{{ ansible_distribution_major_version }}_{{ compliance_profile }}"
...

Some files were not shown because too many files have changed in this diff Show More