diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
commit | a453ac31f3428614cceb99027f8efbdb9258a40b (patch) | |
tree | f61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/amazon | |
parent | Initial commit. (diff) | |
download | ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.tar.xz ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.zip |
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/amazon')
455 files changed, 75835 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/amazon/aws/.github/BOTMETA.yml b/collections-debian-merged/ansible_collections/amazon/aws/.github/BOTMETA.yml new file mode 100644 index 00000000..5612e8b5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/.github/BOTMETA.yml @@ -0,0 +1,110 @@ +automerge: false +files: + maintainers: $team_aws + docs/: + labels: docs + $action/: + labels: action + $action/aws_s3.py: + $callback/: + labels: callback + $doc_fragments/: + labels: doc_fragments + $doc_fragments/aws.py: + $doc_fragments/aws_credentials.py: + $doc_fragments/aws_region.py: + $doc_fragments/ec2.py: + $inventory/: + labels: inventory + $inventory/aws_ec2.py: + labels: inventory + $inventory/aws_rds.py: + labels: inventory + $lookup/: + labels: lookup + $module_utils/: + labels: module_utils + $module_utils/ec2.py: + labels: ec2 + $modules/: + authors: wimnat + maintainers: $team_aws + ignore: erydo nadirollo seiffert tedder + labels: modules + $modules/_aws_az_facts.py: + authors: Sodki + $modules/_aws_caller_facts.py: + authors: orthanc sdubrul + $modules/_cloudformation_facts.py: + authors: jmenga waffie1 + $modules/_ec2_ami_facts.py: + authors: prasadkatti + $modules/_ec2_group_facts.py: + authors: Sodki + $modules/_ec2_vpc_dhcp_option_facts.py: + authors: naslanidis + $modules/aws_az_info.py: + authors: Sodki + $modules/aws_caller_info.py: + authors: orthanc sdubrul + $modules/aws_s3.py: + authors: lwade s-hertel + $modules/cloudformation.py: + authors: jsmartin + $modules/cloudformation_info.py: + authors: jmenga waffie1 + $modules/ec2.py: + authors: lwade skvidal tgerla + labels: ec2 + ignore: erydo nadirollo seiffert skvidal + $modules/ec2_ami.py: + authors: Constantin07 gunzy83 scicoin-project wilvk + $modules/ec2_ami_info.py: + authors: prasadkatti + $modules/ec2_elb_lb.py: + authors: jsdalton + $modules/ec2_group.py: + authors: adq + $modules/ec2_group_info.py: + authors: Sodki + $modules/ec2_key.py: + authors: prasadkatti zbal + $modules/ec2_metadata_facts.py: + authors: roadmapper silviud + $modules/ec2_snapshot.py: + authors: willthames + $modules/ec2_tag.py: + authors: flowerysong lwade + $modules/ec2_tag_info.py: + authors: tremble + maintainers: jillr s-hertel + $modules/ec2_vol.py: + authors: lwade + $modules/ec2_vpc_dhcp_option.py: + authors: joelthompson + $modules/ec2_vpc_dhcp_option_info.py: + authors: naslanidis + $modules/ec2_vpc_net.py: + authors: defionscode s-hertel + ignore: defionscode joshsouza ryansb simplesteph + $modules/ec2_vpc_net_info.py: + maintainers: whiter + $modules/ec2_vpc_subnet.py: + authors: brandond erydo + scripts/inventory/: + labels: inventory + tests/: + labels: tests + tests/integration/: + labels: integrations + tests/units/: + labels: units +macros: + action: plugins/action + callback: plugins/callback + doc_fragments: plugins/doc_fragments + inventory: plugins/inventory + lookup: plugins/lookup + module_utils: plugins/module_utils + modules: plugins/modules + team_aws: jillr s-hertel tremble diff --git a/collections-debian-merged/ansible_collections/amazon/aws/.github/settings.yml b/collections-debian-merged/ansible_collections/amazon/aws/.github/settings.yml new file mode 100644 index 00000000..b27b575f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/.github/settings.yml @@ -0,0 +1,5 @@ +# DO NOT MODIFY + +# Settings: https://probot.github.io/apps/settings/ +# Pull settings from https://github.com/ansible-collections/.github/blob/master/.github/settings.yml +_extends: ".github" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/.gitignore b/collections-debian-merged/ansible_collections/amazon/aws/.gitignore new file mode 100644 index 00000000..6058f0fa --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/.gitignore @@ -0,0 +1,390 @@ + +# Created by https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv +# Edit at https://www.gitignore.io/?templates=git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv + +### dotenv ### +.env + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +#!! ERROR: jupyternotebook is undefined. Use list command to see defined gitignore types !!# + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### PyCharm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### pydev ### +.pydevproject + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### WebStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/**/sonarlint/ + +# SonarQube Plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Antsibull-changelog +changelogs/.plugin-cache.yaml + +# End of https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv diff --git a/collections-debian-merged/ansible_collections/amazon/aws/CHANGELOG.rst b/collections-debian-merged/ansible_collections/amazon/aws/CHANGELOG.rst new file mode 100644 index 00000000..e69e4df9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/CHANGELOG.rst @@ -0,0 +1,157 @@ +=========================== +community.aws Release Notes +=========================== + +.. contents:: Topics + + +v1.4.0 +====== + +Minor Changes +------------- + +- aws_ec2 - Add hostname options concatenation +- aws_ec2 inventory plugin - avoid a superfluous import of ``ansible.utils.display.Display`` (https://github.com/ansible-collections/amazon.aws/pull/226). +- aws_ec2 module - Replace inverse aws instance-state-name filters !terminated, !shutting-down in favor of postive filters pending, running, stopping, stopped. Issue 235. (https://github.com/ansible-collections/amazon.aws/pull/237) +- aws_secret - add ``bypath`` functionality (https://github.com/ansible-collections/amazon.aws/pull/192). +- ec2_key - add AWSRetry decorator to automatically retry on common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/213). +- ec2_vol - Add support for gp3 volumes and support for modifying existing volumes (https://github.com/ansible-collections/amazon.aws/issues/55). +- module_utils/elbv2 - add logic to compare_rules to suit Values list nested within dicts unique to each field type. Fixes issue (https://github.com/ansible-collections/amazon.aws/issues/187) +- various AWS plugins and module_utils - Cleanup unused imports (https://github.com/ansible-collections/amazon.aws/pull/217). + +Bugfixes +-------- + +- ec2_vol - a creation or update now returns a structure with an up to date list of tags (https://github.com/ansible-collections/amazon.aws/pull/241). + +v1.3.0 +====== + +Minor Changes +------------- + +- aws_caller_info - add AWSRetry decorator to automatically retry on common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/208) +- aws_s3 - Add support for uploading templated content (https://github.com/ansible-collections/amazon.aws/pull/20). +- aws_secret - add "on_missing" and "on_denied" option (https://github.com/ansible-collections/amazon.aws/pull/122). +- ec2_ami - Add retries for ratelimiting related errors (https://github.com/ansible-collections/amazon.aws/pull/195). +- ec2_ami - fixed and streamlined ``max_attempts`` logic when waiting for AMI creation to finish (https://github.com/ansible-collections/amazon.aws/pull/194). +- ec2_ami - increased default ``wait_timeout`` to 1200 seconds (https://github.com/ansible-collections/amazon.aws/pull/194). +- ec2_ami_info - Add retries for ratelimiting related errors (https://github.com/ansible-collections/amazon.aws/pull/195). +- ec2_eni - Improve reliability of the module by adding waiters and performing lookups by ENI ID rather than repeated searches (https://github.com/ansible-collections/amazon.aws/pull/180). +- ec2_eni_info - Improve reliability of the module by adding waiters and performing lookups by ENI ID rather than repeated searches (https://github.com/ansible-collections/amazon.aws/pull/180). +- ec2_group - add AWSRetry decorator to automatically retry on common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/207) +- ec2_group_info - add AWSRetry decorator to automatically retry on common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/207) +- ec2_snapshot_info - add AWSRetry decorator to automatically retry on common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/208) +- ec2_vol - Add automatic retries on AWS rate limit errors (https://github.com/ansible-collections/amazon.aws/pull/199). +- ec2_vol - ported ec2_vol to use boto3 (https://github.com/ansible-collections/amazon.aws/pull/53). +- ec2_vpc_dhcp_option_info - add AWSRetry decorator to automatically retry on common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/208) +- module_utils/core - add helper function ``scrub_none_parameters`` to remove params set to ``None`` (https://github.com/ansible-collections/community.aws/issues/251). +- module_utils/waiters - Add retries to our waiters for the same failure codes that we retry with AWSRetry (https://github.com/ansible-collections/amazon.aws/pull/185) +- s3_bucket - Add support for managing the ``public_access`` settings (https://github.com/ansible-collections/amazon.aws/pull/171). + +Bugfixes +-------- + +- ec2 - Code fix so module can create ec2 instances with ``ec2_volume_iops`` option (https://github.com/ansible-collections/amazon.aws/pull/177). +- ec2 - ignore terminated instances and instances that are shutting down when starting and stopping (https://github.com/ansible-collections/amazon.aws/issues/146). +- ec2_group - Fixes error handling during tagging failures (https://github.com/ansible-collections/amazon.aws/issues/210). +- ec2_group_info - Code fix so module works with Python 3.8 (make dict immutable in loop) (https://github.com/ansible-collections/amazon.aws/pull/181) + +v1.2.1 +====== + +Minor Changes +------------- + +- ec2_eni - Add support for tagging. +- ec2_eni - Port ec2_eni module to boto3 and add an integration test suite. +- ec2_eni_info - Add retries on transient AWS failures. +- ec2_eni_info - Add support for providing an ENI ID. + +v1.2.0 +====== + +Minor Changes +------------- + +- ec2 module_utils - Update ``ec2_connect`` (boto2) behaviour so that ``ec2_url`` overrides ``region``. +- module_utils.core - Support passing arbitrary extra keys to fail_json_aws, matching capabilities of fail_json. + +Deprecated Features +------------------- + +- All AWS Modules - ``aws_access_key``, ``aws_secret_key`` and ``security_token`` will be made mutually exclusive with ``profile`` after 2022-06-01. + +Bugfixes +-------- + +- ec2 module_utils - Ensure boto3 verify parameter isn't overridden by setting a profile (https://github.com/ansible-collections/amazon.aws/issues/129) +- s3_bucket - Ceph compatibility: treat error code NoSuchTagSetError used by Ceph synonymously to NoSuchTagSet used by AWS + +v1.1.0 +====== + +Major Changes +------------- + +- ec2 module_utils - The ``AWSRetry`` decorator no longer catches ``NotFound`` exceptions by default. ``NotFound`` exceptions need to be explicitly added using ``catch_extra_error_codes``. Some AWS modules may see an increase in transient failures due to AWS''s eventual consistency model. + +Minor Changes +------------- + +- Add `aws_security_token`, `aws_endpoint_url` and `endpoint_url` aliases to improve AWS module parameter naming consistency. +- Add support for `aws_ca_bundle` to boto3 based AWS modules +- Add support for configuring boto3 profiles using `AWS_PROFILE` and `AWS_DEFAULT_PROFILE` +- Added check_mode support to aws_az_info +- Added check_mode support to ec2_eni_info +- Added check_mode support to ec2_snapshot_info +- ansible_dict_to_boto3_filter_list - convert integers and bools to strings before using them in filters. +- aws_direct_connect_virtual_interface - add direct_connect_gateway_id parameter. This field is only applicable in private VIF cases (public=False) and is mutually exclusive to virtual_gateway_id. +- cloudformation - Return change_set_id in the cloudformation output if a change set was created. +- ec2 - deprecate allowing both group and group_id - currently we ignore group_id if both are passed. +- ec2_ami_info - allow integer and bool values for filtering images (https://github.com/ansible/ansible/issues/43570). +- ec2_asg - Add support for Max Instance Lifetime +- ec2_asg - Add the ability to use mixed_instance_policy in launch template driven autoscaling groups +- ec2_asg - Migrated to AnsibleAWSModule +- ec2_placement_group - make `name` a required field. +- ec2_vol_info - Code cleanup and use of the AWSRetry decorator to improve stability +- ec2_vpc_net - Enable IPv6 CIDR assignment + +Breaking Changes / Porting Guide +-------------------------------- + +- aws_s3 - can now delete versioned buckets even when they are not empty - set mode to delete to delete a versioned bucket and everything in it. + +Deprecated Features +------------------- + +- cloudformation - The ``template_format`` option had no effect since Ansible 2.3 and will be removed after 2022-06-01 +- cloudformation - the ``template_format`` option has been deprecated and will be removed in a later release. It has been ignored by the module since Ansible 2.3. +- data_pipeline - The ``version`` option had no effect and will be removed in after 2022-06-01 +- ec2 - in a later release, the ``group`` and ``group_id`` options will become mutually exclusive. Currently ``group_id`` is ignored if you pass both. +- ec2_ami - The ``no_device`` alias ``NoDevice`` has been deprecated and will be removed after 2022-06-01 +- ec2_ami - The ``virtual_name`` alias ``VirtualName`` has been deprecated and will be removed after 2022-06-01 +- ec2_eip - The ``wait_timeout`` option had no effect and will be removed after 2022-06-01 +- ec2_key - The ``wait_timeout`` option had no effect and will be removed after 2022-06-01 +- ec2_key - The ``wait`` option had no effect and will be removed after 2022-06-01 +- ec2_key - the ``wait_timeout`` option has been deprecated and will be removed in a later release. It has had no effect since Ansible 2.5. +- ec2_key - the ``wait`` option has been deprecated and will be removed in a later release. It has had no effect since Ansible 2.5. +- ec2_lc - The ``associate_public_ip_address`` option had no effect and will be removed after 2022-06-01 +- ec2_tag - deprecate the ``list`` option in favor of ec2_tag_info +- ec2_tag - support for ``list`` as a state has been deprecated and will be removed in a later release. The ``ec2_tag_info`` can be used to fetch the tags on an EC2 resource. + +Bugfixes +-------- + +- aws_ec2 - fix idempotency when managing tags +- aws_ec2 - fix idempotency when metrics are enable +- aws_s3 - Delete objects and delete markers so versioned buckets can be removed. +- aws_s3 - Try to wait for the bucket to exist before setting the access control list. +- cloudformation_info - Fix a KeyError returning information about the stack(s). +- ec2_asg - Ensure "wait" is honored during replace operations +- ec2_launch_template - Update output to include latest_version and default_version, matching the documentation +- ec2_transit_gateway - Use AWSRetry before ClientError is handled when describing transit gateways +- ec2_transit_gateway - fixed issue where auto_attach set to yes was not being honored (https://github.com/ansible/ansible/issues/61907) +- ec2_vol - fix filtering bug +- s3_bucket - Accept XNotImplemented response to support NetApp StorageGRID. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/CONTRIBUTING.md b/collections-debian-merged/ansible_collections/amazon/aws/CONTRIBUTING.md new file mode 100644 index 00000000..cead3517 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing + +## Getting Started + +General information about setting up your Python environment, testing modules, +Ansible coding styles, and more can be found in the [Ansible Community Guide]( +https://docs.ansible.com/ansible/latest/community/index.html). + +Information about boto library usage, module utils, testing, and more can be +found in the [AWS Guidelines](https://docs.ansible.com/ansible/devel/dev_guide/platforms/aws_guidelines.html) +documentation. + +## AWS Collections + +There are two related collections containing AWS content (modules and plugins). + +### amazon.aws +This collection contains the `module_utils` (shared libraries) used by both collections. +Content in this collection is included downstream in Red Hat Ansible Automation Platform. + +Code standards, test coverage, and other supportability criteria may be higher in this collection. + +The `amazon.aws` collection is an [Ansible-maintained collection](https://docs.ansible.com/ansible/devel/community/contributing_maintained_collections.html). + +### community.aws +This collection contains modules and plugins contributed and maintained by the Ansible AWS +community. The `community.aws` collection is tested and generally assured to work in +conjunction with `amazon.aws`. + +New modules and plugins developed by the community should be proposed to `community.aws`. +Content in this collection that is stable and meets other acceptance criteria has the potential +to be promoted and migrated into `amazon.aws`. + +## Submitting Issues +All software has bugs, and the `amazon.aws` collection is no exception. When you find a bug, +you can help tremendously by [telling us about it](https://github.com/ansible-collections/amazon.aws/issues/new/choose). + +If you should discover that the bug you're trying to file already exists in an issue, +you can help by verifying the behavior of the reported bug with a comment in that +issue, or by reporting any additional information + +## Pull Requests + +All modules MUST have integration tests for new features. Upgrading to boto3 shall be considered a feature request. +Bug fixes for modules that currently have integration tests SHOULD have tests added. +New modules should be submitted to the [community.aws](https://github.com/ansible-collections/community.aws) collection +and MUST have integration tests. + +Expected test criteria: +* Resource creation under check mode +* Resource creation +* Resource creation again (idempotency) under check mode +* Resource creation again (idempotency) +* Resource modification under check mode +* Resource modification +* Resource modification again (idempotency) under check mode +* Resource modification again (idempotency) +* Resource deletion under check mode +* Resource deletion +* Resource deletion (of a non-existent resource) under check mode +* Resource deletion (of a non-existent resource) + +Where modules have multiple parameters we recommend running through the 4-step modification cycle for each parameter the module accepts, as well as a modification cycle where as most, if not all, parameters are modified at the same time. + +For general information on running the integration tests see the +[Integration Tests page of the Module Development Guide](https://docs.ansible.com/ansible/devel/dev_guide/testing_integration.html#testing-integration), +especially the section on configuration for cloud tests. For questions about writing tests the Ansible AWS community can +be found on Freenode IRC as detailed below. + + +### Code of Conduct +The `amazon.aws` collection follows the Ansible project's +[Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html). +Please read and familiarize yourself with this document. + +### IRC +Our IRC channels may require you to register your nickname. If you receive an error when you connect, see +[Freenode's Nickname Registration guide](https://freenode.net/kb/answer/registration) for instructions + +The `#ansible-aws` channel on Freenode irc is the main and official place to discuss use and development +of the `amazon.aws` collection. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/COPYING b/collections-debian-merged/ansible_collections/amazon/aws/COPYING new file mode 100644 index 00000000..10926e87 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/COPYING @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/FILES.json b/collections-debian-merged/ansible_collections/amazon/aws/FILES.json new file mode 100644 index 00000000..f91811d0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/FILES.json @@ -0,0 +1,4338 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/changelog.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c11f14f5af90e54bf60a276db5a6ad365118c7fec40ecc41d2e40cef91f2c091", + "format": 1 + }, + { + "name": "changelogs/config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a5108e9a705d8037b5e214c95ff2bba76e09c8ff4c391c144f1f6f7a5edb051f", + "format": 1 + }, + { + "name": "changelogs/fragments", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/fragments/.keep", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "CHANGELOG.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c2e0baff48431908d7716a6a0fab8e5f679f90d9c40921cae93e3974c5865ff2", + "format": 1 + }, + { + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe8e222150e8f462c17cb8616ad8de4e39f3be13c469bf3d629edd7c0d30d65a", + "format": 1 + }, + { + "name": "tests/integration/targets", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6104b125462eb5b6c5e5067e6c5b9041f0804c29755200fda62f0472a4a29f1e", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/templates/boto_config.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba7335ce0c8b8a32fc82bf7522a0f93d69190ff9895f4804985d2c08b7b3fd37", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/setup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d416d3ebcd9ea58c450a07ec98a78f42423bde3fdf2396971c8af836169e7b17", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2c43952ee9e000ff2acc4e2ed0897c0a72ad5fa72c3d934e81741cbd54f05bd1", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "22b557a27055b33606b6559f37703928d3e4ad79f110b407d04986e1843543d1", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6a143b3afe5a63a2455faaeaaa91684381fd151bb0564127c55053fe24a23b63", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/library", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e673db3241a636fc7adc6a5671bc26c6c9801c023107f522d219e4da4c2940ba", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "707e210e0248bb78f07974b075bd9d4f51431f2548e07512a8208f5ae33978a3", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3f64a7de906e34cbe83554d810e3fd34cd7a207cd50d6ce1639b332ec9c17ca6", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "76d8276184fc168b0605e815cff209bf9c0ac2d44355897fa49df810f113db4c", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ab5dc8cea9fe6409d9e3b06981d1099b8f3fe5095cc456a4122409fcd375e9c4", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2c43952ee9e000ff2acc4e2ed0897c0a72ad5fa72c3d934e81741cbd54f05bd1", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "22b557a27055b33606b6559f37703928d3e4ad79f110b407d04986e1843543d1", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6a143b3afe5a63a2455faaeaaa91684381fd151bb0564127c55053fe24a23b63", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "257a81fed0b3e0550a69f3ca5b193b5e5c368cbc66ea71871fc43295f87b561a", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "707e210e0248bb78f07974b075bd9d4f51431f2548e07512a8208f5ae33978a3", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "96175177c117770c4dcbae1f55c2ae806a20c53932c55cf84b11a92024e83b7c", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "76d8276184fc168b0605e815cff209bf9c0ac2d44355897fa49df810f113db4c", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "421c1c2dc4df830214661bffd4f86ebfaa3a172ee7fa7e85a1ce6e933d1b9a72", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/ec2_connect.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "12b062e10935591224b45ef3b4d8541f11b22708f5a36baf61053529ae75c5db", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4514e38376fcaaeb52cb4841f3aeeb15370a01099c19e4f2ed6a5f287a49b89a", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7be7e4db58d37a19ccc989c5ebf0be883e51742a81941e9d29fc6055885fc99d", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fbc264a0f164ac3c9f9ec885da5e7c9eda894f01d567b7e68e9031f556f50aa4", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_ec2/connect_to_aws.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "38a11bb8d17cc41967fa50689a92713ca14a7c53f896c4d40c772cd0854c6313", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8b0b9969b59c016232538a26a80f2befd29e452824aeb16723213291153e035a", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8b64ebd46dd54314cfd660b03486d5786ab663547a43b5cca0a653159b709fc1", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_subnet/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1614b27ae5be38ffd45279f1196cb4eff20b11505203386e69d71d600834c6e8", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/templates/inventory_with_cache.yml.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "212ab399c1d1f49130ad7755b99b619def84d42129f1a7d4e66ba24fcbd76c10", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/templates/inventory_with_constructed.yml.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "052ed7f1c0238f101cd97972a209871be718417d34a08164a138a775646fa0bf", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/templates/inventory.yml.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7b9771837ad83a89cc76088bf8aa09be6f6d5e8c980f3ed4d72fb41fcb192af6", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/templates/inventory_with_concatenation.yml.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6777b9eaea5da24615ec813fcab4f75cfd6fb02870eff6021fad80ca104f505b", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dfd08779f7170cd572bd30bd277562ab0ba7d35bf6f94c67bf194ffb04c77ea9", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "529de310b12b1d514f5f89e3697076c50ec6c114180f883cf92d57f64dbb53a9", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/test.aws_ec2.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3c1670564284272619dee9b374aa21278cc265c8db4d014d9a7add29fffd1d3f", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/setup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "35e8c023d4aeff7399dd2b955fd50508d467e8d1f76f4612d48c5adba827f4b1", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_concatenation.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a72f35ed14d9205ceae2467a29b59906499bb97a3e2bdac56e8a19f05712f015", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/tear_down.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fb7b069cb3653ca58ed6793a4e85a414ea6e9843fba4547a2768367fc4fbe7c3", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/test_refresh_inventory.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9c4cfb83bbbbe73c2f0acfa8a3d694303b20cbc5d20bfab9b7dcdb1af6d267ac", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/test_invalid_aws_ec2_inventory_config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a57efe1ec08416ea90c3a80c03d0d3240a928933d5f46251acf97c9375b0a0e", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/test_inventory_cache.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f343af936f7105a81f703b55b5ed86bd3aab8b35ca6dc0672c5e5cca8dda3c16", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/populate_cache.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9aca47de38e5d7627fa0186adcc410920965b8fd2fbb8ede5cd2f19f7fad7198", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_constructed.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ce4856fea65d5a040c2e875e16e7f9e6e57835dac495ec6828bdc509ee96b4b1", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/empty_inventory_config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "44a9f1885f675a872bebe0a1af0c40551688c8ccc1aeb700e74926a8edf69278", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "605a7f63f212908dfca5b8f52a01def2c2cb06500c4c4bc33f7356d6b4eb35d9", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/handlers", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "050157a29c48915cf220b3cdcf5a032e53e359bdc4a210cd457c4836e8e32a4d", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2441ac1753320d2cd3bea299c160540e6ae31739ed235923ca478284d1fcfe09", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "766ab141899717320ba54e2bb1a6ba8cbc3cc7642d0023670154b49981ed1a91", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3fd85bd6c3cf51c061eb221197d5653e5da0e101543b3c037f5066d6c73b1501", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e29ee6a8db94d6de88c8458762f594f05d906f454f7c9977fd618d52b09e52f0", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e273324ab90d72180a971d99b9ab69f08689c8be2e6adb991154fc294cf1056e", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/templates/inventory.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1bf87d023bb7e807f4e8be7408e0fe4400d451d740402e7e9a64fbe0cc41d75b", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "04933e57c67f5052c8be5df91ee0656dd0af863d71c77dad580844d1f5b81c3c", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bc4362a0e08261f353f20a25bdff675183addfdca62c700c6d04315efb908f47", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/playbooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/playbooks/setup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9e6b33f19f7793a052f40551fa192acbe22874fdcc54576eb975c2eb04f57a80", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/playbooks/teardown.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e320cdd4309239a31c1a8733695308c6910c599faff3f6d0e62cc46fac3d178d", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_metadata_facts/playbooks/test_metadata.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe1ef6e4b1c97be4c5f0ce71c95e5540140067c5ca44635ce5657062617c88d5", + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/files/cf_template.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5f612313fe9e8c40c55eba290f6af3b814a3702cf728a6c5630e24f0e8787fa8", + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9749b5e6b87158663ff5b736c1a71d8fb6f1e80feefbc98eb1c9e37af5430202", + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2cb542a61d1e7428abc30a839c547019e6435295ebd00f3efb310f61069661f8", + "format": 1 + }, + { + "name": "tests/integration/targets/cloudformation/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "925e63579970a8ba468c7dac6a31faf1c79effcac055ace429b915e87b81044b", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b3db779a67fc33ee5db96cb2c912a70c6c072bb7461baefed35890bc6ea25f1d", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1712fb3e9db585017ca3a3c80209b1974957454860c6ad6cff4de5fb28fbb791", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f2281d88e947484d75d24198c279de09c5f72cad949ec6d052220723e8ad63e0", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/tasks/tests.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "322e30a7259c3ecff9ad611cacdefcc1192ed032dadda1b836f671deb8668477", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6b8c7b9958683b814cc8d374f3cdfa890092477544e190f98dc6049e5551f443", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vol/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2329fcab5278fe59cf85f3af6ccf908cb210cd0fd956ecf0b3cc834eee69911e", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e63089a34e6352d80cece0c5551d5a43c560295facbb549e9277c2c3e113afa2", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_attachment.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ce197a62dec62e6fb9e0154f58ad32e8d2f68bc883a2498adbf838cd087c0be3", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_ipaddress_assign.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f5c8339ca1e030209fb906c72b1bee2bfa3341332a47a4f1326aaf843796d0c2", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_modifying_delete_on_termination.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7f50e7e1c03369199470cbdf786672ed4fd162c4e3e3dd74126f59aed277a5ac", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_deletion.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "10ad55a7d48756d905cc88cd4d782a8a4433dcf5d45974cf5d0eab0874d3a884", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_modifying_tags.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9df827ae5ae90964bcb7635fdb2b97fa64247db11b80f4bbb318e87ddad9360f", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cf482252171594376f3002630fc71ddca1a9d7f3385746bf12e29ada86d23013", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_eni_basic_creation.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fc992583af2fdb54fa4ce80014c5f3b821538eca245c23b310fe35a5c4a4764d", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/tasks/test_modifying_source_dest_check.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "36f45d56422d2399cec3b26a7d5c1e82c60ddc64c191b20f1cd624474d4c7ce1", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_eni/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9f847717a0e89090d0e86bcdcc7c8bc9345965e40db00844b1f04ab337bb0b6", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6ac3c7909ab221450ea97faf81f0951713c66b46192253f34b254b473369b7fd", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2ac244b40203f0e9432527c3373b0cd55b08f0c4581ef2cafb51eebf5bf07068", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7be7e4db58d37a19ccc989c5ebf0be883e51742a81941e9d29fc6055885fc99d", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c11c67f4b66a693d3183caaee0abd97d2f02694e5998c0040b5f381dc68eeb3c", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2/tasks/common.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "386a6e24ee18825f0103bdbe6690b11da5beb2b2bdc53e842f50ce9b3c3fae69", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2/vars", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_ec2/vars/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "03b695815f4b4833868e52a22f310bcac3be7e6e40e10ed1dcb2a7c9b13556e4", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b756aced2d19afadd3589244b1937cc90f8a96f709d5ea966f6a55a96bc4d3a3", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e6fb06892e32d700d8ff0184ce094dd44bae709b2e5728e88c43a1706beb614b", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5b94eec6f2beccdb47497babc7fb72cbd169679aac13d799e3750df752fd96d0", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/vars", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_tag/vars/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "79db6a6656e23e90127a8759ccb5371abb6b58652f871c5e12c72d9387bec871", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "47d7f0170663266b9c80b357a113128c721f64f7782736c399471404ef6170be", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1e8d632f9db7209967c5b2f6d734bede09841acc7b898dafc19f31c72cee9929", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/tasks/tests.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5b3094196fc15f7a04f146d6f5340557c07a236179983a1a22199d4fcf2171a3", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bdfd79f34b90502c52cc59580cf68374eb252cce697bdb26b4f7925638ecd4b6", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_az_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "46949b818113f294754176e1bbd4620f3f320a9d985fd690da597e002db91ef6", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_snapshot", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_snapshot/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_snapshot/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "873903f9abb784a3e395685d19806c065347dad6f1ace7bc67638e3e842692e9", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_snapshot/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_snapshot/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f2082df10291e9fe95a5e2654f5e902dc9fbadb174c48bbf7ef94d4072305eed", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_snapshot/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "80cb66fdb877239c68c5e09113e872a193adfbd8db7e2d0a2d80c716d0c03dae", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "26aad832330421916caec9fe34ebc8d1bfa90d867b66ad745f4c12ebe84cc3c3", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b2abccc927cfe77a04c442fe4cb680cef1163c594f5b1b91afbb7769b8d392cf", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d883bda1a880e1cffe467cb1196bbe840f1673e27f1fd54db12c6a9aea0c3ea", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_key/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9819a2eafef4b14f0c325412689a7b0cc4a3e2b364fb4af1304783caa1971c9b", + "format": 1 + }, + { + "name": "tests/integration/targets/setup_sshkey", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_sshkey/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/setup_sshkey/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "016d4c789eb18f7d6cd03c5cd32300a5e1ccec5bb6695aa35b60105c0a5d5d2d", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_caller_info", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_caller_info/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_caller_info/tasks/main.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ee3b4355d2876a8648831474ce0b430c22c21035551ba77c0a125f4e2866a0e8", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_caller_info/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9819a2eafef4b14f0c325412689a7b0cc4a3e2b364fb4af1304783caa1971c9b", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/templates/session_credentials.yml.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6104b125462eb5b6c5e5067e6c5b9041f0804c29755200fda62f0472a4a29f1e", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/templates/boto_config.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba7335ce0c8b8a32fc82bf7522a0f93d69190ff9895f4804985d2c08b7b3fd37", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/setup.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d416d3ebcd9ea58c450a07ec98a78f42423bde3fdf2396971c8af836169e7b17", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/amazonroot.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2c43952ee9e000ff2acc4e2ed0897c0a72ad5fa72c3d934e81741cbd54f05bd1", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/isrg-x1.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "22b557a27055b33606b6559f37703928d3e4ad79f110b407d04986e1843543d1", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6a143b3afe5a63a2455faaeaaa91684381fd151bb0564127c55053fe24a23b63", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/library", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/library/example_module.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6bcaf886524922e05fae62d6b7efefd576925c7148e948fe0b43ba41f14bdb47", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/profiles.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bbc4902462428729910b71ea6bd2fb11013fad58e998be8f9f2e4a86f97a8387", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/ca_bundle.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "96f95ee62565f62141122c6ebf63bb25d472f88135703716f395ba64c8ed30d3", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/credentials.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "90995fadd544d2ac3490121a30cd7414fdb89495231bdf16535a6b6c7d491638", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b63ff3b3058da02396d2322c56e9fe7dd6ed282a247bcc841647ee7dab6e2127", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/endpoints.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7c8d0f5147bcb991f8f393e55d775d1eb135b38e5704f53ef2944efa85fc8d8d", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40fd2ac4ad62f120b0ab06ebc1b597f8df9a56b02772cff353ac457aa7cc6023", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4514e38376fcaaeb52cb4841f3aeeb15370a01099c19e4f2ed6a5f287a49b89a", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9819a2eafef4b14f0c325412689a7b0cc4a3e2b364fb4af1304783caa1971c9b", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_core/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0d48d5adc889ec75147bf7ed1200f2cd1cde582de74e2523b9687e0204167cb5", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_dhcp_option", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_dhcp_option/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_dhcp_option/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a1a63e4e346ae31af24867279086058701f3bdb09586918e6451fc4766459488", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_dhcp_option/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_dhcp_option/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "490bb182ad2f316922f891167aa5f62da65456b27dedb63c524194fa0cfb87bd", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_dhcp_option/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dfd08779f7170cd572bd30bd277562ab0ba7d35bf6f94c67bf194ffb04c77ea9", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/files/hello.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/files/test.png", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bae277f309fbffab9590300ccc1e75805c9795bbcef69edfda22c5b2327e12ba", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/templates/put-template.txt.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d8c9f0fc47011f7279babb0a29cb8f7812e4037c757d28e258d81ab7e82ca113", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6540dfee340bb1255b63cae84f0ee9df35ceaa7443df77c396c381d89c59e858", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a36bf856e6cb5a43da98e2967d50337b183e36c6164b7b917c172ba3f5d53f1c", + "format": 1 + }, + { + "name": "tests/integration/targets/aws_s3/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7be7e4db58d37a19ccc989c5ebf0be883e51742a81941e9d29fc6055885fc99d", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6a143b3afe5a63a2455faaeaaa91684381fd151bb0564127c55053fe24a23b63", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter/library", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter/library/example_module.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bc44c40027380e6a9a3a956be9f78bec67c8380287860c7db30f0f03d9e76cee", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/roles/get_waiter/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0ba97256d76043838f14cc1e067aeb46643d4c1d40defca3f8332fe8c2de157a", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2013d9803d3dfbf66388e1ef4228f2d74d348f524c01c3018bc7b464c0ec88b8", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4514e38376fcaaeb52cb4841f3aeeb15370a01099c19e4f2ed6a5f287a49b89a", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9819a2eafef4b14f0c325412689a7b0cc4a3e2b364fb4af1304783caa1971c9b", + "format": 1 + }, + { + "name": "tests/integration/targets/module_utils_waiter/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b36bef221fbf1264fb6d387a52e5ca42d167ef7973225a30c7cd6005d6494ca4", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy-updated.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ac915127d3f199f92c67fd3ddf98b30186fe3eeb95f362f6d1bfe0c667e96b63", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "67f56078d3189e6ed7b781f96399453df8a1862659886a5ad595420a2018f380", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5d03623caeb5e4db80f7aa835297b445780c70c7693cc2770b77b426556340ce", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8e70400d05c6717b0a358ccb693d782e120d9dd534973734b083406a08c7525", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/tags.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eb9f4e0fc85a60bb15ba2bbc5036d8c38ec0e7ac8d9ee52566644f169e569afb", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/dotted.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "381ebad3ee826f273b781bf8a279b76fa7265204db4a0e4a06a05989b694a7a0", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eb329e1232fcd539f96bda674734113096dac7d481948b0cec7cb375866ce8db", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_kms.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3875b9d162c2ed0d5122485a60747cbeab4c12f63e19043721ada6e750a8f558", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/complex.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fd8459be71890eb3f694f433535114246b2f7d7297a1746e46093734d790ef92", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/public_access.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4015b3e0a746f3994ac7bf2d56f8ca5a876f1ad6046841ca85f4ff7156efd30e", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/missing.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cedf599310d119d5bc70742ebbad144ed648b61b342e01dd9ef20ea744d2e4a3", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_sse.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a3d3210c2c7473e9271cca042f4abd5001b7f5ddf1ac9c42527a3a1a66fe8c81", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/simple.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ecbc66cf1dbc8f863fb52ef8d07271c1c3bd3a94c906ec43024a9b1d325eaba1", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8395f20d527042f70de0e5a24a1db4d728bac43bcde06c3ac053c885774e0e6a", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/inventory", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "88b92b8c6612050e67525f2284089924f4fbd89d528ead7cba3f8583ec0770c3", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8e70400d05c6717b0a358ccb693d782e120d9dd534973734b083406a08c7525", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dfd08779f7170cd572bd30bd277562ab0ba7d35bf6f94c67bf194ffb04c77ea9", + "format": 1 + }, + { + "name": "tests/integration/targets/s3_bucket/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d2e53b13c18d9f57b9ac05cf209ab9ea0db765e0b8c4e0698e26747cef903d23", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "06a9acc0627c1ed030eca26a0013f8044e3001105a91983dd26a9a1f55599106", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "acfd289610d14642bef5ae982d7098b7259a5b1ac01f18c835721b0aa612e7c3", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9819a2eafef4b14f0c325412689a7b0cc4a3e2b364fb4af1304783caa1971c9b", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/vars", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_elb_lb/vars/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a37372c5df29a85df3d7759887f11d5caceba506dfd51e32059f86f8fa879c8b", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9623c681ca59d28bbc7ba1e8f4d03d2a8205f0cfd6686eb7ba87a820fb619303", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e43ccfc02d4588f0df454c2d08c4a0afe8f4c058a5abc3ad7bedce2fbec344cf", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4ab18b39f059ed5526197677441560c3d4d1be8ee846a32da74893a3e5f133bb", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/vars", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_ami/vars/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ac9125dea1e9dfcac93d6142fe3deb7f2d84c6f25c9c5ed72718073ad304fe9", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b2f7ee850fd4a897e2bb408586fde1de3fcf1e73159f7d2fdb4451621c413fe0", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ff19b83ede3e2c9210e830f515b86c52f8efa3c3f5c2a58ca14e6f7682b24e56", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_vpc_net/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5e49f9e68fff79eefcd3d2214f47d0042418e8a500351e77dd07f3d35c751887", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0f708cce7788b24124e9ac7b36c00ebefe26cc05ce69404f5a6538b09a928e0a", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "addf553f8dde7a6ec25f08591c282eee5491f9815963c51285a4c00294af2863", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/ipv6_default_tests.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f5e31c187ae076f3fc2f56b32526515b419319301459030a2dfccb9ed48c5887", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/rule_group_create.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3e6ba49498995b770799754f49f565d14c1b7d9ab50848beaccb65aa527100a6", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/group_info.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe9c254d9db27cb08dd78f8a915affa46b8c29bd3910c8bf36fc6a6887f94dda", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/multi_account.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c5249cb541d660e400607344b991860732c733b0db1b02a471b9e1a531446a49", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/credential_tests.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9a42387e9e3c5a0339b52d2d26e282a113c6017e8eaee5f29cf4ace3250f75b7", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/diff_mode.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a2e7c53854f63ff9d694e53c71d918577b9db2813e898844c1e218fb717be1f9", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "863ad64dc7b11c1587ac64e3492aef457e8a71b0e51b893bbf0896a3f4bc4171", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/ec2_classic.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a73d5c1b081c005988fef557e489304eaa5e3c336e75d4630930e316c64cf86c", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/data_validation.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "abdc617375c38e979faec977c117e0222b562dd57790967cd70285eae414a564", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/multi_nested_target.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c0e3bf023c0515b10dc60136e6764b152d38f2235df06d4c566d7140c8ebd47a", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/egress_tests.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "45866ac187b9b2d08e62c7192534c1fb4324d1074c7ce0e99f23af7a4542725b", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/tasks/numeric_protos.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "255ae824e4a300df540242151e8cc8035b06646af0761009dcd4b68dfd807579", + "format": 1 + }, + { + "name": "tests/integration/targets/ec2_group/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "232e1f5f95608b31744b51bd7546eea3404c39d8494dff7cca0d29020dc1b9cf", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/templates/inventory.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "046bbce61938b67a8f51d9e99de64b82a588659550436b858d10975ddaf716ce", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/templates/inventory_with_cache.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "54ede14b2ec95c3c6606905775d3885120039da90433e409ad5002ad78c65d5b", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/templates/inventory_with_constructed.j2", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0325381aecfd342ec4baa42347d91b8b2267b29bfb0b053443729370c906b749", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/aliases", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b6b7573399ec5210a67f93fa47cb62827da6839b4ce43490bbfa70d51e731259", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "20ef326de7a3c0f50b51c8598ed5edd3b58c7a1c408dc6b43fef9334d8379f91", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/test.aws_rds.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ba250c63bc4a8712322cca3f0b5b3d54057c8f53f464c2b27567a8f930cb301a", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/test_invalid_aws_rds_inventory_config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b22eb19a90f4ac43ea966bd586df79c6ada6ef3e6a6e46df2f5b65cf82e4f00a", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/test_refresh_inventory.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d23d9fc75960645599aacb9b4796dcdead6938b92ca9abc4188609a9335d39eb", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/test_inventory_cache.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "79c8d37631bfbc5a896140e0c9ca74f4144f51d5a161da353fab4026ac797d8c", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/populate_cache.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c408a3a38bbd609623acb08a01fc1c14638bc5984287ba81e7ff50938b8e73b7", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory_with_constructed.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "caf7202e4c03ab6654cb6a4aa507af3c4ac42832ea0ac76a936502c6283b4260", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/empty_inventory_config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "31b80c73e9e0abea01d5836da0de13fa1bf5a391313b4543ad8bdd2adfd415cf", + "format": 1 + }, + { + "name": "tests/integration/targets/inventory_aws_rds/playbooks/create_inventory_config.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1424ca34811cf10a2176b56269860dcc9e82cdfc3e7bc91db10658aceb8f11e0", + "format": 1 + }, + { + "name": "tests/integration/targets/prepare_tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/prepare_tests/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/prepare_tests/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/mock", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/mock/procenv.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3d53f1c9e04f808df10e62a3eddb460cc8251d03a2f89c0cbd907d09b5c785d9", + "format": 1 + }, + { + "name": "tests/unit/mock/yaml_helper.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fada9f3506c951e21c60c2a0e68d3cdf3cadd71c8858b2d14a55c4b778f10983", + "format": 1 + }, + { + "name": "tests/unit/mock/vault_helper.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4535613601c419f7d20f0c21e638dabccf69b4a7fac99d5f6f9b81d1519dafd6", + "format": 1 + }, + { + "name": "tests/unit/mock/path.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c44806a59e879ac95330d058f5ea6177d0db856f6e8d222f2ac70e9df31e5e12", + "format": 1 + }, + { + "name": "tests/unit/mock/loader.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c45064a0beb765cd0b6cfbb74ca0cd491ceed2f4c2d22808f60a57071d9712cc", + "format": 1 + }, + { + "name": "tests/unit/mock/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/compat", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/compat/builtins.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7163336aa20ba9db9643835a38c25097c8a01d558ca40869b2b4c82af25a009c", + "format": 1 + }, + { + "name": "tests/unit/compat/mock.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0af958450cf6de3fbafe94b1111eae8ba5a8dbe1d785ffbb9df81f26e4946d99", + "format": 1 + }, + { + "name": "tests/unit/compat/unittest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5401a046e5ce71fa19b6d905abd0f9bdf816c0c635f7bdda6730b3ef06e67096", + "format": 1 + }, + { + "name": "tests/unit/compat/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "653eef28acdaac13c5b0d35736ad45adde454ecfce69ca4d887bb4783c9052af", + "format": 1 + }, + { + "name": "tests/unit/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/module_utils/test_iam.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2798ce8132ee7ec1e88799ddb6f393d4ac0f7d1b5a95a62872fe9b8fa29d5894", + "format": 1 + }, + { + "name": "tests/unit/module_utils/test_ec2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8836d0e6ac39d93a6c5bf4f831b27cc4fb82a2793a46dd4101e9b821e2039b00", + "format": 1 + }, + { + "name": "tests/unit/module_utils/test_elbv2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2ae880bf42284fc29a0f7013b91bff55cf581e0c5fe685b007f784147482ed23", + "format": 1 + }, + { + "name": "tests/unit/module_utils/conftest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2e722f74b02b3af62498536794cf19d8ecc9dcafa0fa06eb750a32f1fff7a7cc", + "format": 1 + }, + { + "name": "tests/unit/module_utils/core", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/module_utils/core/ansible_aws_module", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/module_utils/core/ansible_aws_module/test_fail_json_aws.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8a974b1c541a6808c56710cab0e272e808960d36b3968ec0088c837f0c6e5ea6", + "format": 1 + }, + { + "name": "tests/unit/module_utils/core/test_is_boto3_error_code.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d1a5787e87b8256843d0e073cb1252a9ef0017a69cb9b769710da94cdab3b93", + "format": 1 + }, + { + "name": "tests/unit/module_utils/core/test_is_boto3_error_message.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b18fb9bc2fcde61c82113fd3065663c75c1a4731d52aa196a6e710845bfe1c99", + "format": 1 + }, + { + "name": "tests/unit/module_utils/core/test_scrub_none_parameters.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bc8f91a2d5fb05e6d21a1ecdb100b605510e5e81f5fff9ba5ef25b0b6e1aa611", + "format": 1 + }, + { + "name": "tests/unit/module_utils/core/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/module_utils/ec2", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/module_utils/ec2/test_compare_policies.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da57576455084ccc71d4c22985bbc75c1668a94923b11ab5f608b36d27ed6920", + "format": 1 + }, + { + "name": "tests/unit/module_utils/ec2/test_aws.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9d5d76b80943c248a28f3e193192aad22498d19868fd7b1fd6b6331f0984392", + "format": 1 + }, + { + "name": "tests/unit/module_utils/ec2/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/module_utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/utils.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b68f9ac9c8f02f1e87b0294a125adb102c718f6e3e5f856ec3401b2b890003cf", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/conftest.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "909818cefd5093894a41494d1e43bd625538f57821375a564c52fe4219960967", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/thezip.zip", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "02a319fb1a6d33b682f555eefb98f2a75b2a3be363e1614c373431b4f30fda7f", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ef1018e15bb9fad1e7a4f15aa6191e80042fc7fc08ef4bec3e115d96a9924b98", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9e4b01f50b09f45fcb7813e7d262a4e201786f0ecd76b45708abe55911b88fd2", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d9e3dfae7a19d402a8de1a2b65fcc49c43ff489946e8ca9e96efa48783e26546", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4e15c84bcf1024f5bb0b2940844fdc4ed97ba90ef7991b513d1659b43a0e7783", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "428e852fcbe67bbdbb2d36fb35bef4b2fb22808587212e19f3225206ceb21c12", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0325c21e49992708528ebf66162c18e1e1eb2a0837c6d802b1cf3bde73ec06bc", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/b.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2937cb7102c4d4902b09aada2731c1b0165e331dbfde9990644c4c3ee1544b21", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9e4b01f50b09f45fcb7813e7d262a4e201786f0ecd76b45708abe55911b88fd2", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/chain-4.cert", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "37fb85917db1cd90b5881c8d3d3a9d51ae7c9b904020d0ffbf0734bcf11bb666", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/certs/a.pem", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ef0266ee8cf74a85694bf3ce1495260913b5ca07189b0891bbfc8d4c25b374ea", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/fixtures/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/test_cloudformation.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6e029bd5bebd2a0f9949d07342700f70d66a7effeb88e24711f31ed79c2e987c", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/test_ec2_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b79c910f567ec1b8856cf68ed6723479428115fb97f4cc10333b0d78a6aa0332", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1cb3d77662d35b7703f65278ffdca78e6eb520e96fb3807d39ea3aa02086c1b7", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6d677c34f0715af2049abef7a479d1362760a0c089ff741d9ac0beed56849251", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe29fabc6f34c58976b23132558a2024af53e655f825cd8e5d1b2f39cc89ddcd", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "91a1e065a4854be515095aba447d6a011bb3bac6f8d5b0e3a9081f74ef873096", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "afe46889f0ec4537b13694f164343440b1fcb0334c539a5a7ec895d36fcf7953", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0e2832a3c70031ba07c44b0f8b291a04251052c22f764110bf0cd034d406bfbd", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "985c5ada32ac440bc971b553e75cb8516c52b9e78b50e6750d4d92ab2e4a9634", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dfc55200a0f4d01d94845448b7c67f175cdf56e49df4bf9305525e7ffe543c64", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d1a0160fbde4f68c768aaf73182e2369a95721f2bb2e7ab5e9ee42016747dfa7", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "362d4ebe6fb6b538c0f74a6326a7697d6d129a77c3bfffedc24a5cac14b20e5a", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a58899e48f620454d1a1c59a261dec5f527970ae4b68f60a2e7bccef29ab5df8", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "15ea45877e966ada21f276359194aea32061bbb5fbf9269782518ff9c94fecc7", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c585064211547fe7d0e560cabf12512ee49ca2bbc8622c3a615333aec1eb3dbb", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ce2c0c2869f52248ea296808b1f10cf0ee3491c18784c9270c8bd55087a8250", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "450c976b530fffd60e39b84a237375f46fb82bb8d09ec77a38d5ac3b87c59e18", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0cfa4607aa88d41fa7229383460169b9fc76c3bba6593f82d320f71c6c866325", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f197d5a0c7cb66d160864e359e2a62c857af76c1d1b0530180ea2e35fdb20efe", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d18b0ee2d7aab11783d7ddca1bd7b822775a2e87286cae87a8bb37a25c8dbd22", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "69ca28e411219d5afe76ad868d648f072fbbd2047223aed04f51c451a901dcc7", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cc3383f46239477355a56db754bdaa2185283e10cf6e9a7bfeb1813c4488afd3", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "18bbc5347bdc51e636840dda8020e4fe198d144d2d7bdfb5b800fbbf9521b551", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b829b3d4436f35e812bdb84da99c1d31a1c9b8be476a4b1ecab3587f7ee0f6e9", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3a2720e847e9e878deab88eaa919c0e14e97210f581138c6341ff97a85da1b38", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7163b875588f3e98a210fb5291149ec0f6c83213a533086ad8f37e2f9dfa012f", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e53c53e08397b8fca0f8e6a69a5254bb092b4f403f0fec0d9bff4352c3cc1192", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f4d8ae33d4fe9f0aaa4c6c744174b1ad849d5881154fc8a5eb32fd8ee07566e0", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6f57b0469a084bb8891bdd14610b2dba1ef3baaab8436cff1413065e276012db", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "41999395cf5a12be8eccc991675c44fe12b20433ed7cc7ca541f568b377c7a33", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "05ac9850aa91e5ed4753d901e9bf0641c08c7be9148b633681cea76c94747fc8", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ead6f1c137dfc1628237502c6a955d3338770ff85f1715027f022e7773ed9992", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bf9e2fefb8c13c2b5040c8b502f6aa799f6da6b69c1a8e48e4e870536222df8b", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4b83e42429a361b2be7b2524340d20264b43f5b0e4cb44fe5bafc3670e9f9d03", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d75b82445e89303b2b4af1ef3161d4e315c6d02014c6df00165a9c526fc9bc56", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d3bec7dd62d084a3d115ea7f05a34052625b80d56839022b9ebcee2583053412", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1659b6d17d4004dbeba28d635a752c4601c08c0f99a0d8c10f18487e0a215d8e", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7edd15131c553f2bff19840a66cd2498cf98c4f93bd8164a51ab3eb81a619ba9", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fa726d3ba3ac078180b857893f2c5aec60526a8d60323a8cc06121a4bacdf982", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e6f4a74e04f58505d1132c6981fffc1f24e79cbad86c69883677b3cb1703df5d", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c9ab3c3b4d2e19ca6f764f2e8288dea1e52157dc1d49a319717bd65a3cc770e1", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "148d631b9e7bf07824a845880565c98a102dd0864a40328320db40f545ee7834", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6acc11fdfc1929b45d589d8c77c2f9fae80d48e840d0e9cf630e362d6b288d4a", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "026ca2db13f88bfb4b469d5cd3c2ad5cf6635305fdbbab11e9d5d1d3330b26c2", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b8f4dc01c750d860f317a98f598bf3acd7edfbc970054b2793013dfcad61c82f", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3d1711eba6a7c18f0ed7e00a1602dcd0dde519205fe6afc42446e1f222b9fe48", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2864dab59c7432ad2ae594e121ee581cface7b130025cd88d0cb4aead4215168", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c09d7d26c96cb5b734e0198b88b00a13fc0d54d65b444278497c17c0f877fa29", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b3fcc844d47ebcb9a759b328b8b513245bc2f9e6feded2b42806301d878d7bff", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "664f1be3c9bb37661bcccba1de32aa3de0fdb08edcf8b276d726acfecc29baad", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0306607b12dcf8490f9e5fdca401eb9bb39e3f5507327f87996b804c825a50c5", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ffa553afc86b6600a849bdc2baa7fff8a27b94019800ffe85e7edd0ea81ad000", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0ab32bc29e7af611043dafd9d25ba246951cd826f96baafcae8d27d2432ae1d3", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "49426404c58cc23230c46a32d193591ee51bb270486618bb5f76bf8b1cd63d86", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "790f9822331226175639d3e8c2645cfca0152f1e0fe24c82ab715e499ea070ac", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "adf858c3c40416e8bc9273ea17d578448c6497841cd05ae48616f49d0a44d723", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e8967f3d6bfc91be380066e1a70070f0a33a239c9548b02c44c92ad550741cdc", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "18a0d86b4fe1d679715ab099b8413d22e6a47ff960c876525ef3dd79e77d18f6", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1659b6d17d4004dbeba28d635a752c4601c08c0f99a0d8c10f18487e0a215d8e", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "be7d4745aa48792bbb544043808428f39dd75a1dd0f75d928d2e7626d22ed762", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "938495a4a09e83ede7e4c3a282cb93b1de0dd10435e4f670c301ab4ab4bc63e6", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a016652b1a0138353843f04780fae13e60226a22c4093d200bc07c8a89d75d44", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a628694b44084384a0e2dbe1800797ef96ea12de2c05c65f716f37d26a1a0006", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "98875bdf3813bbeeb89c537778974a65f6365644de28431a35412753124848fa", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/.gitkeep", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/placebo_recordings/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/modules/test_aws_s3.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "54f9deff367de9af88b72f7107ba603e138cccce6c84cef87372ee50990e9943", + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/test_aws_ssm.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "86c742ca6e162e77e984356eb5be049ada1eeb47e5bbf281b4d86d517304d0e7", + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/fixtures", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/fixtures/avi.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3739de410d134591fada61f62053bfab6fcbd5c80fe2267faa7971f9fe36570d", + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/test_aws_secret.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cc4e751181bd9dd42c123600f9b54372a96d6848ccb2e4c1f73d30cb1cfc0278", + "format": 1 + }, + { + "name": "tests/unit/plugins/lookup/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/inventory", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/plugins/inventory/test_aws_ec2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40d9e28270f6e0d7546a3b9ebd1024383ed40f577b4d5c3ad3e88f015f6ab7da", + "format": 1 + }, + { + "name": "tests/unit/plugins/inventory/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/plugins/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/unit/utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/unit/utils/amazon_placebo_fixtures.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "64958b54e3404669d340a120f6b2c7ae79f323e6c930289514eba4569d1586c1", + "format": 1 + }, + { + "name": "tests/unit/utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "tests/requirements.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8bde57f76898361f17cbc2cd56a489b8a0c92f6a2da4eb68db801b041bcad732", + "format": 1 + }, + { + "name": "tests/.gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e33e9227e6fb67d4bf8c2e9b095ed2d6d324684dadf237cf749467f92d14e0f4", + "format": 1 + }, + { + "name": "tests/sanity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.9.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "03af206125772ad245f9727b2278bc1d36cfb4396c132cf89a1b5d9b79a45765", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.10.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0905475eae97f564148f9bb289da3405b7e19d25c2c0e059efb6d251ed8b9914", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.11.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0905475eae97f564148f9bb289da3405b7e19d25c2c0e059efb6d251ed8b9914", + "format": 1 + }, + { + "name": "tests/utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/utils/shippable", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/utils/shippable/timing.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ebb7d3553349747ad41d80899ed353e13cf32fcbecbb6566cf36e9d2bc33703e", + "format": 1 + }, + { + "name": "tests/utils/shippable/units.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f7cb5eb0d65c282c5adfea998108add25fb65cf613dbf32e08c815b21a6bc891", + "format": 1 + }, + { + "name": "tests/utils/shippable/aws.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dd953f7e779b9962e76492c389142e03174e84a8115f53e56628e2af9e66b818", + "format": 1 + }, + { + "name": "tests/utils/shippable/shippable.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "839b8cdcff0abd70ce9ff698b10f868917d46aecb132b173d8ff5d06c6aef3c1", + "format": 1 + }, + { + "name": "tests/utils/shippable/check_matrix.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2d356ce0e8072dfc57771c71c5a3f7b37945b2bf198f764eebb5d98f069f094a", + "format": 1 + }, + { + "name": "tests/utils/shippable/sanity.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d978d5d0ac8b6e266d493fc37b2ac6c153bdbbef64de0de049a43d680778a81a", + "format": 1 + }, + { + "name": "tests/utils/shippable/timing.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f3f3cc03a997cdba719b0542fe668fc612451841cbe840ab36865f30aa54a1bd", + "format": 1 + }, + { + "name": ".github", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/BOTMETA.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dbef302faa0f7585d254d59d95d0505439564b68a7d5c2484089a25f8c1c4034", + "format": 1 + }, + { + "name": ".github/settings.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cb31353134cff7d91b546a03cc6fec7caaf0dba62079ea66776e2994461e6c7b", + "format": 1 + }, + { + "name": "COPYING", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0ae0485a5bd37a63e63603596417e4eb0e653334fa6c7f932ca3a0e85d4af227", + "format": 1 + }, + { + "name": "requirements.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b53f558c89560c3ce433e583abe819def7d3857bd60be1e2481e7d6d68bd0017", + "format": 1 + }, + { + "name": "docs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vpc_dhcp_option_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "32a5d692477c88676063d89391a0fb2f084b84d7cadac04f554aeb2bc32919a1", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vol_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77e59aae0b5940e94bff957dba07c8c0fdfddaecf7f38c145e1dc60fc80f5d0c", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_ami_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1f4ff011411053b10c2ec71455aa800e9141d5580b7df3b214838d4f1da8be66", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vpc_subnet_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "78e7e8a938ef2b4ecf5f365d807aa7cbfe15923d4fb3db216a246bde9c274065", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_elb_lb_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dd1f2af55cea5527803814457d566093b9683045b05e4dc4a201243c6dc81981", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_eni_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "accc2fe9512253949459941073c8d44a3949ae6f8c1a742c35b113f11b49fa1c", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_account_attribute_lookup.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "53d15bdf65a13e933f96641b058a530ccc963e295e1305c0ad10592b14d15d9a", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_ec2_inventory.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "402d8bec724651ca9ecd9150f07c066308b981a4c23bbfcdfc2bab70e81757c7", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_ssm_lookup.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9b2055913079f8468f2282f8e1b3755bbbcb57ca43717e040e10f209d86de79", + "format": 1 + }, + { + "name": "docs/amazon.aws.cloudformation_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8fc6538aae32219a106f1fb5db0998452274aab9c9d12512326844492fc783f", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vpc_net_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c3652ba58c120b4219e3397ab213223d1c4f0d1928e5f9b72f1f22bdda49bb96", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_snapshot_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8246ec5ee79ac35d9806af060bd9464da262e4dab5fc8054b7a0dff65dc33f60", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_az_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2e1cae722c27d6a706e2a599c5e215a0286bdcbd7c1301e2152c32617db692e0", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_s3_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "74bcc24f1d36a7c4193abb1fa0b44e150f912e0bcfebb14c61bfe6ba6d5647c4", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_caller_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73f829c28ed25b4c0909fbd6e5bb9f3cfd6d75737e4b90347f77d50ea6b3d009", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vol_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d91c06e2eeef51bb4c9a420f7887100ac741b797df01cd6e08e873f4c18d641e", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_key_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "83d55df8ebc60be6ffffc0ae5e8457832b205952af1a934fa0f1d55680aa9aad", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vpc_subnet_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "79749d36e5272eea214c652acd124a3cc5b9d986c1d931bc0adacb6a07208032", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_snapshot_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "68d0057b23a5fa064614d43090166708831c1cc6240df6c577062a6319722220", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vpc_net_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6bc21993ee32a26225c4d22e7a6143ae85472254151e881d8ca60852254b5a18", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_service_ip_ranges_lookup.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d092d1b46d80e6055019e854daaabd36617dc661bc261d55424c55a1db1eebf4", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_vpc_dhcp_option_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f420de62deb21fee05c24a47302a771730fbdfb5c25973b4fd00228cbee43641", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ea67b7477c2261df4a0828a1b1760249888beb87716849a39feb541c492774f3", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_group_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a59f04d3d504e91177b006f86da9506da697180c6273a82a1cab89b6160a1a20", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_tag_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "758fd967b3273db8d97861e0bc8d5c1a1ef308ba872a53930e71b2a1df145f69", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_group_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5a80116eb55ae573b578be4f1b028b13e638458ec3e11acc2e46c8dfb3d65fb6", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_secret_lookup.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1e2e0bf7e3a665db01864482300b7fc85651ed82dc26263a3fc15fb0f756b422", + "format": 1 + }, + { + "name": "docs/amazon.aws.cloudformation_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4d6922fe00c0029cf8a9dc7567635d7338a45349b769a324fc2367deb5721f1c", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_ami_info_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c8df5c59c4f0fbcd17aaeee222925d1083a649fc08dc6b58984929e70094a7a2", + "format": 1 + }, + { + "name": "docs/amazon.aws.s3_bucket_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "76ea67b4acf1a948824721de9d8c7212a708186ceb347e96e11059980a6251b6", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_tag_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c05f33f615a5c0b6f4da3b719f90754a23c34eb183c168445d992d13969b11eb", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_eni_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d7dffcb948d9d11cf4487053148b43b720aefa1230af5b62ea5c56720a637746", + "format": 1 + }, + { + "name": "docs/amazon.aws.ec2_metadata_facts_module.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "abc3d518f1266abcd38e5bdb5ae35344be14c2dd708464301a201381062974f9", + "format": 1 + }, + { + "name": "docs/amazon.aws.aws_rds_inventory.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "78dee1da9611190bcb19413273113c65c8251fc80fa707f2140e032c10826692", + "format": 1 + }, + { + "name": "meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "meta/runtime.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e948a061b4225e12a11e60a79ca13c90e58bedecb7544fac029d35b3d48deb08", + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "927a9b2a7115b09d59db63d052357789b75786d8b0035f267609515d5a6a54dc", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/ec2_vol.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a7af250360449b16ec7db2136841d04b2cb432f98b331da29cd19acf47807fa4", + "format": 1 + }, + { + "name": "plugins/modules/ec2_eni_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1c021c86eb78c06beb826ee70684e3f1d780ab73651d51f7f6a8cf92c332f408", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_net.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "39442e227e393c84a50dba1989d4dfc5118a4f19feb367b040df4c8fe5bc3008", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_subnet.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40bd4588f3408eb180bae5b495cb5d72849e434141a6bf6ae430ae0663467fa4", + "format": 1 + }, + { + "name": "plugins/modules/ec2_snapshot.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "58e75d360ccdb312493bc441a85f4b240a14b1ac4e46c2d408bca3a161849e49", + "format": 1 + }, + { + "name": "plugins/modules/ec2_metadata_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4b550f68ef961c0477b95d2bf49e9e43b5de7fd163856c492c0039a6205ec214", + "format": 1 + }, + { + "name": "plugins/modules/aws_s3.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e395afc75c88ce83f4017d9287638c64d78aaac279a27a14a947f8c7c4ba4553", + "format": 1 + }, + { + "name": "plugins/modules/ec2_ami.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "95a8f60b020a216a62bd01df6bff083cf87ed0afbdabacc9bae3ecaed5bf48af", + "format": 1 + }, + { + "name": "plugins/modules/aws_caller_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f45eb7a2af067331d6b8c8cec02c893c43a6bfba53ecb9eacb07478574dd0100", + "format": 1 + }, + { + "name": "plugins/modules/ec2_snapshot_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d0d0d8c15cb5d4528bda49fd36a0a9d941d51ec1b1bddf80ff5b1ccbb48ed2e6", + "format": 1 + }, + { + "name": "plugins/modules/ec2_group_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9694a8587e2cbba1c83b7ced7df62062b4a6833fb48035ce8e2eefd0461f27ff", + "format": 1 + }, + { + "name": "plugins/modules/ec2_elb_lb.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "689a09dc94e63862fe71833a463f550254d2caa7d11798a9727a59553f438ee8", + "format": 1 + }, + { + "name": "plugins/modules/ec2_tag.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d4f11efc2c54900e4b2924064c1fb41b16442f3842a0c4a7022b06bf2f6a38e4", + "format": 1 + }, + { + "name": "plugins/modules/ec2_tag_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cb88ac0d260f4716093c519b639f98a1bc4a923c7e210c76180e32785af9e1ec", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_dhcp_option_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f286b7314d1b6017461703c013d1afa76ead3030e29b4c66042edda2a2cfa2aa", + "format": 1 + }, + { + "name": "plugins/modules/s3_bucket.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8c46561e7bb4b893286e2cd6c67a3f6f385db59eef5833aceaef06125f423817", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_dhcp_option_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f286b7314d1b6017461703c013d1afa76ead3030e29b4c66042edda2a2cfa2aa", + "format": 1 + }, + { + "name": "plugins/modules/cloudformation.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9210e7581128a1c89fb0bff9e749e5649483934a3dece2c224c3e1d760393435", + "format": 1 + }, + { + "name": "plugins/modules/ec2_key.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0bd0e912d569205808b94f546595977f8f3f8ddf934da094a5d4487f600ba5ed", + "format": 1 + }, + { + "name": "plugins/modules/ec2_ami_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ceffc9af5428d36d9cb8d9b5e522ed88002b884046459a584484dda62874530", + "format": 1 + }, + { + "name": "plugins/modules/aws_az_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a477e43b4e0c7fde844cb605fe9643a293233d8678fdb0bf0e493fbbae057a43", + "format": 1 + }, + { + "name": "plugins/modules/cloudformation_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ab27d21bd4b89ff1b019f4438ac77b0a2fa469c470db9083ce5f55d5bc9ae7b6", + "format": 1 + }, + { + "name": "plugins/modules/ec2_snapshot_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d0d0d8c15cb5d4528bda49fd36a0a9d941d51ec1b1bddf80ff5b1ccbb48ed2e6", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vol_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0249c52f742eae1eea1b7270879adec058e76f4f712704f153c5aeb8d5a409b9", + "format": 1 + }, + { + "name": "plugins/modules/ec2_eni_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1c021c86eb78c06beb826ee70684e3f1d780ab73651d51f7f6a8cf92c332f408", + "format": 1 + }, + { + "name": "plugins/modules/ec2_group.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a1d04fbb36dd5452fe39d7c8c9f458c3f7b73c5f53c4ea8275c40befd2335e45", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_subnet_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3440fa4cc5ae7453ce15205fd7493abb85d7dff0f2581729134a47476002e9ef", + "format": 1 + }, + { + "name": "plugins/modules/ec2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "abbe31a6c886d63793dd82a0ec71032e0b0037d356c67f513a0376b89722a2d0", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_subnet_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3440fa4cc5ae7453ce15205fd7493abb85d7dff0f2581729134a47476002e9ef", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_net_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56dfd55fda81033e5b2f3e2d6dab66fdb7a6f8ad375f862c1722d7f707560110", + "format": 1 + }, + { + "name": "plugins/modules/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_dhcp_option.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7355c0d092bf01518a6bf9e3d39d27a7f78fbb7a8de9e28744642ea14c0ff154", + "format": 1 + }, + { + "name": "plugins/modules/ec2_group_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9694a8587e2cbba1c83b7ced7df62062b4a6833fb48035ce8e2eefd0461f27ff", + "format": 1 + }, + { + "name": "plugins/modules/cloudformation_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ab27d21bd4b89ff1b019f4438ac77b0a2fa469c470db9083ce5f55d5bc9ae7b6", + "format": 1 + }, + { + "name": "plugins/modules/ec2_ami_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8ceffc9af5428d36d9cb8d9b5e522ed88002b884046459a584484dda62874530", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vol_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0249c52f742eae1eea1b7270879adec058e76f4f712704f153c5aeb8d5a409b9", + "format": 1 + }, + { + "name": "plugins/modules/ec2_vpc_net_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "56dfd55fda81033e5b2f3e2d6dab66fdb7a6f8ad375f862c1722d7f707560110", + "format": 1 + }, + { + "name": "plugins/modules/aws_caller_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f45eb7a2af067331d6b8c8cec02c893c43a6bfba53ecb9eacb07478574dd0100", + "format": 1 + }, + { + "name": "plugins/modules/aws_az_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a477e43b4e0c7fde844cb605fe9643a293233d8678fdb0bf0e493fbbae057a43", + "format": 1 + }, + { + "name": "plugins/modules/ec2_eni.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "815ac07f781a9cd6168e4d7453db3466fb37bf66bd3d43b9f54f0d4fdaf8d0a8", + "format": 1 + }, + { + "name": "plugins/lookup", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/lookup/aws_secret.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1c6ce30345ad429e9861ac2ccc839893b0d3a255ef05c7d7d6b326bc1e7e236e", + "format": 1 + }, + { + "name": "plugins/lookup/aws_ssm.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "48b7663ca2da41e7ba572a1acdbeb3b81448ec40005de5a0c0aff9a0b58ebce7", + "format": 1 + }, + { + "name": "plugins/lookup/aws_service_ip_ranges.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ee589271ba94e6bd9dcc89f1bfb26fb4b962d588821acbe0a68ae3c4f270ef40", + "format": 1 + }, + { + "name": "plugins/lookup/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/lookup/aws_account_attribute.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9e35df7b9c617797e2f35dffcb5aa59bde2a5b160624d5bd635160f028ba495", + "format": 1 + }, + { + "name": "plugins/action", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/action/aws_s3.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "348e233ca01687aa88c78606f8721f8be738f26163860cfa14dfa80eb10673a7", + "format": 1 + }, + { + "name": "plugins/action/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/inventory", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/inventory/aws_rds.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "2f62e7312eda324f2f0841d54a307ddd5de8b2b8cdd6a20363768518a8abc704", + "format": 1 + }, + { + "name": "plugins/inventory/aws_ec2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "38a8acfecff520dcbe694252e6cfd062a834f87721189b0da67318bcbe110ed4", + "format": 1 + }, + { + "name": "plugins/inventory/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/callback", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/callback/aws_resource_actions.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fb17984f9f244aba88f721c2df47ac6820e83992cde662e04bf4f2eab1a60629", + "format": 1 + }, + { + "name": "plugins/callback/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/cloud.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a118a42990a9d145c74e8afbf7cecad1b50bd5c2c358dea167b8a7f8e199c24", + "format": 1 + }, + { + "name": "plugins/module_utils/urls.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "089b532522cfff202a7da5cedb5d6e2e46932bbd273094b6c69504b4b4e21262", + "format": 1 + }, + { + "name": "plugins/module_utils/waiters.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6148677d039315a9618535ebcb922c7247273edb49212e635a9cb257b3d5f337", + "format": 1 + }, + { + "name": "plugins/module_utils/rds.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6664907704c27f96f29cfc5b92407a8491be46f56eeda8459f774ba6883b8b70", + "format": 1 + }, + { + "name": "plugins/module_utils/elb_utils.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fdb692e5d99229f7bbbf7b7a8db6069c83a149d441124f013fad973b51fa036f", + "format": 1 + }, + { + "name": "plugins/module_utils/acm.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "73614c7c2d701bafb3ae46f4c819cf4f3888efa92b39cee71e619beba24f446d", + "format": 1 + }, + { + "name": "plugins/module_utils/batch.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1ee897b11875f13f8dd12d245d0c4d680a95830886a489990137d0af2fb5d0db", + "format": 1 + }, + { + "name": "plugins/module_utils/waf.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "643bc1da71db0c08de7e13b645330355961c20f72b021e8e9c1e682c5530674a", + "format": 1 + }, + { + "name": "plugins/module_utils/cloudfront_facts.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4a36e6b5861974b51cd81855b76f099249bcbb6a208d78db5de35d282ca4d5c9", + "format": 1 + }, + { + "name": "plugins/module_utils/ec2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "820e03dbb778bbfe31e79ec66b6200250de28959f73a69b0d9fcd4e003864b72", + "format": 1 + }, + { + "name": "plugins/module_utils/iam.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0d0f188188df4a059e3f855c2cab85a6eb5d6e908c8e8c18410b5853b48d1f86", + "format": 1 + }, + { + "name": "plugins/module_utils/elbv2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6f7aa213c7e96062c9a706716e30e1efcffcce773d433a0b032442794c406ed1", + "format": 1 + }, + { + "name": "plugins/module_utils/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins/module_utils/direct_connect.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "57e6f1bbf32388e3864419baa48bc57d509f56dccbb8bbec0787bcdc4c54dcb6", + "format": 1 + }, + { + "name": "plugins/module_utils/core.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "327f6b429cea531270faf0e81c5838c9c515bb322cf85b3cd6622585a2ae5767", + "format": 1 + }, + { + "name": "plugins/module_utils/s3.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "17a249de004b14b26a443d6456c4416c4f23e281298772d8bd38c70508a754a4", + "format": 1 + }, + { + "name": "plugins/doc_fragments", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/doc_fragments/aws_region.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "074b3f366d8214f956b0aff167e9940e08ab7fc2f697815eff50021069a8b708", + "format": 1 + }, + { + "name": "plugins/doc_fragments/aws_credentials.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5bf58fccfb29994200623e8e2122544477c3e649b1527fd6fb683e3e90b3de15", + "format": 1 + }, + { + "name": "plugins/doc_fragments/ec2.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "683daff2b7de94f68d574e05ab3d5405a9d9fc672910f412b104cb326f648c11", + "format": 1 + }, + { + "name": "plugins/doc_fragments/aws.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f8d798285451b4e66673c224afb982e7a32b93aa0276c936efe688b0a81e2d6", + "format": 1 + }, + { + "name": "plugins/doc_fragments/__init__.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": ".gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "5a00777ca107231dc822535458402764507be2cf2efa433ea184bb2163e07027", + "format": 1 + }, + { + "name": "shippable.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d4cfbb6a5f0c9dd24048bc4ddccfcc4a71bf992b9807eb22068124f160aba068", + "format": 1 + }, + { + "name": "CONTRIBUTING.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "1f198b56472d1ec885aa9ddffe98b92c19966ed7af81037aef360c08b0b0eb95", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/MANIFEST.json b/collections-debian-merged/ansible_collections/amazon/aws/MANIFEST.json new file mode 100644 index 00000000..989b7cef --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/MANIFEST.json @@ -0,0 +1,34 @@ +{ + "collection_info": { + "namespace": "amazon", + "name": "aws", + "version": "1.4.0", + "authors": [ + "Ansible (https://github.com/ansible)" + ], + "readme": "README.md", + "tags": [ + "amazon", + "aws", + "cloud" + ], + "description": null, + "license": [], + "license_file": "COPYING", + "dependencies": { + "ansible.netcommon": ">=0.0.1" + }, + "repository": "https://github.com/ansible-collections/amazon.aws", + "documentation": "https://github.com/ansible-collections/amazon.aws/tree/main/docs", + "homepage": "https://github.com/ansible-collections/amazon.aws", + "issues": "https://github.com/ansible-collections/amazon.aws/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d5ce356bc41180dbc6fd24bca3ecd89b291c91b525a77fd235f69e3914f249c3", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/README.md b/collections-debian-merged/ansible_collections/amazon/aws/README.md new file mode 100644 index 00000000..fb57e278 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/README.md @@ -0,0 +1,159 @@ +# Amazon AWS Collection +[![Shippable build status](https://api.shippable.com/projects/5e4451b6aa9a61000733064c/badge?branch=main)](https://api.shippable.com/projects/5e4451b6aa9a61000733064c/badge?branch=main) +[![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/amazon.aws)](https://codecov.io/gh/ansible-collections/amazon.aws) + +The Ansible Amazon AWS collection includes a variety of Ansible content to help automate the management of AWS instances. This collection is maintained by the Ansible cloud team. + +AWS related modules and plugins supported by the Ansible community are in the [community.aws](https://github.com/ansible-collections/community.aws/) collection. + +<!--start requires_ansible--> +## Ansible version compatibility + +This collection has been tested against following Ansible versions: **>=2.9.10**. + +Plugins and modules within a collection may be tested with only specific Ansible versions. +A collection may contain metadata that identifies these versions. +PEP440 is the schema used to describe the versions of Ansible. +<!--end requires_ansible--> + +## Python version compatibility + +This collection depends on the AWS SDK for Python (Boto3 and Botocore). As AWS has [ceased supporting Python 2.6](https://aws.amazon.com/blogs/developer/deprecation-of-python-2-6-and-python-3-3-in-botocore-boto3-and-the-aws-cli/), this collection requires Python 2.7 or greater. + +## Included content + +<!--start collection content--> +### Inventory plugins +Name | Description +--- | --- +[amazon.aws.aws_ec2](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_ec2_inventory.rst)|EC2 inventory source +[amazon.aws.aws_rds](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_rds_inventory.rst)|rds instance source + +### Lookup plugins +Name | Description +--- | --- +[amazon.aws.aws_account_attribute](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_account_attribute_lookup.rst)|Look up AWS account attributes. +[amazon.aws.aws_secret](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_secret_lookup.rst)|Look up secrets stored in AWS Secrets Manager. +[amazon.aws.aws_service_ip_ranges](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_service_ip_ranges_lookup.rst)|Look up the IP ranges for services provided in AWS such as EC2 and S3. +[amazon.aws.aws_ssm](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_ssm_lookup.rst)|Get the value for a SSM parameter or all parameters under a path. + +### Modules +Name | Description +--- | --- +[amazon.aws.aws_az_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_az_info_module.rst)|Gather information about availability zones in AWS. +[amazon.aws.aws_caller_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_caller_info_module.rst)|Get information about the user and account being used to make AWS calls. +[amazon.aws.aws_s3](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.aws_s3_module.rst)|manage objects in S3. +[amazon.aws.cloudformation](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.cloudformation_module.rst)|Create or delete an AWS CloudFormation stack +[amazon.aws.cloudformation_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.cloudformation_info_module.rst)|Obtain information about an AWS CloudFormation stack +[amazon.aws.ec2](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_module.rst)|create, terminate, start or stop an instance in ec2 +[amazon.aws.ec2_ami](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_ami_module.rst)|Create or destroy an image (AMI) in ec2 +[amazon.aws.ec2_ami_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_ami_info_module.rst)|Gather information about ec2 AMIs +[amazon.aws.ec2_elb_lb](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_elb_lb_module.rst)|Creates, updates or destroys an Amazon ELB. +[amazon.aws.ec2_eni](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_eni_module.rst)|Create and optionally attach an Elastic Network Interface (ENI) to an instance +[amazon.aws.ec2_eni_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_eni_info_module.rst)|Gather information about ec2 ENI interfaces in AWS +[amazon.aws.ec2_group](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_group_module.rst)|maintain an ec2 VPC security group. +[amazon.aws.ec2_group_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_group_info_module.rst)|Gather information about ec2 security groups in AWS. +[amazon.aws.ec2_key](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_key_module.rst)|create or delete an ec2 key pair +[amazon.aws.ec2_metadata_facts](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_metadata_facts_module.rst)|Gathers facts (instance metadata) about remote hosts within ec2 +[amazon.aws.ec2_snapshot](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_snapshot_module.rst)|Creates a snapshot from an existing volume +[amazon.aws.ec2_snapshot_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_snapshot_info_module.rst)|Gather information about ec2 volume snapshots in AWS +[amazon.aws.ec2_tag](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_tag_module.rst)|create and remove tags on ec2 resources +[amazon.aws.ec2_tag_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_tag_info_module.rst)|list tags on ec2 resources +[amazon.aws.ec2_vol](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vol_module.rst)|Create and attach a volume, return volume id and device map +[amazon.aws.ec2_vol_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vol_info_module.rst)|Gather information about ec2 volumes in AWS +[amazon.aws.ec2_vpc_dhcp_option](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vpc_dhcp_option_module.rst)|Manages DHCP Options, and can ensure the DHCP options for the given VPC match what's requested +[amazon.aws.ec2_vpc_dhcp_option_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vpc_dhcp_option_info_module.rst)|Gather information about dhcp options sets in AWS +[amazon.aws.ec2_vpc_net](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vpc_net_module.rst)|Configure AWS virtual private clouds +[amazon.aws.ec2_vpc_net_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vpc_net_info_module.rst)|Gather information about ec2 VPCs in AWS +[amazon.aws.ec2_vpc_subnet](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vpc_subnet_module.rst)|Manage subnets in AWS virtual private clouds +[amazon.aws.ec2_vpc_subnet_info](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.ec2_vpc_subnet_info_module.rst)|Gather information about ec2 VPC subnets in AWS +[amazon.aws.s3_bucket](https://github.com/ansible-collections/amazon.aws/blob/main/docs/amazon.aws.s3_bucket_module.rst)|Manage S3 buckets in AWS, DigitalOcean, Ceph, Walrus, FakeS3 and StorageGRID + +<!--end collection content--> + +## Installing this collection + +You can install the AWS collection with the Ansible Galaxy CLI: + + ansible-galaxy collection install amazon.aws + +You can also include it in a `requirements.yml` file and install it with `ansible-galaxy collection install -r requirements.yml`, using the format: + +```yaml +--- +collections: + - name: amazon.aws +``` + +The python module dependencies are not installed by `ansible-galaxy`. They can +be manually installed using pip: + + pip install requirements.txt + +or: + + pip install boto boto3 botocore + +## Using this collection + + +You can either call modules by their Fully Qualified Collection Namespace (FQCN), such as `amazon.aws.ec2_instance`, or you can call modules by their short name if you list the `amazon.aws` collection in the playbook's `collections` keyword: + +```yaml +--- + - name: Setup an instance for testing + amazon.aws.ec2_instance: + name: '{{ resource_prefix }}' + instance_type: t2.nano + image_id: "{{ (amis.images | sort(attribute='creation_date') | last).image_id }}" + wait: yes + volumes: + - device_name: /dev/xvda + ebs: + volume_size: 8 + delete_on_termination: true + register: instance +``` + +**NOTE**: For Ansible 2.9, you may not see deprecation warnings when you run your playbooks with this collection. Use this documentation to track when a module is deprecated. + + +### See Also: + +* [Amazon Web Services Guide](https://docs.ansible.com/ansible/latest/scenario_guides/guide_aws.html) +* [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details. + +## Contributing to this collection + +We welcome community contributions to this collection. If you find problems, please open an issue or create a PR against the [Amazon AWS collection repository](https://github.com/ansible-collections/amazon.aws). See [Contributing to Ansible-maintained collections](https://docs.ansible.com/ansible/devel/community/contributing_maintained_collections.html#contributing-maintained-collections) for more details. + +You can also join us on: + +- Freenode IRC - ``#ansible-aws`` Freenode channel + +### More information about contributing + +- [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html) - Details on contributing to Ansible +- [Contributing to Collections](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#contributing-to-collections) - How to check out collection git repositories correctly +- [Guidelines for Ansible Amazon AWS module development](https://docs.ansible.com/ansible/latest/dev_guide/platforms/aws_guidelines.html) +- [Getting Started With AWS Ansible Module Development and Community Contribution](https://www.ansible.com/blog/getting-started-with-aws-ansible-module-development) + +## Release notes +<!--Add a link to a changelog.rst file or an external docsite to cover this information. --> + +## Roadmap + +<!-- Optional. Include the roadmap for this collection, and the proposed release/versioning strategy so users can anticipate the upgrade/update cycle. --> + +## More information + +- [Ansible Collection overview](https://github.com/ansible-collections/overview) +- [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html) +- [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html) +- [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) + +## Licensing + +GNU General Public License v3.0 or later. + +See [COPYING](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/changelogs/changelog.yaml b/collections-debian-merged/ansible_collections/amazon/aws/changelogs/changelog.yaml new file mode 100644 index 00000000..5bb2eb13 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/changelogs/changelog.yaml @@ -0,0 +1,230 @@ +ancestor: null +releases: + 1.1.0: + changes: + breaking_changes: + - aws_s3 - can now delete versioned buckets even when they are not empty - set + mode to delete to delete a versioned bucket and everything in it. + bugfixes: + - aws_ec2 - fix idempotency when managing tags + - aws_ec2 - fix idempotency when metrics are enable + - aws_s3 - Delete objects and delete markers so versioned buckets can be removed. + - aws_s3 - Try to wait for the bucket to exist before setting the access control + list. + - cloudformation_info - Fix a KeyError returning information about the stack(s). + - ec2_asg - Ensure "wait" is honored during replace operations + - ec2_launch_template - Update output to include latest_version and default_version, + matching the documentation + - ec2_transit_gateway - Use AWSRetry before ClientError is handled when describing + transit gateways + - ec2_transit_gateway - fixed issue where auto_attach set to yes was not being + honored (https://github.com/ansible/ansible/issues/61907) + - ec2_vol - fix filtering bug + - s3_bucket - Accept XNotImplemented response to support NetApp StorageGRID. + deprecated_features: + - cloudformation - The ``template_format`` option had no effect since Ansible + 2.3 and will be removed after 2022-06-01 + - cloudformation - the ``template_format`` option has been deprecated and will + be removed in a later release. It has been ignored by the module since Ansible + 2.3. + - data_pipeline - The ``version`` option had no effect and will be removed in + after 2022-06-01 + - ec2 - in a later release, the ``group`` and ``group_id`` options will become + mutually exclusive. Currently ``group_id`` is ignored if you pass both. + - ec2_ami - The ``no_device`` alias ``NoDevice`` has been deprecated and will + be removed after 2022-06-01 + - ec2_ami - The ``virtual_name`` alias ``VirtualName`` has been deprecated and + will be removed after 2022-06-01 + - ec2_eip - The ``wait_timeout`` option had no effect and will be removed after + 2022-06-01 + - ec2_key - The ``wait_timeout`` option had no effect and will be removed after + 2022-06-01 + - ec2_key - The ``wait`` option had no effect and will be removed after 2022-06-01 + - ec2_key - the ``wait_timeout`` option has been deprecated and will be removed + in a later release. It has had no effect since Ansible 2.5. + - ec2_key - the ``wait`` option has been deprecated and will be removed in a + later release. It has had no effect since Ansible 2.5. + - ec2_lc - The ``associate_public_ip_address`` option had no effect and will + be removed after 2022-06-01 + - ec2_tag - deprecate the ``list`` option in favor of ec2_tag_info + - ec2_tag - support for ``list`` as a state has been deprecated and will be + removed in a later release. The ``ec2_tag_info`` can be used to fetch the + tags on an EC2 resource. + major_changes: + - ec2 module_utils - The ``AWSRetry`` decorator no longer catches ``NotFound`` + exceptions by default. ``NotFound`` exceptions need to be explicitly added + using ``catch_extra_error_codes``. Some AWS modules may see an increase in + transient failures due to AWS''s eventual consistency model. + minor_changes: + - Add `aws_security_token`, `aws_endpoint_url` and `endpoint_url` aliases to + improve AWS module parameter naming consistency. + - Add support for `aws_ca_bundle` to boto3 based AWS modules + - Add support for configuring boto3 profiles using `AWS_PROFILE` and `AWS_DEFAULT_PROFILE` + - Added check_mode support to aws_az_info + - Added check_mode support to ec2_eni_info + - Added check_mode support to ec2_snapshot_info + - ansible_dict_to_boto3_filter_list - convert integers and bools to strings + before using them in filters. + - aws_direct_connect_virtual_interface - add direct_connect_gateway_id parameter. + This field is only applicable in private VIF cases (public=False) and is mutually + exclusive to virtual_gateway_id. + - cloudformation - Return change_set_id in the cloudformation output if a change + set was created. + - ec2 - deprecate allowing both group and group_id - currently we ignore group_id + if both are passed. + - ec2_ami_info - allow integer and bool values for filtering images (https://github.com/ansible/ansible/issues/43570). + - ec2_asg - Add support for Max Instance Lifetime + - ec2_asg - Add the ability to use mixed_instance_policy in launch template + driven autoscaling groups + - ec2_asg - Migrated to AnsibleAWSModule + - ec2_placement_group - make `name` a required field. + - ec2_vol_info - Code cleanup and use of the AWSRetry decorator to improve stability + - ec2_vpc_net - Enable IPv6 CIDR assignment + fragments: + - 107_info_check_mode.yml + - 108-ec2_vol-deprecate-list.yml + - 27800-ec2_vpc_net-ipv6-support.yml + - 28-ec2_ami_info_int_bool_filter.yml + - 52_direct_connect_gateway_id.yml + - 54435_aws_s3_fix_removing_versioned_buckets.yaml + - 61279-ec2_launch_template-output.yml + - 61284-ec2_asg-idempotency.yml + - 61735-wait-for-s3-bucket-to-exist-before-modifying.yaml + - 61933-ec2_transit_gateway-honor-auto_attach-setting.yaml + - 62290-fix-cloudformation_info-KeyError.yaml + - 63752-cloudformation-return-changeset-id.yaml + - 64230-deprecate-unused.yml + - 64368-deprecate-unused.yml + - 65555-amazon-sanity-required.yml + - 65960-ec2_vol-filtering-bugfix.yml + - 66840-ec2_tag-deprecate-list.yaml + - 66863-ec2_asg-max_instance_lifetime-and-honor-wait-on-replace.yaml + - 66966-ec2-group-and-group_id.yml + - 66979-ec2_vol_info-ansibleawsmodule.yaml + - 67045-ec2_asg_mixed_instance_policy.yml + - 67247-fix-ec2_transit_gateway-retries.yaml + - 67462-s3_bucket-accept-storagegrid-response.yaml + - 93-deprecate-accidental.yml + - 99-awsmodule.yml + - porting-guide.yml + release_date: '2020-08-13' + 1.2.0: + changes: + bugfixes: + - ec2 module_utils - Ensure boto3 verify parameter isn't overridden by setting + a profile (https://github.com/ansible-collections/amazon.aws/issues/129) + - 's3_bucket - Ceph compatibility: treat error code NoSuchTagSetError used by + Ceph synonymously to NoSuchTagSet used by AWS' + deprecated_features: + - All AWS Modules - ``aws_access_key``, ``aws_secret_key`` and ``security_token`` + will be made mutually exclusive with ``profile`` after 2022-06-01. + minor_changes: + - ec2 module_utils - Update ``ec2_connect`` (boto2) behaviour so that ``ec2_url`` + overrides ``region``. + - module_utils.core - Support passing arbitrary extra keys to fail_json_aws, + matching capabilities of fail_json. + fragments: + - 121-ec2_url-resolution-order.yaml + - 129-verify_overridden.yml + - 140-fail_json_aws_keys.yml + - 151-deprecate-profile-credential-combination.yml + - 71484-ceph-tag-set-compat.yaml + release_date: '2020-08-28' + 1.2.1: + changes: + minor_changes: + - ec2_eni - Add support for tagging. + - ec2_eni - Port ec2_eni module to boto3 and add an integration test suite. + - ec2_eni_info - Add retries on transient AWS failures. + - ec2_eni_info - Add support for providing an ENI ID. + fragments: + - 141-ec2_eni-boto3.yml + release_date: '2020-10-07' + 1.3.0: + changes: + bugfixes: + - ec2 - Code fix so module can create ec2 instances with ``ec2_volume_iops`` + option (https://github.com/ansible-collections/amazon.aws/pull/177). + - ec2 - ignore terminated instances and instances that are shutting down when + starting and stopping (https://github.com/ansible-collections/amazon.aws/issues/146). + - ec2_group - Fixes error handling during tagging failures (https://github.com/ansible-collections/amazon.aws/issues/210). + - ec2_group_info - Code fix so module works with Python 3.8 (make dict immutable + in loop) (https://github.com/ansible-collections/amazon.aws/pull/181) + minor_changes: + - aws_caller_info - add AWSRetry decorator to automatically retry on common + temporary failures (https://github.com/ansible-collections/amazon.aws/pull/208) + - aws_s3 - Add support for uploading templated content (https://github.com/ansible-collections/amazon.aws/pull/20). + - aws_secret - add "on_missing" and "on_denied" option (https://github.com/ansible-collections/amazon.aws/pull/122). + - ec2_ami - Add retries for ratelimiting related errors (https://github.com/ansible-collections/amazon.aws/pull/195). + - ec2_ami - fixed and streamlined ``max_attempts`` logic when waiting for AMI + creation to finish (https://github.com/ansible-collections/amazon.aws/pull/194). + - ec2_ami - increased default ``wait_timeout`` to 1200 seconds (https://github.com/ansible-collections/amazon.aws/pull/194). + - ec2_ami_info - Add retries for ratelimiting related errors (https://github.com/ansible-collections/amazon.aws/pull/195). + - ec2_eni - Improve reliability of the module by adding waiters and performing + lookups by ENI ID rather than repeated searches (https://github.com/ansible-collections/amazon.aws/pull/180). + - ec2_eni_info - Improve reliability of the module by adding waiters and performing + lookups by ENI ID rather than repeated searches (https://github.com/ansible-collections/amazon.aws/pull/180). + - ec2_group - add AWSRetry decorator to automatically retry on common temporary + failures (https://github.com/ansible-collections/amazon.aws/pull/207) + - ec2_group_info - add AWSRetry decorator to automatically retry on common temporary + failures (https://github.com/ansible-collections/amazon.aws/pull/207) + - ec2_snapshot_info - add AWSRetry decorator to automatically retry on common + temporary failures (https://github.com/ansible-collections/amazon.aws/pull/208) + - ec2_vol - Add automatic retries on AWS rate limit errors (https://github.com/ansible-collections/amazon.aws/pull/199). + - ec2_vol - ported ec2_vol to use boto3 (https://github.com/ansible-collections/amazon.aws/pull/53). + - ec2_vpc_dhcp_option_info - add AWSRetry decorator to automatically retry on + common temporary failures (https://github.com/ansible-collections/amazon.aws/pull/208) + - module_utils/core - add helper function ``scrub_none_parameters`` to remove + params set to ``None`` (https://github.com/ansible-collections/community.aws/issues/251). + - module_utils/waiters - Add retries to our waiters for the same failure codes + that we retry with AWSRetry (https://github.com/ansible-collections/amazon.aws/pull/185) + - s3_bucket - Add support for managing the ``public_access`` settings (https://github.com/ansible-collections/amazon.aws/pull/171). + fragments: + - 122-aws_secret-add-on_missing-and-on_denied-option.yml + - 171-s3_bucket-public_access.yml + - 177-fix-ec2-volume-creation-issue-with-iops.yaml + - 180-ec2_eni-stabilisation.yml + - 181-ec2-group-info-python-fix.yaml + - 184-scrub-none-params.yaml + - 185-waiter-retry-failures.yml + - 194-ec2-ami-max-attempts.yaml + - 195-ec2_ami-retries.yml + - 197-ignore-terminated-instances.yaml + - 199-ec2_vol-retries.yml + - 20-aws_s3-content.yml + - 207-ec2_group-retries.yml + - 208-info-retries.yaml + - 211-fix-error-handling-during-tagging-failure.yaml + - 53-ec2_vol-boto3-port.yml + release_date: '2020-12-10' + 1.4.0: + changes: + bugfixes: + - ec2_vol - a creation or update now returns a structure with an up to date + list of tags (https://github.com/ansible-collections/amazon.aws/pull/241). + minor_changes: + - aws_ec2 - Add hostname options concatenation + - aws_ec2 inventory plugin - avoid a superfluous import of ``ansible.utils.display.Display`` + (https://github.com/ansible-collections/amazon.aws/pull/226). + - aws_ec2 module - Replace inverse aws instance-state-name filters !terminated, + !shutting-down in favor of postive filters pending, running, stopping, stopped. + Issue 235. (https://github.com/ansible-collections/amazon.aws/pull/237) + - aws_secret - add ``bypath`` functionality (https://github.com/ansible-collections/amazon.aws/pull/192). + - ec2_key - add AWSRetry decorator to automatically retry on common temporary + failures (https://github.com/ansible-collections/amazon.aws/pull/213). + - ec2_vol - Add support for gp3 volumes and support for modifying existing volumes + (https://github.com/ansible-collections/amazon.aws/issues/55). + - module_utils/elbv2 - add logic to compare_rules to suit Values list nested + within dicts unique to each field type. Fixes issue (https://github.com/ansible-collections/amazon.aws/issues/187) + - various AWS plugins and module_utils - Cleanup unused imports (https://github.com/ansible-collections/amazon.aws/pull/217). + fragments: + - 188-httprequestmethodconfig-keyerror.yaml + - 192-aws_secret-bypath-option.yaml + - 213-ec2_key-retries.yml + - 215-gp3-and-change-support-for-ec2_vol.yaml + - 217-duplicate-imports.yml + - 226_avoid_extra_Display_import.yaml + - 237_replace_inverse_ec2_aws_filter.yaml + - 241_ec2_vol-returns-an-up-to-date-tag-dict-of-the-volume.yaml + - 25-aws_ec2-hostname-options-concatenation.yaml + release_date: '2021-02-05' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/changelogs/config.yaml b/collections-debian-merged/ansible_collections/amazon/aws/changelogs/config.yaml new file mode 100644 index 00000000..df8a7220 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/changelogs/config.yaml @@ -0,0 +1,29 @@ +changelog_filename_template: ../CHANGELOG.rst +changelog_filename_version_depth: 0 +changes_file: changelog.yaml +changes_format: combined +keep_fragments: false +mention_ancestor: true +new_plugins_after_name: removed_features +notesdir: fragments +prelude_section_name: release_summary +prelude_section_title: Release Summary +sections: +- - major_changes + - Major Changes +- - minor_changes + - Minor Changes +- - breaking_changes + - Breaking Changes / Porting Guide +- - deprecated_features + - Deprecated Features +- - removed_features + - Removed Features (previously deprecated) +- - security_fixes + - Security Fixes +- - bugfixes + - Bugfixes +- - known_issues + - Known Issues +title: community.aws +trivial_section_name: trivial diff --git a/collections-debian-merged/ansible_collections/amazon/aws/changelogs/fragments/.keep b/collections-debian-merged/ansible_collections/amazon/aws/changelogs/fragments/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/changelogs/fragments/.keep diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_account_attribute_lookup.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_account_attribute_lookup.rst new file mode 100644 index 00000000..45f0a9c3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_account_attribute_lookup.rst @@ -0,0 +1,236 @@ +.. _amazon.aws.aws_account_attribute_lookup: + + +******************************** +amazon.aws.aws_account_attribute +******************************** + +**Look up AWS account attributes.** + + + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Describes attributes of your AWS account. You can specify one of the listed attribute choices or omit it to see all attributes. + + + +Requirements +------------ +The below requirements are needed on the local Ansible controller node that executes this lookup. + +- boto3 +- botocore + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th>Configuration</th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>attribute</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>supported-platforms</li> + <li>default-vpc</li> + <li>max-instances</li> + <li>vpc-max-security-groups-per-interface</li> + <li>max-elastic-ips</li> + <li>vpc-max-elastic-ips</li> + <li>has-ec2-classic</li> + </ul> + </td> + <td> + </td> + <td> + <div>The attribute for which to get the value(s).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY_ID</div> + </td> + <td> + <div>The AWS access key to use.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_access_key_id</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:AWS_DEFAULT_PROFILE</div> + <div>env:AWS_PROFILE</div> + </td> + <td> + <div>The AWS profile</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: boto_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECRET_KEY</div> + <div>env:AWS_SECRET_KEY</div> + <div>env:AWS_SECRET_ACCESS_KEY</div> + </td> + <td> + <div>The AWS secret key that corresponds to the access key.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_secret_access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECURITY_TOKEN</div> + <div>env:AWS_SESSION_TOKEN</div> + <div>env:AWS_SECURITY_TOKEN</div> + </td> + <td> + <div>The AWS security token if using temporary access and secret keys.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_REGION</div> + <div>env:AWS_REGION</div> + </td> + <td> + <div>The region for which to create the connection.</div> + </td> + </tr> + </table> + <br/> + + + + +Examples +-------- + +.. code-block:: yaml + + vars: + has_ec2_classic: "{{ lookup('aws_account_attribute', attribute='has-ec2-classic') }}" + # true | false + + default_vpc_id: "{{ lookup('aws_account_attribute', attribute='default-vpc') }}" + # vpc-xxxxxxxx | none + + account_details: "{{ lookup('aws_account_attribute', wantlist='true') }}" + # {'default-vpc': ['vpc-xxxxxxxx'], 'max-elastic-ips': ['5'], 'max-instances': ['20'], + # 'supported-platforms': ['VPC', 'EC2'], 'vpc-max-elastic-ips': ['5'], 'vpc-max-security-groups-per-interface': ['5']} + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this lookup: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>_raw</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td></td> + <td> + <div>Returns a boolean when <em>attribute</em> is check_ec2_classic. Otherwise returns the value(s) of the attribute (or all attributes if one is not specified).</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Sloane Hertel <shertel@redhat.com> + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_az_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_az_info_module.rst new file mode 100644 index 00000000..11661b89 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_az_info_module.rst @@ -0,0 +1,314 @@ +.. _amazon.aws.aws_az_info_module: + + +********************** +amazon.aws.aws_az_info +********************** + +**Gather information about availability zones in AWS.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about availability zones in AWS. +- This module was called :ref:`amazon.aws.aws_az_facts <amazon.aws.aws_az_facts_module>` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + <div>A dict of filters to apply.</div> + <div>Each dict item consists of a filter key and a filter value.</div> + <div>See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAvailabilityZones.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAvailabilityZones.html</a> for possible filters.</div> + <div>Filter names and values are case sensitive.</div> + <div>You can use underscores instead of dashes (-) in the filter keys.</div> + <div>Filter keys with underscores will take precedence in case of conflict.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: Gather information about all availability zones + amazon.aws.aws_az_info: + + - name: Gather information about a single availability zone + amazon.aws.aws_az_info: + filters: + zone-name: eu-west-1a + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>availability_zones</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>on success</td> + <td> + <div>Availability zones that match the provided filters. Each element consists of a dict with all the information related to that available zone.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[ { 'messages': [], 'region_name': 'us-west-1', 'state': 'available', 'zone_name': 'us-west-1b' }, { 'messages': [], 'region_name': 'us-west-1', 'state': 'available', 'zone_name': 'us-west-1c' } ]</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Henrique Rodrigues (@Sodki) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_caller_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_caller_info_module.rst new file mode 100644 index 00000000..5d145142 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_caller_info_module.rst @@ -0,0 +1,343 @@ +.. _amazon.aws.aws_caller_info_module: + + +************************** +amazon.aws.aws_caller_info +************************** + +**Get information about the user and account being used to make AWS calls.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module returns information about the account and user / role from which the AWS access tokens originate. +- The primary use of this is to get the account id for templating into ARNs or similar to avoid needing to specify this information in inventory. +- This module was called :ref:`amazon.aws.aws_caller_facts <amazon.aws.aws_caller_facts_module>` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: Get the current caller identity information + amazon.aws.aws_caller_info: + register: caller_info + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>account</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>success</td> + <td> + <div>The account id the access credentials are associated with.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">123456789012</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>account_alias</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when caller has the iam:ListAccountAliases permission</td> + <td> + <div>The account alias the access credentials are associated with.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">acme-production</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>arn</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>success</td> + <td> + <div>The arn identifying the user the credentials are associated with.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">arn:aws:sts::123456789012:federated-user/my-federated-user-name</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>user_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>success</td> + <td> + <div>The user id the access credentials are associated with. Note that this may not correspond to + anything you can look up in the case of roles or federated identities.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">123456789012:my-federated-user-name</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Ed Costello (@orthanc) +- Stijn Dubrul (@sdubrul) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_ec2_inventory.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_ec2_inventory.rst new file mode 100644 index 00000000..f92cef2f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_ec2_inventory.rst @@ -0,0 +1,637 @@ +.. _amazon.aws.aws_ec2_inventory: + + +****************** +amazon.aws.aws_ec2 +****************** + +**EC2 inventory source** + + + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Get inventory hosts from Amazon Web Services EC2. +- Uses a YAML configuration file that ends with ``aws_ec2.(yml|yaml``). + + + +Requirements +------------ +The below requirements are needed on the local Ansible controller node that executes this inventory. + +- boto3 +- botocore + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th>Configuration</th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY_ID</div> + </td> + <td> + <div>The AWS access key to use.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_access_key_id</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:AWS_DEFAULT_PROFILE</div> + <div>env:AWS_PROFILE</div> + </td> + <td> + <div>The AWS profile</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: boto_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECRET_KEY</div> + <div>env:AWS_SECRET_KEY</div> + <div>env:AWS_SECRET_ACCESS_KEY</div> + </td> + <td> + <div>The AWS secret key that corresponds to the access key.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_secret_access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECURITY_TOKEN</div> + <div>env:AWS_SESSION_TOKEN</div> + <div>env:AWS_SECURITY_TOKEN</div> + </td> + <td> + <div>The AWS security token if using temporary access and secret keys.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div> ini entries: + <p>[inventory]<br>cache = no</p> + </div> + <div>env:ANSIBLE_INVENTORY_CACHE</div> + </td> + <td> + <div>Toggle to enable/disable the caching of the inventory's source data, requires a cache plugin setup to work.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_connection</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div> ini entries: + <p>[defaults]<br>fact_caching_connection = VALUE</p> + <p>[inventory]<br>cache_connection = VALUE</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN_CONNECTION</div> + <div>env:ANSIBLE_INVENTORY_CACHE_CONNECTION</div> + </td> + <td> + <div>Cache connection data or path, read cache plugin documentation for specifics.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_plugin</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"memory"</div> + </td> + <td> + <div> ini entries: + <p>[defaults]<br>fact_caching = memory</p> + <p>[inventory]<br>cache_plugin = memory</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN</div> + <div>env:ANSIBLE_INVENTORY_CACHE_PLUGIN</div> + </td> + <td> + <div>Cache plugin to use for the inventory's source data.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_prefix</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"ansible_inventory_"</div> + </td> + <td> + <div> ini entries: + <p>[default]<br>fact_caching_prefix = ansible_inventory_</p> + <p>[inventory]<br>cache_prefix = ansible_inventory_</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN_PREFIX</div> + <div>env:ANSIBLE_INVENTORY_CACHE_PLUGIN_PREFIX</div> + </td> + <td> + <div>Prefix to use for cache plugin files/tables</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">3600</div> + </td> + <td> + <div> ini entries: + <p>[defaults]<br>fact_caching_timeout = 3600</p> + <p>[inventory]<br>cache_timeout = 3600</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN_TIMEOUT</div> + <div>env:ANSIBLE_INVENTORY_CACHE_TIMEOUT</div> + </td> + <td> + <div>Cache duration in seconds</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>compose</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + </td> + <td> + <div>Create vars from jinja2 expressions.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + </td> + <td> + <div>A dictionary of filter value pairs.</div> + <div>Available filters are listed here <a href='http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options'>http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options</a>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>groups</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + </td> + <td> + <div>Add hosts to group based on Jinja2 conditionals.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>hostnames</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + </td> + <td> + <div>A list in order of precedence for hostname variables.</div> + <div>You can use the options specified in <a href='http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options'>http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options</a>.</div> + <div>To use tags as hostnames use the syntax tag:Name=Value to use the hostname Name_Value, or tag:Name to use the value of the Name tag.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>iam_role_arn</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>The ARN of the IAM role to assume to perform the inventory lookup. You should still provide AWS credentials with enough privilege to perform the AssumeRole action.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>include_extra_api_calls</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + </td> + <td> + <div>Add two additional API calls for every instance to include 'persistent' and 'events' host variables.</div> + <div>Spot instances may be persistent and instances may have associated events.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>keyed_groups</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + </td> + <td> + <div>Add hosts to group based on the values of a variable.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>leading_separator</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.11</div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"yes"</div> + </td> + <td> + </td> + <td> + <div>Use in conjunction with keyed_groups.</div> + <div>By default, a keyed group that does not have a prefix or a separator provided will have a name that starts with an underscore.</div> + <div>This is because the default prefix is "" and the default separator is "_".</div> + <div>Set this option to False to omit the leading underscore (or other separator) if no prefix is given.</div> + <div>If the group name is derived from a mapping the separator is still used to concatenate the items.</div> + <div>To not use a separator in the group name at all, set the separator for the keyed group to an empty string instead.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>plugin</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>aws_ec2</li> + <li>amazon.aws.aws_ec2</li> + </ul> + </td> + <td> + </td> + <td> + <div>Token that ensures this is a source file for the plugin.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>regions</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + </td> + <td> + <div>A list of regions in which to describe EC2 instances.</div> + <div>If empty (the default) default this will include all regions, except possibly restricted ones like us-gov-west-1 and cn-north-1.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>strict</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + </td> + <td> + <div>If <code>yes</code> make invalid entries a fatal error, otherwise skip and continue.</div> + <div>Since it is possible to use facts in the expressions they might not always be available and we ignore those errors by default.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>strict_permissions</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + </td> + <td> + <div>By default if a 403 (Forbidden) error code is encountered this plugin will fail.</div> + <div>You can set this option to False in the inventory config file which will allow 403 errors to be gracefully skipped.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>use_contrib_script_compatible_sanitization</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + </td> + <td> + <div>By default this plugin is using a general group name sanitization to create safe and usable group names for use in Ansible. This option allows you to override that, in efforts to allow migration from the old inventory script and matches the sanitization of groups when the script's ``replace_dash_in_groups`` option is set to ``False``. To replicate behavior of ``replace_dash_in_groups = True`` with constructed groups, you will need to replace hyphens with underscores via the regex_replace filter for those entries.</div> + <div>For this to work you should also turn off the TRANSFORM_INVALID_GROUP_CHARS setting, otherwise the core engine will just use the standard sanitization on top.</div> + <div>This is not the default as such names break certain functionality as not all characters are valid Python identifiers which group names end up being used as.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>use_extra_vars</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.11</div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div> ini entries: + <p>[inventory_plugins]<br>use_extra_vars = no</p> + </div> + <div>env:ANSIBLE_INVENTORY_USE_EXTRA_VARS</div> + </td> + <td> + <div>Merge extra vars into the available variables for composition (highest precedence).</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If no credentials are provided and the control node has an associated IAM instance profile then the role will be used for authentication. + + + +Examples +-------- + +.. code-block:: yaml + + # Minimal example using environment vars or instance role credentials + # Fetch all hosts in us-east-1, the hostname is the public DNS if it exists, otherwise the private IP address + plugin: aws_ec2 + regions: + - us-east-1 + + # Example using filters, ignoring permission errors, and specifying the hostname precedence + plugin: aws_ec2 + boto_profile: aws_profile + # Populate inventory with instances in these regions + regions: + - us-east-1 + - us-east-2 + filters: + # All instances with their `Environment` tag set to `dev` + tag:Environment: dev + # All dev and QA hosts + tag:Environment: + - dev + - qa + instance.group-id: sg-xxxxxxxx + # Ignores 403 errors rather than failing + strict_permissions: False + # Note: I(hostnames) sets the inventory_hostname. To modify ansible_host without modifying + # inventory_hostname use compose (see example below). + hostnames: + - tag:Name=Tag1,Name=Tag2 # Return specific hosts only + - tag:CustomDNSName + - dns-name + - name: 'tag:Name=Tag1,Name=Tag2' + - name: 'private-ip-address' + separator: '_' + prefix: 'tag:Name' + + # Example using constructed features to create groups and set ansible_host + plugin: aws_ec2 + regions: + - us-east-1 + - us-west-1 + # keyed_groups may be used to create custom groups + strict: False + keyed_groups: + # Add e.g. x86_64 hosts to an arch_x86_64 group + - prefix: arch + key: 'architecture' + # Add hosts to tag_Name_Value groups for each Name/Value tag pair + - prefix: tag + key: tags + # Add hosts to e.g. instance_type_z3_tiny + - prefix: instance_type + key: instance_type + # Create security_groups_sg_abcd1234 group for each SG + - key: 'security_groups|json_query("[].group_id")' + prefix: 'security_groups' + # Create a group for each value of the Application tag + - key: tags.Application + separator: '' + # Create a group per region e.g. aws_region_us_east_2 + - key: placement.region + prefix: aws_region + # Create a group (or groups) based on the value of a custom tag "Role" and add them to a metagroup called "project" + - key: tags['Role'] + prefix: foo + parent_group: "project" + # Set individual variables with compose + compose: + # Use the private IP address to connect to the host + # (note: this does not modify inventory_hostname, which is set via I(hostnames)) + ansible_host: private_ip_address + + + + +Status +------ + + +Authors +~~~~~~~ + +- Sloane Hertel (@s-hertel) + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_rds_inventory.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_rds_inventory.rst new file mode 100644 index 00000000..de00b859 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_rds_inventory.rst @@ -0,0 +1,522 @@ +.. _amazon.aws.aws_rds_inventory: + + +****************** +amazon.aws.aws_rds +****************** + +**rds instance source** + + + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Get instances and clusters from Amazon Web Services RDS. +- Uses a YAML configuration file that ends with aws_rds.(yml|yaml). + + + +Requirements +------------ +The below requirements are needed on the local Ansible controller node that executes this inventory. + +- boto3 +- botocore + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th>Configuration</th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY_ID</div> + </td> + <td> + <div>The AWS access key to use.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_access_key_id</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:AWS_DEFAULT_PROFILE</div> + <div>env:AWS_PROFILE</div> + </td> + <td> + <div>The AWS profile</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: boto_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECRET_KEY</div> + <div>env:AWS_SECRET_KEY</div> + <div>env:AWS_SECRET_ACCESS_KEY</div> + </td> + <td> + <div>The AWS secret key that corresponds to the access key.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_secret_access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECURITY_TOKEN</div> + <div>env:AWS_SESSION_TOKEN</div> + <div>env:AWS_SECURITY_TOKEN</div> + </td> + <td> + <div>The AWS security token if using temporary access and secret keys.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div> ini entries: + <p>[inventory]<br>cache = no</p> + </div> + <div>env:ANSIBLE_INVENTORY_CACHE</div> + </td> + <td> + <div>Toggle to enable/disable the caching of the inventory's source data, requires a cache plugin setup to work.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_connection</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div> ini entries: + <p>[defaults]<br>fact_caching_connection = VALUE</p> + <p>[inventory]<br>cache_connection = VALUE</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN_CONNECTION</div> + <div>env:ANSIBLE_INVENTORY_CACHE_CONNECTION</div> + </td> + <td> + <div>Cache connection data or path, read cache plugin documentation for specifics.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_plugin</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"memory"</div> + </td> + <td> + <div> ini entries: + <p>[defaults]<br>fact_caching = memory</p> + <p>[inventory]<br>cache_plugin = memory</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN</div> + <div>env:ANSIBLE_INVENTORY_CACHE_PLUGIN</div> + </td> + <td> + <div>Cache plugin to use for the inventory's source data.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_prefix</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"ansible_inventory_"</div> + </td> + <td> + <div> ini entries: + <p>[default]<br>fact_caching_prefix = ansible_inventory_</p> + <p>[inventory]<br>cache_prefix = ansible_inventory_</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN_PREFIX</div> + <div>env:ANSIBLE_INVENTORY_CACHE_PLUGIN_PREFIX</div> + </td> + <td> + <div>Prefix to use for cache plugin files/tables</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cache_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">3600</div> + </td> + <td> + <div> ini entries: + <p>[defaults]<br>fact_caching_timeout = 3600</p> + <p>[inventory]<br>cache_timeout = 3600</p> + </div> + <div>env:ANSIBLE_CACHE_PLUGIN_TIMEOUT</div> + <div>env:ANSIBLE_INVENTORY_CACHE_TIMEOUT</div> + </td> + <td> + <div>Cache duration in seconds</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>compose</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + </td> + <td> + <div>Create vars from jinja2 expressions.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + </td> + <td> + <div>A dictionary of filter value pairs. Available filters are listed here <a href='https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-instances.html#options'>https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-instances.html#options</a>. If you filter by db-cluster-id and <em>include_clusters</em> is True it will apply to clusters as well.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>groups</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + </td> + <td> + <div>Add hosts to group based on Jinja2 conditionals.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>iam_role_arn</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>The ARN of the IAM role to assume to perform the inventory lookup. You should still provide AWS credentials with enough privilege to perform the AssumeRole action.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>include_clusters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + </td> + <td> + <div>Whether or not to query for Aurora clusters as well as instances</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>keyed_groups</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + </td> + <td> + <div>Add hosts to group based on the values of a variable.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>leading_separator</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.11</div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"yes"</div> + </td> + <td> + </td> + <td> + <div>Use in conjunction with keyed_groups.</div> + <div>By default, a keyed group that does not have a prefix or a separator provided will have a name that starts with an underscore.</div> + <div>This is because the default prefix is "" and the default separator is "_".</div> + <div>Set this option to False to omit the leading underscore (or other separator) if no prefix is given.</div> + <div>If the group name is derived from a mapping the separator is still used to concatenate the items.</div> + <div>To not use a separator in the group name at all, set the separator for the keyed group to an empty string instead.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>regions</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + </td> + <td> + <div>A list of regions in which to describe RDS instances and clusters. Available regions are listed here <a href='https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html'>https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html</a></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>statuses</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">["creating", "available"]</div> + </td> + <td> + </td> + <td> + <div>A list of desired states for instances/clusters to be added to inventory. Set to ['all'] as a shorthand to find everything.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>strict</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + </td> + <td> + <div>If <code>yes</code> make invalid entries a fatal error, otherwise skip and continue.</div> + <div>Since it is possible to use facts in the expressions they might not always be available and we ignore those errors by default.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>strict_permissions</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + </td> + <td> + <div>By default if an AccessDenied exception is encountered this plugin will fail. You can set strict_permissions to False in the inventory config file which will allow the restrictions to be gracefully skipped.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>use_extra_vars</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 2.11</div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div> ini entries: + <p>[inventory_plugins]<br>use_extra_vars = no</p> + </div> + <div>env:ANSIBLE_INVENTORY_USE_EXTRA_VARS</div> + </td> + <td> + <div>Merge extra vars into the available variables for composition (highest precedence).</div> + </td> + </tr> + </table> + <br/> + + + + +Examples +-------- + +.. code-block:: yaml + + plugin: aws_rds + regions: + - us-east-1 + - ca-central-1 + keyed_groups: + - key: 'db_parameter_groups|json_query("[].db_parameter_group_name")' + prefix: rds_parameter_group + - key: engine + prefix: rds + - key: tags + - key: region + + + + +Status +------ + + +Authors +~~~~~~~ + +- Sloane Hertel (@s-hertel) + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_s3_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_s3_module.rst new file mode 100644 index 00000000..642bac91 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_s3_module.rst @@ -0,0 +1,866 @@ +.. _amazon.aws.aws_s3_module: + + +***************** +amazon.aws.aws_s3 +***************** + +**manage objects in S3.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module allows the user to manage S3 buckets and the objects within them. Includes support for creating and deleting both objects and buckets, retrieving objects as files or strings and generating download links. This module has a dependency on boto3 and botocore. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>bucket</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>Bucket name.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>content</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + </td> + <td> + <div>The content to PUT into an object.</div> + <div>The parameter value will be treated as a string and converted to UTF-8 before sending it to S3. To send binary data, use the <em>content_base64</em> parameter instead.</div> + <div>Either <em>content</em>, <em>content_base64</em> or <em>src</em> must be specified for a PUT operation. Ignored otherwise.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>content_base64</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + </td> + <td> + <div>The base64-encoded binary data to PUT into an object.</div> + <div>Use this if you need to put raw binary data, and don't forget to encode in base64.</div> + <div>Either <em>content</em>, <em>content_base64</em> or <em>src</em> must be specified for a PUT operation. Ignored otherwise.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dest</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The destination file path when downloading an object/key with a GET operation.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dualstack</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enables Amazon S3 Dual-Stack Endpoints, allowing S3 communications using both IPv4 and IPv6.</div> + <div>Requires at least botocore version 1.4.45.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encrypt</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set for PUT mode, asks for server-side encryption.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encryption_kms_key_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>KMS key id to use when encrypting objects using <em>encrypting=aws:kms</em>. Ignored if <em>encryption</em> is not <code>aws:kms</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encryption_mode</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>AES256</b> ←</div></li> + <li>aws:kms</li> + </ul> + </td> + <td> + <div>What encryption mode to use if <em>encrypt=true</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>expiry</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">600</div> + </td> + <td> + <div>Time limit (in seconds) for the URL generated and returned by S3/Walrus when performing a <em>mode=put</em> or <em>mode=geturl</em> operation.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: expiration</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>headers</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>Custom headers for PUT operation, as a dictionary of <code>key=value</code> and <code>key=value,key=value</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ignore_nonexistent_bucket</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Overrides initial bucket lookups in case bucket or iam policies are restrictive. Example: a user may have the GetObject permission but no other permissions. In this case using the option mode: get will fail without specifying <em>ignore_nonexistent_bucket=true</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>marker</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Specifies the key to start with when using list mode. Object keys are returned in alphabetical order, starting with key after the marker in order.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>max_keys</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">1000</div> + </td> + <td> + <div>Max number of results to return in list mode, set this if you want to retrieve fewer than the default 1000 keys.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>metadata</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>Metadata for PUT operation, as a dictionary of <code>key=value</code> and <code>key=value,key=value</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>mode</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>get</li> + <li>put</li> + <li>delete</li> + <li>create</li> + <li>geturl</li> + <li>getstr</li> + <li>delobj</li> + <li>list</li> + </ul> + </td> + <td> + <div>Switches the module behaviour between <code>put</code> (upload), <code>get</code> (download), <code>geturl</code> (return download url, Ansible 1.3+), <code>getstr</code> (download object as string (1.3+)), <code>list</code> (list keys, Ansible 2.0+), <code>create</code> (bucket), <code>delete</code> (bucket), and delobj (delete object, Ansible 2.0+).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>object</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Keyname of the object inside the bucket. Can be used to create "virtual directories", see examples.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>overwrite</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"always"</div> + </td> + <td> + <div>Force overwrite either locally on the filesystem or remotely with the object/key. Used with PUT and GET operations.</div> + <div>Must be a Boolean, <code>always</code>, <code>never</code> or <code>different</code>.</div> + <div><code>true</code> is the same as <code>always</code>.</div> + <div><code>false</code> is equal to <code>never</code>.</div> + <div>When this is set to <code>different</code> the MD5 sum of the local file is compared with the 'ETag' of the object/key in S3. The ETag may or may not be an MD5 digest of the object data. See the ETag response header here <a href='https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html'>https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html</a>.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: force</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>permission</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">["private"]</div> + </td> + <td> + <div>This option lets the user set the canned permissions on the object/bucket that are created. The permissions that can be set are <code>private</code>, <code>public-read</code>, <code>public-read-write</code>, <code>authenticated-read</code> for a bucket or <code>private</code>, <code>public-read</code>, <code>public-read-write</code>, <code>aws-exec-read</code>, <code>authenticated-read</code>, <code>bucket-owner-read</code>, <code>bucket-owner-full-control</code> for an object. Multiple permissions can be specified as a list.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>prefix</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">""</div> + </td> + <td> + <div>Limits the response to keys that begin with the specified prefix for list mode.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>retries</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">0</div> + </td> + <td> + <div>On recoverable failure, how many times to retry before actually failing.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: retry</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>rgw</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable Ceph RGW S3 support. This option requires an explicit url via <em>s3_url</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>s3_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>S3 URL endpoint for usage with Ceph, Eucalyptus and fakes3 etc. Otherwise assumes AWS.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: S3_URL</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>src</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The source file path when performing a PUT operation.</div> + <div>Either <em>content</em>, <em>content_base64</em> or <em>src</em> must be specified for a PUT operation. Ignored otherwise.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>version</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Version ID of the object inside the bucket. Can be used to get a specific version of a file if versioning is enabled in the target bucket.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + - name: Simple PUT operation + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + + - name: PUT operation from a rendered template + amazon.aws.aws_s3: + bucket: mybucket + object: /object.yaml + content: "{{ lookup('template', 'templates/object.yaml.j2') }}" + mode: put + + - name: Simple PUT operation in Ceph RGW S3 + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + rgw: true + s3_url: "http://localhost:8000" + + - name: Simple GET operation + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + dest: /usr/local/myfile.txt + mode: get + + - name: Get a specific version of an object. + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + version: 48c9ee5131af7a716edc22df9772aa6f + dest: /usr/local/myfile.txt + mode: get + + - name: PUT/upload with metadata + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + metadata: 'Content-Encoding=gzip,Cache-Control=no-cache' + + - name: PUT/upload with custom headers + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + headers: 'x-amz-grant-full-control=emailAddress=owner@example.com' + + - name: List keys simple + amazon.aws.aws_s3: + bucket: mybucket + mode: list + + - name: List keys all options + amazon.aws.aws_s3: + bucket: mybucket + mode: list + prefix: /my/desired/ + marker: /my/desired/0023.txt + max_keys: 472 + + - name: Create an empty bucket + amazon.aws.aws_s3: + bucket: mybucket + mode: create + permission: public-read + + - name: Create a bucket with key as directory, in the EU region + amazon.aws.aws_s3: + bucket: mybucket + object: /my/directory/path + mode: create + region: eu-west-1 + + - name: Delete a bucket and all contents + amazon.aws.aws_s3: + bucket: mybucket + mode: delete + + - name: GET an object but don't download if the file checksums match. New in 2.0 + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + dest: /usr/local/myfile.txt + mode: get + overwrite: different + + - name: Delete an object from a bucket + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + mode: delobj + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>contents</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>(for getstr operation)</td> + <td> + <div>Contents of the object as string.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">Hello, world!</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>expiry</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td>(for geturl operation)</td> + <td> + <div>Number of seconds the presigned url is valid for.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">600</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>msg</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Message indicating the status of the operation.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">PUT operation complete</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>s3_keys</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td>(for list operation)</td> + <td> + <div>List of object keys.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">['prefix1/', 'prefix1/key1', 'prefix1/key2']</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>url</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>(for put and geturl operations)</td> + <td> + <div>URL of the object.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">https://my-bucket.s3.amazonaws.com/my-key.txt?AWSAccessKeyId=<access-key>&Expires=1506888865&Signature=<signature></div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Lester Wade (@lwade) +- Sloane Hertel (@s-hertel) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_secret_lookup.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_secret_lookup.rst new file mode 100644 index 00000000..dcb22f14 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_secret_lookup.rst @@ -0,0 +1,387 @@ +.. _amazon.aws.aws_secret_lookup: + + +********************* +amazon.aws.aws_secret +********************* + +**Look up secrets stored in AWS Secrets Manager.** + + + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Look up secrets stored in AWS Secrets Manager provided the caller has the appropriate permissions to read the secret. +- Lookup is based on the secret's *Name* value. +- Optional parameters can be passed into this lookup; *version_id* and *version_stage* + + + +Requirements +------------ +The below requirements are needed on the local Ansible controller node that executes this lookup. + +- boto3 +- botocore>=1.10.0 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th>Configuration</th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>_terms</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>Name of the secret to look up in AWS Secrets Manager.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY</div> + <div>env:AWS_ACCESS_KEY_ID</div> + </td> + <td> + <div>The AWS access key to use.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_access_key_id</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:AWS_DEFAULT_PROFILE</div> + <div>env:AWS_PROFILE</div> + </td> + <td> + <div>The AWS profile</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: boto_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECRET_KEY</div> + <div>env:AWS_SECRET_KEY</div> + <div>env:AWS_SECRET_ACCESS_KEY</div> + </td> + <td> + <div>The AWS secret key that corresponds to the access key.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_secret_access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_SECURITY_TOKEN</div> + <div>env:AWS_SESSION_TOKEN</div> + <div>env:AWS_SECURITY_TOKEN</div> + </td> + <td> + <div>The AWS security token if using temporary access and secret keys.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>bypath</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.4.0</div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"no"</div> + </td> + <td> + </td> + <td> + <div>A boolean to indicate whether the parameter is provided as a hierarchy.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>join</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"no"</div> + </td> + <td> + </td> + <td> + <div>Join two or more entries to form an extended secret.</div> + <div>This is useful for overcoming the 4096 character limit imposed by AWS.</div> + <div>No effect when used with <em>bypath</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>nested</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.4.0</div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"no"</div> + </td> + <td> + </td> + <td> + <div>A boolean to indicate the secret contains nested values.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>on_denied</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>error</b> ←</div></li> + <li>skip</li> + <li>warn</li> + </ul> + </td> + <td> + </td> + <td> + <div>Action to take if access to the secret is denied.</div> + <div><code>error</code> will raise a fatal error when access to the secret is denied.</div> + <div><code>skip</code> will silently ignore the denied secret.</div> + <div><code>warn</code> will skip over the denied secret but issue a warning.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>on_missing</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>error</b> ←</div></li> + <li>skip</li> + <li>warn</li> + </ul> + </td> + <td> + </td> + <td> + <div>Action to take if the secret is missing.</div> + <div><code>error</code> will raise a fatal error when the secret is missing.</div> + <div><code>skip</code> will silently ignore the missing secret.</div> + <div><code>warn</code> will skip over the missing secret but issue a warning.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>env:EC2_REGION</div> + <div>env:AWS_REGION</div> + </td> + <td> + <div>The region for which to create the connection.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>version_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>Version of the secret(s).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>version_stage</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>Stage of the secret version.</div> + </td> + </tr> + </table> + <br/> + + + + +Examples +-------- + +.. code-block:: yaml + + - name: lookup secretsmanager secret in the current region + debug: msg="{{ lookup('amazon.aws.aws_secret', '/path/to/secrets', bypath=true) }}" + + - name: Create RDS instance with aws_secret lookup for password param + rds: + command: create + instance_name: app-db + db_engine: MySQL + size: 10 + instance_type: db.m1.small + username: dbadmin + password: "{{ lookup('amazon.aws.aws_secret', 'DbSecret') }}" + tags: + Environment: staging + + - name: skip if secret does not exist + debug: msg="{{ lookup('amazon.aws.aws_secret', 'secret-not-exist', on_missing='skip')}}" + + - name: warn if access to the secret is denied + debug: msg="{{ lookup('amazon.aws.aws_secret', 'secret-denied', on_denied='warn')}}" + + - name: lookup secretsmanager secret in the current region using the nested feature + debug: msg="{{ lookup('amazon.aws.aws_secret', 'secrets.environments.production.password', nested=true) }}" + # The secret can be queried using the following syntax: `aws_secret_object_name.key1.key2.key3`. + # If an object is of the form `{"key1":{"key2":{"key3":1}}}` the query would return the value `1`. + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this lookup: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>_raw</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td></td> + <td> + <div>Returns the value of the secret stored in AWS Secrets Manager.</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Aaron Smith <ajsmith10381@gmail.com> + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_service_ip_ranges_lookup.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_service_ip_ranges_lookup.rst new file mode 100644 index 00000000..194929b4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_service_ip_ranges_lookup.rst @@ -0,0 +1,144 @@ +.. _amazon.aws.aws_service_ip_ranges_lookup: + + +******************************** +amazon.aws.aws_service_ip_ranges +******************************** + +**Look up the IP ranges for services provided in AWS such as EC2 and S3.** + + + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- AWS publishes IP ranges used on the public internet by EC2, S3, CloudFront, CodeBuild, Route53, and Route53 Health Checking. +- This module produces a list of all the ranges (by default) or can narrow down the list to the specified region or service. + + + +Requirements +------------ +The below requirements are needed on the local Ansible controller node that executes this lookup. + +- must have public internet connectivity + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th>Configuration</th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>The AWS region to narrow the ranges to. Examples: us-east-1, eu-west-2, ap-southeast-1</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>service</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td> + </td> + <td> + </td> + <td> + <div>The service to filter ranges by. Options: EC2, S3, CLOUDFRONT, CODEbUILD, ROUTE53, ROUTE53_HEALTHCHECKS</div> + </td> + </tr> + </table> + <br/> + + + + +Examples +-------- + +.. code-block:: yaml + + vars: + ec2_ranges: "{{ lookup('aws_service_ip_ranges', region='ap-southeast-2', service='EC2', wantlist=True) }}" + tasks: + + - name: "use list return option and iterate as a loop" + debug: msg="{% for cidr in ec2_ranges %}{{ cidr }} {% endfor %}" + # "52.62.0.0/15 52.64.0.0/17 52.64.128.0/17 52.65.0.0/16 52.95.241.0/24 52.95.255.16/28 54.66.0.0/16 " + + - name: "Pull S3 IP ranges, and print the default return style" + debug: msg="{{ lookup('aws_service_ip_ranges', region='us-east-1', service='S3') }}" + # "52.92.16.0/20,52.216.0.0/15,54.231.0.0/17" + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this lookup: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>_raw</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">-</span> + </div> + </td> + <td></td> + <td> + <div>comma-separated list of CIDR ranges</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- James Turner <turnerjsm@gmail.com> + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_ssm_lookup.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_ssm_lookup.rst new file mode 100644 index 00000000..522bf4b9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.aws_ssm_lookup.rst @@ -0,0 +1,192 @@ +.. _amazon.aws.aws_ssm_lookup: + + +****************** +amazon.aws.aws_ssm +****************** + +**Get the value for a SSM parameter or all parameters under a path.** + + + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Get the value for an Amazon Simple Systems Manager parameter or a hierarchy of parameters. The first argument you pass the lookup can either be a parameter name or a hierarchy of parameters. Hierarchies start with a forward slash and end with the parameter name. Up to 5 layers may be specified. +- If looking up an explicitly listed parameter by name which does not exist then the lookup will return a None value which will be interpreted by Jinja2 as an empty string. You can use the ```default``` filter to give a default value in this case but must set the second parameter to true (see examples below) +- When looking up a path for parameters under it a dictionary will be returned for each path. If there is no parameter under that path then the return will be successful but the dictionary will be empty. +- If the lookup fails due to lack of permissions or due to an AWS client error then the aws_ssm will generate an error, normally crashing the current ansible task. This is normally the right thing since ignoring a value that IAM isn't giving access to could cause bigger problems and wrong behaviour or loss of data. If you want to continue in this case then you will have to set up two ansible tasks, one which sets a variable and ignores failures one which uses the value of that variable with a default. See the examples below. + + + +Requirements +------------ +The below requirements are needed on the local Ansible controller node that executes this lookup. + +- boto3 +- botocore + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th>Configuration</th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>bypath</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"no"</div> + </td> + <td> + </td> + <td> + <div>A boolean to indicate whether the parameter is provided as a hierarchy.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>decrypt</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"yes"</div> + </td> + <td> + </td> + <td> + <div>A boolean to indicate whether to decrypt the parameter.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>recursive</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"no"</div> + </td> + <td> + </td> + <td> + <div>A boolean to indicate whether to retrieve all parameters within a hierarchy.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>shortnames</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"no"</div> + </td> + <td> + </td> + <td> + <div>Indicates whether to return the name only without path if using a parameter hierarchy.</div> + </td> + </tr> + </table> + <br/> + + + + +Examples +-------- + +.. code-block:: yaml + + # lookup sample: + - name: lookup ssm parameter store in the current region + debug: msg="{{ lookup('aws_ssm', 'Hello' ) }}" + + - name: lookup ssm parameter store in nominated region + debug: msg="{{ lookup('aws_ssm', 'Hello', region='us-east-2' ) }}" + + - name: lookup ssm parameter store without decrypted + debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=False ) }}" + + - name: lookup ssm parameter store in nominated aws profile + debug: msg="{{ lookup('aws_ssm', 'Hello', aws_profile='myprofile' ) }}" + + - name: lookup ssm parameter store using explicit aws credentials + debug: msg="{{ lookup('aws_ssm', 'Hello', aws_access_key=my_aws_access_key, aws_secret_key=my_aws_secret_key, aws_security_token=my_security_token ) }}" + + - name: lookup ssm parameter store with all options. + debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=false, region='us-east-2', aws_profile='myprofile') }}" + + - name: lookup a key which doesn't exist, returns "" + debug: msg="{{ lookup('aws_ssm', 'NoKey') }}" + + - name: lookup a key which doesn't exist, returning a default ('root') + debug: msg="{{ lookup('aws_ssm', 'AdminID') | default('root', true) }}" + + - name: lookup a key which doesn't exist failing to store it in a fact + set_fact: + temp_secret: "{{ lookup('aws_ssm', '/NoAccess/hiddensecret') }}" + ignore_errors: true + + - name: show fact default to "access failed" if we don't have access + debug: msg="{{ 'the secret was:' ~ temp_secret | default('could not access secret') }}" + + - name: return a dictionary of ssm parameters from a hierarchy path + debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region='ap-southeast-2', bypath=true, recursive=true ) }}" + + - name: return a dictionary of ssm parameters from a hierarchy path with shortened names (param instead of /PATH/to/param) + debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region='ap-southeast-2', shortnames=true, bypath=true, recursive=true ) }}" + + - name: Iterate over a parameter hierarchy (one iteration per parameter) + debug: msg='Key contains {{ item.key }} , with value {{ item.value }}' + loop: '{{ lookup("aws_ssm", "/demo/", region="ap-southeast-2", bypath=True) | dict2items }}' + + - name: Iterate over multiple paths as dictionaries (one iteration per path) + debug: msg='Path contains {{ item }}' + loop: '{{ lookup("aws_ssm", "/demo/", "/demo1/", bypath=True)}}' + + + + +Status +------ + + +Authors +~~~~~~~ + +- Bill Wang <ozbillwang(at)gmail.com> +- Marat Bakeev <hawara(at)gmail.com> +- Michael De La Rue <siblemitcom.mddlr@spamgourmet.com> + + +.. hint:: + Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.cloudformation_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.cloudformation_info_module.rst new file mode 100644 index 00000000..e7dc6c8c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.cloudformation_info_module.rst @@ -0,0 +1,585 @@ +.. _amazon.aws.cloudformation_info_module: + + +****************************** +amazon.aws.cloudformation_info +****************************** + +**Obtain information about an AWS CloudFormation stack** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gets information about an AWS CloudFormation stack. +- This module was called ``amazon.aws.cloudformation_facts`` before Ansible 2.9, returning ``ansible_facts``. Note that the :ref:`amazon.aws.cloudformation_info <amazon.aws.cloudformation_info_module>` module no longer returns ``ansible_facts``! + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 >= 1.0.0 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>all_facts</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Get all stack information for the stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_change_sets</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Get stack change sets for the stack</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_events</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Get stack events for the stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The name or id of the CloudFormation stack. Gathers information on all stacks by default.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_policy</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Get stack policy for the stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_resources</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Get stack resources for the stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_template</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Get stack template body for the stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: Get summary information about a stack + amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + register: output + + - debug: + msg: "{{ output['cloudformation']['my-cloudformation-stack'] }}" + + # When the module is called as cloudformation_facts, return values are published + # in ansible_facts['cloudformation'][<stack_name>] and can be used as follows. + # Note that this is deprecated and will stop working in Ansible after 2021-12-01. + + - amazon.aws.cloudformation_facts: + stack_name: my-cloudformation-stack + + - debug: + msg: "{{ ansible_facts['cloudformation']['my-cloudformation-stack'] }}" + + # Get stack outputs, when you have the stack name available as a fact + - set_fact: + stack_name: my-awesome-stack + + - amazon.aws.cloudformation_info: + stack_name: "{{ stack_name }}" + register: my_stack + + - debug: + msg: "{{ my_stack.cloudformation[stack_name].stack_outputs }}" + + # Get all stack information about a stack + - amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + all_facts: true + + # Get stack resource and stack policy information about a stack + - amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + stack_resources: true + stack_policy: true + + # Fail if the stack doesn't exist + - name: try to get facts about a stack but fail if it doesn't exist + amazon.aws.cloudformation_info: + stack_name: nonexistent-stack + all_facts: yes + failed_when: cloudformation['nonexistent-stack'] is undefined + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_change_sets</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>only if all_facts or stack_change_sets is true and the stack exists</td> + <td> + <div>A list of stack change sets. Each item in the list represents the details of a specific changeset</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>if the stack exists</td> + <td> + <div>Summary facts about the stack</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_events</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>only if all_facts or stack_events is true and the stack exists</td> + <td> + <div>All stack events for the stack</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_outputs</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>if the stack exists</td> + <td> + <div>Dictionary of stack outputs keyed by the value of each output 'OutputKey' parameter and corresponding value of each output 'OutputValue' parameter</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'ApplicationDatabaseName': 'dazvlpr01xj55a.ap-southeast-2.rds.amazonaws.com'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_parameters</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>if the stack exists</td> + <td> + <div>Dictionary of stack parameters keyed by the value of each parameter 'ParameterKey' parameter and corresponding value of each parameter 'ParameterValue' parameter</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'DatabaseEngine': 'mysql', 'DatabasePassword': '***'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_policy</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>only if all_facts or stack_policy is true and the stack exists</td> + <td> + <div>Describes the stack policy for the stack</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_resource_list</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>only if all_facts or stack_resources is true and the stack exists</td> + <td> + <div>Describes stack resources for the stack</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_resources</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>only if all_facts or stack_resources is true and the stack exists</td> + <td> + <div>Dictionary of stack resources keyed by the value of each resource 'LogicalResourceId' parameter and corresponding value of each resource 'PhysicalResourceId' parameter</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'AutoScalingGroup': 'dev-someapp-AutoscalingGroup-1SKEXXBCAN0S7', 'AutoScalingSecurityGroup': 'sg-abcd1234', 'ApplicationDatabase': 'dazvlpr01xj55a'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_template</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>only if all_facts or stack_template is true and the stack exists</td> + <td> + <div>Describes the stack template for the stack</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Justin Menga (@jmenga) +- Kevin Coming (@waffie1) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.cloudformation_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.cloudformation_module.rst new file mode 100644 index 00000000..57059af5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.cloudformation_module.rst @@ -0,0 +1,839 @@ +.. _amazon.aws.cloudformation_module: + + +************************* +amazon.aws.cloudformation +************************* + +**Create or delete an AWS CloudFormation stack** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Launches or updates an AWS CloudFormation stack and waits for it complete. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore>=1.5.45 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>backoff_delay</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">3</div> + </td> + <td> + <div>Number of seconds to wait for the next retry.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>backoff_max_delay</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">30</div> + </td> + <td> + <div>Maximum amount of time to wait between retries.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>backoff_retries</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">10</div> + </td> + <td> + <div>Number of times to retry operation.</div> + <div>AWS API throttling mechanism fails CloudFormation module so we have to retry a couple of times.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>capabilities</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]</div> + </td> + <td> + <div>Specify capabilities that stack template contains.</div> + <div>Valid values are <code>CAPABILITY_IAM</code>, <code>CAPABILITY_NAMED_IAM</code> and <code>CAPABILITY_AUTO_EXPAND</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>changeset_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Name given to the changeset when creating a changeset.</div> + <div>Only used when <em>create_changeset=true</em>.</div> + <div>By default a name prefixed with Ansible-STACKNAME is generated based on input parameters. See the AWS Change Sets docs for more information <a href='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html'>https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html</a></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>create_changeset</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>If stack already exists create a changeset instead of directly applying changes. See the AWS Change Sets docs <a href='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html'>https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html</a>.</div> + <div>WARNING: if the stack does not exist, it will be created without changeset. If <em>state=absent</em>, the stack will be deleted immediately with no changeset.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>create_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The amount of time (in minutes) that can pass before the stack status becomes CREATE_FAILED</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>disable_rollback</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>If a stacks fails to form, rollback will remove the stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>events_limit</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">200</div> + </td> + <td> + <div>Maximum number of CloudFormation events to fetch from a stack when creating or updating it.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>notification_arns</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>A comma separated list of Simple Notification Service (SNS) topic ARNs to publish stack related events.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>on_create_failure</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>DO_NOTHING</li> + <li>ROLLBACK</li> + <li>DELETE</li> + </ul> + </td> + <td> + <div>Action to take upon failure of stack creation. Incompatible with the <em>disable_rollback</em> option.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>role_arn</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The role that AWS CloudFormation assumes to create the stack. See the AWS CloudFormation Service Role docs <a href='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-servicerole.html'>https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-servicerole.html</a></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the CloudFormation stack.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stack_policy</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The path of the CloudFormation stack policy. A policy cannot be removed once placed, but it can be modified. for instance, allow all updates <a href='https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html#d0e9051'>https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html#d0e9051</a></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>If <em>state=present</em>, stack will be created.</div> + <div>If <em>state=present</em> and if stack exists and template has changed, it will be updated.</div> + <div>If <em>state=absent</em>, stack will be removed.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>Dictionary of tags to associate with stack and its resources during stack creation.</div> + <div>Can be updated later, updating tags removes previous entries.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>template</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The local path of the CloudFormation template.</div> + <div>This must be the full path to the file, relative to the working directory. If using roles this may look like <code>roles/cloudformation/files/cloudformation-example.json</code>.</div> + <div>If <em>state=present</em> and the stack does not exist yet, either <em>template</em>, <em>template_body</em> or <em>template_url</em> must be specified (but only one of them).</div> + <div>If <em>state=present</em>, the stack does exist, and neither <em>template</em>, <em>template_body</em> nor <em>template_url</em> are specified, the previous template will be reused.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>template_body</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Template body. Use this to pass in the actual body of the CloudFormation template.</div> + <div>If <em>state=present</em> and the stack does not exist yet, either <em>template</em>, <em>template_body</em> or <em>template_url</em> must be specified (but only one of them).</div> + <div>If <em>state=present</em>, the stack does exist, and neither <em>template</em>, <em>template_body</em> nor <em>template_url</em> are specified, the previous template will be reused.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>template_format</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>This parameter is ignored since Ansible 2.3 and will be removed after 2022-06-01.</div> + <div>Templates are now passed raw to CloudFormation regardless of format.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>template_parameters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + <div>A list of hashes of all the template variables for the stack. The value can be a string or a dict.</div> + <div>Dict can be used to set additional template parameter attributes like UsePreviousValue (see example).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>template_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Location of file containing the template body. The URL must point to a template (max size 307,200 bytes) located in an S3 bucket in the same region as the stack.</div> + <div>If <em>state=present</em> and the stack does not exist yet, either <em>template</em>, <em>template_body</em> or <em>template_url</em> must be specified (but only one of them).</div> + <div>If <em>state=present</em>, the stack does exist, and neither <em>template</em>, <em>template_body</em> nor <em>template_url</em> are specified, the previous template will be reused.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>termination_protection</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable or disable termination protection on the stack. Only works with botocore >= 1.7.18.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - CloudFormation features change often, and this module tries to keep up. That means your botocore version should be fresh. The version listed in the requirements is the oldest version that works with the module as a whole. Some features may require recent versions, and we do not pinpoint a minimum version for each feature. Instead of relying on the minimum version, keep botocore up to date. AWS is always releasing features and fixing bugs. + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + - name: create a cloudformation stack + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: "present" + region: "us-east-1" + disable_rollback: true + template: "files/cloudformation-example.json" + template_parameters: + KeyName: "jmartin" + DiskType: "ephemeral" + InstanceType: "m1.small" + ClusterSize: 3 + tags: + Stack: "ansible-cloudformation" + + # Basic role example + - name: create a stack, specify role that cloudformation assumes + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: "present" + region: "us-east-1" + disable_rollback: true + template: "roles/cloudformation/files/cloudformation-example.json" + role_arn: 'arn:aws:iam::123456789012:role/cloudformation-iam-role' + + - name: delete a stack + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation-old" + state: "absent" + + # Create a stack, pass in template from a URL, disable rollback if stack creation fails, + # pass in some parameters to the template, provide tags for resources created + - name: create a stack, pass in the template via an URL + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: present + region: us-east-1 + disable_rollback: true + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + template_parameters: + KeyName: jmartin + DiskType: ephemeral + InstanceType: m1.small + ClusterSize: 3 + tags: + Stack: ansible-cloudformation + + # Create a stack, passing in template body using lookup of Jinja2 template, disable rollback if stack creation fails, + # pass in some parameters to the template, provide tags for resources created + - name: create a stack, pass in the template body via lookup template + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: present + region: us-east-1 + disable_rollback: true + template_body: "{{ lookup('template', 'cloudformation.j2') }}" + template_parameters: + KeyName: jmartin + DiskType: ephemeral + InstanceType: m1.small + ClusterSize: 3 + tags: + Stack: ansible-cloudformation + + # Pass a template parameter which uses CloudFormation's UsePreviousValue attribute + # When use_previous_value is set to True, the given value will be ignored and + # CloudFormation will use the value from a previously submitted template. + # If use_previous_value is set to False (default) the given value is used. + - amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: "present" + region: "us-east-1" + template: "files/cloudformation-example.json" + template_parameters: + DBSnapshotIdentifier: + use_previous_value: True + value: arn:aws:rds:es-east-1:000000000000:snapshot:rds:my-db-snapshot + DBName: + use_previous_value: True + tags: + Stack: "ansible-cloudformation" + + # Enable termination protection on a stack. + # If the stack already exists, this will update its termination protection + - name: enable termination protection during stack creation + amazon.aws.cloudformation: + stack_name: my_stack + state: present + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + termination_protection: yes + + # Configure TimeoutInMinutes before the stack status becomes CREATE_FAILED + # In this case, if disable_rollback is not set or is set to false, the stack will be rolled back. + - name: enable termination protection during stack creation + amazon.aws.cloudformation: + stack_name: my_stack + state: present + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + create_timeout: 5 + + # Configure rollback behaviour on the unsuccessful creation of a stack allowing + # CloudFormation to clean up, or do nothing in the event of an unsuccessful + # deployment + # In this case, if on_create_failure is set to "DELETE", it will clean up the stack if + # it fails to create + - name: create stack which will delete on creation failure + amazon.aws.cloudformation: + stack_name: my_stack + state: present + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + on_create_failure: DELETE + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>change_set_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em> and <em>create_changeset=true</em></td> + <td> + <div>The ID of the stack change set if one was created</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">arn:aws:cloudformation:us-east-1:012345678901:changeSet/Ansible-StackName-f4496805bd1b2be824d1e315c6884247ede41eb0</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>events</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>Most recent events in CloudFormation's event log. This may be from a previous run in some cases.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">['StackEvent AWS::CloudFormation::Stack stackname UPDATE_COMPLETE', 'StackEvent AWS::CloudFormation::Stack stackname UPDATE_COMPLETE_CLEANUP_IN_PROGRESS']</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>log</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>Debugging logs. Useful when modifying or finding an error.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">['updating stack']</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_outputs</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>state == present</td> + <td> + <div>A key:value dictionary of all the stack outputs currently defined. If there are no stack outputs, it is an empty dictionary.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'MySg': 'AnsibleModuleTestYAML-CFTestSg-C8UVS567B6NS'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>stack_resources</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>state == present</td> + <td> + <div>AWS stack resources and their status. List of dictionaries, one dict per resource.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'last_updated_time': '2016-10-11T19:40:14.979000+00:00', 'logical_resource_id': 'CFTestSg', 'physical_resource_id': 'cloudformation2-CFTestSg-16UQ4CYQ57O9F', 'resource_type': 'AWS::EC2::SecurityGroup', 'status': 'UPDATE_COMPLETE', 'status_reason': None}]</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- James S. Martin (@jsmartin) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_ami_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_ami_info_module.rst new file mode 100644 index 00000000..ff8702ff --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_ami_info_module.rst @@ -0,0 +1,801 @@ +.. _amazon.aws.ec2_ami_info_module: + + +*********************** +amazon.aws.ec2_ami_info +*********************** + +**Gather information about ec2 AMIs** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 AMIs +- This module was called ``amazon.aws.ec2_ami_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>describe_image_attributes</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Describe attributes (like launchPermission) of the images found.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>executable_users</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>Filter images by users with explicit launch permissions. Valid options are an AWS account ID, self, or all (public AMIs).</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: executable_user</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value.</div> + <div>See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html</a> for possible filters.</div> + <div>Filter names and values are case sensitive.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>image_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>One or more image IDs.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: image_id</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>owners</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>Filter the images by the owner. Valid options are an AWS account ID, self, or an AWS owner alias ( amazon | aws-marketplace | microsoft ).</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: owner</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: gather information about an AMI using ami-id + amazon.aws.ec2_ami_info: + image_ids: ami-5b488823 + + - name: gather information about all AMIs with tag key Name and value webapp + amazon.aws.ec2_ami_info: + filters: + "tag:Name": webapp + + - name: gather information about an AMI with 'AMI Name' equal to foobar + amazon.aws.ec2_ami_info: + filters: + name: foobar + + - name: gather information about Ubuntu 17.04 AMIs published by Canonical (099720109477) + amazon.aws.ec2_ami_info: + owners: 099720109477 + filters: + name: "ubuntu/images/ubuntu-zesty-17.04-*" + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="3">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>images</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A list of images.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>architecture</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The architecture of the image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">x86_64</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>block_device_mappings</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>Any block device mapping entries.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>device_name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The device name exposed to the instance.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/sda1</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ebs</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>EBS volumes</div> + <br/> + </td> + </tr> + + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>creation_date</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The date and time the image was created.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2017-10-16T19:22:13.000Z</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The description of the AMI.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ena_support</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>Whether enhanced networking with ENA is enabled.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">True</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>hypervisor</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The hypervisor type of the image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">xen</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>image_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ami-5b466623</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>image_location</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The location of the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">408466080000/Webapp</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>image_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The type of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">machine</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>launch_permissions</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td>When image is owned by calling account and <em>describe_image_attributes</em> is yes.</td> + <td> + <div>A List of AWS accounts may launch the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'group': 'all'}, {'user_id': '408466080000'}]</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>group</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>A value of 'all' means the AMI is public.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>user_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>An AWS account ID with permissions to launch the AMI.</div> + <br/> + </td> + </tr> + + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The name of the AMI that was provided during image creation.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">Webapp</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The AWS account ID of the image owner.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">408466080000</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>public</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>Whether the image has public launch permissions.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">True</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>root_device_name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The device name of the root device.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/sda1</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>root_device_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The type of root device used by the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ebs</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>sriov_net_support</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Whether enhanced networking is enabled.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">simple</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The current state of the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">available</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>Any tags assigned to the image.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>virtualization_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The type of virtualization of the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">hvm</div> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Prasad Katti (@prasadkatti) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_ami_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_ami_module.rst new file mode 100644 index 00000000..1edb5d24 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_ami_module.rst @@ -0,0 +1,1176 @@ +.. _amazon.aws.ec2_ami_module: + + +****************** +amazon.aws.ec2_ami +****************** + +**Create or destroy an image (AMI) in ec2** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Registers or deregisters ec2 images. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- python >= 2.6 +- boto + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>architecture</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"x86_64"</div> + </td> + <td> + <div>The target architecture of the image to register</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>billing_products</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of valid billing codes. To be used with valid accounts by aws marketplace vendors.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_snapshot</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Delete snapshots when deregistering the AMI.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Human-readable string describing the contents and purpose of the AMI.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>device_mapping</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>List of device hashes/dictionaries with custom configurations (same block-device-mapping parameters).</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_on_termination</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether the device should be automatically deleted when the Instance is terminated.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>device_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The device name. For example <code>/dev/sda</code>.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: DeviceName</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encrypted</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether the volume should be encrypted.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>iops</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>When using an <code>io1</code> <em>volume_type</em> this sets the number of IOPS provisioned for the volume</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>no_device</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Suppresses the specified device included in the block device mapping of the AMI.</div> + <div>Alias <code>NoDevice</code> has been deprecated and will be removed after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: NoDevice</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>snapshot_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The ID of the Snapshot.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>virtual_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The virtual name for the device.</div> + <div>See the AWS documentation for more detail <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html</a>.</div> + <div>Alias <code>VirtualName</code> has been deprecated and will be removed after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: VirtualName</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_size</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The size of the volume (in GiB)</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: size</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The volume type. Defaults to <code>gp2</code> when not set.</div> + </td> + </tr> + + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>enhanced_networking</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>A boolean representing whether enhanced networking with ENA is enabled or not.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>image_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Image ID to be deregistered.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>image_location</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The s3 location of an image to use for the AMI.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Instance ID to create the AMI from.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>kernel_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The target kernel id of the image to register.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>launch_permissions</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>Users and groups that should be able to launch the AMI. Expects dictionary with a key of user_ids and/or group_names. user_ids should be a list of account ids. group_name should be a list of groups, "all" is the only acceptable value currently.</div> + <div>You must pass all desired launch permissions if you wish to modify existing launch permissions (passing just groups will remove all users)</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The name of the new AMI.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>no_reboot</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Flag indicating that the bundling process should not attempt to shutdown the instance before bundling. If this flag is True, the responsibility of maintaining file system integrity is left to the owner of the instance.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether to remove existing tags that aren't passed in the <code>tags</code> parameter</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ramdisk_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The ID of the RAM disk.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>root_device_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The root device name of the image to register.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>sriov_net_support</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Set to simple to enable enhanced networking with the Intel 82599 Virtual Function interface for the AMI and any instances that you launch from the AMI.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>absent</li> + <li><div style="color: blue"><b>present</b> ←</div></li> + </ul> + </td> + <td> + <div>Register or deregister an AMI.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary of tags to add to the new image; '{"key":"value"}' and '{"key":"value","key":"value"}'</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>virtualization_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">"hvm"</div> + </td> + <td> + <div>The virtualization type of the image to register.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Wait for the AMI to be in state 'available' before returning.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">1200</div> + </td> + <td> + <div>How long before wait gives up, in seconds.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: Basic AMI Creation + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + wait: yes + name: newtest + tags: + Name: newtest + Service: TestService + + - name: Basic AMI Creation, without waiting + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + wait: no + name: newtest + + - name: AMI Registration from EBS Snapshot + amazon.aws.ec2_ami: + name: newtest + state: present + architecture: x86_64 + virtualization_type: hvm + root_device_name: /dev/xvda + device_mapping: + - device_name: /dev/xvda + volume_size: 8 + snapshot_id: snap-xxxxxxxx + delete_on_termination: true + volume_type: gp2 + + - name: AMI Creation, with a custom root-device size and another EBS attached + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + name: newtest + device_mapping: + - device_name: /dev/sda1 + size: XXX + delete_on_termination: true + volume_type: gp2 + - device_name: /dev/sdb + size: YYY + delete_on_termination: false + volume_type: gp2 + + - name: AMI Creation, excluding a volume attached at /dev/sdb + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + name: newtest + device_mapping: + - device_name: /dev/sda1 + size: XXX + delete_on_termination: true + volume_type: gp2 + - device_name: /dev/sdb + no_device: yes + + - name: Deregister/Delete AMI (keep associated snapshots) + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + delete_snapshot: False + state: absent + + - name: Deregister AMI (delete associated snapshots too) + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + delete_snapshot: True + state: absent + + - name: Update AMI Launch Permissions, making it public + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + state: present + launch_permissions: + group_names: ['all'] + + - name: Allow AMI to be launched by another account + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + state: present + launch_permissions: + user_ids: ['123456789012'] + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>architecture</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Architecture of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">x86_64</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>block_device_mapping</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Block device mapping associated with image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'/dev/sda1': {'delete_on_termination': True, 'encrypted': False, 'size': 10, 'snapshot_id': 'snap-1a03b80e7', 'volume_type': 'standard'}}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>creationDate</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Creation date of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2015-10-15T22:43:44.000Z</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Description of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">nat-server</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>hypervisor</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Type of hypervisor.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">xen</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>image_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>ID of the image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ami-1234abcd</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>is_public</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Whether image is public.</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>launch_permission</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Permissions allowing other accounts to access the AMI.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'group': 'all'}]</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>location</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Location of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">315210894379/nat-server</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>AMI name of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">nat-server</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ownerId</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Owner of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">435210894375</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>platform</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Platform of image.</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>root_device_name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Root device name of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/sda1</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>root_device_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Root device type of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ebs</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>snapshots_deleted</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>after AMI is deregistered, if <em>delete_snapshot=true</em></td> + <td> + <div>A list of snapshot ids deleted after deregistering image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">['snap-fbcccb8f', 'snap-cfe7cdb4']</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>State of image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">available</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>A dictionary of tags assigned to image.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'Env': 'devel', 'Name': 'nat-server'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>virtualization_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when AMI is created or already exists</td> + <td> + <div>Image virtualization type.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">hvm</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Evan Duffield (@scicoin-project) <eduffield@iacquire.com> +- Constantin Bugneac (@Constantin07) <constantin.bugneac@endava.com> +- Ross Williams (@gunzy83) <gunzy83au@gmail.com> +- Willem van Ketwich (@wilvk) <willvk@gmail.com> diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_elb_lb_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_elb_lb_module.rst new file mode 100644 index 00000000..0f26de99 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_elb_lb_module.rst @@ -0,0 +1,829 @@ +.. _amazon.aws.ec2_elb_lb_module: + + +********************* +amazon.aws.ec2_elb_lb +********************* + +**Creates, updates or destroys an Amazon ELB.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Returns information about the load balancer. +- Will be marked changed when called only if state is changed. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- python >= 2.6 +- boto + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>access_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>An associative array of access logs configuration settings (see examples).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>connection_draining_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>Wait a specified timeout allowing connections to drain before terminating an instance.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cross_az_load_balancing</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Distribute load across all configured Availability Zones.</div> + <div>Defaults to <code>false</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>health_check</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>An associative array of health check configuration settings (see examples).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>idle_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>ELB connections from clients and to servers are timed out after this amount of time.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>List of instance ids to attach to this ELB.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>listeners</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>List of ports/protocols for this ELB to listen on (see examples).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The name of the ELB.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_instance_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Purge existing instance ids on ELB that are not found in instance_ids.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_listeners</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Purge existing listeners on ELB that are not found in listeners.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_subnets</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Purge existing subnet on ELB that are not found in subnets.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_zones</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Purge existing availability zones on ELB that are not found in zones.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>scheme</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>internal</li> + <li><div style="color: blue"><b>internet-facing</b> ←</div></li> + </ul> + </td> + <td> + <div>The scheme to use when creating the ELB. For a private VPC-visible ELB use <code>internal</code>.</div> + <div>If you choose to update your scheme with a different value the ELB will be destroyed and recreated. To update scheme you must use the option <em>wait</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_group_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of security groups to apply to the ELB.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_group_names</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of security group names to apply to the ELB.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>absent</li> + <li>present</li> + </ul> + </td> + <td> + <div>Create or destroy the ELB.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>stickiness</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>An associative array of stickiness policy settings. Policy will be applied to all listeners (see examples).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>subnets</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of VPC subnets to use when creating ELB. Zones should be empty if using this.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>An associative array of tags. To delete all tags, supply an empty dict (<code>{}</code>).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to <code>no</code>, SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>When specified, Ansible will check the status of the load balancer to ensure it has been successfully removed from AWS.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">60</div> + </td> + <td> + <div>Used in conjunction with wait. Number of seconds to wait for the ELB to be terminated.</div> + <div>A maximum of 600 seconds (10 minutes) is allowed.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>zones</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>List of availability zones to enable on this ELB.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: None of these examples set aws_access_key, aws_secret_key, or region. + # It is assumed that their matching environment variables are set. + + # Basic provisioning example (non-VPC) + + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http # options are http, https, ssl, tcp + load_balancer_port: 80 + instance_port: 80 + proxy_protocol: True + - protocol: https + load_balancer_port: 443 + instance_protocol: http # optional, defaults to value of protocol setting + instance_port: 80 + # ssl certificate required for https or ssl + ssl_certificate_id: "arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert" + + # Internal ELB example + + - amazon.aws.ec2_elb_lb: + name: "test-vpc" + scheme: internal + state: present + instance_ids: + - i-abcd1234 + purge_instance_ids: true + subnets: + - subnet-abcd1234 + - subnet-1a2b3c4d + listeners: + - protocol: http # options are http, https, ssl, tcp + load_balancer_port: 80 + instance_port: 80 + + # Configure a health check and the access logs + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + health_check: + ping_protocol: http # options are http, https, ssl, tcp + ping_port: 80 + ping_path: "/index.html" # not required for tcp or ssl + response_timeout: 5 # seconds + interval: 30 # seconds + unhealthy_threshold: 2 + healthy_threshold: 10 + access_logs: + interval: 5 # minutes (defaults to 60) + s3_location: "my-bucket" # This value is required if access_logs is set + s3_prefix: "logs" + + # Ensure ELB is gone + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: absent + + # Ensure ELB is gone and wait for check (for default timeout) + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: absent + wait: yes + + # Ensure ELB is gone and wait for check with timeout value + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: absent + wait: yes + wait_timeout: 600 + + # Normally, this module will purge any listeners that exist on the ELB + # but aren't specified in the listeners parameter. If purge_listeners is + # false it leaves them alone + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_listeners: no + + # Normally, this module will leave availability zones that are enabled + # on the ELB alone. If purge_zones is true, then any extraneous zones + # will be removed + - amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_zones: yes + + # Creates a ELB and assigns a list of subnets to it. + - amazon.aws.ec2_elb_lb: + state: present + name: 'New ELB' + security_group_ids: 'sg-123456, sg-67890' + region: us-west-2 + subnets: 'subnet-123456,subnet-67890' + purge_subnets: yes + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + + # Create an ELB with connection draining, increased idle timeout and cross availability + # zone load balancing + - amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + connection_draining_timeout: 60 + idle_timeout: 300 + cross_az_load_balancing: "yes" + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + + # Create an ELB with load balancer stickiness enabled + - amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + stickiness: + type: loadbalancer + enabled: yes + expiration: 300 + + # Create an ELB with application stickiness enabled + - amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + stickiness: + type: application + enabled: yes + cookie: SESSIONID + + # Create an ELB and add tags + - amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + tags: + Name: "New ELB" + stack: "production" + client: "Bob" + + # Delete all tags from an ELB + - amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + tags: {} + + + + +Status +------ + + +Authors +~~~~~~~ + +- Jim Dalton (@jsdalton) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_eni_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_eni_info_module.rst new file mode 100644 index 00000000..b075c2cb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_eni_info_module.rst @@ -0,0 +1,724 @@ +.. _amazon.aws.ec2_eni_info_module: + + +*********************** +amazon.aws.ec2_eni_info +*********************** + +**Gather information about ec2 ENI interfaces in AWS** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 ENI interfaces in AWS. +- This module was called ``ec2_eni_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>eni_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + </td> + <td> + <div>The ID of the ENI.</div> + <div>This option is mutually exclusive of <em>filters</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value. See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html</a> for possible filters.</div> + <div>This option is mutually exclusive of <em>eni_id</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Gather information about all ENIs + - amazon.aws.ec2_eni_info: + + # Gather information about a particular ENI + - amazon.aws.ec2_eni_info: + filters: + network-interface-id: eni-xxxxxxx + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>network_interfaces</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>List of matching elastic network interfaces</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>association</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>When an ENI is associated with an EIP</td> + <td> + <div>Info of associated elastic IP (EIP)</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'allocation_id': 'eipalloc-5sdf123', 'association_id': 'eipassoc-8sdf123', 'ip_owner_id': '4415120123456', 'public_dns_name': 'ec2-52-1-0-63.compute-1.amazonaws.com', 'public_ip': '52.1.0.63'}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>attachment</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>When an ENI is attached to an ec2 instance</td> + <td> + <div>Info about attached ec2 instance</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'attach_time': '2017-08-05T15:25:47+00:00', 'attachment_id': 'eni-attach-149d21234', 'delete_on_termination': False, 'device_index': 1, 'instance_id': 'i-15b8d3cadbafa1234', 'instance_owner_id': '4415120123456', 'status': 'attached'}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>availability_zone</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Availability zone of ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1b</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Description text for ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">My favourite network interface</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>groups</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>List of attached security groups</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'group_id': 'sg-26d0f1234', 'group_name': 'my_ec2_security_group'}]</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The id of the ENI (alias for network_interface_id)</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">eni-392fsdf</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>interface_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Type of the network interface</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">interface</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_addresses</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>List of IPv6 addresses for this interface</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>mac_address</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>MAC address of the network interface</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">0a:f8:10:2f:ab:a1</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> </td> + <td>When a Name tag has been set</td> + <td> + <div>The Name tag of the ENI, often displayed in the AWS UIs as Name</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>network_interface_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The id of the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">eni-392fsdf</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>AWS account id of the owner of the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">4415120123456</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>private_dns_name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Private DNS name for the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ip-172-16-1-180.ec2.internal</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>private_ip_address</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Private IP address for the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">172.16.1.180</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>private_ip_addresses</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>List of private IP addresses attached to the ENI</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>requester_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the entity that launched the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">AIDAIONYVJQNIAZFT3ABC</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>requester_managed</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>Indicates whether the network interface is being managed by an AWS service.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>source_dest_check</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>Indicates whether the network interface performs source/destination checking.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>status</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Indicates if the network interface is attached to an instance or not</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">in-use</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>subnet_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Subnet ID the ENI is in</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">subnet-7bbf01234</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tag_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>Dictionary of tags added to the ENI</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> </td> + <td>always</td> + <td> + <div>Dictionary of tags added to the ENI</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>ID of the VPC the network interface it part of</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vpc-b3f1f123</div> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_eni_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_eni_module.rst new file mode 100644 index 00000000..bc50a90e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_eni_module.rst @@ -0,0 +1,932 @@ +.. _amazon.aws.ec2_eni_module: + + +****************** +amazon.aws.ec2_eni +****************** + +**Create and optionally attach an Elastic Network Interface (ENI) to an instance** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Create and optionally attach an Elastic Network Interface (ENI) to an instance. If an ENI ID or private_ip is provided, the existing ENI (if any) will be modified. The 'attached' parameter controls the attachment status of the network interface. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- python >= 2.6 +- boto + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>allow_reassignment</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Indicates whether to allow an IP address that is already assigned to another network interface or instance to be reassigned to the specified network interface.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>attached</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Specifies if network interface should be attached or detached from instance. If omitted, attachment status won't change</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_on_termination</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Delete the interface when the instance it is attached to is terminated. You can only specify this flag when the interface is being modified, not on creation.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Optional description of the ENI.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>device_index</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">0</div> + </td> + <td> + <div>The index of the device for the network interface attachment on the instance.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>eni_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The ID of the ENI (to modify).</div> + <div>If <em>eni_id=None</em> and <em>state=present</em>, a new eni will be created.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>force_detach</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Force detachment of the interface. This applies either when explicitly detaching the interface by setting <em>instance_id=None</em> or when deleting an interface with <em>state=absent</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Instance ID that you wish to attach ENI to.</div> + <div>Since version 2.2, use the <em>attached</em> parameter to attach or detach an ENI. Prior to 2.2, to detach an ENI from an instance, use <code>None</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Name for the ENI. This will create a tag called "Name" with the value assigned here.</div> + <div>This can be used in conjunction with <em>subnet_id</em> as another means of identifiying a network interface.</div> + <div>AWS does not enforce unique Name tags, so duplicate names are possible if you configure it that way. If that is the case, you will need to provide other identifying information such as <em>private_ip_address</em> or <em>eni_id</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>private_ip_address</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Private IP address.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_secondary_private_ip_addresses</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>To be used with <em>secondary_private_ip_addresses</em> to determine whether or not to remove any secondary IP addresses other than those specified.</div> + <div>Set <em>secondary_private_ip_addresses=[]</em> to purge all secondary addresses.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Indicates whether to remove tags not specified in <em>tags</em> or <em>name</em>. This means you have to specify all the desired tags on each task affecting a network interface.</div> + <div>If <em>tags</em> is omitted or None this option is disregarded.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>secondary_private_ip_address_count</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The number of secondary IP addresses to assign to the network interface. This option is mutually exclusive of <em>secondary_private_ip_addresses</em></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>secondary_private_ip_addresses</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of IP addresses to assign as secondary IP addresses to the network interface. This option is mutually exclusive of <em>secondary_private_ip_address_count</em></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_groups</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>List of security groups associated with the interface. Only used when <em>state=present</em>.</div> + <div>Since version 2.2, you can specify security groups by ID or by name or a combination of both. Prior to 2.2, you can specify only by ID.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>source_dest_check</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>By default, interfaces perform source/destination checks. NAT instances however need this check to be disabled. You can only specify this flag when the interface is being modified, not on creation.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>Create or delete ENI.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>subnet_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>ID of subnet in which to create the ENI.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + </td> + <td> + <div>A hash/dictionary of tags to add to the new ENI or to add/remove from an existing one. Please note that the name field sets the "Name" tag.</div> + <div>To clear all tags, set this option to an empty dictionary to use in conjunction with <em>purge_tags</em>. If you provide <em>name</em>, that tag will not be removed.</div> + <div>To prevent removing any tags set <em>purge_tags</em> to false.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - This module identifies and ENI based on either the *eni_id*, a combination of *private_ip_address* and *subnet_id*, or a combination of *instance_id* and *device_id*. Any of these options will let you specify a particular ENI. + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Create an ENI. As no security group is defined, ENI will be created in default security group + - amazon.aws.ec2_eni: + private_ip_address: 172.31.0.20 + subnet_id: subnet-xxxxxxxx + state: present + + # Create an ENI and attach it to an instance + - amazon.aws.ec2_eni: + instance_id: i-xxxxxxx + device_index: 1 + private_ip_address: 172.31.0.20 + subnet_id: subnet-xxxxxxxx + state: present + + # Create an ENI with two secondary addresses + - amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxxx + state: present + secondary_private_ip_address_count: 2 + + # Assign a secondary IP address to an existing ENI + # This will purge any existing IPs + - amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxxx + eni_id: eni-yyyyyyyy + state: present + secondary_private_ip_addresses: + - 172.16.1.1 + + # Remove any secondary IP addresses from an existing ENI + - amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxxx + eni_id: eni-yyyyyyyy + state: present + secondary_private_ip_address_count: 0 + + # Destroy an ENI, detaching it from any instance if necessary + - amazon.aws.ec2_eni: + eni_id: eni-xxxxxxx + force_detach: true + state: absent + + # Update an ENI + - amazon.aws.ec2_eni: + eni_id: eni-xxxxxxx + description: "My new description" + state: present + + # Update an ENI using name and subnet_id + - amazon.aws.ec2_eni: + name: eni-20 + subnet_id: subnet-xxxxxxx + description: "My new description" + state: present + + # Update an ENI identifying it by private_ip_address and subnet_id + - amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxx + private_ip_address: 172.16.1.1 + description: "My new description" + + # Detach an ENI from an instance + - amazon.aws.ec2_eni: + eni_id: eni-xxxxxxx + instance_id: None + state: present + + ### Delete an interface on termination + # First create the interface + - amazon.aws.ec2_eni: + instance_id: i-xxxxxxx + device_index: 1 + private_ip_address: 172.31.0.20 + subnet_id: subnet-xxxxxxxx + state: present + register: eni + + # Modify the interface to enable the delete_on_terminaton flag + - amazon.aws.ec2_eni: + eni_id: "{{ eni.interface.id }}" + delete_on_termination: true + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>interface</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>when state != absent</td> + <td> + <div>Network interface attributes</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>interface description</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">Firewall network interface</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>groups</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td></td> + <td> + <div>list of security groups</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'sg-f8a8a9da': 'default'}]</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>network interface id</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">eni-1d889198</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>mac_address</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>interface's physical address</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">00:00:5E:00:53:23</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The name of the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">my-eni-20</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>aws account id</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">812381371</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>private_ip_address</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>primary ip address of this interface</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.20.30.40</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>private_ip_addresses</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td></td> + <td> + <div>list of all private ip addresses associated to this interface</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'primary_address': True, 'private_ip_address': '10.20.30.40'}]</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>source_dest_check</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td></td> + <td> + <div>value of source/dest check flag</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">True</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>status</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>network interface status</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">pending</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>subnet_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>which vpc subnet the interface is bound</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">subnet-b0a0393c</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td></td> + <td> + <div>The dictionary of tags associated with the ENI</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'Name': 'my-eni', 'group': 'Finance'}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>which vpc this network interface is bound</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vpc-9a9a9da</div> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) +- Mike Healey (@healem) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_group_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_group_info_module.rst new file mode 100644 index 00000000..0c4f7f9c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_group_info_module.rst @@ -0,0 +1,342 @@ +.. _amazon.aws.ec2_group_info_module: + + +************************* +amazon.aws.ec2_group_info +************************* + +**Gather information about ec2 security groups in AWS.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 security groups in AWS. +- This module was called ``amazon.aws.ec2_group_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value. See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html</a> for possible filters. Filter names and values are case sensitive. You can also use underscores (_) instead of dashes (-) in the filter keys, which will take precedence in case of conflict.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - By default, the module will return all security groups. To limit results use the appropriate filters. + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Gather information about all security groups + - amazon.aws.ec2_group_info: + + # Gather information about all security groups in a specific VPC + - amazon.aws.ec2_group_info: + filters: + vpc-id: vpc-12345678 + + # Gather information about all security groups in a specific VPC + - amazon.aws.ec2_group_info: + filters: + vpc-id: vpc-12345678 + + # Gather information about a security group + - amazon.aws.ec2_group_info: + filters: + group-name: example-1 + + # Gather information about a security group by id + - amazon.aws.ec2_group_info: + filters: + group-id: sg-12345678 + + # Gather information about a security group with multiple filters, also mixing the use of underscores as filter keys + - amazon.aws.ec2_group_info: + filters: + group_id: sg-12345678 + vpc-id: vpc-12345678 + + # Gather information about various security groups + - amazon.aws.ec2_group_info: + filters: + group-name: + - example-1 + - example-2 + - example-3 + + # Gather information about any security group with a tag key Name and value Example. + # The quotes around 'tag:name' are important because of the colon in the value + - amazon.aws.ec2_group_info: + filters: + "tag:Name": Example + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>security_groups</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>Security groups that match the provided filters. Each element consists of a dict with all the information related to that security group.</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Henrique Rodrigues (@Sodki) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_group_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_group_module.rst new file mode 100644 index 00000000..6472f309 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_group_module.rst @@ -0,0 +1,1042 @@ +.. _amazon.aws.ec2_group_module: + + +******************** +amazon.aws.ec2_group +******************** + +**maintain an ec2 VPC security group.** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Maintains ec2 security groups. This module has a dependency on python-boto >= 2.5. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Description of the security group. Required when <code>state</code> is <code>present</code>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Id of group to delete (works only with absent).</div> + <div>One of and only one of <em>name</em> or <em>group_id</em> is required.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the security group.</div> + <div>One of and only one of <em>name</em> or <em>group_id</em> is required.</div> + <div>Required if <em>state=present</em>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_rules</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + <b>Default:</b><br/><div style="color: blue">"true"</div> + </td> + <td> + <div>Purge existing rules on security group that are not found in rules.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_rules_egress</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + <b>Default:</b><br/><div style="color: blue">"true"</div> + </td> + <td> + <div>Purge existing rules_egress on security group that are not found in rules_egress.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>If yes, existing tags will be purged from the resource to match exactly what is defined by <em>tags</em> parameter. If the <em>tags</em> parameter is not set then tags will not be modified.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>rules</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>List of firewall inbound rules to enforce in this group (see example). If none are supplied, no inbound rules will be enabled. Rules list may include its own name in `group_name`. This allows idempotent loopback additions (e.g. allow group to access itself). Rule sources list support was added in version 2.4. This allows to define multiple sources per source type as well as multiple source types per rule. Prior to 2.4 an individual source is allowed. In version 2.5 support for rule descriptions was added.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cidr_ip</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IPv4 CIDR range traffic is coming from.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cidr_ipv6</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IPv6 CIDR range traffic is coming from.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>from_port</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The start of the range of ports that traffic is coming from. A value of <code>-1</code> indicates all ports.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_desc</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>If the <em>group_name</em> is set and the Security Group doesn't exist a new Security Group will be created with <em>group_desc</em> as the description.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The ID of the Security Group that traffic is coming from.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the Security Group that traffic is coming from.</div> + <div>If the Security Group doesn't exist a new Security Group will be created with <em>group_desc</em> as the description.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ip_prefix</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IP Prefix <a href='https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-prefix-lists.html'>https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-prefix-lists.html</a> that traffic is coming from.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>proto</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IP protocol name (<code>tcp</code>, <code>udp</code>, <code>icmp</code>, <code>icmpv6</code>) or number (<a href='https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers'>https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers</a>)</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>rule_desc</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>A description for the rule.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>to_port</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The end of the range of ports that traffic is coming from. A value of <code>-1</code> indicates all ports.</div> + </td> + </tr> + + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>rules_egress</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>List of firewall outbound rules to enforce in this group (see example). If none are supplied, a default all-out rule is assumed. If an empty list is supplied, no outbound rules will be enabled. Rule Egress sources list support was added in version 2.4. In version 2.5 support for rule descriptions was added.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cidr_ip</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IPv4 CIDR range traffic is going to.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cidr_ipv6</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IPv6 CIDR range traffic is going to.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>from_port</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The start of the range of ports that traffic is going to. A value of <code>-1</code> indicates all ports.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_desc</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>If the <em>group_name</em> is set and the Security Group doesn't exist a new Security Group will be created with <em>group_desc</em> as the description.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The ID of the Security Group that traffic is going to.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the Security Group that traffic is going to.</div> + <div>If the Security Group doesn't exist a new Security Group will be created with <em>group_desc</em> as the description.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ip_prefix</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IP Prefix <a href='https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-prefix-lists.html'>https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-prefix-lists.html</a> that traffic is going to.</div> + <div>You can specify only one of <em>cidr_ip</em>, <em>cidr_ipv6</em>, <em>ip_prefix</em>, <em>group_id</em> and <em>group_name</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>proto</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IP protocol name (<code>tcp</code>, <code>udp</code>, <code>icmp</code>, <code>icmpv6</code>) or number (<a href='https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers'>https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers</a>)</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>rule_desc</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>A description for the rule.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>to_port</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The end of the range of ports that traffic is going to. A value of <code>-1</code> indicates all ports.</div> + </td> + </tr> + + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>Create or delete a security group.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary of one or more tags to assign to the security group.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: resource_tags</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>ID of the VPC to create the group in.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If a rule declares a group_name and that group doesn't exist, it will be automatically created. In that case, group_desc should be provided as well. The module will refuse to create a depended-on group without a description. + - Preview diff mode support is added in version 2.7. + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + - name: example using security group rule descriptions + amazon.aws.ec2_group: + name: "{{ name }}" + description: sg with rule descriptions + vpc_id: vpc-xxxxxxxx + profile: "{{ aws_profile }}" + region: us-east-1 + rules: + - proto: tcp + ports: + - 80 + cidr_ip: 0.0.0.0/0 + rule_desc: allow all on port 80 + + - name: example ec2 group + amazon.aws.ec2_group: + name: example + description: an example EC2 group + vpc_id: 12345 + region: eu-west-1 + aws_secret_key: SECRET + aws_access_key: ACCESS + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 10.0.0.0/8 + - proto: tcp + from_port: 443 + to_port: 443 + # this should only be needed for EC2 Classic security group rules + # because in a VPC an ELB will use a user-account security group + group_id: amazon-elb/sg-87654321/amazon-elb-sg + - proto: tcp + from_port: 3306 + to_port: 3306 + group_id: 123412341234/sg-87654321/exact-name-of-sg + - proto: udp + from_port: 10050 + to_port: 10050 + cidr_ip: 10.0.0.0/8 + - proto: udp + from_port: 10051 + to_port: 10051 + group_id: sg-12345678 + - proto: icmp + from_port: 8 # icmp type, -1 = any type + to_port: -1 # icmp subtype, -1 = any subtype + cidr_ip: 10.0.0.0/8 + - proto: all + # the containing group name may be specified here + group_name: example + - proto: all + # in the 'proto' attribute, if you specify -1, all, or a protocol number other than tcp, udp, icmp, or 58 (ICMPv6), + # traffic on all ports is allowed, regardless of any ports you specify + from_port: 10050 # this value is ignored + to_port: 10050 # this value is ignored + cidr_ip: 10.0.0.0/8 + + rules_egress: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + cidr_ipv6: 64:ff9b::/96 + group_name: example-other + # description to use if example-other needs to be created + group_desc: other example EC2 group + + - name: example2 ec2 group + amazon.aws.ec2_group: + name: example2 + description: an example2 EC2 group + vpc_id: 12345 + region: eu-west-1 + rules: + # 'ports' rule keyword was introduced in version 2.4. It accepts a single port value or a list of values including ranges (from_port-to_port). + - proto: tcp + ports: 22 + group_name: example-vpn + - proto: tcp + ports: + - 80 + - 443 + - 8080-8099 + cidr_ip: 0.0.0.0/0 + # Rule sources list support was added in version 2.4. This allows to define multiple sources per source type as well as multiple source types per rule. + - proto: tcp + ports: + - 6379 + - 26379 + group_name: + - example-vpn + - example-redis + - proto: tcp + ports: 5665 + group_name: example-vpn + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + cidr_ipv6: + - 2607:F8B0::/32 + - 64:ff9b::/96 + group_id: + - sg-edcd9784 + diff: True + + - name: "Delete group by its id" + amazon.aws.ec2_group: + region: eu-west-1 + group_id: sg-33b4ee5b + state: absent + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>Description of security group</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">My Security Group</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>group_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>Security group id</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">sg-abcd1234</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>group_name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>Security group name</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">My Security Group</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ip_permissions</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>Inbound rules associated with the security group.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'from_port': 8182, 'ip_protocol': 'tcp', 'ip_ranges': [{'cidr_ip': '198.51.100.1/32'}], 'ipv6_ranges': [], 'prefix_list_ids': [], 'to_port': 8182, 'user_id_group_pairs': []}]</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ip_permissions_egress</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>Outbound rules associated with the security group.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">[{'ip_protocol': -1, 'ip_ranges': [{'cidr_ip': '0.0.0.0/0', 'ipv6_ranges': [], 'prefix_list_ids': [], 'user_id_group_pairs': []}]}]</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>AWS Account ID of the security group</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">123456789012</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>Tags associated with the security group</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'Name': 'My Security Group', 'Purpose': 'protecting stuff'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>on create/update</td> + <td> + <div>ID of VPC to which the security group belongs</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vpc-abcd1234</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Andrew de Quincey (@adq) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_key_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_key_module.rst new file mode 100644 index 00000000..f05e37c5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_key_module.rst @@ -0,0 +1,501 @@ +.. _amazon.aws.ec2_key_module: + + +****************** +amazon.aws.ec2_key +****************** + +**create or delete an ec2 key pair** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- create or delete an ec2 key pair. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>force</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Force overwrite of already existing key pair if key has changed.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>key_material</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Public key material.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the key pair.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>create or delete keypair</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>This option has no effect since version 2.5 and will be removed after 2022-06-01.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>This option has no effect since version 2.5 and will be removed after 2022-06-01.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: create a new ec2 key pair, returns generated private key + amazon.aws.ec2_key: + name: my_keypair + + - name: create key pair using provided key_material + amazon.aws.ec2_key: + name: my_keypair + key_material: 'ssh-rsa AAAAxyz...== me@example.com' + + - name: create key pair using key_material obtained using 'file' lookup plugin + amazon.aws.ec2_key: + name: my_keypair + key_material: "{{ lookup('file', '/path/to/public_key/id_rsa.pub') }}" + + # try creating a key pair with the name of an already existing keypair + # but don't overwrite it even if the key is different (force=false) + - name: try creating a key pair with name of an already existing keypair + amazon.aws.ec2_key: + name: my_existing_keypair + key_material: 'ssh-rsa AAAAxyz...== me@example.com' + force: false + + - name: remove key pair by name + amazon.aws.ec2_key: + name: my_keypair + state: absent + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>changed</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>whether a keypair was created/deleted</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">True</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>key</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>details of the keypair (this is set to null when state is absent)</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>fingerprint</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when state is present</td> + <td> + <div>fingerprint of the key</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">b0:22:49:61:d9:44:9d:0c:7e:ac:8a:32:93:21:6c:e8:fb:59:62:43</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when state is present</td> + <td> + <div>name of the keypair</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">my_keypair</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>private_key</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when a new keypair is created by AWS (key_material is not provided)</td> + <td> + <div>private key of a newly created keypair</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKC... -----END RSA PRIVATE KEY-----</div> + </td> + </tr> + + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>msg</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>short message describing the action taken</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">key pair created</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Vincent Viallet (@zbal) +- Prasad Katti (@prasadkatti) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_metadata_facts_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_metadata_facts_module.rst new file mode 100644 index 00000000..56197455 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_metadata_facts_module.rst @@ -0,0 +1,1609 @@ +.. _amazon.aws.ec2_metadata_facts_module: + + +***************************** +amazon.aws.ec2_metadata_facts +***************************** + +**Gathers facts (instance metadata) about remote hosts within ec2** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module fetches data from the instance metadata endpoint in ec2 as per https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html. +- The module must be called from within the EC2 instance itself. + + + + + +Notes +----- + +.. note:: + - Parameters to filter on ec2_metadata_facts may be added later. + + + +Examples +-------- + +.. code-block:: yaml + + # Gather EC2 metadata facts + - amazon.aws.ec2_metadata_facts: + + - debug: + msg: "This instance is a t1.micro" + when: ansible_ec2_instance_type == "t1.micro" + + +Returned Facts +-------------- +Facts returned by this module are added/updated in the ``hostvars`` host facts and can be referenced by name just like any other host fact. They do not need to be registered in order to use them. + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Fact</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_ami_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The AMI ID used to launch the instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ami-XXXXXXXX</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_ami_launch_index</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>If you started more than one instance at the same time, this value indicates the order in which the instance was launched. + </div> + <div>The value of the first instance launched is 0. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">0</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_ami_manifest_path</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The path to the AMI manifest file in Amazon S3. + </div> + <div>If you used an Amazon EBS-backed AMI to launch the instance, the returned result is unknown. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">(unknown)</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_ancestor_ami_ids</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The AMI IDs of any instances that were rebundled to create this AMI. + </div> + <div>This value will only exist if the AMI manifest file contained an ancestor-amis key. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">(unknown)</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_block_device_mapping_ami</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The virtual device that contains the root/boot file system. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/sda1</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_block_device_mapping_ebsN</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The virtual devices associated with Amazon EBS volumes, if any are present. + </div> + <div>Amazon EBS volumes are only available in metadata if they were present at launch time or when the instance was last started. + </div> + <div>The N indicates the index of the Amazon EBS volume (such as ebs1 or ebs2). + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/xvdb</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_block_device_mapping_ephemeralN</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The virtual devices associated with ephemeral devices, if any are present. The N indicates the index of the ephemeral volume. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/xvdc</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_block_device_mapping_root</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The virtual devices or partitions associated with the root devices, or partitions on the virtual device, where the root (/ or C) file system is associated with the given instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/sda1</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_block_device_mapping_swap</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The virtual devices associated with swap. Not always present. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/dev/sda2</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_fws_instance_monitoring</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Value showing whether the customer has enabled detailed one-minute monitoring in CloudWatch. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">enabled</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_hostname</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The private IPv4 DNS hostname of the instance. + </div> + <div>In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ip-10-0-0-1.ec2.internal</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_info</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td></td> + <td> + <div>If there is an IAM role associated with the instance, contains information about the last time the instance profile was updated, including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. Otherwise, not present. + </div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1" colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>InstanceProfileArn</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ARN of the InstanceProfile associated with the Instance. + </div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1" colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>InstanceProfileId</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Id of the InstanceProfile associated with the Instance. + </div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1" colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>LastUpdated</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The last time which InstanceProfile is associated with the Instance changed. + </div> + <br/> + </td> + </tr> + + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_info_instanceprofilearn</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IAM instance profile ARN. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">arn:aws:iam::<account id>:instance-profile/<role name></div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_info_instanceprofileid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM instance profile ID. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_info_lastupdated</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM info last updated time. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2017-05-12T02:42:27Z</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_instance_profile_role</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM instance role. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">role_name</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name></b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>If there is an IAM role associated with the instance, role-name is the name of the role, and role-name contains the temporary security credentials associated with the role. Otherwise, not present. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_accesskeyid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM role access key ID. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_code</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM code. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">Success</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_expiration</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM role credentials expiration time. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2017-05-12T09:11:41Z</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_lastupdated</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM role last updated time. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2017-05-12T02:40:44Z</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_secretaccesskey</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM role secret access key. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_token</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM role token. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_iam_security_credentials_<role name>_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>IAM role type. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">AWS-HMAC</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_action</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Notifies the instance that it should reboot in preparation for bundling. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">none</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of this instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">i-XXXXXXXXXXXXXXXXX</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>JSON containing instance attributes, such as instance-id, private IP address, etc. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_accountid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div> + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">012345678901</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_architecture</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Instance system architecture. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">x86_64</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_availabilityzone</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Availability Zone in which the instance launched. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1a</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_billingproducts</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Billing products for this instance. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_devpayproductcodes</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Product codes for the launched AMI. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_imageid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The AMI ID used to launch the instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ami-01234567</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_instanceid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of this instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">i-0123456789abcdef0</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_instancetype</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The type of instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">m4.large</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_kernelid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the kernel launched with this instance, if applicable. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_pendingtime</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The instance pending time. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2017-05-11T20:51:20Z</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_privateip</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The private IPv4 address of the instance. + </div> + <div>In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.0.1</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_ramdiskid</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the RAM disk specified at launch time, if applicable. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_region</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Region in which the instance launched. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_document_version</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Identity document version. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2010-08-31</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_pkcs7</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Used to verify the document's authenticity and content against the signature. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_rsa2048</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Used to verify the document's authenticity and content against the signature. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_identity_signature</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Data that can be used by other parties to verify its origin and authenticity. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_life_cycle</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The purchasing option of the instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">on-demand</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_instance_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The type of the instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">m4.large</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_local_hostname</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The private IPv4 DNS hostname of the instance. + </div> + <div>In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ip-10-0-0-1.ec2.internal</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_local_ipv4</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The private IPv4 address of the instance. + </div> + <div>In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.0.1</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_mac</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The instance's media access control (MAC) address. + </div> + <div>In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">00:11:22:33:44:55</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_metrics_vhostmd</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Metrics. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_device_number</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The unique device number associated with that interface. The device number corresponds to the device name; for example, a device-number of 2 is for the eth2 device. + </div> + <div>This category corresponds to the DeviceIndex and device-index fields that are used by the Amazon EC2 API and the EC2 commands for the AWS CLI. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">0</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_interface_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The elastic network interface ID. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">eni-12345678</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_ipv4_associations_<ip address></b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The private IPv4 addresses that are associated with each public-ip address and assigned to that interface. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_ipv6s</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IPv6 addresses associated with the interface. Returned only for instances launched into a VPC. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_local_hostname</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The interface's local hostname. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_local_ipv4s</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The private IPv4 addresses associated with the interface. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_mac</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The instance's MAC address. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">00:11:22:33:44:55</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the owner of the network interface. + </div> + <div>In multiple-interface environments, an interface can be attached by a third party, such as Elastic Load Balancing. + </div> + <div>Traffic on an interface is always billed to the interface owner. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">01234567890</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_public_hostname</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The interface's public DNS (IPv4). If the instance is in a VPC, this category is only returned if the enableDnsHostnames attribute is set to true. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ec2-1-2-3-4.compute-1.amazonaws.com</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_public_ipv4s</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Elastic IP addresses associated with the interface. There may be multiple IPv4 addresses on an instance. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">1.2.3.4</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_security_group_ids</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IDs of the security groups to which the network interface belongs. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">sg-01234567,sg-01234568</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_security_groups</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Security groups to which the network interface belongs. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">secgroup1,secgroup2</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_subnet_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the subnet in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">subnet-01234567</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_subnet_ipv4_cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IPv4 CIDR block of the subnet in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.1.0/24</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_subnet_ipv6_cidr_blocks</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IPv6 CIDR block of the subnet in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the VPC in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vpc-0123456</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv4_cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IPv4 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.0.0/16</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv4_cidr_blocks</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IPv4 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.0.0/16</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv6_cidr_blocks</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The IPv6 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_placement_availability_zone</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Availability Zone in which the instance launched. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1a</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_placement_region</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Region in which the instance launched. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_product_codes</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Product codes associated with the instance, if any. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">aw0evgkw8e5c1q413zgy5pjce</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_profile</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>EC2 instance hardware profile. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">default-hvm</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_public_hostname</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The instance's public DNS. If the instance is in a VPC, this category is only returned if the enableDnsHostnames attribute is set to true. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">ec2-1-2-3-4.compute-1.amazonaws.com</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_public_ipv4</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The public IPv4 address. If an Elastic IP address is associated with the instance, the value returned is the Elastic IP address. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">1.2.3.4</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_public_key</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>Public key. Only available if supplied at instance launch time. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_ramdisk_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the RAM disk specified at launch time, if applicable. + </div> + <br/> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_reservation_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the reservation. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">r-0123456789abcdef0</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_security_groups</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The names of the security groups applied to the instance. After launch, you can only change the security groups of instances running in a VPC. + </div> + <div>Such changes are reflected here and in network/interfaces/macs/mac/security-groups. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">securitygroup1,securitygroup2</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_services_domain</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The domain for AWS resources for the region; for example, amazonaws.com for us-east-1. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">amazonaws.com</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_services_partition</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The partition that the resource is in. For standard AWS regions, the partition is aws. + </div> + <div>If you have resources in other partitions, the partition is aws-partitionname. + </div> + <div>For example, the partition for resources in the China (Beijing) region is aws-cn. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">aws</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_spot_termination_time</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The approximate time, in UTC, that the operating system for your Spot instance will receive the shutdown signal. + </div> + <div>This item is present and contains a time value only if the Spot instance has been marked for termination by Amazon EC2. + </div> + <div>The termination-time item is not set to a time if you terminated the Spot instance yourself. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2015-01-05T18:02:00Z</div> + </td> + </tr> + <tr> + <td colspan="2" colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ansible_ec2_user_data</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this fact"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The instance user data. + </div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">#!/bin/bash</div> + </td> + </tr> + </table> + <br/><br/> + + + +Status +------ + + +Authors +~~~~~~~ + +- Silviu Dicu (@silviud) +- Vinay Dandekar (@roadmapper) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_module.rst new file mode 100644 index 00000000..6ba2ab4d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_module.rst @@ -0,0 +1,1305 @@ +.. _amazon.aws.ec2_module: + + +************** +amazon.aws.ec2 +************** + +**create, terminate, start or stop an instance in ec2** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Creates or terminates ec2 instances. +- Note: This module uses the older boto Python module to interact with the EC2 API. :ref:`amazon.aws.ec2 <amazon.aws.ec2_module>` will still receive bug fixes, but no new features. Consider using the :ref:`amazon.aws.ec2_instance <amazon.aws.ec2_instance_module>` module instead. If :ref:`amazon.aws.ec2_instance <amazon.aws.ec2_instance_module>` does not support a feature you need that is available in :ref:`amazon.aws.ec2 <amazon.aws.ec2_module>`, please file a feature request. + + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- python >= 2.6 +- boto + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>assign_public_ip</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>When provisioning within vpc, assign a public IP address. Boto library must be 2.13.0+.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>count</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">1</div> + </td> + <td> + <div>Number of instances to launch.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>count_tag</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">raw</span> + </div> + </td> + <td> + </td> + <td> + <div>Used with <em>exact_count</em> to determine how many nodes based on a specific tag criteria should be running. This can be expressed in multiple ways and is shown in the EXAMPLES section. For instance, one can request 25 servers that are tagged with <code>class=webserver</code>. The specified tag must already exist or be passed in as the <em>instance_tags</em> option.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ebs_optimized</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether instance is using optimized EBS volumes, see <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html'>https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html</a>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>exact_count</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>An integer value which indicates how many instances that match the 'count_tag' parameter should be running. Instances are either created or terminated based on this value.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>Security group (or list of groups) to use with the instance.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: groups</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>group_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>Security group id (or list of ids) to use with the instance.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Identifier for this instance or set of instances, so that the module will be idempotent with respect to EC2 instances.</div> + <div>This identifier is valid for at least 24 hours after the termination of the instance, and should not be reused for another call later on.</div> + <div>For details, see the description of client token at <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Run_Instance_Idempotency.html'>https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Run_Instance_Idempotency.html</a>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>image</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div><em>ami</em> ID to use for the instance.</div> + <div>Required when <em>state=present</em>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>list of instance ids, currently used for states: absent, running, stopped</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: instance_id</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_initiated_shutdown_behavior</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>stop</b> ←</div></li> + <li>terminate</li> + </ul> + </td> + <td> + <div>Set whether AWS will Stop or Terminate an instance on shutdown. This parameter is ignored when using instance-store. images (which require termination on shutdown).</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_profile_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the IAM instance profile (i.e. what the EC2 console refers to as an "IAM Role") to use. Boto library must be 2.5.0+.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A hash/dictionary of tags to add to the new instance or for instances to start/stop by tag. For example <code>{"key":"value"}</code> or <code>{"key":"value","key2":"value2"}</code>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Instance type to use for the instance, see <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html'>https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html</a>.</div> + <div>Required when creating a new instance.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: type</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>kernel</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Kernel eki to use for the instance.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>key_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Key pair to use on the instance.</div> + <div>The SSH key must already exist in AWS in order to use this argument.</div> + <div>Keys can be created / deleted using the <span class='module'>amazon.aws.ec2_key</span> module.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: keypair</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>monitoring</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable detailed monitoring (CloudWatch) for the instance.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>network_interfaces</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of existing network interfaces to attach to the instance at launch. When specifying existing network interfaces, none of the <em>assign_public_ip</em>, <em>private_ip</em>, <em>vpc_subnet_id</em>, <em>group</em>, or <em>group_id</em> parameters may be used. (Those parameters are for creating a new network interface at launch.)</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: network_interface</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>placement_group</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Placement group for the instance when using EC2 Clustered Compute.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>private_ip</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The private ip address to assign the instance (from the vpc subnet).</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ramdisk</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Ramdisk eri to use for the instance.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>source_dest_check</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable or Disable the Source/Destination checks (for NAT instances and Virtual Routers). When initially creating an instance the EC2 API defaults this to <code>True</code>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>spot_launch_group</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Launch group for spot requests, see <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-spot-instances-work.html#spot-launch-group'>https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-spot-instances-work.html#spot-launch-group</a>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>spot_price</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Maximum spot price to bid. If not set, a regular on-demand instance is requested.</div> + <div>A spot request is made with this maximum bid. When it is filled, the instance is started.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>spot_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>one-time</b> ←</div></li> + <li>persistent</li> + </ul> + </td> + <td> + <div>The type of spot request.</div> + <div>After being interrupted a <code>persistent</code> spot instance will be started once there is capacity to fill the request again.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>spot_wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">600</div> + </td> + <td> + <div>How long to wait for the spot instance request to be fulfilled. Affects 'Request valid until' for setting spot request lifespan.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>absent</li> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>restarted</li> + <li>running</li> + <li>stopped</li> + </ul> + </td> + <td> + <div>Create, terminate, start, stop or restart instances. The state 'restarted' was added in Ansible 2.2.</div> + <div>When <em>state=absent</em>, <em>instance_ids</em> is required.</div> + <div>When <em>state=running</em>, <em>state=stopped</em> or <em>state=restarted</em> then either <em>instance_ids</em> or <em>instance_tags</em> is required.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tenancy</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>default</b> ←</div></li> + <li>dedicated</li> + </ul> + </td> + <td> + <div>An instance with a tenancy of <code>dedicated</code> runs on single-tenant hardware and can only be launched into a VPC.</div> + <div>Note that to use dedicated tenancy you MUST specify a <em>vpc_subnet_id</em> as well.</div> + <div>Dedicated tenancy is not available for EC2 "micro" instances.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>termination_protection</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable or Disable the Termination Protection.</div> + <div>Defaults to <code>false</code>.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>user_data</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Opaque blob of data which is made available to the EC2 instance.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volumes</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of hash/dictionaries of volumes to add to the new instance.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_on_termination</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether the volume should be automatically deleted when the instance is terminated.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>device_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>A name for the device (For example <code>/dev/sda</code>).</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encrypted</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether the volume should be encrypted using the 'aws/ebs' KMS CMK.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ephemeral</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Whether the volume should be ephemeral.</div> + <div>Data on ephemeral volumes is lost when the instance is stopped.</div> + <div>Mutually exclusive with the <em>snapshot</em> parameter.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>iops</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The number of IOPS per second to provision for the volume.</div> + <div>Required when <em>volume_type=io1</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>snapshot</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The ID of an EBS snapshot to copy when creating the volume.</div> + <div>Mutually exclusive with the <em>ephemeral</em> parameter.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_size</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The size of the volume (in GiB).</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The type of volume to create.</div> + <div>See <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html'>https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html</a> for more information on the available volume types.</div> + </td> + </tr> + + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>vpc_subnet_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The subnet ID in which to launch the instance (VPC).</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Wait for the instance to reach its desired state before returning.</div> + <div>Does not wait for SSH, see the 'wait_for_connection' example for details.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">300</div> + </td> + <td> + <div>How long before wait gives up, in seconds.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>zone</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS availability zone in which to launch the instance.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_zone, ec2_zone</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Basic provisioning example + - amazon.aws.ec2: + key_name: mykey + instance_type: t2.micro + image: ami-123456 + wait: yes + group: webserver + count: 3 + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # Advanced example with tagging and CloudWatch + - amazon.aws.ec2: + key_name: mykey + group: databases + instance_type: t2.micro + image: ami-123456 + wait: yes + wait_timeout: 500 + count: 5 + instance_tags: + db: postgres + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # Single instance with additional IOPS volume from snapshot and volume delete on termination + - amazon.aws.ec2: + key_name: mykey + group: webserver + instance_type: c3.medium + image: ami-123456 + wait: yes + wait_timeout: 500 + volumes: + - device_name: /dev/sdb + snapshot: snap-abcdef12 + volume_type: io1 + iops: 1000 + volume_size: 100 + delete_on_termination: true + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # Single instance with ssd gp2 root volume + - amazon.aws.ec2: + key_name: mykey + group: webserver + instance_type: c3.medium + image: ami-123456 + wait: yes + wait_timeout: 500 + volumes: + - device_name: /dev/xvda + volume_type: gp2 + volume_size: 8 + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + count_tag: + Name: dbserver + exact_count: 1 + + # Multiple groups example + - amazon.aws.ec2: + key_name: mykey + group: ['databases', 'internal-services', 'sshable', 'and-so-forth'] + instance_type: m1.large + image: ami-6e649707 + wait: yes + wait_timeout: 500 + count: 5 + instance_tags: + db: postgres + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # Multiple instances with additional volume from snapshot + - amazon.aws.ec2: + key_name: mykey + group: webserver + instance_type: m1.large + image: ami-6e649707 + wait: yes + wait_timeout: 500 + count: 5 + volumes: + - device_name: /dev/sdb + snapshot: snap-abcdef12 + volume_size: 10 + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # Dedicated tenancy example + - amazon.aws.ec2: + assign_public_ip: yes + group_id: sg-1dc53f72 + key_name: mykey + image: ami-6e649707 + instance_type: m1.small + tenancy: dedicated + vpc_subnet_id: subnet-29e63245 + wait: yes + + # Spot instance example + - amazon.aws.ec2: + spot_price: 0.24 + spot_wait_timeout: 600 + keypair: mykey + group_id: sg-1dc53f72 + instance_type: m1.small + image: ami-6e649707 + wait: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + spot_launch_group: report_generators + instance_initiated_shutdown_behavior: terminate + + # Examples using pre-existing network interfaces + - amazon.aws.ec2: + key_name: mykey + instance_type: t2.small + image: ami-f005ba11 + network_interface: eni-deadbeef + + - amazon.aws.ec2: + key_name: mykey + instance_type: t2.small + image: ami-f005ba11 + network_interfaces: ['eni-deadbeef', 'eni-5ca1ab1e'] + + # Launch instances, runs some tasks + # and then terminate them + + - name: Create a sandbox instance + hosts: localhost + gather_facts: False + vars: + keypair: my_keypair + instance_type: m1.small + security_group: my_securitygroup + image: my_ami_id + region: us-east-1 + tasks: + - name: Launch instance + amazon.aws.ec2: + key_name: "{{ keypair }}" + group: "{{ security_group }}" + instance_type: "{{ instance_type }}" + image: "{{ image }}" + wait: true + region: "{{ region }}" + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + register: ec2 + + - name: Add new instance to host group + add_host: + hostname: "{{ item.public_ip }}" + groupname: launched + loop: "{{ ec2.instances }}" + + - name: Wait for SSH to come up + delegate_to: "{{ item.public_dns_name }}" + wait_for_connection: + delay: 60 + timeout: 320 + loop: "{{ ec2.instances }}" + + - name: Configure instance(s) + hosts: launched + become: True + gather_facts: True + roles: + - my_awesome_role + - my_awesome_test + + - name: Terminate instances + hosts: localhost + tasks: + - name: Terminate instances that were previously launched + amazon.aws.ec2: + state: 'absent' + instance_ids: '{{ ec2.instance_ids }}' + + # Start a few existing instances, run some tasks + # and stop the instances + + - name: Start sandbox instances + hosts: localhost + gather_facts: false + vars: + instance_ids: + - 'i-xxxxxx' + - 'i-xxxxxx' + - 'i-xxxxxx' + region: us-east-1 + tasks: + - name: Start the sandbox instances + amazon.aws.ec2: + instance_ids: '{{ instance_ids }}' + region: '{{ region }}' + state: running + wait: True + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + roles: + - do_neat_stuff + - do_more_neat_stuff + + - name: Stop sandbox instances + hosts: localhost + gather_facts: false + vars: + instance_ids: + - 'i-xxxxxx' + - 'i-xxxxxx' + - 'i-xxxxxx' + region: us-east-1 + tasks: + - name: Stop the sandbox instances + amazon.aws.ec2: + instance_ids: '{{ instance_ids }}' + region: '{{ region }}' + state: stopped + wait: True + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # + # Start stopped instances specified by tag + # + - amazon.aws.ec2: + instance_tags: + Name: ExtraPower + state: running + + # + # Restart instances specified by tag + # + - amazon.aws.ec2: + instance_tags: + Name: ExtraPower + state: restarted + + # + # Enforce that 5 instances with a tag "foo" are running + # (Highly recommended!) + # + + - amazon.aws.ec2: + key_name: mykey + instance_type: c1.medium + image: ami-40603AD1 + wait: yes + group: webserver + instance_tags: + foo: bar + exact_count: 5 + count_tag: foo + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # + # Enforce that 5 running instances named "database" with a "dbtype" of "postgres" + # + + - amazon.aws.ec2: + key_name: mykey + instance_type: c1.medium + image: ami-40603AD1 + wait: yes + group: webserver + instance_tags: + Name: database + dbtype: postgres + exact_count: 5 + count_tag: + Name: database + dbtype: postgres + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + + # + # count_tag complex argument examples + # + + # instances with tag foo + - amazon.aws.ec2: + count_tag: + foo: + + # instances with tag foo=bar + - amazon.aws.ec2: + count_tag: + foo: bar + + # instances with tags foo=bar & baz + - amazon.aws.ec2: + count_tag: + foo: bar + baz: + + # instances with tags foo & bar & baz=bang + - amazon.aws.ec2: + count_tag: + - foo + - bar + - baz: bang + + + + +Status +------ + + +Authors +~~~~~~~ + +- Tim Gerla (@tgerla) +- Lester Wade (@lwade) +- Seth Vidal (@skvidal) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_snapshot_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_snapshot_info_module.rst new file mode 100644 index 00000000..67456665 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_snapshot_info_module.rst @@ -0,0 +1,604 @@ +.. _amazon.aws.ec2_snapshot_info_module: + + +**************************** +amazon.aws.ec2_snapshot_info +**************************** + +**Gather information about ec2 volume snapshots in AWS** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 volume snapshots in AWS. +- This module was called ``ec2_snapshot_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value. See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSnapshots.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSnapshots.html</a> for possible filters. Filter names and values are case sensitive.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>owner_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + <div>If you specify one or more snapshot owners, only snapshots from the specified owners and for which you have access are returned.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>restorable_by_user_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + <div>If you specify a list of restorable users, only snapshots with create snapshot permissions for those users are returned.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>snapshot_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">[]</div> + </td> + <td> + <div>If you specify one or more snapshot IDs, only snapshots that have the specified IDs are returned.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - By default, the module will return all snapshots, including public ones. To limit results to snapshots owned by the account use the filter 'owner-id'. + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Gather information about all snapshots, including public ones + - amazon.aws.ec2_snapshot_info: + + # Gather information about all snapshots owned by the account 0123456789 + - amazon.aws.ec2_snapshot_info: + filters: + owner-id: 0123456789 + + # Or alternatively... + - amazon.aws.ec2_snapshot_info: + owner_ids: + - 0123456789 + + # Gather information about a particular snapshot using ID + - amazon.aws.ec2_snapshot_info: + filters: + snapshot-id: snap-00112233 + + # Or alternatively... + - amazon.aws.ec2_snapshot_info: + snapshot_ids: + - snap-00112233 + + # Gather information about any snapshot with a tag key Name and value Example + - amazon.aws.ec2_snapshot_info: + filters: + "tag:Name": Example + + # Gather information about any snapshot with an error status + - amazon.aws.ec2_snapshot_info: + filters: + status: error + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>data_encryption_key_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The data encryption key identifier for the snapshot. This value is a unique identifier that corresponds to the data encryption key that was used to encrypt the original volume or snapshot copy.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">arn:aws:kms:ap-southeast-2:012345678900:key/74c9742a-a1b2-45cb-b3fe-abcdef123456</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The description for the snapshot.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">My important backup</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>encrypted</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>Indicates whether the snapshot is encrypted.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">True</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>kms_key_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The full ARN of the AWS Key Management Service (AWS KMS) customer master key (CMK) that was used to protect the volume encryption key for the parent volume.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">74c9742a-a1b2-45cb-b3fe-abcdef123456</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_alias</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The AWS account alias (for example, amazon, self) or AWS account ID that owns the snapshot.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">033440102211</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The AWS account ID of the EBS snapshot owner.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">099720109477</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>progress</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The progress of the snapshot, as a percentage.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">100%</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>snapshot_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the snapshot. Each snapshot receives a unique identifier when it is created.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">snap-01234567</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>start_time</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The time stamp when the snapshot was initiated.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2015-02-12T02:14:02+00:00</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The snapshot state (completed, pending or error).</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">completed</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state_message</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Encrypted Amazon EBS snapshots are copied asynchronously. If a snapshot copy operation fails (for example, if the proper AWS Key Management Service (AWS KMS) permissions are not obtained) this field displays error state details to help you diagnose why the error occurred.</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>Any tags assigned to the snapshot.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{ 'my_tag_key': 'my_tag_value' }</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the volume that was used to create the snapshot.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vol-01234567</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume_size</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td>always</td> + <td> + <div>The size of the volume, in GiB.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">8</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_snapshot_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_snapshot_module.rst new file mode 100644 index 00000000..1e97ed05 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_snapshot_module.rst @@ -0,0 +1,521 @@ +.. _amazon.aws.ec2_snapshot_module: + + +*********************** +amazon.aws.ec2_snapshot +*********************** + +**Creates a snapshot from an existing volume** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Creates an EC2 snapshot from an existing EBS volume. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- python >= 2.6 +- boto + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>description</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Description to be applied to the snapshot.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>device_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Device name of a mounted volume to be snapshotted.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Instance that has the required volume to snapshot mounted.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>last_snapshot_min_age</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">0</div> + </td> + <td> + <div>If the volume's most recent snapshot has started less than `last_snapshot_min_age' minutes ago, a new snapshot will not be created.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>snapshot_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Snapshot id to remove.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>snapshot_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary of tags to add to the snapshot.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>absent</li> + <li><div style="color: blue"><b>present</b> ←</div></li> + </ul> + </td> + <td> + <div>Whether to add or create a snapshot.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Volume from which to take the snapshot.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Wait for the snapshot to be ready.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">0</div> + </td> + <td> + <div>How long before wait gives up, in seconds.</div> + <div>Specify 0 to wait forever.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Simple snapshot of volume using volume_id + - amazon.aws.ec2_snapshot: + volume_id: vol-abcdef12 + description: snapshot of /data from DB123 taken 2013/11/28 12:18:32 + + # Snapshot of volume mounted on device_name attached to instance_id + - amazon.aws.ec2_snapshot: + instance_id: i-12345678 + device_name: /dev/sdb1 + description: snapshot of /data from DB123 taken 2013/11/28 12:18:32 + + # Snapshot of volume with tagging + - amazon.aws.ec2_snapshot: + instance_id: i-12345678 + device_name: /dev/sdb1 + snapshot_tags: + frequency: hourly + source: /data + + # Remove a snapshot + - amazon.aws.ec2_snapshot: + snapshot_id: snap-abcd1234 + state: absent + + # Create a snapshot only if the most recent one is older than 1 hour + - amazon.aws.ec2_snapshot: + volume_id: vol-abcdef12 + last_snapshot_min_age: 60 + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>snapshot_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the snapshot. Each snapshot receives a unique identifier when it is created.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">snap-01234567</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>Any tags assigned to the snapshot.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{ 'Name': 'instance-name' }</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the volume that was used to create the snapshot.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vol-01234567</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume_size</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td>always</td> + <td> + <div>The size of the volume, in GiB.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">8</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Will Thames (@willthames) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_tag_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_tag_info_module.rst new file mode 100644 index 00000000..896d4f78 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_tag_info_module.rst @@ -0,0 +1,310 @@ +.. _amazon.aws.ec2_tag_info_module: + + +*********************** +amazon.aws.ec2_tag_info +*********************** + +**list tags on ec2 resources** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Lists tags for any EC2 resource. +- Resources are referenced by their resource id (e.g. an instance being i-XXXXXXX, a vpc being vpc-XXXXXX). +- Resource tags can be managed using the :ref:`amazon.aws.ec2_tag <amazon.aws.ec2_tag_module>` module. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>resource</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The EC2 resource id (for example i-XXXXXX or vpc-XXXXXX).</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + - name: Retrieve all tags on an instance + amazon.aws.ec2_tag_info: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + register: instance_tags + + - name: Retrieve all tags on a VPC + amazon.aws.ec2_tag_info: + region: eu-west-1 + resource: vpc-xxxxxxxxxxxxxxxxx + register: vpc_tags + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A dict containing the tags on the resource</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Mark Chappell (@tremble) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_tag_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_tag_module.rst new file mode 100644 index 00000000..d2f1bda7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_tag_module.rst @@ -0,0 +1,431 @@ +.. _amazon.aws.ec2_tag_module: + + +****************** +amazon.aws.ec2_tag +****************** + +**create and remove tags on ec2 resources** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Creates, modifies and removes tags for any EC2 resource. +- Resources are referenced by their resource id (for example, an instance being i-XXXXXXX, a VPC being vpc-XXXXXXX). +- This module is designed to be used with complex args (tags), see the examples. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether unspecified tags should be removed from the resource.</div> + <div>Note that when combined with <em>state=absent</em>, specified tags with non-matching values are not purged.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>resource</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The EC2 resource id.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + <li>list</li> + </ul> + </td> + <td> + <div>Whether the tags should be present or absent on the resource.</div> + <div>The use of <em>state=list</em> to interrogate the tags of an instance has been deprecated and will be removed after 2022-06-01. The 'list' functionality has been moved to a dedicated module <span class='module'>amazon.aws.ec2_tag_info</span>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary of tags to add or remove from the resource.</div> + <div>If the value provided for a key is not set and <em>state=absent</em>, the tag will be removed regardless of its current value.</div> + <div>Required when <em>state=present</em> or <em>state=absent</em>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + - name: Ensure tags are present on a resource + amazon.aws.ec2_tag: + region: eu-west-1 + resource: vol-XXXXXX + state: present + tags: + Name: ubervol + env: prod + + - name: Ensure all volumes are tagged + amazon.aws.ec2_tag: + region: eu-west-1 + resource: '{{ item.id }}' + state: present + tags: + Name: dbserver + Env: production + loop: '{{ ec2_vol.volumes }}' + + - name: Remove the Env tag + amazon.aws.ec2_tag: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + tags: + Env: + state: absent + + - name: Remove the Env tag if it's currently 'development' + amazon.aws.ec2_tag: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + tags: + Env: development + state: absent + + - name: Remove all tags except for Name from an instance + amazon.aws.ec2_tag: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + tags: + Name: '' + state: absent + purge_tags: true + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>added_tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>If tags were added</td> + <td> + <div>A dict of tags that were added to the resource</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>removed_tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>If tags were removed</td> + <td> + <div>A dict of tags that were removed from the resource</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A dict containing the tags on the resource</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Lester Wade (@lwade) +- Paul Arthur (@flowerysong) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vol_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vol_info_module.rst new file mode 100644 index 00000000..d1f0c2d3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vol_info_module.rst @@ -0,0 +1,510 @@ +.. _amazon.aws.ec2_vol_info_module: + + +*********************** +amazon.aws.ec2_vol_info +*********************** + +**Gather information about ec2 volumes in AWS** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 volumes in AWS. +- This module was called ``ec2_vol_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value.</div> + <div>See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVolumes.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVolumes.html</a> for possible filters.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Gather information about all volumes + - amazon.aws.ec2_vol_info: + + # Gather information about a particular volume using volume ID + - amazon.aws.ec2_vol_info: + filters: + volume-id: vol-00112233 + + # Gather information about any volume with a tag key Name and value Example + - amazon.aws.ec2_vol_info: + filters: + "tag:Name": Example + + # Gather information about any volume that is attached + - amazon.aws.ec2_vol_info: + filters: + attachment.status: attached + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volumes</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>Volumes that match the provided filters. Each element consists of a dict with all the information related to that volume.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>attachment_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td></td> + <td> + <div>Information about the volume attachments.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'attach_time': '2015-10-23T00:22:29.000Z', 'deleteOnTermination': 'false', 'device': '/dev/sdf', 'instance_id': 'i-8356263c', 'status': 'attached'}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>create_time</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The time stamp when volume creation was initiated.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2015-10-21T14:36:08.870Z</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>encrypted</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td></td> + <td> + <div>Indicates whether the volume is encrypted.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The ID of the volume.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vol-35b333d9</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>iops</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td></td> + <td> + <div>The number of I/O operations per second (IOPS) that the volume supports.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>size</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td></td> + <td> + <div>The size of the volume, in GiBs.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">1</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>snapshot_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The snapshot from which the volume was created, if applicable.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>status</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The volume state.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">in-use</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td></td> + <td> + <div>Any tags assigned to the volume.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'env': 'dev'}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The volume type. This can be gp2, io1, st1, sc1, or standard.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">standard</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>zone</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td></td> + <td> + <div>The Availability Zone of the volume.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1b</div> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vol_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vol_module.rst new file mode 100644 index 00000000..06533b86 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vol_module.rst @@ -0,0 +1,693 @@ +.. _amazon.aws.ec2_vol_module: + + +****************** +amazon.aws.ec2_vol +****************** + +**Create and attach a volume, return volume id and device map** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Creates an EBS volume and optionally attaches it to an instance. +- If both *instance* and *name* are given and the instance has a device at the device name, then no volume is created and no attachment is made. +- This module has a dependency on python-boto. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3>=1.16.33 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_on_termination</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>When set to <code>true</code>, the volume will be deleted upon instance termination.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>device_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Device id to override device mapping. Assumes /dev/sdf for Linux/UNIX and /dev/xvdf for Windows.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encrypted</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable encryption at rest for this volume.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Volume id if you wish to attach an existing volume (requires instance) or remove an existing volume</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>instance</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Instance ID if you wish to attach the volume. Since 1.9 you can set to None to detach.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>iops</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>The provisioned IOPs you want to associate with this volume (integer).</div> + <div>By default AWS will set this to 100.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>kms_key_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Specify the id of the KMS key to use.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>modify_volume</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.4.0</div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>The volume won't be modify unless this key is <code>true</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Volume Name tag if you wish to attach an existing volume (requires instance)</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>snapshot</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Snapshot ID on which to base the volume.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>absent</li> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>list</li> + </ul> + </td> + <td> + <div>Whether to ensure the volume is present or absent.</div> + <div>The use of <em>state=list</em> to interrogate the volume has been deprecated and will be removed after 2022-06-01. The 'list' functionality has been moved to a dedicated module <span class='module'>amazon.aws.ec2_vol_info</span>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">{}</div> + </td> + <td> + <div>tag:value pairs to add to the volume after creation.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>throughput</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.4.0</div> + </td> + <td> + </td> + <td> + <div>Volume throughput in MB/s.</div> + <div>This parameter is only valid for gp3 volumes.</div> + <div>Valid range is from 125 to 1000.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_size</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>Size of volume (in GiB) to create.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>volume_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>standard</b> ←</div></li> + <li>gp2</li> + <li>io1</li> + <li>st1</li> + <li>sc1</li> + <li>gp3</li> + <li>io2</li> + </ul> + </td> + <td> + <div>Type of EBS volume; standard (magnetic), gp2 (SSD), gp3 (SSD), io1 (Provisioned IOPS), io2 (Provisioned IOPS), st1 (Throughput Optimized HDD), sc1 (Cold HDD). "Standard" is the old EBS default and continues to remain the Ansible default for backwards compatibility.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>zone</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Zone in which to create the volume, if unset uses the zone the instance is in (if set).</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: availability_zone, aws_zone, ec2_zone</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Simple attachment action + - amazon.aws.ec2_vol: + instance: XXXXXX + volume_size: 5 + device_name: sdd + region: us-west-2 + + # Example using custom iops params + - amazon.aws.ec2_vol: + instance: XXXXXX + volume_size: 5 + iops: 100 + device_name: sdd + region: us-west-2 + + # Example using snapshot id + - amazon.aws.ec2_vol: + instance: XXXXXX + snapshot: "{{ snapshot }}" + + # Playbook example combined with instance launch + - amazon.aws.ec2: + keypair: "{{ keypair }}" + image: "{{ image }}" + wait: yes + count: 3 + register: ec2 + - amazon.aws.ec2_vol: + instance: "{{ item.id }}" + volume_size: 5 + loop: "{{ ec2.instances }}" + register: ec2_vol + + # Example: Launch an instance and then add a volume if not already attached + # * Volume will be created with the given name if not already created. + # * Nothing will happen if the volume is already attached. + # * Requires Ansible 2.0 + + - amazon.aws.ec2: + keypair: "{{ keypair }}" + image: "{{ image }}" + zone: YYYYYY + id: my_instance + wait: yes + count: 1 + register: ec2 + + - amazon.aws.ec2_vol: + instance: "{{ item.id }}" + name: my_existing_volume_Name_tag + device_name: /dev/xvdf + loop: "{{ ec2.instances }}" + register: ec2_vol + + # Remove a volume + - amazon.aws.ec2_vol: + id: vol-XXXXXXXX + state: absent + + # Detach a volume (since 1.9) + - amazon.aws.ec2_vol: + id: vol-XXXXXXXX + instance: None + region: us-west-2 + + # List volumes for an instance + - amazon.aws.ec2_vol: + instance: i-XXXXXX + state: list + region: us-west-2 + + # Create new volume using SSD storage + - amazon.aws.ec2_vol: + instance: XXXXXX + volume_size: 50 + volume_type: gp2 + device_name: /dev/xvdf + + # Attach an existing volume to instance. The volume will be deleted upon instance termination. + - amazon.aws.ec2_vol: + instance: XXXXXX + id: XXXXXX + device_name: /dev/sdf + delete_on_termination: yes + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>device</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when success</td> + <td> + <div>device name of attached volume</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/def/sdf</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when success</td> + <td> + <div>a dictionary containing detailed attributes of the volume</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'attachment_set': {'attach_time': '2015-10-23T00:22:29.000Z', 'deleteOnTermination': 'false', 'device': '/dev/sdf', 'instance_id': 'i-8356263c', 'status': 'attached'}, 'create_time': '2015-10-21T14:36:08.870Z', 'encrypted': False, 'id': 'vol-35b333d9', 'iops': None, 'size': 1, 'snapshot_id': '', 'status': 'in-use', 'tags': {'env': 'dev'}, 'type': 'standard', 'zone': 'us-east-1b'}</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when success</td> + <td> + <div>the id of volume</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vol-35b333d9</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>volume_type</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when success</td> + <td> + <div>the volume type</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">standard</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Lester Wade (@lwade) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_dhcp_option_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_dhcp_option_info_module.rst new file mode 100644 index 00000000..38926339 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_dhcp_option_info_module.rst @@ -0,0 +1,370 @@ +.. _amazon.aws.ec2_vpc_dhcp_option_info_module: + + +*********************************** +amazon.aws.ec2_vpc_dhcp_option_info +*********************************** + +**Gather information about dhcp options sets in AWS** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about dhcp options sets in AWS. +- This module was called ``ec2_vpc_dhcp_option_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dhcp_options_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>Get details of specific DHCP Option IDs.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: DhcpOptionIds</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dry_run</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Checks whether you have the required permissions to view the DHCP Options.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: DryRun</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value. See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeDhcpOptions.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeDhcpOptions.html</a> for possible filters.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: Gather information about all DHCP Option sets for an account or profile + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + register: dhcp_info + + - name: Gather information about a filtered list of DHCP Option sets + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + filters: + "tag:Name": "abc-123" + register: dhcp_info + + - name: Gather information about a specific DHCP Option set by DhcpOptionId + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + DhcpOptionsIds: dopt-123fece2 + register: dhcp_info + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>changed</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True if listing the dhcp options succeeds</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>dhcp_options</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>always</td> + <td> + <div>The dhcp option sets for the account</div> + <br/> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Nick Aslanidis (@naslanidis) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_dhcp_option_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_dhcp_option_module.rst new file mode 100644 index 00000000..70f255be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_dhcp_option_module.rst @@ -0,0 +1,551 @@ +.. _amazon.aws.ec2_vpc_dhcp_option_module: + + +****************************** +amazon.aws.ec2_vpc_dhcp_option +****************************** + +**Manages DHCP Options, and can ensure the DHCP options for the given VPC match what's requested** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- This module removes, or creates DHCP option sets, and can associate them to a VPC. Optionally, a new DHCP Options set can be created that converges a VPC's existing DHCP option set with values provided. When dhcp_options_id is provided, the module will 1. remove (with state='absent') 2. ensure tags are applied (if state='present' and tags are provided 3. attach it to a VPC (if state='present' and a vpc_id is provided. If any of the optional values are missing, they will either be treated as a no-op (i.e., inherit what already exists for the VPC) To remove existing options while inheriting, supply an empty value (e.g. set ntp_servers to [] if you want to remove them from the VPC's options) Most of the options should be self-explanatory. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_old</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Whether to delete the old VPC DHCP option set when associating a new one. This is primarily useful for debugging/development purposes when you want to quickly roll back to the old option set. Note that this setting will be ignored, and the old DHCP option set will be preserved, if it is in use by any other VPC. (Otherwise, AWS will return an error.)</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dhcp_options_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The resource_id of an existing DHCP options set. If this is specified, then it will override other settings, except tags (which will be updated to match)</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dns_servers</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of hosts to set the DNS servers for the VPC to. (Should be a list of IP addresses rather than host names.)</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>domain_name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The domain name to set in the DHCP option sets</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>inherit_existing</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>For any DHCP options not specified in these parameters, whether to inherit them from the options set already applied to vpc_id, or to reset them to be empty.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>netbios_name_servers</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>List of hosts to advertise as NetBIOS servers.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>netbios_node_type</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + </td> + <td> + <div>NetBIOS node type to advertise in the DHCP options. The AWS recommendation is to use 2 (when using netbios name services) <a href='https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html'>https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html</a></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ntp_servers</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>List of hosts to advertise as NTP servers for the VPC.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>absent</li> + <li><div style="color: blue"><b>present</b> ←</div></li> + </ul> + </td> + <td> + <div>create/assign or remove the DHCP options. If state is set to absent, then a DHCP options set matched either by id, or tags and options will be removed if possible.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>Tags to be applied to a VPC options set if a new one is created, or if the resource_id is provided. (options must match)</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: resource_tags</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>VPC ID to associate with the requested DHCP option set. If no vpc id is provided, and no matching option set is found then a new DHCP option set is created.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Completely overrides the VPC DHCP options associated with VPC vpc-123456 and deletes any existing + # DHCP option set that may have been attached to that VPC. + - amazon.aws.ec2_vpc_dhcp_option: + domain_name: "foo.example.com" + region: us-east-1 + dns_servers: + - 10.0.0.1 + - 10.0.1.1 + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + netbios_node_type: 2 + vpc_id: vpc-123456 + delete_old: True + inherit_existing: False + + + # Ensure the DHCP option set for the VPC has 10.0.0.4 and 10.0.1.4 as the specified DNS servers, but + # keep any other existing settings. Also, keep the old DHCP option set around. + - amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dns_servers: + - "{{groups['dns-primary']}}" + - "{{groups['dns-secondary']}}" + vpc_id: vpc-123456 + inherit_existing: True + delete_old: False + + + ## Create a DHCP option set with 4.4.4.4 and 8.8.8.8 as the specified DNS servers, with tags + ## but do not assign to a VPC + - amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dns_servers: + - 4.4.4.4 + - 8.8.8.8 + tags: + Name: google servers + Environment: Test + + ## Delete a DHCP options set that matches the tags and options specified + - amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dns_servers: + - 4.4.4.4 + - 8.8.8.8 + tags: + Name: google servers + Environment: Test + state: absent + + ## Associate a DHCP options set with a VPC by ID + - amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dhcp_options_id: dopt-12345678 + vpc_id: vpc-123456 + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>changed</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>Whether the dhcp options were changed</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>dhcp_options_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>when available</td> + <td> + <div>The aws resource id of the primary DCHP options set created, found or removed</div> + <br/> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>new_options</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>when appropriate</td> + <td> + <div>The DHCP options created, associated or found</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'domain-name-servers': ['10.0.0.1', '10.0.1.1'], 'netbois-name-servers': ['10.0.0.1', '10.0.1.1'], 'netbios-node-type': 2, 'domain-name': 'my.example.com'}</div> + </td> + </tr> + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Joel Thompson (@joelthompson) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_net_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_net_info_module.rst new file mode 100644 index 00000000..87e599d7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_net_info_module.rst @@ -0,0 +1,713 @@ +.. _amazon.aws.ec2_vpc_net_info_module: + + +*************************** +amazon.aws.ec2_vpc_net_info +*************************** + +**Gather information about ec2 VPCs in AWS** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 VPCs in AWS +- This module was called ``ec2_vpc_net_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value. See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html</a> for possible filters.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>vpc_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of VPC IDs that exist in your account.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Gather information about all VPCs + - amazon.aws.ec2_vpc_net_info: + + # Gather information about a particular VPC using VPC ID + - amazon.aws.ec2_vpc_net_info: + vpc_ids: vpc-00112233 + + # Gather information about any VPC with a tag key Name and value Example + - amazon.aws.ec2_vpc_net_info: + filters: + "tag:Name": Example + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="4">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="4"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpcs</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>success</td> + <td> + <div>Returns an array of complex objects as described below.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The IPv4 CIDR block assigned to the VPC.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block_association_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>An array of IPv4 cidr block association set information.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>association_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The association ID</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The IPv4 CIDR block that is associated with the VPC.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block_state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A hash/dict that contains a single item. The state of the cidr block association.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The CIDR block association state.</div> + <br/> + </td> + </tr> + + + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>classic_link_dns_supported</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True/False depending on attribute setting for classic link DNS support.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>classic_link_enabled</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True/False depending on if classic link support is enabled.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>dhcp_options_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the DHCP options associated with this VPC.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">dopt-12345678</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>enable_dns_hostnames</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True/False depending on attribute setting for DNS hostnames support.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>enable_dns_support</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True/False depending on attribute setting for DNS support.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the VPC (for backwards compatibility).</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>instance_tenancy</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The instance tenancy setting for the VPC.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_association_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>An array of IPv6 cidr block association set information.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>association_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The association ID</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The IPv6 CIDR block that is associated with the VPC.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A hash/dict that contains a single item. The state of the cidr block association.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The CIDR block association state.</div> + <br/> + </td> + </tr> + + + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>is_default</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True if this is the default VPC for account.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The AWS account which owns the VPC.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">123456789012</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The state of the VPC.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A dict of tags associated with the VPC.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the VPC .</div> + <br/> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_net_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_net_module.rst new file mode 100644 index 00000000..864646f8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_net_module.rst @@ -0,0 +1,711 @@ +.. _amazon.aws.ec2_vpc_net_module: + + +********************** +amazon.aws.ec2_vpc_net +********************** + +**Configure AWS virtual private clouds** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Create, modify, and terminate AWS virtual private clouds. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cidr_block</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The primary CIDR of the VPC. After 2.5 a list of CIDRs can be provided. The first in the list will be used as the primary CIDR and is used in conjunction with the <code>name</code> to ensure idempotence.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dhcp_opts_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The id of the DHCP options to use for this VPC.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dns_hostnames</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Whether to enable AWS hostname support.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>dns_support</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Whether to enable AWS DNS support.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ipv6_cidr</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Request an Amazon-provided IPv6 CIDR block with /56 prefix length. You cannot specify the range of IPv6 addresses, or the size of the CIDR block.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>multi_ok</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>By default the module will not create another VPC if there is another VPC with the same name and CIDR block. Specify this as true if you want duplicate VPCs created.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The name to give your VPC. This is used in combination with <code>cidr_block</code> to determine if a VPC already exists.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_cidrs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Remove CIDRs that are associated with the VPC and are not specified in <code>cidr_block</code>.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>The state of the VPC. Either absent or present.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>The tags you want attached to the VPC. This is independent of the name value, note if you pass a 'Name' key it would override the Name of the VPC if it's different.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: resource_tags</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tenancy</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>default</b> ←</div></li> + <li>dedicated</li> + </ul> + </td> + <td> + <div>Whether to be default or dedicated tenancy. This cannot be changed after the VPC has been created.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: create a VPC with dedicated tenancy and a couple of tags + amazon.aws.ec2_vpc_net: + name: Module_dev2 + cidr_block: 10.10.0.0/16 + region: us-east-1 + tags: + module: ec2_vpc_net + this: works + tenancy: dedicated + + - name: create a VPC with dedicated tenancy and request an IPv6 CIDR + amazon.aws.ec2_vpc_net: + name: Module_dev2 + cidr_block: 10.10.0.0/16 + ipv6_cidr: True + region: us-east-1 + tenancy: dedicated + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="3">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>info about the VPC that was created or deleted</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The CIDR of the VPC</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.0.0/16</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block_association_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>success</td> + <td> + <div>IPv4 CIDR blocks associated with the VPC</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'cidr_block_association_set': [{'association_id': 'vpc-cidr-assoc-97aeeefd', 'cidr_block': '10.0.0.0/24', 'cidr_block_state': {'state': 'associated'}}]}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>classic_link_enabled</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>indicates whether ClassicLink is enabled</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>dhcp_options_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>the id of the DHCP options associated with this VPC</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">dopt-12345678</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>VPC resource id</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vpc-12345678</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>instance_tenancy</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>indicates whether VPC uses default or dedicated tenancy</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">default</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_association_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + </div> + </td> + <td>success</td> + <td> + <div>IPv6 CIDR blocks associated with the VPC</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'ipv6_cidr_block_association_set': [{'association_id': 'vpc-cidr-assoc-97aeeefd', 'ipv6_cidr_block': '2001:db8::/56', 'ipv6_cidr_block_state': {'state': 'associated'}}]}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>is_default</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>indicates whether this is the default VPC</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>owner_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The AWS account which owns the VPC.</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">123456789012</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>state of the VPC</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">available</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>tags attached to the VPC, includes name</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>Name</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>name tag for the VPC</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">pk_vpc4</div> + </td> + </tr> + + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Jonathan Davila (@defionscode) +- Sloane Hertel (@s-hertel) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_subnet_info_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_subnet_info_module.rst new file mode 100644 index 00000000..c537c4e6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_subnet_info_module.rst @@ -0,0 +1,613 @@ +.. _amazon.aws.ec2_vpc_subnet_info_module: + + +****************************** +amazon.aws.ec2_vpc_subnet_info +****************************** + +**Gather information about ec2 VPC subnets in AWS** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Gather information about ec2 VPC subnets in AWS +- This module was called ``ec2_vpc_subnet_facts`` before Ansible 2.9. The usage did not change. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- botocore +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>filters</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of filters to apply. Each dict item consists of a filter key and a filter value. See <a href='https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html'>https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html</a> for possible filters.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>subnet_ids</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">list</span> + / <span style="color: purple">elements=string</span> + </div> + </td> + <td> + </td> + <td> + <div>A list of subnet IDs to gather information for.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: subnet_id</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Gather information about all VPC subnets + - amazon.aws.ec2_vpc_subnet_info: + + # Gather information about a particular VPC subnet using ID + - amazon.aws.ec2_vpc_subnet_info: + subnet_ids: subnet-00112233 + + # Gather information about any VPC subnet with a tag key Name and value Example + - amazon.aws.ec2_vpc_subnet_info: + filters: + "tag:Name": Example + + # Gather information about any VPC subnet within VPC with ID vpc-abcdef00 + - amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: vpc-abcdef00 + + # Gather information about a set of VPC subnets, publicA, publicB and publicC within a + # VPC with ID vpc-abcdef00 and then use the jinja map function to return the + # subnet_ids as a list. + + - amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: vpc-abcdef00 + "tag:Name": "{{ item }}" + loop: + - publicA + - publicB + - publicC + register: subnet_info + + - set_fact: + subnet_ids: "{{ subnet_info.subnets|map(attribute='id')|list }}" + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="4">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="4"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>subnets</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>success</td> + <td> + <div>Returns an array of complex objects as described below.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>assign_ipv6_address_on_creation</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True/False depending on attribute setting for IPv6 address assignment.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>availability_zone</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The availability zone where the subnet exists.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>available_ip_address_count</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>Count of available IPs in subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The IPv4 CIDR block assigned to the subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>default_for_az</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True if this is the default subnet for AZ.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the Subnet (for backwards compatibility).</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_association_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td>always</td> + <td> + <div>An array of IPv6 cidr block association set information.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>association_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The association ID</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The IPv6 CIDR block that is associated with the subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A hash/dict that contains a single item. The state of the cidr block association.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The CIDR block association state.</div> + <br/> + </td> + </tr> + + + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>map_public_ip_on_launch</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td>always</td> + <td> + <div>True/False depending on attribute setting for public IP mapping.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The state of the subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>subnet_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the Subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A dict of tags associated with the Subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The ID of the VPC .</div> + <br/> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_subnet_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_subnet_module.rst new file mode 100644 index 00000000..a15e3e03 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.ec2_vpc_subnet_module.rst @@ -0,0 +1,799 @@ +.. _amazon.aws.ec2_vpc_subnet_module: + + +************************* +amazon.aws.ec2_vpc_subnet +************************* + +**Manage subnets in AWS virtual private clouds** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Manage subnets in AWS virtual private clouds. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="1">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>assign_instances_ipv6</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Specify <code>yes</code> to indicate that instances launched into the subnet should be automatically assigned an IPv6 address.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>az</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The availability zone for the subnet.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>cidr</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>The CIDR block for the subnet. E.g. 192.0.2.0/24.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ipv6_cidr</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The IPv6 CIDR block for the subnet. The VPC must have a /56 block assigned and this value must be a valid IPv6 /64 that falls in the VPC range.</div> + <div>Required if <em>assign_instances_ipv6=true</em></div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>map_public</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Specify <code>yes</code> to indicate that instances launched into the subnet should be assigned public IP address by default.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Whether or not to remove tags that do not appear in the <em>tags</em> list.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>Create or remove the subnet.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dict of tags to apply to the subnet. Any tags currently applied to the subnet and not present here will be removed.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: resource_tags</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>VPC ID of the VPC in which to create or delete the subnet.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When <em>wait=true</em> and <em>state=present</em>, module will wait for subnet to be in available state before continuing.</div> + </td> + </tr> + <tr> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>wait_timeout</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">integer</span> + </div> + </td> + <td> + <b>Default:</b><br/><div style="color: blue">300</div> + </td> + <td> + <div>Number of seconds to wait for subnet to become available <em>wait=True</em>.</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + - name: Create subnet for database servers + amazon.aws.ec2_vpc_subnet: + state: present + vpc_id: vpc-123456 + cidr: 10.0.1.16/28 + tags: + Name: Database Subnet + register: database_subnet + + - name: Remove subnet for database servers + amazon.aws.ec2_vpc_subnet: + state: absent + vpc_id: vpc-123456 + cidr: 10.0.1.16/28 + + - name: Create subnet with IPv6 block assigned + amazon.aws.ec2_vpc_subnet: + state: present + vpc_id: vpc-123456 + cidr: 10.1.100.0/24 + ipv6_cidr: 2001:db8:0:102::/64 + + - name: Remove IPv6 block assigned to subnet + amazon.aws.ec2_vpc_subnet: + state: present + vpc_id: vpc-123456 + cidr: 10.1.100.0/24 + ipv6_cidr: '' + + + +Return Values +------------- +Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module: + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="4">Key</th> + <th>Returned</th> + <th width="100%">Description</th> + </tr> + <tr> + <td colspan="4"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>subnet</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>Dictionary of subnet values</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>assign_ipv6_address_on_creation</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>whether IPv6 address is auto-assigned to new instances</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>availability_zone</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>Availability zone of the Subnet</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">us-east-1a</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>available_ip_address_count</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>number of available IPv4 addresses</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">251</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>The IPv4 CIDR of the Subnet</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">10.0.0.0/16</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>default_for_az</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>indicates whether this is the default Subnet for this Availability Zone</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>Subnet resource id</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">subnet-b883b2c4</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_association_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>The IPv6 association ID for the currently associated CIDR</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">subnet-cidr-assoc-b85c74d2</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>The IPv6 CIDR block actively associated with the Subnet</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">2001:db8:0:102::/64</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_association_set</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">complex</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>An array of IPv6 cidr block association set information.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>association_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The association ID</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The IPv6 CIDR block that is associated with the subnet.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>ipv6_cidr_block_state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td>always</td> + <td> + <div>A hash/dict that contains a single item. The state of the cidr block association.</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td class="elbow-placeholder"> </td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td>always</td> + <td> + <div>The CIDR block association state.</div> + <br/> + </td> + </tr> + + + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>map_public_ip_on_launch</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>whether public IP is auto-assigned to new instances</div> + <br/> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>state of the Subnet</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">available</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>tags attached to the Subnet, includes name</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">{'Name': 'My Subnet', 'env': 'staging'}</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"> </td> + <td colspan="3"> + <div class="ansibleOptionAnchor" id="return-"></div> + <b>vpc_id</b> + <a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td><em>state=present</em></td> + <td> + <div>the id of the VPC where this Subnet exists</div> + <br/> + <div style="font-size: smaller"><b>Sample:</b></div> + <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">vpc-67236184</div> + </td> + </tr> + + </table> + <br/><br/> + + +Status +------ + + +Authors +~~~~~~~ + +- Robert Estelle (@erydo) +- Brad Davidson (@brandond) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.s3_bucket_module.rst b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.s3_bucket_module.rst new file mode 100644 index 00000000..794a0125 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/docs/amazon.aws.s3_bucket_module.rst @@ -0,0 +1,652 @@ +.. _amazon.aws.s3_bucket_module: + + +******************** +amazon.aws.s3_bucket +******************** + +**Manage S3 buckets in AWS, DigitalOcean, Ceph, Walrus, FakeS3 and StorageGRID** + + +Version added: 1.0.0 + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- +- Manage S3 buckets in AWS, DigitalOcean, Ceph, Walrus, FakeS3 and StorageGRID. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- boto +- boto3 +- python >= 2.6 + + +Parameters +---------- + +.. raw:: html + + <table border=0 cellpadding=0 class="documentation-table"> + <tr> + <th colspan="2">Parameter</th> + <th>Choices/<font color="blue">Defaults</font></th> + <th width="100%">Comments</th> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_access_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_access_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_access_key, access_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_ca_bundle</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">path</span> + </div> + </td> + <td> + </td> + <td> + <div>The location of a CA Bundle to use when validating SSL certificates.</div> + <div>Only used for boto3 based modules.</div> + <div>Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_config</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>A dictionary to modify the botocore configuration.</div> + <div>Parameters can be found at <a href='https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config'>https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config</a>.</div> + <div>Only the 'user_agent' key is used for boto modules. See <a href='http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto'>http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto</a> for more boto configuration.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>aws_secret_key</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>aws_secret_key</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: ec2_secret_key, secret_key</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ceph</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Enable API compatibility with Ceph. It takes into account the S3 API subset working with Ceph in order to provide the same module behaviour where possible.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>debug_botocore_endpoint_logs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>delete_public_access</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Delete public access block configuration from bucket.</div> + <div>This option cannot be used together with a <em>public_access</em> definition.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ec2_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). Ignored for modules where region is required. Must be specified for all other modules if region is not used. If not set then the value of the EC2_URL environment variable, if any, is used.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_endpoint_url, endpoint_url</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encryption</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>none</li> + <li>AES256</li> + <li>aws:kms</li> + </ul> + </td> + <td> + <div>Describes the default server-side encryption to apply to new objects in the bucket. In order to remove the server-side encryption, the encryption needs to be set to 'none' explicitly.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>encryption_key_id</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>KMS master key ID to use for the default encryption. This parameter is allowed if <em>encryption</em> is <code>aws:kms</code>. If not specified then it will default to the AWS provided KMS key.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>force</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>When trying to delete a bucket, delete all keys (including versions and delete markers) in the bucket first (an S3 bucket must be empty for a successful deletion).</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>name</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + / <span style="color: red">required</span> + </div> + </td> + <td> + </td> + <td> + <div>Name of the S3 bucket.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>policy</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">json</span> + </div> + </td> + <td> + </td> + <td> + <div>The JSON policy as a string.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>profile</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>Uses a boto profile. Only works with boto >= 2.24.0.</div> + <div>Using <em>profile</em> will override <em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> and support for passing them at the same time as <em>profile</em> has been deprecated.</div> + <div><em>aws_access_key</em>, <em>aws_secret_key</em> and <em>security_token</em> will be made mutually exclusive with <em>profile</em> after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_profile</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>public_access</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + <div style="font-style: italic; font-size: small; color: darkgreen">added in 1.3.0</div> + </td> + <td> + </td> + <td> + <div>Configure public access block for S3 bucket.</div> + <div>This option cannot be used together with <em>delete_public_access</em>.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>block_public_acls</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Sets BlockPublicAcls value.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>block_public_policy</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Sets BlockPublicPolicy value.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>ignore_public_acls</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Sets IgnorePublicAcls value.</div> + </td> + </tr> + <tr> + <td class="elbow-placeholder"></td> + <td colspan="1"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>restrict_public_buckets</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>no</b> ←</div></li> + <li>yes</li> + </ul> + </td> + <td> + <div>Sets RestrictPublicAcls value.</div> + </td> + </tr> + + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>purge_tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>Whether to remove tags that aren't present in the <em>tags</em> parameter.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>region</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. See <a href='http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region'>http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region</a></div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_region, ec2_region</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>requester_pays</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>With Requester Pays buckets, the requester instead of the bucket owner pays the cost of the request and the data download from the bucket.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>s3_url</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>S3 URL endpoint for usage with DigitalOcean, Ceph, Eucalyptus and FakeS3 etc.</div> + <div>Assumes AWS if not specified.</div> + <div>For Walrus, use FQDN of the endpoint without scheme nor path.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: S3_URL</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>security_token</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + </td> + <td> + <div>AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used.</div> + <div>If <em>profile</em> is set this parameter is ignored.</div> + <div>Passing the <em>security_token</em> and <em>profile</em> options at the same time has been deprecated and the options will be made mutually exclusive after 2022-06-01.</div> + <div style="font-size: small; color: darkgreen"><br/>aliases: aws_security_token, access_token</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>state</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">string</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li><div style="color: blue"><b>present</b> ←</div></li> + <li>absent</li> + </ul> + </td> + <td> + <div>Create or remove the S3 bucket.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>tags</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">dictionary</span> + </div> + </td> + <td> + </td> + <td> + <div>Tags dict to apply to bucket.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>validate_certs</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li><div style="color: blue"><b>yes</b> ←</div></li> + </ul> + </td> + <td> + <div>When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.</div> + </td> + </tr> + <tr> + <td colspan="2"> + <div class="ansibleOptionAnchor" id="parameter-"></div> + <b>versioning</b> + <a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a> + <div style="font-size: small"> + <span style="color: purple">boolean</span> + </div> + </td> + <td> + <ul style="margin: 0; padding: 0"><b>Choices:</b> + <li>no</li> + <li>yes</li> + </ul> + </td> + <td> + <div>Whether versioning is enabled or disabled (note that once versioning is enabled, it can only be suspended).</div> + </td> + </tr> + </table> + <br/> + + +Notes +----- + +.. note:: + - If ``requestPayment``, ``policy``, ``tagging`` or ``versioning`` operations/API aren't implemented by the endpoint, module doesn't fail if each parameter satisfies the following condition. *requester_pays* is ``False``, *policy*, *tags*, and *versioning* are ``None``. + - If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence ``AWS_URL`` or ``EC2_URL``, ``AWS_PROFILE`` or ``AWS_DEFAULT_PROFILE``, ``AWS_ACCESS_KEY_ID`` or ``AWS_ACCESS_KEY`` or ``EC2_ACCESS_KEY``, ``AWS_SECRET_ACCESS_KEY`` or ``AWS_SECRET_KEY`` or ``EC2_SECRET_KEY``, ``AWS_SECURITY_TOKEN`` or ``EC2_SECURITY_TOKEN``, ``AWS_REGION`` or ``EC2_REGION``, ``AWS_CA_BUNDLE`` + - Ansible uses the boto configuration file (typically ~/.boto) if no credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - ``AWS_REGION`` or ``EC2_REGION`` can be typically be used to specify the AWS region, when required, but this can also be configured in the boto config file + + + +Examples +-------- + +.. code-block:: yaml + + # Note: These examples do not set authentication details, see the AWS Guide for details. + + # Create a simple S3 bucket + - amazon.aws.s3_bucket: + name: mys3bucket + state: present + + # Create a simple S3 bucket on Ceph Rados Gateway + - amazon.aws.s3_bucket: + name: mys3bucket + s3_url: http://your-ceph-rados-gateway-server.xxx + ceph: true + + # Remove an S3 bucket and any keys it contains + - amazon.aws.s3_bucket: + name: mys3bucket + state: absent + force: yes + + # Create a bucket, add a policy from a file, enable requester pays, enable versioning and tag + - amazon.aws.s3_bucket: + name: mys3bucket + policy: "{{ lookup('file','policy.json') }}" + requester_pays: yes + versioning: yes + tags: + example: tag1 + another: tag2 + + # Create a simple DigitalOcean Spaces bucket using their provided regional endpoint + - amazon.aws.s3_bucket: + name: mydobucket + s3_url: 'https://nyc3.digitaloceanspaces.com' + + # Create a bucket with AES256 encryption + - amazon.aws.s3_bucket: + name: mys3bucket + state: present + encryption: "AES256" + + # Create a bucket with aws:kms encryption, KMS key + - amazon.aws.s3_bucket: + name: mys3bucket + state: present + encryption: "aws:kms" + encryption_key_id: "arn:aws:kms:us-east-1:1234/5678example" + + # Create a bucket with aws:kms encryption, default key + - amazon.aws.s3_bucket: + name: mys3bucket + state: present + encryption: "aws:kms" + + # Create a bucket with public policy block configuration + - amazon.aws.s3_bucket: + name: mys3bucket + state: present + public_access: + BlockPublicAcls: true + IgnorePublicAcls: true + ## keys == 'false' can be ommited, undefined keys defaults to 'false' + # BlockPublicPolicy: false + # RestrictPublicBuckets: false + + # Delete public policy block from bucket + - amazon.aws.s3_bucket: + name: mys3bucket + state: present + delete_public_access: true + + + + +Status +------ + + +Authors +~~~~~~~ + +- Rob White (@wimnat) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/meta/runtime.yml b/collections-debian-merged/ansible_collections/amazon/aws/meta/runtime.yml new file mode 100644 index 00000000..6b938e41 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/meta/runtime.yml @@ -0,0 +1,120 @@ +--- +requires_ansible: '>=2.9.10' +action_groups: + aws: + - aws_s3 + - ec2 + - aws_secret + - cloudfront_facts + - iam + - rds + - ec2 + - aws_az_facts + - aws_caller_facts + - cloudformation_facts + - ec2_ami_facts + - ec2_eni_facts + - ec2_group_facts + - ec2_snapshot_facts + - ec2_vol_facts + - ec2_vpc_dhcp_option_facts + - ec2_vpc_net_facts + - ec2_vpc_subnet_facts + - aws_az_info + - aws_caller_info + - aws_s3 + - cloudformation + - cloudformation_info + - ec2 + - ec2_ami + - ec2_ami_info + - ec2_elb_lb + - ec2_eni + - ec2_eni_info + - ec2_group + - ec2_group_info + - ec2_key + - ec2_snapshot + - ec2_snapshot_info + - ec2_tag + - ec2_tag_info + - ec2_vol + - ec2_vol_info + - ec2_vpc_dhcp_option + - ec2_vpc_dhcp_option_info + - ec2_vpc_net + - ec2_vpc_net_info + - ec2_vpc_subnet + - ec2_vpc_subnet_info + - s3_bucket + +plugin_routing: + modules: + aws_az_facts: + deprecation: + removal_date: 2022-06-01 + warning_text: >- + aws_az_facts was renamed in Ansible 2.9 to aws_az_info. + Please update your tasks. + aws_caller_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + aws_caller_facts was renamed in Ansible 2.9 to aws_caller_info. + Please update your tasks. + cloudformation_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + cloudformation_facts has been deprecated and will be removed. + The cloudformation_info module returns the same information, but + not as ansible_facts. See the module documentation for more + information. + ec2_ami_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_ami_facts was renamed in Ansible 2.9 to ec2_ami_info. + Please update your tasks. + ec2_eni_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_eni_facts was renamed in Ansible 2.9 to ec2_eni_info. + Please update your tasks. + ec2_group_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_group_facts was renamed in Ansible 2.9 to ec2_group_info. + Please update your tasks. + ec2_snapshot_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_snapshot_facts was renamed in Ansible 2.9 to ec2_snapshot_info. + Please update your tasks. + ec2_vol_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_vol_facts was renamed in Ansible 2.9 to ec2_vol_info. + Please update your tasks. + ec2_vpc_dhcp_option_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_vpc_dhcp_option_facts was renamed in Ansible 2.9 to + ec2_vpc_dhcp_option_info. Please update your tasks. + ec2_vpc_net_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_vpc_net_facts was renamed in Ansible 2.9 to ec2_vpc_net_info. + Please update your tasks. + ec2_vpc_subnet_facts: + deprecation: + removal_date: 2021-12-01 + warning_text: >- + ec2_vpc_subnet_facts was renamed in Ansible 2.9 to + ec2_vpc_subnet_info. Please update your tasks. diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/action/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/action/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/action/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/action/aws_s3.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/action/aws_s3.py new file mode 100644 index 00000000..a454922a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/action/aws_s3.py @@ -0,0 +1,71 @@ +# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com> +# (c) 2018, Will Thames <will@thames.id.au> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionFail, AnsibleFileNotFound +from ansible.module_utils._text import to_text +from ansible.plugins.action import ActionBase +from ansible.utils.vars import merge_hash + + +class ActionModule(ActionBase): + + TRANSFERS_FILES = True + + def run(self, tmp=None, task_vars=None): + ''' handler for aws_s3 operations ''' + self._supports_async = True + + if task_vars is None: + task_vars = dict() + + result = super(ActionModule, self).run(tmp, task_vars) + del tmp # tmp no longer has any effect + + source = self._task.args.get('src', None) + + try: + new_module_args = self._task.args.copy() + if source: + source = os.path.expanduser(source) + + # For backward compatibility check if the file exists on the remote; it should take precedence + if not self._remote_file_exists(source): + try: + source = self._loader.get_real_file(self._find_needle('files', source), decrypt=False) + new_module_args['src'] = source + except AnsibleFileNotFound as e: + # module handles error message for nonexistent files + new_module_args['src'] = source + except AnsibleError as e: + raise AnsibleActionFail(to_text(e)) + + wrap_async = self._task.async_val and not self._connection.has_native_async + # execute the aws_s3 module with the updated args + result = merge_hash(result, self._execute_module(module_args=new_module_args, task_vars=task_vars, wrap_async=wrap_async)) + + if not wrap_async: + # remove a temporary path we created + self._remove_tmp_path(self._connection._shell.tmpdir) + + except AnsibleAction as e: + result.update(e.result) + return result diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/callback/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/callback/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/callback/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/callback/aws_resource_actions.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/callback/aws_resource_actions.py new file mode 100644 index 00000000..9dae8e6f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/callback/aws_resource_actions.py @@ -0,0 +1,71 @@ +# (C) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' + callback: aws_resource_actions + type: aggregate + short_description: summarizes all "resource:actions" completed + description: + - Ansible callback plugin for collecting the AWS actions completed by all boto3 modules using + AnsibleAWSModule in a playbook. Botocore endpoint logs need to be enabled for those modules, which can + be done easily by setting debug_botocore_endpoint_logs to True for group/aws using module_defaults. + requirements: + - whitelisting in configuration - see examples section below for details. +''' + +EXAMPLES = ''' +example: > + To enable, add this to your ansible.cfg file in the defaults block + [defaults] + callback_whitelist = aws_resource_actions +sample output: > +# +# AWS ACTIONS: ['s3:PutBucketAcl', 's3:HeadObject', 's3:DeleteObject', 's3:PutObjectAcl', 's3:CreateMultipartUpload', +# 's3:DeleteBucket', 's3:GetObject', 's3:DeleteObjects', 's3:CreateBucket', 's3:CompleteMultipartUpload', +# 's3:ListObjectsV2', 's3:HeadBucket', 's3:UploadPart', 's3:PutObject'] +# +sample output: > +# +# AWS ACTIONS: ['ec2:DescribeVpcAttribute', 'ec2:DescribeVpcClassicLink', 'ec2:ModifyVpcAttribute', 'ec2:CreateTags', +# 'sts:GetCallerIdentity', 'ec2:DescribeSecurityGroups', 'ec2:DescribeTags', 'ec2:DescribeVpcs', 'ec2:CreateVpc'] +# +''' + +from ansible.plugins.callback import CallbackBase +from ansible.module_utils._text import to_native + + +class CallbackModule(CallbackBase): + CALLBACK_VERSION = 2.8 + CALLBACK_TYPE = 'aggregate' + CALLBACK_NAME = 'amazon.aws.aws_resource_actions' + CALLBACK_NEEDS_WHITELIST = True + + def __init__(self): + self.aws_resource_actions = [] + super(CallbackModule, self).__init__() + + def extend_aws_resource_actions(self, result): + if result.get('resource_actions'): + self.aws_resource_actions.extend(result['resource_actions']) + + def runner_on_ok(self, host, res): + self.extend_aws_resource_actions(res) + + def runner_on_failed(self, host, res, ignore_errors=False): + self.extend_aws_resource_actions(res) + + def v2_runner_item_on_ok(self, result): + self.extend_aws_resource_actions(result._result) + + def v2_runner_item_on_failed(self, result): + self.extend_aws_resource_actions(result._result) + + def playbook_on_stats(self, stats): + if self.aws_resource_actions: + self.aws_resource_actions = sorted(list(to_native(action) for action in set(self.aws_resource_actions))) + self._display.display("AWS ACTIONS: {0}".format(self.aws_resource_actions)) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws.py new file mode 100644 index 00000000..9eec9a8b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2014, Will Thames <will@thames.id.au> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + + # AWS only documentation fragment + DOCUMENTATION = r''' +options: + debug_botocore_endpoint_logs: + description: + - Use a botocore.endpoint logger to parse the unique (rather than total) "resource:action" API calls made during a task, outputing + the set to the resource_actions key in the task results. Use the aws_resource_action callback to output to total list made during + a playbook. The ANSIBLE_DEBUG_BOTOCORE_LOGS environment variable may also be used. + type: bool + default: 'no' + ec2_url: + description: + - Url to use to connect to EC2 or your Eucalyptus cloud (by default the module will use EC2 endpoints). + Ignored for modules where region is required. Must be specified for all other modules if region is not used. + If not set then the value of the EC2_URL environment variable, if any, is used. + type: str + aliases: [ aws_endpoint_url, endpoint_url ] + aws_secret_key: + description: + - AWS secret key. If not set then the value of the AWS_SECRET_ACCESS_KEY, AWS_SECRET_KEY, or EC2_SECRET_KEY environment variable is used. + - If I(profile) is set this parameter is ignored. + - Passing the I(aws_secret_key) and I(profile) options at the same time has been deprecated + and the options will be made mutually exclusive after 2022-06-01. + type: str + aliases: [ ec2_secret_key, secret_key ] + aws_access_key: + description: + - AWS access key. If not set then the value of the AWS_ACCESS_KEY_ID, AWS_ACCESS_KEY or EC2_ACCESS_KEY environment variable is used. + - If I(profile) is set this parameter is ignored. + - Passing the I(aws_access_key) and I(profile) options at the same time has been deprecated + and the options will be made mutually exclusive after 2022-06-01. + type: str + aliases: [ ec2_access_key, access_key ] + security_token: + description: + - AWS STS security token. If not set then the value of the AWS_SECURITY_TOKEN or EC2_SECURITY_TOKEN environment variable is used. + - If I(profile) is set this parameter is ignored. + - Passing the I(security_token) and I(profile) options at the same time has been deprecated + and the options will be made mutually exclusive after 2022-06-01. + type: str + aliases: [ aws_security_token, access_token ] + aws_ca_bundle: + description: + - "The location of a CA Bundle to use when validating SSL certificates." + - "Only used for boto3 based modules." + - "Note: The CA Bundle is read 'module' side and may need to be explicitly copied from the controller if not run locally." + type: path + validate_certs: + description: + - When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0. + type: bool + default: yes + profile: + description: + - Uses a boto profile. Only works with boto >= 2.24.0. + - Using I(profile) will override I(aws_access_key), I(aws_secret_key) and I(security_token) + and support for passing them at the same time as I(profile) has been deprecated. + - I(aws_access_key), I(aws_secret_key) and I(security_token) will be made mutually exclusive with I(profile) after 2022-06-01. + type: str + aliases: [ aws_profile ] + aws_config: + description: + - A dictionary to modify the botocore configuration. + - Parameters can be found at U(https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config). + - Only the 'user_agent' key is used for boto modules. See U(http://boto.cloudhackers.com/en/latest/boto_config_tut.html#boto) for more boto configuration. + type: dict +requirements: + - python >= 2.6 + - boto +notes: + - If parameters are not set within the module, the following + environment variables can be used in decreasing order of precedence + C(AWS_URL) or C(EC2_URL), + C(AWS_PROFILE) or C(AWS_DEFAULT_PROFILE), + C(AWS_ACCESS_KEY_ID) or C(AWS_ACCESS_KEY) or C(EC2_ACCESS_KEY), + C(AWS_SECRET_ACCESS_KEY) or C(AWS_SECRET_KEY) or C(EC2_SECRET_KEY), + C(AWS_SECURITY_TOKEN) or C(EC2_SECURITY_TOKEN), + C(AWS_REGION) or C(EC2_REGION), + C(AWS_CA_BUNDLE) + - Ansible uses the boto configuration file (typically ~/.boto) if no + credentials are provided. See https://boto.readthedocs.io/en/latest/boto_config_tut.html + - C(AWS_REGION) or C(EC2_REGION) can be typically be used to specify the + AWS region, when required, but this can also be configured in the boto config file +''' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws_credentials.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws_credentials.py new file mode 100644 index 00000000..73eff046 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws_credentials.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + + # Plugin options for AWS credentials + DOCUMENTATION = r''' +options: + aws_profile: + description: The AWS profile + type: str + aliases: [ boto_profile ] + env: + - name: AWS_DEFAULT_PROFILE + - name: AWS_PROFILE + aws_access_key: + description: The AWS access key to use. + type: str + aliases: [ aws_access_key_id ] + env: + - name: EC2_ACCESS_KEY + - name: AWS_ACCESS_KEY + - name: AWS_ACCESS_KEY_ID + aws_secret_key: + description: The AWS secret key that corresponds to the access key. + type: str + aliases: [ aws_secret_access_key ] + env: + - name: EC2_SECRET_KEY + - name: AWS_SECRET_KEY + - name: AWS_SECRET_ACCESS_KEY + aws_security_token: + description: The AWS security token if using temporary access and secret keys. + type: str + env: + - name: EC2_SECURITY_TOKEN + - name: AWS_SESSION_TOKEN + - name: AWS_SECURITY_TOKEN +''' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws_region.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws_region.py new file mode 100644 index 00000000..52152660 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/aws_region.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + + # Plugin option for AWS region + DOCUMENTATION = r''' +options: + region: + description: The region for which to create the connection. + type: str + env: + - name: EC2_REGION + - name: AWS_REGION +''' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/ec2.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/ec2.py new file mode 100644 index 00000000..09613882 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/doc_fragments/ec2.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2015, Ansible, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +class ModuleDocFragment(object): + + # EC2 only documentation fragment + DOCUMENTATION = r''' +options: + region: + description: + - The AWS region to use. If not specified then the value of the AWS_REGION or EC2_REGION environment variable, if any, is used. + See U(http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region) + type: str + aliases: [ aws_region, ec2_region ] +''' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/aws_ec2.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/aws_ec2.py new file mode 100644 index 00000000..01f9de03 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/aws_ec2.py @@ -0,0 +1,663 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' + name: aws_ec2 + plugin_type: inventory + short_description: EC2 inventory source + requirements: + - boto3 + - botocore + extends_documentation_fragment: + - inventory_cache + - constructed + - amazon.aws.aws_credentials + + description: + - Get inventory hosts from Amazon Web Services EC2. + - Uses a YAML configuration file that ends with C(aws_ec2.(yml|yaml)). + notes: + - If no credentials are provided and the control node has an associated IAM instance profile then the + role will be used for authentication. + author: + - Sloane Hertel (@s-hertel) + options: + plugin: + description: Token that ensures this is a source file for the plugin. + required: True + choices: ['aws_ec2', 'amazon.aws.aws_ec2'] + iam_role_arn: + description: The ARN of the IAM role to assume to perform the inventory lookup. You should still provide AWS + credentials with enough privilege to perform the AssumeRole action. + regions: + description: + - A list of regions in which to describe EC2 instances. + - If empty (the default) default this will include all regions, except possibly restricted ones like us-gov-west-1 and cn-north-1. + type: list + default: [] + hostnames: + description: + - A list in order of precedence for hostname variables. + - You can use the options specified in U(http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options). + - To use tags as hostnames use the syntax tag:Name=Value to use the hostname Name_Value, or tag:Name to use the value of the Name tag. + type: list + default: [] + filters: + description: + - A dictionary of filter value pairs. + - Available filters are listed here U(http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options). + type: dict + default: {} + include_extra_api_calls: + description: + - Add two additional API calls for every instance to include 'persistent' and 'events' host variables. + - Spot instances may be persistent and instances may have associated events. + type: bool + default: False + strict_permissions: + description: + - By default if a 403 (Forbidden) error code is encountered this plugin will fail. + - You can set this option to False in the inventory config file which will allow 403 errors to be gracefully skipped. + type: bool + default: True + use_contrib_script_compatible_sanitization: + description: + - By default this plugin is using a general group name sanitization to create safe and usable group names for use in Ansible. + This option allows you to override that, in efforts to allow migration from the old inventory script and + matches the sanitization of groups when the script's ``replace_dash_in_groups`` option is set to ``False``. + To replicate behavior of ``replace_dash_in_groups = True`` with constructed groups, + you will need to replace hyphens with underscores via the regex_replace filter for those entries. + - For this to work you should also turn off the TRANSFORM_INVALID_GROUP_CHARS setting, + otherwise the core engine will just use the standard sanitization on top. + - This is not the default as such names break certain functionality as not all characters are valid Python identifiers + which group names end up being used as. + type: bool + default: False +''' + +EXAMPLES = ''' +# Minimal example using environment vars or instance role credentials +# Fetch all hosts in us-east-1, the hostname is the public DNS if it exists, otherwise the private IP address +plugin: aws_ec2 +regions: + - us-east-1 + +# Example using filters, ignoring permission errors, and specifying the hostname precedence +plugin: aws_ec2 +boto_profile: aws_profile +# Populate inventory with instances in these regions +regions: + - us-east-1 + - us-east-2 +filters: + # All instances with their `Environment` tag set to `dev` + tag:Environment: dev + # All dev and QA hosts + tag:Environment: + - dev + - qa + instance.group-id: sg-xxxxxxxx +# Ignores 403 errors rather than failing +strict_permissions: False +# Note: I(hostnames) sets the inventory_hostname. To modify ansible_host without modifying +# inventory_hostname use compose (see example below). +hostnames: + - tag:Name=Tag1,Name=Tag2 # Return specific hosts only + - tag:CustomDNSName + - dns-name + - name: 'tag:Name=Tag1,Name=Tag2' + - name: 'private-ip-address' + separator: '_' + prefix: 'tag:Name' + +# Example using constructed features to create groups and set ansible_host +plugin: aws_ec2 +regions: + - us-east-1 + - us-west-1 +# keyed_groups may be used to create custom groups +strict: False +keyed_groups: + # Add e.g. x86_64 hosts to an arch_x86_64 group + - prefix: arch + key: 'architecture' + # Add hosts to tag_Name_Value groups for each Name/Value tag pair + - prefix: tag + key: tags + # Add hosts to e.g. instance_type_z3_tiny + - prefix: instance_type + key: instance_type + # Create security_groups_sg_abcd1234 group for each SG + - key: 'security_groups|json_query("[].group_id")' + prefix: 'security_groups' + # Create a group for each value of the Application tag + - key: tags.Application + separator: '' + # Create a group per region e.g. aws_region_us_east_2 + - key: placement.region + prefix: aws_region + # Create a group (or groups) based on the value of a custom tag "Role" and add them to a metagroup called "project" + - key: tags['Role'] + prefix: foo + parent_group: "project" +# Set individual variables with compose +compose: + # Use the private IP address to connect to the host + # (note: this does not modify inventory_hostname, which is set via I(hostnames)) + ansible_host: private_ip_address +''' + +import re + +from ansible.errors import AnsibleError +from ansible.module_utils._text import to_native, to_text +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list, boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable + +try: + import boto3 + import botocore +except ImportError: + raise AnsibleError('The ec2 dynamic inventory plugin requires boto3 and botocore.') + +# The mappings give an array of keys to get from the filter name to the value +# returned by boto3's EC2 describe_instances method. + +instance_meta_filter_to_boto_attr = { + 'group-id': ('Groups', 'GroupId'), + 'group-name': ('Groups', 'GroupName'), + 'network-interface.attachment.instance-owner-id': ('OwnerId',), + 'owner-id': ('OwnerId',), + 'requester-id': ('RequesterId',), + 'reservation-id': ('ReservationId',), +} + +instance_data_filter_to_boto_attr = { + 'affinity': ('Placement', 'Affinity'), + 'architecture': ('Architecture',), + 'availability-zone': ('Placement', 'AvailabilityZone'), + 'block-device-mapping.attach-time': ('BlockDeviceMappings', 'Ebs', 'AttachTime'), + 'block-device-mapping.delete-on-termination': ('BlockDeviceMappings', 'Ebs', 'DeleteOnTermination'), + 'block-device-mapping.device-name': ('BlockDeviceMappings', 'DeviceName'), + 'block-device-mapping.status': ('BlockDeviceMappings', 'Ebs', 'Status'), + 'block-device-mapping.volume-id': ('BlockDeviceMappings', 'Ebs', 'VolumeId'), + 'client-token': ('ClientToken',), + 'dns-name': ('PublicDnsName',), + 'host-id': ('Placement', 'HostId'), + 'hypervisor': ('Hypervisor',), + 'iam-instance-profile.arn': ('IamInstanceProfile', 'Arn'), + 'image-id': ('ImageId',), + 'instance-id': ('InstanceId',), + 'instance-lifecycle': ('InstanceLifecycle',), + 'instance-state-code': ('State', 'Code'), + 'instance-state-name': ('State', 'Name'), + 'instance-type': ('InstanceType',), + 'instance.group-id': ('SecurityGroups', 'GroupId'), + 'instance.group-name': ('SecurityGroups', 'GroupName'), + 'ip-address': ('PublicIpAddress',), + 'kernel-id': ('KernelId',), + 'key-name': ('KeyName',), + 'launch-index': ('AmiLaunchIndex',), + 'launch-time': ('LaunchTime',), + 'monitoring-state': ('Monitoring', 'State'), + 'network-interface.addresses.private-ip-address': ('NetworkInterfaces', 'PrivateIpAddress'), + 'network-interface.addresses.primary': ('NetworkInterfaces', 'PrivateIpAddresses', 'Primary'), + 'network-interface.addresses.association.public-ip': ('NetworkInterfaces', 'PrivateIpAddresses', 'Association', 'PublicIp'), + 'network-interface.addresses.association.ip-owner-id': ('NetworkInterfaces', 'PrivateIpAddresses', 'Association', 'IpOwnerId'), + 'network-interface.association.public-ip': ('NetworkInterfaces', 'Association', 'PublicIp'), + 'network-interface.association.ip-owner-id': ('NetworkInterfaces', 'Association', 'IpOwnerId'), + 'network-interface.association.allocation-id': ('ElasticGpuAssociations', 'ElasticGpuId'), + 'network-interface.association.association-id': ('ElasticGpuAssociations', 'ElasticGpuAssociationId'), + 'network-interface.attachment.attachment-id': ('NetworkInterfaces', 'Attachment', 'AttachmentId'), + 'network-interface.attachment.instance-id': ('InstanceId',), + 'network-interface.attachment.device-index': ('NetworkInterfaces', 'Attachment', 'DeviceIndex'), + 'network-interface.attachment.status': ('NetworkInterfaces', 'Attachment', 'Status'), + 'network-interface.attachment.attach-time': ('NetworkInterfaces', 'Attachment', 'AttachTime'), + 'network-interface.attachment.delete-on-termination': ('NetworkInterfaces', 'Attachment', 'DeleteOnTermination'), + 'network-interface.availability-zone': ('Placement', 'AvailabilityZone'), + 'network-interface.description': ('NetworkInterfaces', 'Description'), + 'network-interface.group-id': ('NetworkInterfaces', 'Groups', 'GroupId'), + 'network-interface.group-name': ('NetworkInterfaces', 'Groups', 'GroupName'), + 'network-interface.ipv6-addresses.ipv6-address': ('NetworkInterfaces', 'Ipv6Addresses', 'Ipv6Address'), + 'network-interface.mac-address': ('NetworkInterfaces', 'MacAddress'), + 'network-interface.network-interface-id': ('NetworkInterfaces', 'NetworkInterfaceId'), + 'network-interface.owner-id': ('NetworkInterfaces', 'OwnerId'), + 'network-interface.private-dns-name': ('NetworkInterfaces', 'PrivateDnsName'), + # 'network-interface.requester-id': (), + 'network-interface.requester-managed': ('NetworkInterfaces', 'Association', 'IpOwnerId'), + 'network-interface.status': ('NetworkInterfaces', 'Status'), + 'network-interface.source-dest-check': ('NetworkInterfaces', 'SourceDestCheck'), + 'network-interface.subnet-id': ('NetworkInterfaces', 'SubnetId'), + 'network-interface.vpc-id': ('NetworkInterfaces', 'VpcId'), + 'placement-group-name': ('Placement', 'GroupName'), + 'platform': ('Platform',), + 'private-dns-name': ('PrivateDnsName',), + 'private-ip-address': ('PrivateIpAddress',), + 'product-code': ('ProductCodes', 'ProductCodeId'), + 'product-code.type': ('ProductCodes', 'ProductCodeType'), + 'ramdisk-id': ('RamdiskId',), + 'reason': ('StateTransitionReason',), + 'root-device-name': ('RootDeviceName',), + 'root-device-type': ('RootDeviceType',), + 'source-dest-check': ('SourceDestCheck',), + 'spot-instance-request-id': ('SpotInstanceRequestId',), + 'state-reason-code': ('StateReason', 'Code'), + 'state-reason-message': ('StateReason', 'Message'), + 'subnet-id': ('SubnetId',), + 'tag': ('Tags',), + 'tag-key': ('Tags',), + 'tag-value': ('Tags',), + 'tenancy': ('Placement', 'Tenancy'), + 'virtualization-type': ('VirtualizationType',), + 'vpc-id': ('VpcId',), +} + + +class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): + + NAME = 'amazon.aws.aws_ec2' + + def __init__(self): + super(InventoryModule, self).__init__() + + self.group_prefix = 'aws_ec2_' + + # credentials + self.boto_profile = None + self.aws_secret_access_key = None + self.aws_access_key_id = None + self.aws_security_token = None + self.iam_role_arn = None + + def _compile_values(self, obj, attr): + ''' + :param obj: A list or dict of instance attributes + :param attr: A key + :return The value(s) found via the attr + ''' + if obj is None: + return + + temp_obj = [] + + if isinstance(obj, list) or isinstance(obj, tuple): + for each in obj: + value = self._compile_values(each, attr) + if value: + temp_obj.append(value) + else: + temp_obj = obj.get(attr) + + has_indexes = any([isinstance(temp_obj, list), isinstance(temp_obj, tuple)]) + if has_indexes and len(temp_obj) == 1: + return temp_obj[0] + + return temp_obj + + def _get_boto_attr_chain(self, filter_name, instance): + ''' + :param filter_name: The filter + :param instance: instance dict returned by boto3 ec2 describe_instances() + ''' + allowed_filters = sorted(list(instance_data_filter_to_boto_attr.keys()) + list(instance_meta_filter_to_boto_attr.keys())) + if filter_name not in allowed_filters: + raise AnsibleError("Invalid filter '%s' provided; filter must be one of %s." % (filter_name, + allowed_filters)) + if filter_name in instance_data_filter_to_boto_attr: + boto_attr_list = instance_data_filter_to_boto_attr[filter_name] + else: + boto_attr_list = instance_meta_filter_to_boto_attr[filter_name] + + instance_value = instance + for attribute in boto_attr_list: + instance_value = self._compile_values(instance_value, attribute) + return instance_value + + def _get_credentials(self): + ''' + :return A dictionary of boto client credentials + ''' + boto_params = {} + for credential in (('aws_access_key_id', self.aws_access_key_id), + ('aws_secret_access_key', self.aws_secret_access_key), + ('aws_session_token', self.aws_security_token)): + if credential[1]: + boto_params[credential[0]] = credential[1] + + return boto_params + + def _get_connection(self, credentials, region='us-east-1'): + try: + connection = boto3.session.Session(profile_name=self.boto_profile).client('ec2', region, **credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + if self.boto_profile: + try: + connection = boto3.session.Session(profile_name=self.boto_profile).client('ec2', region) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + else: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + return connection + + def _boto3_assume_role(self, credentials, region): + """ + Assume an IAM role passed by iam_role_arn parameter + + :return: a dict containing the credentials of the assumed role + """ + + iam_role_arn = self.iam_role_arn + + try: + sts_connection = boto3.session.Session(profile_name=self.boto_profile).client('sts', region, **credentials) + sts_session = sts_connection.assume_role(RoleArn=iam_role_arn, RoleSessionName='ansible_aws_ec2_dynamic_inventory') + return dict( + aws_access_key_id=sts_session['Credentials']['AccessKeyId'], + aws_secret_access_key=sts_session['Credentials']['SecretAccessKey'], + aws_session_token=sts_session['Credentials']['SessionToken'] + ) + except botocore.exceptions.ClientError as e: + raise AnsibleError("Unable to assume IAM role: %s" % to_native(e)) + + def _boto3_conn(self, regions): + ''' + :param regions: A list of regions to create a boto3 client + + Generator that yields a boto3 client and the region + ''' + + credentials = self._get_credentials() + iam_role_arn = self.iam_role_arn + + if not regions: + try: + # as per https://boto3.amazonaws.com/v1/documentation/api/latest/guide/ec2-example-regions-avail-zones.html + client = self._get_connection(credentials) + resp = client.describe_regions() + regions = [x['RegionName'] for x in resp.get('Regions', [])] + except botocore.exceptions.NoRegionError: + # above seems to fail depending on boto3 version, ignore and lets try something else + pass + + # fallback to local list hardcoded in boto3 if still no regions + if not regions: + session = boto3.Session() + regions = session.get_available_regions('ec2') + + # I give up, now you MUST give me regions + if not regions: + raise AnsibleError('Unable to get regions list from available methods, you must specify the "regions" option to continue.') + + for region in regions: + connection = self._get_connection(credentials, region) + try: + if iam_role_arn is not None: + assumed_credentials = self._boto3_assume_role(credentials, region) + else: + assumed_credentials = credentials + connection = boto3.session.Session(profile_name=self.boto_profile).client('ec2', region, **assumed_credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + if self.boto_profile: + try: + connection = boto3.session.Session(profile_name=self.boto_profile).client('ec2', region) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + else: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + yield connection, region + + def _get_instances_by_region(self, regions, filters, strict_permissions): + ''' + :param regions: a list of regions in which to describe instances + :param filters: a list of boto3 filter dictionaries + :param strict_permissions: a boolean determining whether to fail or ignore 403 error codes + :return A list of instance dictionaries + ''' + all_instances = [] + + for connection, region in self._boto3_conn(regions): + try: + # By default find non-terminated/terminating instances + if not any([f['Name'] == 'instance-state-name' for f in filters]): + filters.append({'Name': 'instance-state-name', 'Values': ['running', 'pending', 'stopping', 'stopped']}) + paginator = connection.get_paginator('describe_instances') + reservations = paginator.paginate(Filters=filters).build_full_result().get('Reservations') + instances = [] + for r in reservations: + new_instances = r['Instances'] + for instance in new_instances: + instance.update(self._get_reservation_details(r)) + if self.get_option('include_extra_api_calls'): + instance.update(self._get_event_set_and_persistence(connection, instance['InstanceId'], instance.get('SpotInstanceRequestId'))) + instances.extend(new_instances) + except botocore.exceptions.ClientError as e: + if e.response['ResponseMetadata']['HTTPStatusCode'] == 403 and not strict_permissions: + instances = [] + else: + raise AnsibleError("Failed to describe instances: %s" % to_native(e)) + except botocore.exceptions.BotoCoreError as e: + raise AnsibleError("Failed to describe instances: %s" % to_native(e)) + + all_instances.extend(instances) + + return sorted(all_instances, key=lambda x: x['InstanceId']) + + def _get_reservation_details(self, reservation): + return { + 'OwnerId': reservation['OwnerId'], + 'RequesterId': reservation.get('RequesterId', ''), + 'ReservationId': reservation['ReservationId'] + } + + def _get_event_set_and_persistence(self, connection, instance_id, spot_instance): + host_vars = {'Events': '', 'Persistent': False} + try: + kwargs = {'InstanceIds': [instance_id]} + host_vars['Events'] = connection.describe_instance_status(**kwargs)['InstanceStatuses'][0].get('Events', '') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + if not self.get_option('strict_permissions'): + pass + else: + raise AnsibleError("Failed to describe instance status: %s" % to_native(e)) + if spot_instance: + try: + kwargs = {'SpotInstanceRequestIds': [spot_instance]} + host_vars['Persistent'] = bool( + connection.describe_spot_instance_requests(**kwargs)['SpotInstanceRequests'][0].get('Type') == 'persistent' + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + if not self.get_option('strict_permissions'): + pass + else: + raise AnsibleError("Failed to describe spot instance requests: %s" % to_native(e)) + return host_vars + + def _get_tag_hostname(self, preference, instance): + tag_hostnames = preference.split('tag:', 1)[1] + if ',' in tag_hostnames: + tag_hostnames = tag_hostnames.split(',') + else: + tag_hostnames = [tag_hostnames] + tags = boto3_tag_list_to_ansible_dict(instance.get('Tags', [])) + for v in tag_hostnames: + if '=' in v: + tag_name, tag_value = v.split('=') + if tags.get(tag_name) == tag_value: + return to_text(tag_name) + "_" + to_text(tag_value) + else: + tag_value = tags.get(v) + if tag_value: + return to_text(tag_value) + return None + + def _get_hostname(self, instance, hostnames): + ''' + :param instance: an instance dict returned by boto3 ec2 describe_instances() + :param hostnames: a list of hostname destination variables in order of preference + :return the preferred identifer for the host + ''' + if not hostnames: + hostnames = ['dns-name', 'private-dns-name'] + + hostname = None + for preference in hostnames: + if isinstance(preference, dict): + if 'name' not in preference: + raise AnsibleError("A 'name' key must be defined in a hostnames dictionary.") + hostname = self._get_hostname(instance, [preference["name"]]) + hostname_from_prefix = self._get_hostname(instance, [preference["prefix"]]) + separator = preference.get("separator", "_") + if hostname and hostname_from_prefix and 'prefix' in preference: + hostname = hostname_from_prefix + separator + hostname + elif preference.startswith('tag:'): + hostname = self._get_tag_hostname(preference, instance) + else: + hostname = self._get_boto_attr_chain(preference, instance) + if hostname: + break + if hostname: + if ':' in to_text(hostname): + return self._sanitize_group_name((to_text(hostname))) + else: + return to_text(hostname) + + def _query(self, regions, filters, strict_permissions): + ''' + :param regions: a list of regions to query + :param filters: a list of boto3 filter dictionaries + :param hostnames: a list of hostname destination variables in order of preference + :param strict_permissions: a boolean determining whether to fail or ignore 403 error codes + ''' + return {'aws_ec2': self._get_instances_by_region(regions, filters, strict_permissions)} + + def _populate(self, groups, hostnames): + for group in groups: + group = self.inventory.add_group(group) + self._add_hosts(hosts=groups[group], group=group, hostnames=hostnames) + self.inventory.add_child('all', group) + + def _add_hosts(self, hosts, group, hostnames): + ''' + :param hosts: a list of hosts to be added to a group + :param group: the name of the group to which the hosts belong + :param hostnames: a list of hostname destination variables in order of preference + ''' + for host in hosts: + hostname = self._get_hostname(host, hostnames) + + host = camel_dict_to_snake_dict(host, ignore_list=['Tags']) + host['tags'] = boto3_tag_list_to_ansible_dict(host.get('tags', [])) + + # Allow easier grouping by region + host['placement']['region'] = host['placement']['availability_zone'][:-1] + + if not hostname: + continue + self.inventory.add_host(hostname, group=group) + for hostvar, hostval in host.items(): + self.inventory.set_variable(hostname, hostvar, hostval) + + # Use constructed if applicable + + strict = self.get_option('strict') + + # Composed variables + self._set_composite_vars(self.get_option('compose'), host, hostname, strict=strict) + + # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group + self._add_host_to_composed_groups(self.get_option('groups'), host, hostname, strict=strict) + + # Create groups based on variable values and add the corresponding hosts to it + self._add_host_to_keyed_groups(self.get_option('keyed_groups'), host, hostname, strict=strict) + + def _set_credentials(self): + ''' + :param config_data: contents of the inventory config file + ''' + + self.boto_profile = self.get_option('aws_profile') + self.aws_access_key_id = self.get_option('aws_access_key') + self.aws_secret_access_key = self.get_option('aws_secret_key') + self.aws_security_token = self.get_option('aws_security_token') + self.iam_role_arn = self.get_option('iam_role_arn') + + if not self.boto_profile and not (self.aws_access_key_id and self.aws_secret_access_key): + session = botocore.session.get_session() + try: + credentials = session.get_credentials().get_frozen_credentials() + except AttributeError: + pass + else: + self.aws_access_key_id = credentials.access_key + self.aws_secret_access_key = credentials.secret_key + self.aws_security_token = credentials.token + + if not self.boto_profile and not (self.aws_access_key_id and self.aws_secret_access_key): + raise AnsibleError("Insufficient boto credentials found. Please provide them in your " + "inventory configuration file or set them as environment variables.") + + def verify_file(self, path): + ''' + :param loader: an ansible.parsing.dataloader.DataLoader object + :param path: the path to the inventory config file + :return the contents of the config file + ''' + if super(InventoryModule, self).verify_file(path): + if path.endswith(('aws_ec2.yml', 'aws_ec2.yaml')): + return True + self.display.debug("aws_ec2 inventory filename must end with 'aws_ec2.yml' or 'aws_ec2.yaml'") + return False + + def parse(self, inventory, loader, path, cache=True): + + super(InventoryModule, self).parse(inventory, loader, path) + + self._read_config_data(path) + + if self.get_option('use_contrib_script_compatible_sanitization'): + self._sanitize_group_name = self._legacy_script_compatible_group_sanitization + + self._set_credentials() + + # get user specifications + regions = self.get_option('regions') + filters = ansible_dict_to_boto3_filter_list(self.get_option('filters')) + hostnames = self.get_option('hostnames') + strict_permissions = self.get_option('strict_permissions') + + cache_key = self.get_cache_key(path) + # false when refresh_cache or --flush-cache is used + if cache: + # get the user-specified directive + cache = self.get_option('cache') + + # Generate inventory + cache_needs_update = False + if cache: + try: + results = self._cache[cache_key] + except KeyError: + # if cache expires or cache file doesn't exist + cache_needs_update = True + + if not cache or cache_needs_update: + results = self._query(regions, filters, strict_permissions) + + self._populate(results, hostnames) + + # If the cache has expired/doesn't exist or if refresh_inventory/flush cache is used + # when the user is using caching, update the cached inventory + if cache_needs_update or (not cache and self.get_option('cache')): + self._cache[cache_key] = results + + @staticmethod + def _legacy_script_compatible_group_sanitization(name): + + # note that while this mirrors what the script used to do, it has many issues with unicode and usability in python + regex = re.compile(r"[^A-Za-z0-9\_\-]") + + return regex.sub('_', name) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/aws_rds.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/aws_rds.py new file mode 100644 index 00000000..bd30db2e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/inventory/aws_rds.py @@ -0,0 +1,370 @@ +# Copyright (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' + name: aws_rds + plugin_type: inventory + short_description: rds instance source + description: + - Get instances and clusters from Amazon Web Services RDS. + - Uses a YAML configuration file that ends with aws_rds.(yml|yaml). + options: + regions: + description: A list of regions in which to describe RDS instances and clusters. Available regions are listed here + U(https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html) + default: [] + filters: + description: A dictionary of filter value pairs. Available filters are listed here + U(https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-instances.html#options). If you filter by + db-cluster-id and I(include_clusters) is True it will apply to clusters as well. + default: {} + strict_permissions: + description: By default if an AccessDenied exception is encountered this plugin will fail. You can set strict_permissions to + False in the inventory config file which will allow the restrictions to be gracefully skipped. + type: bool + default: True + include_clusters: + description: Whether or not to query for Aurora clusters as well as instances + type: bool + default: False + statuses: + description: A list of desired states for instances/clusters to be added to inventory. Set to ['all'] as a shorthand to find everything. + type: list + default: + - creating + - available + iam_role_arn: + description: The ARN of the IAM role to assume to perform the inventory lookup. You should still provide + AWS credentials with enough privilege to perform the AssumeRole action. + extends_documentation_fragment: + - inventory_cache + - constructed + - amazon.aws.aws_credentials + + requirements: + - boto3 + - botocore + author: Sloane Hertel (@s-hertel) +''' + +EXAMPLES = ''' +plugin: aws_rds +regions: + - us-east-1 + - ca-central-1 +keyed_groups: + - key: 'db_parameter_groups|json_query("[].db_parameter_group_name")' + prefix: rds_parameter_group + - key: engine + prefix: rds + - key: tags + - key: region +''' + +from ansible.errors import AnsibleError +from ansible.module_utils._text import to_native +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list, boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable + +try: + import boto3 + import botocore +except ImportError: + raise AnsibleError('The RDS dynamic inventory plugin requires boto3 and botocore.') + + +class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): + + NAME = 'amazon.aws.aws_rds' + + def __init__(self): + super(InventoryModule, self).__init__() + self.credentials = {} + self.boto_profile = None + self.iam_role_arn = None + + def _get_connection(self, credentials, region='us-east-1'): + try: + connection = boto3.session.Session(profile_name=self.boto_profile).client('rds', region, **credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + if self.boto_profile: + try: + connection = boto3.session.Session(profile_name=self.boto_profile).client('rds', region) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + else: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + return connection + + def _boto3_assume_role(self, credentials, region): + """ + Assume an IAM role passed by iam_role_arn parameter + :return: a dict containing the credentials of the assumed role + """ + + iam_role_arn = self.iam_role_arn + + try: + sts_connection = boto3.session.Session(profile_name=self.boto_profile).client('sts', region, **credentials) + sts_session = sts_connection.assume_role(RoleArn=iam_role_arn, RoleSessionName='ansible_aws_rds_dynamic_inventory') + return dict( + aws_access_key_id=sts_session['Credentials']['AccessKeyId'], + aws_secret_access_key=sts_session['Credentials']['SecretAccessKey'], + aws_session_token=sts_session['Credentials']['SessionToken'] + ) + except botocore.exceptions.ClientError as e: + raise AnsibleError("Unable to assume IAM role: %s" % to_native(e)) + + def _boto3_conn(self, regions): + ''' + :param regions: A list of regions to create a boto3 client + + Generator that yields a boto3 client and the region + ''' + iam_role_arn = self.iam_role_arn + credentials = self.credentials + for region in regions: + try: + if iam_role_arn is not None: + assumed_credentials = self._boto3_assume_role(credentials, region) + else: + assumed_credentials = credentials + connection = boto3.session.Session(profile_name=self.boto_profile).client('rds', region, **assumed_credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + if self.boto_profile: + try: + connection = boto3.session.Session(profile_name=self.boto_profile).client('rds', region) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + else: + raise AnsibleError("Insufficient credentials found: %s" % to_native(e)) + yield connection, region + + def _get_hosts_by_region(self, connection, filters, strict): + + def _add_tags_for_hosts(connection, hosts, strict): + for host in hosts: + if 'DBInstanceArn' in host: + resource_arn = host['DBInstanceArn'] + else: + resource_arn = host['DBClusterArn'] + + try: + tags = connection.list_tags_for_resource(ResourceName=resource_arn)['TagList'] + except is_boto3_error_code('AccessDenied') as e: + if not strict: + tags = [] + else: + raise e + host['Tags'] = tags + + def wrapper(f, *args, **kwargs): + try: + results = f(*args, **kwargs) + if 'DBInstances' in results: + results = results['DBInstances'] + else: + results = results['DBClusters'] + _add_tags_for_hosts(connection, results, strict) + except is_boto3_error_code('AccessDenied') as e: # pylint: disable=duplicate-except + if not strict: + results = [] + else: + raise AnsibleError("Failed to query RDS: {0}".format(to_native(e))) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + raise AnsibleError("Failed to query RDS: {0}".format(to_native(e))) + return results + return wrapper + + def _get_all_hosts(self, regions, instance_filters, cluster_filters, strict, statuses, gather_clusters=False): + ''' + :param regions: a list of regions in which to describe hosts + :param instance_filters: a list of boto3 filter dictionaries + :param cluster_filters: a list of boto3 filter dictionaries + :param strict: a boolean determining whether to fail or ignore 403 error codes + :param statuses: a list of statuses that the returned hosts should match + :return A list of host dictionaries + ''' + all_instances = [] + all_clusters = [] + for connection, region in self._boto3_conn(regions): + paginator = connection.get_paginator('describe_db_instances') + all_instances.extend( + self._get_hosts_by_region(connection, instance_filters, strict) + (paginator.paginate(Filters=instance_filters).build_full_result) + ) + if gather_clusters: + all_clusters.extend( + self._get_hosts_by_region(connection, cluster_filters, strict) + (connection.describe_db_clusters, **{'Filters': cluster_filters}) + ) + sorted_hosts = list( + sorted(all_instances, key=lambda x: x['DBInstanceIdentifier']) + + sorted(all_clusters, key=lambda x: x['DBClusterIdentifier']) + ) + return self.find_hosts_with_valid_statuses(sorted_hosts, statuses) + + def find_hosts_with_valid_statuses(self, hosts, statuses): + if 'all' in statuses: + return hosts + valid_hosts = [] + for host in hosts: + if host.get('DBInstanceStatus') in statuses: + valid_hosts.append(host) + elif host.get('Status') in statuses: + valid_hosts.append(host) + return valid_hosts + + def _populate(self, hosts): + group = 'aws_rds' + self.inventory.add_group(group) + if hosts: + self._add_hosts(hosts=hosts, group=group) + self.inventory.add_child('all', group) + + def _populate_from_source(self, source_data): + hostvars = source_data.pop('_meta', {}).get('hostvars', {}) + for group in source_data: + if group == 'all': + continue + else: + self.inventory.add_group(group) + hosts = source_data[group].get('hosts', []) + for host in hosts: + self._populate_host_vars([host], hostvars.get(host, {}), group) + self.inventory.add_child('all', group) + + def _get_hostname(self, host): + if host.get('DBInstanceIdentifier'): + return host['DBInstanceIdentifier'] + else: + return host['DBClusterIdentifier'] + + def _format_inventory(self, hosts): + results = {'_meta': {'hostvars': {}}} + group = 'aws_rds' + results[group] = {'hosts': []} + for host in hosts: + hostname = self._get_hostname(host) + results[group]['hosts'].append(hostname) + h = self.inventory.get_host(hostname) + results['_meta']['hostvars'][h.name] = h.vars + return results + + def _add_hosts(self, hosts, group): + ''' + :param hosts: a list of hosts to be added to a group + :param group: the name of the group to which the hosts belong + ''' + for host in hosts: + hostname = self._get_hostname(host) + host = camel_dict_to_snake_dict(host, ignore_list=['Tags']) + host['tags'] = boto3_tag_list_to_ansible_dict(host.get('tags', [])) + + # Allow easier grouping by region + if 'availability_zone' in host: + host['region'] = host['availability_zone'][:-1] + elif 'availability_zones' in host: + host['region'] = host['availability_zones'][0][:-1] + + self.inventory.add_host(hostname, group=group) + for hostvar, hostval in host.items(): + self.inventory.set_variable(hostname, hostvar, hostval) + + # Use constructed if applicable + strict = self.get_option('strict') + # Composed variables + self._set_composite_vars(self.get_option('compose'), host, hostname, strict=strict) + # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group + self._add_host_to_composed_groups(self.get_option('groups'), host, hostname, strict=strict) + # Create groups based on variable values and add the corresponding hosts to it + self._add_host_to_keyed_groups(self.get_option('keyed_groups'), host, hostname, strict=strict) + + def _set_credentials(self): + ''' + :param config_data: contents of the inventory config file + ''' + self.boto_profile = self.get_option('aws_profile') + aws_access_key_id = self.get_option('aws_access_key') + aws_secret_access_key = self.get_option('aws_secret_key') + aws_security_token = self.get_option('aws_security_token') + self.iam_role_arn = self.get_option('iam_role_arn') + + if not self.boto_profile and not (aws_access_key_id and aws_secret_access_key): + session = botocore.session.get_session() + if session.get_credentials() is not None: + aws_access_key_id = session.get_credentials().access_key + aws_secret_access_key = session.get_credentials().secret_key + aws_security_token = session.get_credentials().token + + if not self.boto_profile and not (aws_access_key_id and aws_secret_access_key): + raise AnsibleError("Insufficient boto credentials found. Please provide them in your " + "inventory configuration file or set them as environment variables.") + + if aws_access_key_id: + self.credentials['aws_access_key_id'] = aws_access_key_id + if aws_secret_access_key: + self.credentials['aws_secret_access_key'] = aws_secret_access_key + if aws_security_token: + self.credentials['aws_session_token'] = aws_security_token + + def verify_file(self, path): + ''' + :param loader: an ansible.parsing.dataloader.DataLoader object + :param path: the path to the inventory config file + :return the contents of the config file + ''' + if super(InventoryModule, self).verify_file(path): + if path.endswith(('aws_rds.yml', 'aws_rds.yaml')): + return True + return False + + def parse(self, inventory, loader, path, cache=True): + super(InventoryModule, self).parse(inventory, loader, path) + + config_data = self._read_config_data(path) + self._set_credentials() + + # get user specifications + regions = self.get_option('regions') + filters = self.get_option('filters') + strict_permissions = self.get_option('strict_permissions') + statuses = self.get_option('statuses') + include_clusters = self.get_option('include_clusters') + instance_filters = ansible_dict_to_boto3_filter_list(filters) + cluster_filters = [] + if 'db-cluster-id' in filters and include_clusters: + cluster_filters = ansible_dict_to_boto3_filter_list({'db-cluster-id': filters['db-cluster-id']}) + + cache_key = self.get_cache_key(path) + # false when refresh_cache or --flush-cache is used + if cache: + # get the user-specified directive + cache = self.get_option('cache') + + # Generate inventory + formatted_inventory = {} + cache_needs_update = False + if cache: + try: + results = self._cache[cache_key] + except KeyError: + # if cache expires or cache file doesn't exist + cache_needs_update = True + else: + self._populate_from_source(results) + + if not cache or cache_needs_update: + results = self._get_all_hosts(regions, instance_filters, cluster_filters, strict_permissions, statuses, include_clusters) + self._populate(results) + formatted_inventory = self._format_inventory(results) + + # If the cache has expired/doesn't exist or if refresh_inventory/flush cache is used + # when the user is using caching, update the cached inventory + if cache_needs_update or (not cache and self.get_option('cache')): + self._cache[cache_key] = formatted_inventory diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_account_attribute.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_account_attribute.py new file mode 100644 index 00000000..ca9c57c9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_account_attribute.py @@ -0,0 +1,128 @@ +# (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' +lookup: aws_account_attribute +author: + - Sloane Hertel <shertel@redhat.com> +requirements: + - boto3 + - botocore +extends_documentation_fragment: +- amazon.aws.aws_credentials +- amazon.aws.aws_region + +short_description: Look up AWS account attributes. +description: + - Describes attributes of your AWS account. You can specify one of the listed + attribute choices or omit it to see all attributes. +options: + attribute: + description: The attribute for which to get the value(s). + choices: + - supported-platforms + - default-vpc + - max-instances + - vpc-max-security-groups-per-interface + - max-elastic-ips + - vpc-max-elastic-ips + - has-ec2-classic +''' + +EXAMPLES = """ +vars: + has_ec2_classic: "{{ lookup('aws_account_attribute', attribute='has-ec2-classic') }}" + # true | false + + default_vpc_id: "{{ lookup('aws_account_attribute', attribute='default-vpc') }}" + # vpc-xxxxxxxx | none + + account_details: "{{ lookup('aws_account_attribute', wantlist='true') }}" + # {'default-vpc': ['vpc-xxxxxxxx'], 'max-elastic-ips': ['5'], 'max-instances': ['20'], + # 'supported-platforms': ['VPC', 'EC2'], 'vpc-max-elastic-ips': ['5'], 'vpc-max-security-groups-per-interface': ['5']} + +""" + +RETURN = """ +_raw: + description: + Returns a boolean when I(attribute) is check_ec2_classic. Otherwise returns the value(s) of the attribute + (or all attributes if one is not specified). +""" + +from ansible.errors import AnsibleError + +try: + import boto3 + import botocore +except ImportError: + raise AnsibleError("The lookup aws_account_attribute requires boto3 and botocore.") + +from ansible.module_utils._text import to_native +from ansible.plugins.lookup import LookupBase + + +def _boto3_conn(region, credentials): + boto_profile = credentials.pop('aws_profile', None) + + try: + connection = boto3.session.Session(profile_name=boto_profile).client('ec2', region, **credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + if boto_profile: + try: + connection = boto3.session.Session(profile_name=boto_profile).client('ec2', region) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + raise AnsibleError("Insufficient credentials found.") + else: + raise AnsibleError("Insufficient credentials found.") + return connection + + +def _get_credentials(options): + credentials = {} + credentials['aws_profile'] = options['aws_profile'] + credentials['aws_secret_access_key'] = options['aws_secret_key'] + credentials['aws_access_key_id'] = options['aws_access_key'] + if options['aws_security_token']: + credentials['aws_session_token'] = options['aws_security_token'] + + return credentials + + +class LookupModule(LookupBase): + def run(self, terms, variables, **kwargs): + + self.set_options(var_options=variables, direct=kwargs) + boto_credentials = _get_credentials(self._options) + + region = self._options['region'] + client = _boto3_conn(region, boto_credentials) + + attribute = kwargs.get('attribute') + params = {'AttributeNames': []} + check_ec2_classic = False + if 'has-ec2-classic' == attribute: + check_ec2_classic = True + params['AttributeNames'] = ['supported-platforms'] + elif attribute: + params['AttributeNames'] = [attribute] + + try: + response = client.describe_account_attributes(**params)['AccountAttributes'] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + raise AnsibleError("Failed to describe account attributes: %s" % to_native(e)) + + if check_ec2_classic: + attr = response[0] + return any(value['AttributeValue'] == 'EC2' for value in attr['AttributeValues']) + + if attribute: + attr = response[0] + return [value['AttributeValue'] for value in attr['AttributeValues']] + + flattened = {} + for k_v_dict in response: + flattened[k_v_dict['AttributeName']] = [value['AttributeValue'] for value in k_v_dict['AttributeValues']] + return flattened diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_secret.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_secret.py new file mode 100644 index 00000000..39c49890 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_secret.py @@ -0,0 +1,265 @@ +# Copyright: (c) 2018, Aaron Smith <ajsmith10381@gmail.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = r''' +lookup: aws_secret +author: + - Aaron Smith <ajsmith10381@gmail.com> +requirements: + - boto3 + - botocore>=1.10.0 +extends_documentation_fragment: +- amazon.aws.aws_credentials +- amazon.aws.aws_region + +short_description: Look up secrets stored in AWS Secrets Manager. +description: + - Look up secrets stored in AWS Secrets Manager provided the caller + has the appropriate permissions to read the secret. + - Lookup is based on the secret's I(Name) value. + - Optional parameters can be passed into this lookup; I(version_id) and I(version_stage) +options: + _terms: + description: Name of the secret to look up in AWS Secrets Manager. + required: True + bypath: + description: A boolean to indicate whether the parameter is provided as a hierarchy. + default: false + type: boolean + version_added: 1.4.0 + nested: + description: A boolean to indicate the secret contains nested values. + type: boolean + default: false + version_added: 1.4.0 + version_id: + description: Version of the secret(s). + required: False + version_stage: + description: Stage of the secret version. + required: False + join: + description: + - Join two or more entries to form an extended secret. + - This is useful for overcoming the 4096 character limit imposed by AWS. + - No effect when used with I(bypath). + type: boolean + default: false + on_missing: + description: + - Action to take if the secret is missing. + - C(error) will raise a fatal error when the secret is missing. + - C(skip) will silently ignore the missing secret. + - C(warn) will skip over the missing secret but issue a warning. + default: error + type: string + choices: ['error', 'skip', 'warn'] + on_denied: + description: + - Action to take if access to the secret is denied. + - C(error) will raise a fatal error when access to the secret is denied. + - C(skip) will silently ignore the denied secret. + - C(warn) will skip over the denied secret but issue a warning. + default: error + type: string + choices: ['error', 'skip', 'warn'] +''' + +EXAMPLES = r""" + - name: lookup secretsmanager secret in the current region + debug: msg="{{ lookup('amazon.aws.aws_secret', '/path/to/secrets', bypath=true) }}" + + - name: Create RDS instance with aws_secret lookup for password param + rds: + command: create + instance_name: app-db + db_engine: MySQL + size: 10 + instance_type: db.m1.small + username: dbadmin + password: "{{ lookup('amazon.aws.aws_secret', 'DbSecret') }}" + tags: + Environment: staging + + - name: skip if secret does not exist + debug: msg="{{ lookup('amazon.aws.aws_secret', 'secret-not-exist', on_missing='skip')}}" + + - name: warn if access to the secret is denied + debug: msg="{{ lookup('amazon.aws.aws_secret', 'secret-denied', on_denied='warn')}}" + + - name: lookup secretsmanager secret in the current region using the nested feature + debug: msg="{{ lookup('amazon.aws.aws_secret', 'secrets.environments.production.password', nested=true) }}" + # The secret can be queried using the following syntax: `aws_secret_object_name.key1.key2.key3`. + # If an object is of the form `{"key1":{"key2":{"key3":1}}}` the query would return the value `1`. +""" + +RETURN = r""" +_raw: + description: + Returns the value of the secret stored in AWS Secrets Manager. +""" + +from ansible.errors import AnsibleError +from ansible.module_utils.six import string_types + +try: + import boto3 + import botocore +except ImportError: + raise AnsibleError("The lookup aws_secret requires boto3 and botocore.") + +from ansible.plugins.lookup import LookupBase +from ansible.module_utils._text import to_native +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 + +import json + + +def _boto3_conn(region, credentials): + boto_profile = credentials.pop('aws_profile', None) + + try: + connection = boto3.session.Session(profile_name=boto_profile).client('secretsmanager', region, **credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + if boto_profile: + try: + connection = boto3.session.Session(profile_name=boto_profile).client('secretsmanager', region) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError) as e: + raise AnsibleError("Insufficient credentials found.") + else: + raise AnsibleError("Insufficient credentials found.") + return connection + + +class LookupModule(LookupBase): + def run(self, terms, variables=None, boto_profile=None, aws_profile=None, + aws_secret_key=None, aws_access_key=None, aws_security_token=None, region=None, + bypath=False, nested=False, join=False, version_stage=None, version_id=None, on_missing='error', + on_denied='error'): + ''' + :arg terms: a list of lookups to run. + e.g. ['parameter_name', 'parameter_name_too' ] + :kwarg variables: ansible variables active at the time of the lookup + :kwarg aws_secret_key: identity of the AWS key to use + :kwarg aws_access_key: AWS secret key (matching identity) + :kwarg aws_security_token: AWS session key if using STS + :kwarg decrypt: Set to True to get decrypted parameters + :kwarg region: AWS region in which to do the lookup + :kwarg bypath: Set to True to do a lookup of variables under a path + :kwarg nested: Set to True to do a lookup of nested secrets + :kwarg join: Join two or more entries to form an extended secret + :kwarg version_stage: Stage of the secret version + :kwarg version_id: Version of the secret(s) + :kwarg on_missing: Action to take if the secret is missing + :kwarg on_denied: Action to take if access to the secret is denied + :returns: A list of parameter values or a list of dictionaries if bypath=True. + ''' + if not HAS_BOTO3: + raise AnsibleError('botocore and boto3 are required for aws_ssm lookup.') + + missing = on_missing.lower() + if not isinstance(missing, string_types) or missing not in ['error', 'warn', 'skip']: + raise AnsibleError('"on_missing" must be a string and one of "error", "warn" or "skip", not %s' % missing) + + denied = on_denied.lower() + if not isinstance(denied, string_types) or denied not in ['error', 'warn', 'skip']: + raise AnsibleError('"on_denied" must be a string and one of "error", "warn" or "skip", not %s' % denied) + + credentials = {} + if aws_profile: + credentials['aws_profile'] = aws_profile + else: + credentials['aws_profile'] = boto_profile + credentials['aws_secret_access_key'] = aws_secret_key + credentials['aws_access_key_id'] = aws_access_key + credentials['aws_session_token'] = aws_security_token + + # fallback to IAM role credentials + if not credentials['aws_profile'] and not ( + credentials['aws_access_key_id'] and credentials['aws_secret_access_key']): + session = botocore.session.get_session() + if session.get_credentials() is not None: + credentials['aws_access_key_id'] = session.get_credentials().access_key + credentials['aws_secret_access_key'] = session.get_credentials().secret_key + credentials['aws_session_token'] = session.get_credentials().token + + client = _boto3_conn(region, credentials) + + if bypath: + secrets = {} + for term in terms: + try: + response = client.list_secrets(Filters=[{'Key': 'name', 'Values': [term]}]) + + if 'SecretList' in response: + for secret in response['SecretList']: + secrets.update({secret['Name']: self.get_secret_value(secret['Name'], client, + on_missing=missing, + on_denied=denied)}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + raise AnsibleError("Failed to retrieve secret: %s" % to_native(e)) + secrets = [secrets] + else: + secrets = [] + for term in terms: + value = self.get_secret_value(term, client, + version_stage=version_stage, version_id=version_id, + on_missing=missing, on_denied=denied, nested=nested) + if value: + secrets.append(value) + if join: + joined_secret = [] + joined_secret.append(''.join(secrets)) + return joined_secret + + return secrets + + def get_secret_value(self, term, client, version_stage=None, version_id=None, on_missing=None, on_denied=None, nested=False): + params = {} + params['SecretId'] = term + if version_id: + params['VersionId'] = version_id + if version_stage: + params['VersionStage'] = version_stage + if nested: + if len(term.split('.')) < 2: + raise AnsibleError("Nested query must use the following syntax: `aws_secret_name.<key_name>.<key_name>") + secret_name = term.split('.')[0] + params['SecretId'] = secret_name + + try: + response = client.get_secret_value(**params) + if 'SecretBinary' in response: + return response['SecretBinary'] + if 'SecretString' in response: + if nested: + secrets = [] + query = term.split('.')[1:] + secret_string = json.loads(response['SecretString']) + ret_val = secret_string + for key in query: + if key in ret_val: + ret_val = ret_val[key] + else: + raise AnsibleError("Successfully retrieved secret but there exists no key {0} in the secret".format(key)) + return str(ret_val) + else: + return response['SecretString'] + except is_boto3_error_code('ResourceNotFoundException'): + if on_missing == 'error': + raise AnsibleError("Failed to find secret %s (ResourceNotFound)" % term) + elif on_missing == 'warn': + self._display.warning('Skipping, did not find secret %s' % term) + except is_boto3_error_code('AccessDeniedException'): # pylint: disable=duplicate-except + if on_denied == 'error': + raise AnsibleError("Failed to access secret %s (AccessDenied)" % term) + elif on_denied == 'warn': + self._display.warning('Skipping, access denied for secret %s' % term) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + raise AnsibleError("Failed to retrieve secret: %s" % to_native(e)) + + return None diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_service_ip_ranges.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_service_ip_ranges.py new file mode 100644 index 00000000..6a66120d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_service_ip_ranges.py @@ -0,0 +1,78 @@ +# (c) 2016 James Turner <turnerjsm@gmail.com> +# (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' +lookup: aws_service_ip_ranges +author: + - James Turner <turnerjsm@gmail.com> +requirements: + - must have public internet connectivity +short_description: Look up the IP ranges for services provided in AWS such as EC2 and S3. +description: + - AWS publishes IP ranges used on the public internet by EC2, S3, CloudFront, CodeBuild, Route53, and Route53 Health Checking. + - This module produces a list of all the ranges (by default) or can narrow down the list to the specified region or service. +options: + service: + description: 'The service to filter ranges by. Options: EC2, S3, CLOUDFRONT, CODEbUILD, ROUTE53, ROUTE53_HEALTHCHECKS' + region: + description: 'The AWS region to narrow the ranges to. Examples: us-east-1, eu-west-2, ap-southeast-1' +''' + +EXAMPLES = """ +vars: + ec2_ranges: "{{ lookup('aws_service_ip_ranges', region='ap-southeast-2', service='EC2', wantlist=True) }}" +tasks: + +- name: "use list return option and iterate as a loop" + debug: msg="{% for cidr in ec2_ranges %}{{ cidr }} {% endfor %}" +# "52.62.0.0/15 52.64.0.0/17 52.64.128.0/17 52.65.0.0/16 52.95.241.0/24 52.95.255.16/28 54.66.0.0/16 " + +- name: "Pull S3 IP ranges, and print the default return style" + debug: msg="{{ lookup('aws_service_ip_ranges', region='us-east-1', service='S3') }}" +# "52.92.16.0/20,52.216.0.0/15,54.231.0.0/17" +""" + +RETURN = """ +_raw: + description: comma-separated list of CIDR ranges +""" + + +import json + +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase +from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError +from ansible.module_utils._text import to_native +from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError + + +class LookupModule(LookupBase): + def run(self, terms, variables, **kwargs): + try: + resp = open_url('https://ip-ranges.amazonaws.com/ip-ranges.json') + amazon_response = json.load(resp)['prefixes'] + except getattr(json.decoder, 'JSONDecodeError', ValueError) as e: + # on Python 3+, json.decoder.JSONDecodeError is raised for bad + # JSON. On 2.x it's a ValueError + raise AnsibleError("Could not decode AWS IP ranges: %s" % to_native(e)) + except HTTPError as e: + raise AnsibleError("Received HTTP error while pulling IP ranges: %s" % to_native(e)) + except SSLValidationError as e: + raise AnsibleError("Error validating the server's certificate for: %s" % to_native(e)) + except URLError as e: + raise AnsibleError("Failed look up IP range service: %s" % to_native(e)) + except ConnectionError as e: + raise AnsibleError("Error connecting to IP range service: %s" % to_native(e)) + + if 'region' in kwargs: + region = kwargs['region'] + amazon_response = (item for item in amazon_response if item['region'] == region) + if 'service' in kwargs: + service = str.upper(kwargs['service']) + amazon_response = (item for item in amazon_response if item['service'] == service) + + return [item['ip_prefix'] for item in amazon_response] diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_ssm.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_ssm.py new file mode 100644 index 00000000..fe2798e5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/lookup/aws_ssm.py @@ -0,0 +1,234 @@ +# (c) 2016, Bill Wang <ozbillwang(at)gmail.com> +# (c) 2017, Marat Bakeev <hawara(at)gmail.com> +# (c) 2018, Michael De La Rue <siblemitcom.mddlr(at)spamgourmet.com> +# (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' +lookup: aws_ssm +author: + - Bill Wang <ozbillwang(at)gmail.com> + - Marat Bakeev <hawara(at)gmail.com> + - Michael De La Rue <siblemitcom.mddlr@spamgourmet.com> +requirements: + - boto3 + - botocore +short_description: Get the value for a SSM parameter or all parameters under a path. +description: + - Get the value for an Amazon Simple Systems Manager parameter or a hierarchy of parameters. + The first argument you pass the lookup can either be a parameter name or a hierarchy of + parameters. Hierarchies start with a forward slash and end with the parameter name. Up to + 5 layers may be specified. + - If looking up an explicitly listed parameter by name which does not exist then the lookup will + return a None value which will be interpreted by Jinja2 as an empty string. You can use the + ```default``` filter to give a default value in this case but must set the second parameter to + true (see examples below) + - When looking up a path for parameters under it a dictionary will be returned for each path. + If there is no parameter under that path then the return will be successful but the + dictionary will be empty. + - If the lookup fails due to lack of permissions or due to an AWS client error then the aws_ssm + will generate an error, normally crashing the current ansible task. This is normally the right + thing since ignoring a value that IAM isn't giving access to could cause bigger problems and + wrong behaviour or loss of data. If you want to continue in this case then you will have to set + up two ansible tasks, one which sets a variable and ignores failures one which uses the value + of that variable with a default. See the examples below. + +options: + decrypt: + description: A boolean to indicate whether to decrypt the parameter. + default: true + type: boolean + bypath: + description: A boolean to indicate whether the parameter is provided as a hierarchy. + default: false + type: boolean + recursive: + description: A boolean to indicate whether to retrieve all parameters within a hierarchy. + default: false + type: boolean + shortnames: + description: Indicates whether to return the name only without path if using a parameter hierarchy. + default: false + type: boolean +''' + +EXAMPLES = ''' +# lookup sample: +- name: lookup ssm parameter store in the current region + debug: msg="{{ lookup('aws_ssm', 'Hello' ) }}" + +- name: lookup ssm parameter store in nominated region + debug: msg="{{ lookup('aws_ssm', 'Hello', region='us-east-2' ) }}" + +- name: lookup ssm parameter store without decrypted + debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=False ) }}" + +- name: lookup ssm parameter store in nominated aws profile + debug: msg="{{ lookup('aws_ssm', 'Hello', aws_profile='myprofile' ) }}" + +- name: lookup ssm parameter store using explicit aws credentials + debug: msg="{{ lookup('aws_ssm', 'Hello', aws_access_key=my_aws_access_key, aws_secret_key=my_aws_secret_key, aws_security_token=my_security_token ) }}" + +- name: lookup ssm parameter store with all options. + debug: msg="{{ lookup('aws_ssm', 'Hello', decrypt=false, region='us-east-2', aws_profile='myprofile') }}" + +- name: lookup a key which doesn't exist, returns "" + debug: msg="{{ lookup('aws_ssm', 'NoKey') }}" + +- name: lookup a key which doesn't exist, returning a default ('root') + debug: msg="{{ lookup('aws_ssm', 'AdminID') | default('root', true) }}" + +- name: lookup a key which doesn't exist failing to store it in a fact + set_fact: + temp_secret: "{{ lookup('aws_ssm', '/NoAccess/hiddensecret') }}" + ignore_errors: true + +- name: show fact default to "access failed" if we don't have access + debug: msg="{{ 'the secret was:' ~ temp_secret | default('could not access secret') }}" + +- name: return a dictionary of ssm parameters from a hierarchy path + debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region='ap-southeast-2', bypath=true, recursive=true ) }}" + +- name: return a dictionary of ssm parameters from a hierarchy path with shortened names (param instead of /PATH/to/param) + debug: msg="{{ lookup('aws_ssm', '/PATH/to/params', region='ap-southeast-2', shortnames=true, bypath=true, recursive=true ) }}" + +- name: Iterate over a parameter hierarchy (one iteration per parameter) + debug: msg='Key contains {{ item.key }} , with value {{ item.value }}' + loop: '{{ lookup("aws_ssm", "/demo/", region="ap-southeast-2", bypath=True) | dict2items }}' + +- name: Iterate over multiple paths as dictionaries (one iteration per path) + debug: msg='Path contains {{ item }}' + loop: '{{ lookup("aws_ssm", "/demo/", "/demo1/", bypath=True)}}' + +''' + +try: + from botocore.exceptions import ClientError + import botocore + import boto3 +except ImportError: + pass # will be captured by imported HAS_BOTO3 + +from ansible.errors import AnsibleError +from ansible.module_utils._text import to_native +from ansible.plugins.lookup import LookupBase +from ansible.utils.display import Display + +from ..module_utils.ec2 import HAS_BOTO3 +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + +display = Display() + + +def _boto3_conn(region, credentials): + if 'boto_profile' in credentials: + boto_profile = credentials.pop('boto_profile') + else: + boto_profile = None + + try: + connection = boto3.session.Session(profile_name=boto_profile).client('ssm', region, **credentials) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError): + if boto_profile: + try: + connection = boto3.session.Session(profile_name=boto_profile).client('ssm', region) + # FIXME: we should probably do better passing on of the error information + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError): + raise AnsibleError("Insufficient credentials found.") + else: + raise AnsibleError("Insufficient credentials found.") + return connection + + +class LookupModule(LookupBase): + def run(self, terms, variables=None, boto_profile=None, aws_profile=None, + aws_secret_key=None, aws_access_key=None, aws_security_token=None, region=None, + bypath=False, shortnames=False, recursive=False, decrypt=True): + ''' + :arg terms: a list of lookups to run. + e.g. ['parameter_name', 'parameter_name_too' ] + :kwarg variables: ansible variables active at the time of the lookup + :kwarg aws_secret_key: identity of the AWS key to use + :kwarg aws_access_key: AWS secret key (matching identity) + :kwarg aws_security_token: AWS session key if using STS + :kwarg decrypt: Set to True to get decrypted parameters + :kwarg region: AWS region in which to do the lookup + :kwarg bypath: Set to True to do a lookup of variables under a path + :kwarg recursive: Set to True to recurse below the path (requires bypath=True) + :returns: A list of parameter values or a list of dictionaries if bypath=True. + ''' + + if not HAS_BOTO3: + raise AnsibleError('botocore and boto3 are required for aws_ssm lookup.') + + ret = [] + response = {} + ssm_dict = {} + + credentials = {} + if aws_profile: + credentials['boto_profile'] = aws_profile + else: + credentials['boto_profile'] = boto_profile + credentials['aws_secret_access_key'] = aws_secret_key + credentials['aws_access_key_id'] = aws_access_key + credentials['aws_session_token'] = aws_security_token + + client = _boto3_conn(region, credentials) + + ssm_dict['WithDecryption'] = decrypt + + # Lookup by path + if bypath: + ssm_dict['Recursive'] = recursive + for term in terms: + ssm_dict["Path"] = term + display.vvv("AWS_ssm path lookup term: %s in region: %s" % (term, region)) + try: + response = client.get_parameters_by_path(**ssm_dict) + except ClientError as e: + raise AnsibleError("SSM lookup exception: {0}".format(to_native(e))) + paramlist = list() + paramlist.extend(response['Parameters']) + + # Manual pagination, since boto doesn't support it yet for get_parameters_by_path + while 'NextToken' in response: + response = client.get_parameters_by_path(NextToken=response['NextToken'], **ssm_dict) + paramlist.extend(response['Parameters']) + + # shorten parameter names. yes, this will return duplicate names with different values. + if shortnames: + for x in paramlist: + x['Name'] = x['Name'][x['Name'].rfind('/') + 1:] + + display.vvvv("AWS_ssm path lookup returned: %s" % str(paramlist)) + if len(paramlist): + ret.append(boto3_tag_list_to_ansible_dict(paramlist, + tag_name_key_name="Name", + tag_value_key_name="Value")) + else: + ret.append({}) + # Lookup by parameter name - always returns a list with one or no entry. + else: + display.vvv("AWS_ssm name lookup term: %s" % terms) + ssm_dict["Names"] = terms + try: + response = client.get_parameters(**ssm_dict) + except ClientError as e: + raise AnsibleError("SSM lookup exception: {0}".format(to_native(e))) + params = boto3_tag_list_to_ansible_dict(response['Parameters'], tag_name_key_name="Name", + tag_value_key_name="Value") + for i in terms: + if i.split(':', 1)[0] in params: + ret.append(params[i]) + elif i in response['InvalidParameters']: + ret.append(None) + else: + raise AnsibleError("Ansible internal error: aws_ssm lookup failed to understand boto3 return value: {0}".format(str(response))) + return ret + + display.vvvv("AWS_ssm path lookup returning: %s " % str(ret)) + return ret diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/acm.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/acm.py new file mode 100644 index 00000000..0fc86516 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/acm.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see <http://www.gnu.org/licenses/>. +# +# Author: +# - Matthew Davis <Matthew.Davis.2@team.telstra.com> +# on behalf of Telstra Corporation Limited +# +# Common functionality to be used by the modules: +# - acm + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +""" +Common Amazon Certificate Manager facts shared between modules +""" + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass + +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from .ec2 import AWSRetry +from .ec2 import ansible_dict_to_boto3_tag_list +from .ec2 import boto3_tag_list_to_ansible_dict + + +class ACMServiceManager(object): + """Handles ACM Facts Services""" + + def __init__(self, module): + self.module = module + self.client = module.client('acm') + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0, catch_extra_error_codes=['RequestInProgressException']) + def delete_certificate_with_backoff(self, client, arn): + client.delete_certificate(CertificateArn=arn) + + def delete_certificate(self, client, module, arn): + module.debug("Attempting to delete certificate %s" % arn) + try: + self.delete_certificate_with_backoff(client, arn) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't delete certificate %s" % arn) + module.debug("Successfully deleted certificate %s" % arn) + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0, catch_extra_error_codes=['RequestInProgressException']) + def list_certificates_with_backoff(self, client, statuses=None): + paginator = client.get_paginator('list_certificates') + kwargs = dict() + if statuses: + kwargs['CertificateStatuses'] = statuses + return paginator.paginate(**kwargs).build_full_result()['CertificateSummaryList'] + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0, catch_extra_error_codes=['ResourceNotFoundException', 'RequestInProgressException']) + def get_certificate_with_backoff(self, client, certificate_arn): + response = client.get_certificate(CertificateArn=certificate_arn) + # strip out response metadata + return {'Certificate': response['Certificate'], + 'CertificateChain': response['CertificateChain']} + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0, catch_extra_error_codes=['ResourceNotFoundException', 'RequestInProgressException']) + def describe_certificate_with_backoff(self, client, certificate_arn): + return client.describe_certificate(CertificateArn=certificate_arn)['Certificate'] + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0, catch_extra_error_codes=['ResourceNotFoundException', 'RequestInProgressException']) + def list_certificate_tags_with_backoff(self, client, certificate_arn): + return client.list_tags_for_certificate(CertificateArn=certificate_arn)['Tags'] + + # Returns a list of certificates + # if domain_name is specified, returns only certificates with that domain + # if an ARN is specified, returns only that certificate + # only_tags is a dict, e.g. {'key':'value'}. If specified this function will return + # only certificates which contain all those tags (key exists, value matches). + def get_certificates(self, client, module, domain_name=None, statuses=None, arn=None, only_tags=None): + try: + all_certificates = self.list_certificates_with_backoff(client=client, statuses=statuses) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificates") + if domain_name: + certificates = [cert for cert in all_certificates + if cert['DomainName'] == domain_name] + else: + certificates = all_certificates + + if arn: + # still return a list, not just one item + certificates = [c for c in certificates if c['CertificateArn'] == arn] + + results = [] + for certificate in certificates: + try: + cert_data = self.describe_certificate_with_backoff(client, certificate['CertificateArn']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificate metadata for domain %s" % certificate['DomainName']) + + # in some states, ACM resources do not have a corresponding cert + if cert_data['Status'] not in ['PENDING_VALIDATION', 'VALIDATION_TIMED_OUT', 'FAILED']: + try: + cert_data.update(self.get_certificate_with_backoff(client, certificate['CertificateArn'])) + except (BotoCoreError, ClientError, KeyError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificate data for domain %s" % certificate['DomainName']) + cert_data = camel_dict_to_snake_dict(cert_data) + try: + tags = self.list_certificate_tags_with_backoff(client, certificate['CertificateArn']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain tags for domain %s" % certificate['DomainName']) + + cert_data['tags'] = boto3_tag_list_to_ansible_dict(tags) + results.append(cert_data) + + if only_tags: + for tag_key in only_tags: + try: + results = [c for c in results if ('tags' in c) and (tag_key in c['tags']) and (c['tags'][tag_key] == only_tags[tag_key])] + except (TypeError, AttributeError) as e: + for c in results: + if 'tags' not in c: + module.debug("cert is %s" % str(c)) + module.fail_json(msg="ACM tag filtering err", exception=e) + + return results + + # returns the domain name of a certificate (encoded in the public cert) + # for a given ARN + # A cert with that ARN must already exist + def get_domain_of_cert(self, client, module, arn): + if arn is None: + module.fail(msg="Internal error with ACM domain fetching, no certificate ARN specified") + try: + cert_data = self.describe_certificate_with_backoff(client=client, certificate_arn=arn) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't obtain certificate data for arn %s" % arn) + return cert_data['DomainName'] + + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0) + def import_certificate_with_backoff(self, client, certificate, private_key, certificate_chain, arn): + if certificate_chain: + if arn: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key), + CertificateChain=to_bytes(certificate_chain), + CertificateArn=arn) + else: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key), + CertificateChain=to_bytes(certificate_chain)) + else: + if arn: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key), + CertificateArn=arn) + else: + ret = client.import_certificate(Certificate=to_bytes(certificate), + PrivateKey=to_bytes(private_key)) + return ret['CertificateArn'] + + # Tags are a normal Ansible style dict + # {'Key':'Value'} + @AWSRetry.backoff(tries=5, delay=5, backoff=2.0, catch_extra_error_codes=['ResourceNotFoundException', 'RequestInProgressException']) + def tag_certificate_with_backoff(self, client, arn, tags): + aws_tags = ansible_dict_to_boto3_tag_list(tags) + client.add_tags_to_certificate(CertificateArn=arn, Tags=aws_tags) + + def import_certificate(self, client, module, certificate, private_key, arn=None, certificate_chain=None, tags=None): + + original_arn = arn + + # upload cert + try: + arn = self.import_certificate_with_backoff(client, certificate, private_key, certificate_chain, arn) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Couldn't upload new certificate") + + if original_arn and (arn != original_arn): + # I'm not sure whether the API guarentees that the ARN will not change + # I'm failing just in case. + # If I'm wrong, I'll catch it in the integration tests. + module.fail_json(msg="ARN changed with ACM update, from %s to %s" % (original_arn, arn)) + + # tag that cert + try: + self.tag_certificate_with_backoff(client, arn, tags) + except (BotoCoreError, ClientError) as e: + module.debug("Attempting to delete the cert we just created, arn=%s" % arn) + try: + self.delete_certificate_with_backoff(client, arn) + except Exception as f: + module.warn("Certificate %s exists, and is not tagged. So Ansible will not see it on the next run.") + module.fail_json_aws(e, msg="Couldn't tag certificate %s, couldn't delete it either" % arn) + module.fail_json_aws(e, msg="Couldn't tag certificate %s" % arn) + + return arn diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/batch.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/batch.py new file mode 100644 index 00000000..648d0a38 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/batch.py @@ -0,0 +1,106 @@ +# Copyright (c) 2017 Ansible Project +# +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +This module adds shared support for Batch modules. +""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + from botocore.exceptions import ClientError +except ImportError: + pass + +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + +from .ec2 import boto3_conn +from .ec2 import get_aws_connection_info + + +class AWSConnection(object): + """ + Create the connection object and client objects as required. + """ + + def __init__(self, ansible_obj, resources, boto3=True): + + ansible_obj.deprecate("The 'AWSConnection' class is deprecated, please use 'AnsibleAWSModule.client()'", + date='2022-06-01', collection_name='amazon.aws') + + self.region, self.endpoint, aws_connect_kwargs = get_aws_connection_info(ansible_obj, boto3=boto3) + + self.resource_client = dict() + if not resources: + resources = ['batch'] + + resources.append('iam') + + for resource in resources: + aws_connect_kwargs.update(dict(region=self.region, + endpoint=self.endpoint, + conn_type='client', + resource=resource + )) + self.resource_client[resource] = boto3_conn(ansible_obj, **aws_connect_kwargs) + + # if region is not provided, then get default profile/session region + if not self.region: + self.region = self.resource_client['batch'].meta.region_name + + # set account ID + try: + self.account_id = self.resource_client['iam'].get_user()['User']['Arn'].split(':')[4] + except (ClientError, ValueError, KeyError, IndexError): + self.account_id = '' + + def client(self, resource='batch'): + return self.resource_client[resource] + + +def cc(key): + """ + Changes python key into Camel case equivalent. For example, 'compute_environment_name' becomes + 'computeEnvironmentName'. + + :param key: + :return: + """ + components = key.split('_') + return components[0] + "".join([token.capitalize() for token in components[1:]]) + + +def set_api_params(module, module_params): + """ + Sets module parameters to those expected by the boto3 API. + :param module: + :param module_params: + :return: + """ + api_params = dict((k, v) for k, v in dict(module.params).items() if k in module_params and v is not None) + return snake_dict_to_camel_dict(api_params) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/cloud.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/cloud.py new file mode 100644 index 00000000..8d5eeba3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/cloud.py @@ -0,0 +1,220 @@ +# +# (c) 2016 Allen Sanabria, <asanabria@linuxdynasty.org> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +""" +This module adds shared support for generic cloud modules + +In order to use this module, include it as part of a custom +module as shown below. + +from ansible.module_utils.cloud import CloudRetry + +The 'cloud' module provides the following common classes: + + * CloudRetry + - The base class to be used by other cloud providers, in order to + provide a backoff/retry decorator based on status codes. + + - Example using the AWSRetry class which inherits from CloudRetry. + + @AWSRetry.exponential_backoff(retries=10, delay=3) + get_ec2_security_group_ids_from_names() + + @AWSRetry.jittered_backoff() + get_ec2_security_group_ids_from_names() + +""" +import random +from functools import wraps +import syslog +import time + + +def _exponential_backoff(retries=10, delay=2, backoff=2, max_delay=60): + """ Customizable exponential backoff strategy. + Args: + retries (int): Maximum number of times to retry a request. + delay (float): Initial (base) delay. + backoff (float): base of the exponent to use for exponential + backoff. + max_delay (int): Optional. If provided each delay generated is capped + at this amount. Defaults to 60 seconds. + Returns: + Callable that returns a generator. This generator yields durations in + seconds to be used as delays for an exponential backoff strategy. + Usage: + >>> backoff = _exponential_backoff() + >>> backoff + <function backoff_backoff at 0x7f0d939facf8> + >>> list(backoff()) + [2, 4, 8, 16, 32, 60, 60, 60, 60, 60] + """ + def backoff_gen(): + for retry in range(0, retries): + sleep = delay * backoff ** retry + yield sleep if max_delay is None else min(sleep, max_delay) + return backoff_gen + + +def _full_jitter_backoff(retries=10, delay=3, max_delay=60, _random=random): + """ Implements the "Full Jitter" backoff strategy described here + https://www.awsarchitectureblog.com/2015/03/backoff.html + Args: + retries (int): Maximum number of times to retry a request. + delay (float): Approximate number of seconds to sleep for the first + retry. + max_delay (int): The maximum number of seconds to sleep for any retry. + _random (random.Random or None): Makes this generator testable by + allowing developers to explicitly pass in the a seeded Random. + Returns: + Callable that returns a generator. This generator yields durations in + seconds to be used as delays for a full jitter backoff strategy. + Usage: + >>> backoff = _full_jitter_backoff(retries=5) + >>> backoff + <function backoff_backoff at 0x7f0d939facf8> + >>> list(backoff()) + [3, 6, 5, 23, 38] + >>> list(backoff()) + [2, 1, 6, 6, 31] + """ + def backoff_gen(): + for retry in range(0, retries): + yield _random.randint(0, min(max_delay, delay * 2 ** retry)) + return backoff_gen + + +class CloudRetry(object): + """ CloudRetry can be used by any cloud provider, in order to implement a + backoff algorithm/retry effect based on Status Code from Exceptions. + """ + # This is the base class of the exception. + # AWS Example botocore.exceptions.ClientError + base_class = None + + @staticmethod + def status_code_from_exception(error): + """ Return the status code from the exception object + Args: + error (object): The exception itself. + """ + pass + + @staticmethod + def found(response_code, catch_extra_error_codes=None): + """ Return True if the Response Code to retry on was found. + Args: + response_code (str): This is the Response Code that is being matched against. + """ + pass + + @classmethod + def _backoff(cls, backoff_strategy, catch_extra_error_codes=None): + """ Retry calling the Cloud decorated function using the provided + backoff strategy. + Args: + backoff_strategy (callable): Callable that returns a generator. The + generator should yield sleep times for each retry of the decorated + function. + """ + def deco(f): + @wraps(f) + def retry_func(*args, **kwargs): + for delay in backoff_strategy(): + try: + return f(*args, **kwargs) + except Exception as e: + if isinstance(e, cls.base_class): + response_code = cls.status_code_from_exception(e) + if cls.found(response_code, catch_extra_error_codes): + msg = "{0}: Retrying in {1} seconds...".format(str(e), delay) + syslog.syslog(syslog.LOG_INFO, msg) + time.sleep(delay) + else: + # Return original exception if exception is not a ClientError + raise e + else: + # Return original exception if exception is not a ClientError + raise e + return f(*args, **kwargs) + + return retry_func # true decorator + + return deco + + @classmethod + def exponential_backoff(cls, retries=10, delay=3, backoff=2, max_delay=60, catch_extra_error_codes=None): + """ + Retry calling the Cloud decorated function using an exponential backoff. + + Kwargs: + retries (int): Number of times to retry a failed request before giving up + default=10 + delay (int or float): Initial delay between retries in seconds + default=3 + backoff (int or float): backoff multiplier e.g. value of 2 will + double the delay each retry + default=1.1 + max_delay (int or None): maximum amount of time to wait between retries. + default=60 + """ + return cls._backoff(_exponential_backoff( + retries=retries, delay=delay, backoff=backoff, max_delay=max_delay), catch_extra_error_codes) + + @classmethod + def jittered_backoff(cls, retries=10, delay=3, max_delay=60, catch_extra_error_codes=None): + """ + Retry calling the Cloud decorated function using a jittered backoff + strategy. More on this strategy here: + + https://www.awsarchitectureblog.com/2015/03/backoff.html + + Kwargs: + retries (int): Number of times to retry a failed request before giving up + default=10 + delay (int): Initial delay between retries in seconds + default=3 + max_delay (int): maximum amount of time to wait between retries. + default=60 + """ + return cls._backoff(_full_jitter_backoff( + retries=retries, delay=delay, max_delay=max_delay), catch_extra_error_codes) + + @classmethod + def backoff(cls, tries=10, delay=3, backoff=1.1, catch_extra_error_codes=None): + """ + Retry calling the Cloud decorated function using an exponential backoff. + + Compatibility for the original implementation of CloudRetry.backoff that + did not provide configurable backoff strategies. Developers should use + CloudRetry.exponential_backoff instead. + + Kwargs: + tries (int): Number of times to try (not retry) before giving up + default=10 + delay (int or float): Initial delay between retries in seconds + default=3 + backoff (int or float): backoff multiplier e.g. value of 2 will + double the delay each retry + default=1.1 + """ + return cls.exponential_backoff( + retries=tries - 1, delay=delay, backoff=backoff, max_delay=None, catch_extra_error_codes=catch_extra_error_codes) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/cloudfront_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/cloudfront_facts.py new file mode 100644 index 00000000..994b84da --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/cloudfront_facts.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Willem van Ketwich +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see <http://www.gnu.org/licenses/>. +# +# Author: +# - Willem van Ketwich <willem@vanketwich.com.au> +# +# Common functionality to be used by the modules: +# - cloudfront_distribution +# - cloudfront_invalidation +# - cloudfront_origin_access_identity +""" +Common cloudfront facts shared between modules +""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + import botocore +except ImportError: + pass + +from .ec2 import AWSRetry +from .ec2 import boto3_tag_list_to_ansible_dict + + +class CloudFrontFactsServiceManager(object): + """Handles CloudFront Facts Services""" + + def __init__(self, module): + self.module = module + self.client = module.client('cloudfront', retry_decorator=AWSRetry.jittered_backoff()) + + def get_distribution(self, distribution_id): + try: + return self.client.get_distribution(Id=distribution_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing distribution") + + def get_distribution_config(self, distribution_id): + try: + return self.client.get_distribution_config(Id=distribution_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing distribution configuration") + + def get_origin_access_identity(self, origin_access_identity_id): + try: + return self.client.get_cloud_front_origin_access_identity(Id=origin_access_identity_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing origin access identity") + + def get_origin_access_identity_config(self, origin_access_identity_id): + try: + return self.client.get_cloud_front_origin_access_identity_config(Id=origin_access_identity_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing origin access identity configuration") + + def get_invalidation(self, distribution_id, invalidation_id): + try: + return self.client.get_invalidation(DistributionId=distribution_id, Id=invalidation_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing invalidation") + + def get_streaming_distribution(self, distribution_id): + try: + return self.client.get_streaming_distribution(Id=distribution_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing streaming distribution") + + def get_streaming_distribution_config(self, distribution_id): + try: + return self.client.get_streaming_distribution_config(Id=distribution_id, aws_retry=True) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error describing streaming distribution") + + def list_origin_access_identities(self): + try: + paginator = self.client.get_paginator('list_cloud_front_origin_access_identities') + result = paginator.paginate().build_full_result().get('CloudFrontOriginAccessIdentityList', {}) + return result.get('Items', []) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error listing cloud front origin access identities") + + def list_distributions(self, keyed=True): + try: + paginator = self.client.get_paginator('list_distributions') + result = paginator.paginate().build_full_result().get('DistributionList', {}) + distribution_list = result.get('Items', []) + if not keyed: + return distribution_list + return self.keyed_list_helper(distribution_list) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error listing distributions") + + def list_distributions_by_web_acl_id(self, web_acl_id): + try: + result = self.client.list_distributions_by_web_acl_id(WebAclId=web_acl_id, aws_retry=True) + distribution_list = result.get('DistributionList', {}).get('Items', []) + return self.keyed_list_helper(distribution_list) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error listing distributions by web acl id") + + def list_invalidations(self, distribution_id): + try: + paginator = self.client.get_paginator('list_invalidations') + result = paginator.paginate(DistributionId=distribution_id).build_full_result() + return result.get('InvalidationList', {}).get('Items', []) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error listing invalidations") + + def list_streaming_distributions(self, keyed=True): + try: + paginator = self.client.get_paginator('list_streaming_distributions') + result = paginator.paginate().build_full_result() + streaming_distribution_list = result.get('StreamingDistributionList', {}).get('Items', []) + if not keyed: + return streaming_distribution_list + return self.keyed_list_helper(streaming_distribution_list) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error listing streaming distributions") + + def summary(self): + summary_dict = {} + summary_dict.update(self.summary_get_distribution_list(False)) + summary_dict.update(self.summary_get_distribution_list(True)) + summary_dict.update(self.summary_get_origin_access_identity_list()) + return summary_dict + + def summary_get_origin_access_identity_list(self): + try: + origin_access_identity_list = {'origin_access_identities': []} + origin_access_identities = self.list_origin_access_identities() + for origin_access_identity in origin_access_identities: + oai_id = origin_access_identity['Id'] + oai_full_response = self.get_origin_access_identity(oai_id) + oai_summary = {'Id': oai_id, 'ETag': oai_full_response['ETag']} + origin_access_identity_list['origin_access_identities'].append(oai_summary) + return origin_access_identity_list + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error generating summary of origin access identities") + + def summary_get_distribution_list(self, streaming=False): + try: + list_name = 'streaming_distributions' if streaming else 'distributions' + key_list = ['Id', 'ARN', 'Status', 'LastModifiedTime', 'DomainName', 'Comment', 'PriceClass', 'Enabled'] + distribution_list = {list_name: []} + distributions = self.list_streaming_distributions(False) if streaming else self.list_distributions(False) + for dist in distributions: + temp_distribution = {} + for key_name in key_list: + temp_distribution[key_name] = dist[key_name] + temp_distribution['Aliases'] = [alias for alias in dist['Aliases'].get('Items', [])] + temp_distribution['ETag'] = self.get_etag_from_distribution_id(dist['Id'], streaming) + if not streaming: + temp_distribution['WebACLId'] = dist['WebACLId'] + invalidation_ids = self.get_list_of_invalidation_ids_from_distribution_id(dist['Id']) + if invalidation_ids: + temp_distribution['Invalidations'] = invalidation_ids + resource_tags = self.client.list_tags_for_resource(Resource=dist['ARN'], aws_retry=True) + temp_distribution['Tags'] = boto3_tag_list_to_ansible_dict(resource_tags['Tags'].get('Items', [])) + distribution_list[list_name].append(temp_distribution) + return distribution_list + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error generating summary of distributions") + except Exception as e: + self.module.fail_json_aws(e, msg="Error generating summary of distributions") + + def get_etag_from_distribution_id(self, distribution_id, streaming): + distribution = {} + if not streaming: + distribution = self.get_distribution(distribution_id) + else: + distribution = self.get_streaming_distribution(distribution_id) + return distribution['ETag'] + + def get_list_of_invalidation_ids_from_distribution_id(self, distribution_id): + try: + invalidation_ids = [] + invalidations = self.list_invalidations(distribution_id) + for invalidation in invalidations: + invalidation_ids.append(invalidation['Id']) + return invalidation_ids + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error getting list of invalidation ids") + + def get_distribution_id_from_domain_name(self, domain_name): + try: + distribution_id = "" + distributions = self.list_distributions(False) + distributions += self.list_streaming_distributions(False) + for dist in distributions: + if 'Items' in dist['Aliases']: + for alias in dist['Aliases']['Items']: + if str(alias).lower() == domain_name.lower(): + distribution_id = dist['Id'] + break + return distribution_id + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error getting distribution id from domain name") + + def get_aliases_from_distribution_id(self, distribution_id): + try: + distribution = self.get_distribution(distribution_id) + return distribution['DistributionConfig']['Aliases'].get('Items', []) + except botocore.exceptions.ClientError as e: + self.module.fail_json_aws(e, msg="Error getting list of aliases from distribution_id") + + def keyed_list_helper(self, list_to_key): + keyed_list = dict() + for item in list_to_key: + distribution_id = item['Id'] + if 'Items' in item['Aliases']: + aliases = item['Aliases']['Items'] + for alias in aliases: + keyed_list.update({alias: item}) + keyed_list.update({distribution_id: item}) + return keyed_list diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/core.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/core.py new file mode 100644 index 00000000..349ec3b4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/core.py @@ -0,0 +1,381 @@ +# +# Copyright 2017 Michael De La Rue | Ansible +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +"""This module adds shared support for generic Amazon AWS modules + +In order to use this module, include it as part of a custom +module as shown below. + + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule + module = AnsibleAWSModule(argument_spec=dictionary, supports_check_mode=boolean + mutually_exclusive=list1, required_together=list2) + +The 'AnsibleAWSModule' module provides similar, but more restricted, +interfaces to the normal Ansible module. It also includes the +additional methods for connecting to AWS using the standard module arguments + + m.resource('lambda') # - get an AWS connection as a boto3 resource. + +or + + m.client('sts') # - get an AWS connection as a boto3 client. + +To make use of AWSRetry easier, it can now be wrapped around any call from a +module-created client. To add retries to a client, create a client: + + m.client('ec2', retry_decorator=AWSRetry.jittered_backoff(retries=10)) + +Any calls from that client can be made to use the decorator passed at call-time +using the `aws_retry` argument. By default, no retries are used. + + ec2 = m.client('ec2', retry_decorator=AWSRetry.jittered_backoff(retries=10)) + ec2.describe_instances(InstanceIds=['i-123456789'], aws_retry=True) + +The call will be retried the specified number of times, so the calling functions +don't need to be wrapped in the backoff decorator. +""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re +import logging +import traceback +from functools import wraps +from distutils.version import LooseVersion + +try: + from cStringIO import StringIO +except ImportError: + # Python 3 + from io import StringIO + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils._text import to_native + +from .ec2 import HAS_BOTO3 +from .ec2 import boto3_conn +from .ec2 import ec2_argument_spec +from .ec2 import get_aws_connection_info +from .ec2 import get_aws_region + +# We will also export HAS_BOTO3 so end user modules can use it. +__all__ = ('AnsibleAWSModule', 'HAS_BOTO3', 'is_boto3_error_code', 'is_boto3_error_message') + + +class AnsibleAWSModule(object): + """An ansible module class for AWS modules + + AnsibleAWSModule provides an a class for building modules which + connect to Amazon Web Services. The interface is currently more + restricted than the basic module class with the aim that later the + basic module class can be reduced. If you find that any key + feature is missing please contact the author/Ansible AWS team + (available on #ansible-aws on IRC) to request the additional + features needed. + """ + default_settings = { + "default_args": True, + "check_boto3": True, + "auto_retry": True, + "module_class": AnsibleModule + } + + def __init__(self, **kwargs): + local_settings = {} + for key in AnsibleAWSModule.default_settings: + try: + local_settings[key] = kwargs.pop(key) + except KeyError: + local_settings[key] = AnsibleAWSModule.default_settings[key] + self.settings = local_settings + + if local_settings["default_args"]: + # ec2_argument_spec contains the region so we use that; there's a patch coming which + # will add it to aws_argument_spec so if that's accepted then later we should change + # over + argument_spec_full = ec2_argument_spec() + try: + argument_spec_full.update(kwargs["argument_spec"]) + except (TypeError, NameError): + pass + kwargs["argument_spec"] = argument_spec_full + + self._module = AnsibleAWSModule.default_settings["module_class"](**kwargs) + + if local_settings["check_boto3"] and not HAS_BOTO3: + self._module.fail_json( + msg=missing_required_lib('botocore or boto3')) + + self.check_mode = self._module.check_mode + self._diff = self._module._diff + self._name = self._module._name + + self._botocore_endpoint_log_stream = StringIO() + self.logger = None + if self.params.get('debug_botocore_endpoint_logs'): + self.logger = logging.getLogger('botocore.endpoint') + self.logger.setLevel(logging.DEBUG) + self.logger.addHandler(logging.StreamHandler(self._botocore_endpoint_log_stream)) + + @property + def params(self): + return self._module.params + + def _get_resource_action_list(self): + actions = [] + for ln in self._botocore_endpoint_log_stream.getvalue().split('\n'): + ln = ln.strip() + if not ln: + continue + found_operational_request = re.search(r"OperationModel\(name=.*?\)", ln) + if found_operational_request: + operation_request = found_operational_request.group(0)[20:-1] + resource = re.search(r"https://.*?\.", ln).group(0)[8:-1] + actions.append("{0}:{1}".format(resource, operation_request)) + return list(set(actions)) + + def exit_json(self, *args, **kwargs): + if self.params.get('debug_botocore_endpoint_logs'): + kwargs['resource_actions'] = self._get_resource_action_list() + return self._module.exit_json(*args, **kwargs) + + def fail_json(self, *args, **kwargs): + if self.params.get('debug_botocore_endpoint_logs'): + kwargs['resource_actions'] = self._get_resource_action_list() + return self._module.fail_json(*args, **kwargs) + + def debug(self, *args, **kwargs): + return self._module.debug(*args, **kwargs) + + def warn(self, *args, **kwargs): + return self._module.warn(*args, **kwargs) + + def deprecate(self, *args, **kwargs): + return self._module.deprecate(*args, **kwargs) + + def boolean(self, *args, **kwargs): + return self._module.boolean(*args, **kwargs) + + def md5(self, *args, **kwargs): + return self._module.md5(*args, **kwargs) + + def client(self, service, retry_decorator=None): + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True) + conn = boto3_conn(self, conn_type='client', resource=service, + region=region, endpoint=ec2_url, **aws_connect_kwargs) + return conn if retry_decorator is None else _RetryingBotoClientWrapper(conn, retry_decorator) + + def resource(self, service): + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True) + return boto3_conn(self, conn_type='resource', resource=service, + region=region, endpoint=ec2_url, **aws_connect_kwargs) + + @property + def region(self, boto3=True): + return get_aws_region(self, boto3) + + def fail_json_aws(self, exception, msg=None, **kwargs): + """call fail_json with processed exception + + function for converting exceptions thrown by AWS SDK modules, + botocore, boto3 and boto, into nice error messages. + """ + last_traceback = traceback.format_exc() + + # to_native is trusted to handle exceptions that str() could + # convert to text. + try: + except_msg = to_native(exception.message) + except AttributeError: + except_msg = to_native(exception) + + if msg is not None: + message = '{0}: {1}'.format(msg, except_msg) + else: + message = except_msg + + try: + response = exception.response + except AttributeError: + response = None + + failure = dict( + msg=message, + exception=last_traceback, + **self._gather_versions() + ) + + failure.update(kwargs) + + if response is not None: + failure.update(**camel_dict_to_snake_dict(response)) + + self.fail_json(**failure) + + def _gather_versions(self): + """Gather AWS SDK (boto3 and botocore) dependency versions + + Returns {'boto3_version': str, 'botocore_version': str} + Returns {} if neither are installed + """ + if not HAS_BOTO3: + return {} + import boto3 + import botocore + return dict(boto3_version=boto3.__version__, + botocore_version=botocore.__version__) + + def boto3_at_least(self, desired): + """Check if the available boto3 version is greater than or equal to a desired version. + + Usage: + if module.params.get('assign_ipv6_address') and not module.boto3_at_least('1.4.4'): + # conditionally fail on old boto3 versions if a specific feature is not supported + module.fail_json(msg="Boto3 can't deal with EC2 IPv6 addresses before version 1.4.4.") + """ + existing = self._gather_versions() + return LooseVersion(existing['boto3_version']) >= LooseVersion(desired) + + def botocore_at_least(self, desired): + """Check if the available botocore version is greater than or equal to a desired version. + + Usage: + if not module.botocore_at_least('1.2.3'): + module.fail_json(msg='The Serverless Elastic Load Compute Service is not in botocore before v1.2.3') + if not module.botocore_at_least('1.5.3'): + module.warn('Botocore did not include waiters for Service X before 1.5.3. ' + 'To wait until Service X resources are fully available, update botocore.') + """ + existing = self._gather_versions() + return LooseVersion(existing['botocore_version']) >= LooseVersion(desired) + + +class _RetryingBotoClientWrapper(object): + __never_wait = ( + 'get_paginator', 'can_paginate', + 'get_waiter', 'generate_presigned_url', + ) + + def __init__(self, client, retry): + self.client = client + self.retry = retry + + def _create_optional_retry_wrapper_function(self, unwrapped): + retrying_wrapper = self.retry(unwrapped) + + @wraps(unwrapped) + def deciding_wrapper(aws_retry=False, *args, **kwargs): + if aws_retry: + return retrying_wrapper(*args, **kwargs) + else: + return unwrapped(*args, **kwargs) + return deciding_wrapper + + def __getattr__(self, name): + unwrapped = getattr(self.client, name) + if name in self.__never_wait: + return unwrapped + elif callable(unwrapped): + wrapped = self._create_optional_retry_wrapper_function(unwrapped) + setattr(self, name, wrapped) + return wrapped + else: + return unwrapped + + +def is_boto3_error_code(code, e=None): + """Check if the botocore exception is raised by a specific error code. + + Returns ClientError if the error code matches, a dummy exception if it does not have an error code or does not match + + Example: + try: + ec2.describe_instances(InstanceIds=['potato']) + except is_boto3_error_code('InvalidInstanceID.Malformed'): + # handle the error for that code case + except botocore.exceptions.ClientError as e: + # handle the generic error case for all other codes + """ + from botocore.exceptions import ClientError + if e is None: + import sys + dummy, e, dummy = sys.exc_info() + if not isinstance(code, list): + code = [code] + if isinstance(e, ClientError) and e.response['Error']['Code'] in code: + return ClientError + return type('NeverEverRaisedException', (Exception,), {}) + + +def is_boto3_error_message(msg, e=None): + """Check if the botocore exception contains a specific error message. + + Returns ClientError if the error code matches, a dummy exception if it does not have an error code or does not match + + Example: + try: + ec2.describe_vpc_classic_link(VpcIds=[vpc_id]) + except is_boto3_error_message('The functionality you requested is not available in this region.'): + # handle the error for that error message + except botocore.exceptions.ClientError as e: + # handle the generic error case for all other codes + """ + from botocore.exceptions import ClientError + if e is None: + import sys + dummy, e, dummy = sys.exc_info() + if isinstance(e, ClientError) and msg in e.response['Error']['Message']: + return ClientError + return type('NeverEverRaisedException', (Exception,), {}) + + +def get_boto3_client_method_parameters(client, method_name, required=False): + op = client.meta.method_to_api_mapping.get(method_name) + input_shape = client._service_model.operation_model(op).input_shape + if not input_shape: + parameters = [] + elif required: + parameters = list(input_shape.required_members) + else: + parameters = list(input_shape.members.keys()) + return parameters + + +def scrub_none_parameters(parameters): + """ + Iterate over a dictionary removing any keys that have a None value + + Reference: https://github.com/ansible-collections/community.aws/issues/251 + Credit: https://medium.com/better-programming/how-to-remove-null-none-values-from-a-dictionary-in-python-1bedf1aab5e4 + + :param parameters: parameter dict + :return: parameter dict with all keys = None removed + """ + + clean_parameters = {} + + for k, v in parameters.items(): + if isinstance(v, dict): + clean_parameters[k] = scrub_none_parameters(v) + elif v is not None: + clean_parameters[k] = v + + return clean_parameters diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/direct_connect.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/direct_connect.py new file mode 100644 index 00000000..abcbcfd2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/direct_connect.py @@ -0,0 +1,89 @@ +# Copyright (c) 2017 Ansible Project +# +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +This module adds shared support for Direct Connect modules. +""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import traceback + +try: + import botocore +except ImportError: + pass + +from .ec2 import AWSRetry + + +class DirectConnectError(Exception): + def __init__(self, msg, last_traceback=None, exception=None): + self.msg = msg + self.last_traceback = last_traceback + self.exception = exception + + +def delete_connection(client, connection_id): + try: + AWSRetry.jittered_backoff()(client.delete_connection)(connectionId=connection_id) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Failed to delete DirectConnection {0}.".format(connection_id), + last_traceback=traceback.format_exc(), + exception=e) + + +def associate_connection_and_lag(client, connection_id, lag_id): + try: + AWSRetry.jittered_backoff()(client.associate_connection_with_lag)(connectionId=connection_id, + lagId=lag_id) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Failed to associate Direct Connect connection {0}" + " with link aggregation group {1}.".format(connection_id, lag_id), + last_traceback=traceback.format_exc(), + exception=e) + + +def disassociate_connection_and_lag(client, connection_id, lag_id): + try: + AWSRetry.jittered_backoff()(client.disassociate_connection_from_lag)(connectionId=connection_id, + lagId=lag_id) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Failed to disassociate Direct Connect connection {0}" + " from link aggregation group {1}.".format(connection_id, lag_id), + last_traceback=traceback.format_exc(), + exception=e) + + +def delete_virtual_interface(client, virtual_interface): + try: + AWSRetry.jittered_backoff()(client.delete_virtual_interface)(virtualInterfaceId=virtual_interface) + except botocore.exceptions.ClientError as e: + raise DirectConnectError(msg="Could not delete virtual interface {0}".format(virtual_interface), + last_traceback=traceback.format_exc(), + exception=e) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/ec2.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/ec2.py new file mode 100644 index 00000000..e2278992 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/ec2.py @@ -0,0 +1,807 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013 +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import re +import sys +import traceback + +from ansible.module_utils._text import to_native +from ansible.module_utils._text import to_text +from ansible.module_utils.ansible_release import __version__ +from ansible.module_utils.basic import env_fallback +from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six import binary_type +from ansible.module_utils.six import string_types +from ansible.module_utils.six import text_type +from ansible.module_utils.six import integer_types +# Used to live here, moved into ansible.module_utils.common.dict_transformations +from ansible.module_utils.common.dict_transformations import _camel_to_snake # pylint: disable=unused-import +from ansible.module_utils.common.dict_transformations import _snake_to_camel # pylint: disable=unused-import +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict # pylint: disable=unused-import +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict # pylint: disable=unused-import + +from .cloud import CloudRetry + +BOTO_IMP_ERR = None +try: + import boto + import boto.ec2 # boto does weird import stuff + HAS_BOTO = True +except ImportError: + BOTO_IMP_ERR = traceback.format_exc() + HAS_BOTO = False + +BOTO3_IMP_ERR = None +try: + import boto3 + import botocore + HAS_BOTO3 = True +except ImportError: + BOTO3_IMP_ERR = traceback.format_exc() + HAS_BOTO3 = False + +try: + # Although this is to allow Python 3 the ability to use the custom comparison as a key, Python 2.7 also + # uses this (and it works as expected). Python 2.6 will trigger the ImportError. + from functools import cmp_to_key + PY3_COMPARISON = True +except ImportError: + PY3_COMPARISON = False + + +class AnsibleAWSError(Exception): + pass + + +def _botocore_exception_maybe(): + """ + Allow for boto3 not being installed when using these utils by wrapping + botocore.exceptions instead of assigning from it directly. + """ + if HAS_BOTO3: + return botocore.exceptions.ClientError + return type(None) + + +class AWSRetry(CloudRetry): + base_class = _botocore_exception_maybe() + + @staticmethod + def status_code_from_exception(error): + return error.response['Error']['Code'] + + @staticmethod + def found(response_code, catch_extra_error_codes=None): + # This list of failures is based on this API Reference + # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html + # + # TooManyRequestsException comes from inside botocore when it + # does retrys, unfortunately however it does not try long + # enough to allow some services such as API Gateway to + # complete configuration. At the moment of writing there is a + # botocore/boto3 bug open to fix this. + # + # https://github.com/boto/boto3/issues/876 (and linked PRs etc) + retry_on = [ + 'RequestLimitExceeded', 'Unavailable', 'ServiceUnavailable', + 'InternalFailure', 'InternalError', 'TooManyRequestsException', + 'Throttling' + ] + if catch_extra_error_codes: + retry_on.extend(catch_extra_error_codes) + + return response_code in retry_on + + +def boto3_conn(module, conn_type=None, resource=None, region=None, endpoint=None, **params): + try: + return _boto3_conn(conn_type=conn_type, resource=resource, region=region, endpoint=endpoint, **params) + except ValueError as e: + module.fail_json(msg="Couldn't connect to AWS: %s" % to_native(e)) + except (botocore.exceptions.ProfileNotFound, botocore.exceptions.PartialCredentialsError, + botocore.exceptions.NoCredentialsError, botocore.exceptions.ConfigParseError) as e: + module.fail_json(msg=to_native(e)) + except botocore.exceptions.NoRegionError as e: + module.fail_json(msg="The %s module requires a region and none was found in configuration, " + "environment variables or module parameters" % module._name) + + +def _boto3_conn(conn_type=None, resource=None, region=None, endpoint=None, **params): + profile = params.pop('profile_name', None) + + if conn_type not in ['both', 'resource', 'client']: + raise ValueError('There is an issue in the calling code. You ' + 'must specify either both, resource, or client to ' + 'the conn_type parameter in the boto3_conn function ' + 'call') + + config = botocore.config.Config( + user_agent_extra='Ansible/{0}'.format(__version__), + ) + + if params.get('config') is not None: + config = config.merge(params.pop('config')) + if params.get('aws_config') is not None: + config = config.merge(params.pop('aws_config')) + + session = boto3.session.Session( + profile_name=profile, + ) + + if conn_type == 'resource': + return session.resource(resource, config=config, region_name=region, endpoint_url=endpoint, **params) + elif conn_type == 'client': + return session.client(resource, config=config, region_name=region, endpoint_url=endpoint, **params) + else: + client = session.client(resource, region_name=region, endpoint_url=endpoint, **params) + resource = session.resource(resource, region_name=region, endpoint_url=endpoint, **params) + return client, resource + + +boto3_inventory_conn = _boto3_conn + + +def boto_exception(err): + """ + Extracts the error message from a boto exception. + + :param err: Exception from boto + :return: Error message + """ + if hasattr(err, 'error_message'): + error = err.error_message + elif hasattr(err, 'message'): + error = str(err.message) + ' ' + str(err) + ' - ' + str(type(err)) + else: + error = '%s: %s' % (Exception, err) + + return error + + +def aws_common_argument_spec(): + return dict( + debug_botocore_endpoint_logs=dict(fallback=(env_fallback, ['ANSIBLE_DEBUG_BOTOCORE_LOGS']), default=False, type='bool'), + ec2_url=dict(aliases=['aws_endpoint_url', 'endpoint_url']), + aws_access_key=dict(aliases=['ec2_access_key', 'access_key']), + aws_secret_key=dict(aliases=['ec2_secret_key', 'secret_key'], no_log=True), + security_token=dict(aliases=['access_token', 'aws_security_token'], no_log=True), + validate_certs=dict(default=True, type='bool'), + aws_ca_bundle=dict(type='path'), + profile=dict(aliases=['aws_profile']), + aws_config=dict(type='dict'), + ) + + +def ec2_argument_spec(): + spec = aws_common_argument_spec() + spec.update( + dict( + region=dict(aliases=['aws_region', 'ec2_region']), + ) + ) + return spec + + +def get_aws_region(module, boto3=False): + region = module.params.get('region') + + if region: + return region + + if 'AWS_REGION' in os.environ: + return os.environ['AWS_REGION'] + if 'AWS_DEFAULT_REGION' in os.environ: + return os.environ['AWS_DEFAULT_REGION'] + if 'EC2_REGION' in os.environ: + return os.environ['EC2_REGION'] + + if not boto3: + if not HAS_BOTO: + module.fail_json(msg=missing_required_lib('boto'), exception=BOTO_IMP_ERR) + # boto.config.get returns None if config not found + region = boto.config.get('Boto', 'aws_region') + if region: + return region + return boto.config.get('Boto', 'ec2_region') + + if not HAS_BOTO3: + module.fail_json(msg=missing_required_lib('boto3'), exception=BOTO3_IMP_ERR) + + # here we don't need to make an additional call, will default to 'us-east-1' if the below evaluates to None. + try: + profile_name = module.params.get('profile') + return botocore.session.Session(profile=profile_name).get_config_variable('region') + except botocore.exceptions.ProfileNotFound as e: + return None + + +def get_aws_connection_info(module, boto3=False): + + # Check module args for credentials, then check environment vars + # access_key + + ec2_url = module.params.get('ec2_url') + access_key = module.params.get('aws_access_key') + secret_key = module.params.get('aws_secret_key') + security_token = module.params.get('security_token') + region = get_aws_region(module, boto3) + profile_name = module.params.get('profile') + validate_certs = module.params.get('validate_certs') + ca_bundle = module.params.get('aws_ca_bundle') + config = module.params.get('aws_config') + + # Only read the profile environment variables if we've *not* been passed + # any credentials as parameters. + if not profile_name and not access_key and not secret_key: + if os.environ.get('AWS_PROFILE'): + profile_name = os.environ.get('AWS_PROFILE') + if os.environ.get('AWS_DEFAULT_PROFILE'): + profile_name = os.environ.get('AWS_DEFAULT_PROFILE') + + if profile_name and (access_key or secret_key or security_token): + module.deprecate("Passing both a profile and access tokens has been deprecated." + " Only the profile will be used." + " In later versions of Ansible the options will be mutually exclusive", + date='2022-06-01', collection_name='amazon.aws') + + if not ec2_url: + if 'AWS_URL' in os.environ: + ec2_url = os.environ['AWS_URL'] + elif 'EC2_URL' in os.environ: + ec2_url = os.environ['EC2_URL'] + + if not access_key: + if os.environ.get('AWS_ACCESS_KEY_ID'): + access_key = os.environ['AWS_ACCESS_KEY_ID'] + elif os.environ.get('AWS_ACCESS_KEY'): + access_key = os.environ['AWS_ACCESS_KEY'] + elif os.environ.get('EC2_ACCESS_KEY'): + access_key = os.environ['EC2_ACCESS_KEY'] + elif HAS_BOTO and boto.config.get('Credentials', 'aws_access_key_id'): + access_key = boto.config.get('Credentials', 'aws_access_key_id') + elif HAS_BOTO and boto.config.get('default', 'aws_access_key_id'): + access_key = boto.config.get('default', 'aws_access_key_id') + else: + # in case access_key came in as empty string + access_key = None + + if not secret_key: + if os.environ.get('AWS_SECRET_ACCESS_KEY'): + secret_key = os.environ['AWS_SECRET_ACCESS_KEY'] + elif os.environ.get('AWS_SECRET_KEY'): + secret_key = os.environ['AWS_SECRET_KEY'] + elif os.environ.get('EC2_SECRET_KEY'): + secret_key = os.environ['EC2_SECRET_KEY'] + elif HAS_BOTO and boto.config.get('Credentials', 'aws_secret_access_key'): + secret_key = boto.config.get('Credentials', 'aws_secret_access_key') + elif HAS_BOTO and boto.config.get('default', 'aws_secret_access_key'): + secret_key = boto.config.get('default', 'aws_secret_access_key') + else: + # in case secret_key came in as empty string + secret_key = None + + if not security_token: + if os.environ.get('AWS_SECURITY_TOKEN'): + security_token = os.environ['AWS_SECURITY_TOKEN'] + elif os.environ.get('AWS_SESSION_TOKEN'): + security_token = os.environ['AWS_SESSION_TOKEN'] + elif os.environ.get('EC2_SECURITY_TOKEN'): + security_token = os.environ['EC2_SECURITY_TOKEN'] + elif HAS_BOTO and boto.config.get('Credentials', 'aws_security_token'): + security_token = boto.config.get('Credentials', 'aws_security_token') + elif HAS_BOTO and boto.config.get('default', 'aws_security_token'): + security_token = boto.config.get('default', 'aws_security_token') + else: + # in case secret_token came in as empty string + security_token = None + + if not ca_bundle: + if os.environ.get('AWS_CA_BUNDLE'): + ca_bundle = os.environ.get('AWS_CA_BUNDLE') + + if HAS_BOTO3 and boto3: + boto_params = dict(aws_access_key_id=access_key, + aws_secret_access_key=secret_key, + aws_session_token=security_token) + + if profile_name: + boto_params = dict(aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None) + boto_params['profile_name'] = profile_name + + if validate_certs and ca_bundle: + boto_params['verify'] = ca_bundle + else: + boto_params['verify'] = validate_certs + + else: + boto_params = dict(aws_access_key_id=access_key, + aws_secret_access_key=secret_key, + security_token=security_token) + + # only set profile_name if passed as an argument + if profile_name: + boto_params['profile_name'] = profile_name + + boto_params['validate_certs'] = validate_certs + + if config is not None: + if HAS_BOTO3 and boto3: + boto_params['aws_config'] = botocore.config.Config(**config) + elif HAS_BOTO and not boto3: + if 'user_agent' in config: + sys.modules["boto.connection"].UserAgent = config['user_agent'] + + for param, value in boto_params.items(): + if isinstance(value, binary_type): + boto_params[param] = text_type(value, 'utf-8', 'strict') + + return region, ec2_url, boto_params + + +def get_ec2_creds(module): + ''' for compatibility mode with old modules that don't/can't yet + use ec2_connect method ''' + region, ec2_url, boto_params = get_aws_connection_info(module) + return ec2_url, boto_params['aws_access_key_id'], boto_params['aws_secret_access_key'], region + + +def boto_fix_security_token_in_profile(conn, profile_name): + ''' monkey patch for boto issue boto/boto#2100 ''' + profile = 'profile ' + profile_name + if boto.config.has_option(profile, 'aws_security_token'): + conn.provider.set_security_token(boto.config.get(profile, 'aws_security_token')) + return conn + + +def connect_to_aws(aws_module, region, **params): + try: + conn = aws_module.connect_to_region(region, **params) + except(boto.provider.ProfileNotFoundError): + raise AnsibleAWSError("Profile given for AWS was not found. Please fix and retry.") + if not conn: + if region not in [aws_module_region.name for aws_module_region in aws_module.regions()]: + raise AnsibleAWSError("Region %s does not seem to be available for aws module %s. If the region definitely exists, you may need to upgrade " + "boto or extend with endpoints_path" % (region, aws_module.__name__)) + else: + raise AnsibleAWSError("Unknown problem connecting to region %s for aws module %s." % (region, aws_module.__name__)) + if params.get('profile_name'): + conn = boto_fix_security_token_in_profile(conn, params['profile_name']) + return conn + + +def ec2_connect(module): + + """ Return an ec2 connection""" + + region, ec2_url, boto_params = get_aws_connection_info(module) + + # If ec2_url is present use it + if ec2_url: + try: + ec2 = boto.connect_ec2_endpoint(ec2_url, **boto_params) + except (boto.exception.NoAuthHandlerFound, AnsibleAWSError, boto.provider.ProfileNotFoundError) as e: + module.fail_json(msg=str(e)) + # Otherwise, if we have a region specified, connect to its endpoint. + elif region: + try: + ec2 = connect_to_aws(boto.ec2, region, **boto_params) + except (boto.exception.NoAuthHandlerFound, AnsibleAWSError, boto.provider.ProfileNotFoundError) as e: + module.fail_json(msg=str(e)) + else: + module.fail_json(msg="Either region or ec2_url must be specified") + + return ec2 + + +def ansible_dict_to_boto3_filter_list(filters_dict): + + """ Convert an Ansible dict of filters to list of dicts that boto3 can use + Args: + filters_dict (dict): Dict of AWS filters. + Basic Usage: + >>> filters = {'some-aws-id': 'i-01234567'} + >>> ansible_dict_to_boto3_filter_list(filters) + { + 'some-aws-id': 'i-01234567' + } + Returns: + List: List of AWS filters and their values + [ + { + 'Name': 'some-aws-id', + 'Values': [ + 'i-01234567', + ] + } + ] + """ + + filters_list = [] + for k, v in filters_dict.items(): + filter_dict = {'Name': k} + if isinstance(v, bool): + filter_dict['Values'] = [str(v).lower()] + elif isinstance(v, integer_types): + filter_dict['Values'] = [str(v)] + elif isinstance(v, string_types): + filter_dict['Values'] = [v] + else: + filter_dict['Values'] = v + + filters_list.append(filter_dict) + + return filters_list + + +def boto3_tag_list_to_ansible_dict(tags_list, tag_name_key_name=None, tag_value_key_name=None): + + """ Convert a boto3 list of resource tags to a flat dict of key:value pairs + Args: + tags_list (list): List of dicts representing AWS tags. + tag_name_key_name (str): Value to use as the key for all tag keys (useful because boto3 doesn't always use "Key") + tag_value_key_name (str): Value to use as the key for all tag values (useful because boto3 doesn't always use "Value") + Basic Usage: + >>> tags_list = [{'Key': 'MyTagKey', 'Value': 'MyTagValue'}] + >>> boto3_tag_list_to_ansible_dict(tags_list) + [ + { + 'Key': 'MyTagKey', + 'Value': 'MyTagValue' + } + ] + Returns: + Dict: Dict of key:value pairs representing AWS tags + { + 'MyTagKey': 'MyTagValue', + } + """ + + if tag_name_key_name and tag_value_key_name: + tag_candidates = {tag_name_key_name: tag_value_key_name} + else: + tag_candidates = {'key': 'value', 'Key': 'Value'} + + # minio seems to return [{}] as an empty tags_list + if not tags_list or not any(tag for tag in tags_list): + return {} + for k, v in tag_candidates.items(): + if k in tags_list[0] and v in tags_list[0]: + return dict((tag[k], tag[v]) for tag in tags_list) + raise ValueError("Couldn't find tag key (candidates %s) in tag list %s" % (str(tag_candidates), str(tags_list))) + + +def ansible_dict_to_boto3_tag_list(tags_dict, tag_name_key_name='Key', tag_value_key_name='Value'): + + """ Convert a flat dict of key:value pairs representing AWS resource tags to a boto3 list of dicts + Args: + tags_dict (dict): Dict representing AWS resource tags. + tag_name_key_name (str): Value to use as the key for all tag keys (useful because boto3 doesn't always use "Key") + tag_value_key_name (str): Value to use as the key for all tag values (useful because boto3 doesn't always use "Value") + Basic Usage: + >>> tags_dict = {'MyTagKey': 'MyTagValue'} + >>> ansible_dict_to_boto3_tag_list(tags_dict) + { + 'MyTagKey': 'MyTagValue' + } + Returns: + List: List of dicts containing tag keys and values + [ + { + 'Key': 'MyTagKey', + 'Value': 'MyTagValue' + } + ] + """ + + tags_list = [] + for k, v in tags_dict.items(): + tags_list.append({tag_name_key_name: k, tag_value_key_name: to_native(v)}) + + return tags_list + + +def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id=None, boto3=True): + + """ Return list of security group IDs from security group names. Note that security group names are not unique + across VPCs. If a name exists across multiple VPCs and no VPC ID is supplied, all matching IDs will be returned. This + will probably lead to a boto exception if you attempt to assign both IDs to a resource so ensure you wrap the call in + a try block + """ + + def get_sg_name(sg, boto3): + + if boto3: + return sg['GroupName'] + else: + return sg.name + + def get_sg_id(sg, boto3): + + if boto3: + return sg['GroupId'] + else: + return sg.id + + sec_group_id_list = [] + + if isinstance(sec_group_list, string_types): + sec_group_list = [sec_group_list] + + # Get all security groups + if boto3: + if vpc_id: + filters = [ + { + 'Name': 'vpc-id', + 'Values': [ + vpc_id, + ] + } + ] + all_sec_groups = ec2_connection.describe_security_groups(Filters=filters)['SecurityGroups'] + else: + all_sec_groups = ec2_connection.describe_security_groups()['SecurityGroups'] + else: + if vpc_id: + filters = {'vpc-id': vpc_id} + all_sec_groups = ec2_connection.get_all_security_groups(filters=filters) + else: + all_sec_groups = ec2_connection.get_all_security_groups() + + unmatched = set(sec_group_list).difference(str(get_sg_name(all_sg, boto3)) for all_sg in all_sec_groups) + sec_group_name_list = list(set(sec_group_list) - set(unmatched)) + + if len(unmatched) > 0: + # If we have unmatched names that look like an ID, assume they are + sec_group_id_list[:] = [sg for sg in unmatched if re.match('sg-[a-fA-F0-9]+$', sg)] + still_unmatched = [sg for sg in unmatched if not re.match('sg-[a-fA-F0-9]+$', sg)] + if len(still_unmatched) > 0: + raise ValueError("The following group names are not valid: %s" % ', '.join(still_unmatched)) + + sec_group_id_list += [str(get_sg_id(all_sg, boto3)) for all_sg in all_sec_groups if str(get_sg_name(all_sg, boto3)) in sec_group_name_list] + + return sec_group_id_list + + +def _hashable_policy(policy, policy_list): + """ + Takes a policy and returns a list, the contents of which are all hashable and sorted. + Example input policy: + {'Version': '2012-10-17', + 'Statement': [{'Action': 's3:PutObjectAcl', + 'Sid': 'AddCannedAcl2', + 'Resource': 'arn:aws:s3:::test_policy/*', + 'Effect': 'Allow', + 'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']} + }]} + Returned value: + [('Statement', ((('Action', (u's3:PutObjectAcl',)), + ('Effect', (u'Allow',)), + ('Principal', ('AWS', ((u'arn:aws:iam::XXXXXXXXXXXX:user/username1',), (u'arn:aws:iam::XXXXXXXXXXXX:user/username2',)))), + ('Resource', (u'arn:aws:s3:::test_policy/*',)), ('Sid', (u'AddCannedAcl2',)))), + ('Version', (u'2012-10-17',)))] + + """ + # Amazon will automatically convert bool and int to strings for us + if isinstance(policy, bool): + return tuple([str(policy).lower()]) + elif isinstance(policy, int): + return tuple([str(policy)]) + + if isinstance(policy, list): + for each in policy: + tupleified = _hashable_policy(each, []) + if isinstance(tupleified, list): + tupleified = tuple(tupleified) + policy_list.append(tupleified) + elif isinstance(policy, string_types) or isinstance(policy, binary_type): + policy = to_text(policy) + # convert root account ARNs to just account IDs + if policy.startswith('arn:aws:iam::') and policy.endswith(':root'): + policy = policy.split(':')[4] + return [policy] + elif isinstance(policy, dict): + sorted_keys = list(policy.keys()) + sorted_keys.sort() + for key in sorted_keys: + element = policy[key] + # Special case defined in + # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html + if key in ["NotPrincipal", "Principal"] and policy[key] == "*": + element = {"AWS": "*"} + tupleified = _hashable_policy(element, []) + if isinstance(tupleified, list): + tupleified = tuple(tupleified) + policy_list.append((key, tupleified)) + + # ensure we aren't returning deeply nested structures of length 1 + if len(policy_list) == 1 and isinstance(policy_list[0], tuple): + policy_list = policy_list[0] + if isinstance(policy_list, list): + if PY3_COMPARISON: + policy_list.sort(key=cmp_to_key(py3cmp)) + else: + policy_list.sort() + return policy_list + + +def py3cmp(a, b): + """ Python 2 can sort lists of mixed types. Strings < tuples. Without this function this fails on Python 3.""" + try: + if a > b: + return 1 + elif a < b: + return -1 + else: + return 0 + except TypeError as e: + # check to see if they're tuple-string + # always say strings are less than tuples (to maintain compatibility with python2) + str_ind = to_text(e).find('str') + tup_ind = to_text(e).find('tuple') + if -1 not in (str_ind, tup_ind): + if str_ind < tup_ind: + return -1 + elif tup_ind < str_ind: + return 1 + raise + + +def compare_policies(current_policy, new_policy, default_version="2008-10-17"): + """ Compares the existing policy and the updated policy + Returns True if there is a difference between policies. + """ + # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html + if default_version: + if isinstance(current_policy, dict): + current_policy = current_policy.copy() + current_policy.setdefault("Version", default_version) + if isinstance(new_policy, dict): + new_policy = new_policy.copy() + new_policy.setdefault("Version", default_version) + + return set(_hashable_policy(new_policy, [])) != set(_hashable_policy(current_policy, [])) + + +def sort_json_policy_dict(policy_dict): + + """ Sort any lists in an IAM JSON policy so that comparison of two policies with identical values but + different orders will return true + Args: + policy_dict (dict): Dict representing IAM JSON policy. + Basic Usage: + >>> my_iam_policy = {'Principle': {'AWS':["31","7","14","101"]} + >>> sort_json_policy_dict(my_iam_policy) + Returns: + Dict: Will return a copy of the policy as a Dict but any List will be sorted + { + 'Principle': { + 'AWS': [ '7', '14', '31', '101' ] + } + } + """ + + def value_is_list(my_list): + + checked_list = [] + for item in my_list: + if isinstance(item, dict): + checked_list.append(sort_json_policy_dict(item)) + elif isinstance(item, list): + checked_list.append(value_is_list(item)) + else: + checked_list.append(item) + + # Sort list. If it's a list of dictionaries, sort by tuple of key-value + # pairs, since Python 3 doesn't allow comparisons such as `<` between dictionaries. + checked_list.sort(key=lambda x: sorted(x.items()) if isinstance(x, dict) else x) + return checked_list + + ordered_policy_dict = {} + for key, value in policy_dict.items(): + if isinstance(value, dict): + ordered_policy_dict[key] = sort_json_policy_dict(value) + elif isinstance(value, list): + ordered_policy_dict[key] = value_is_list(value) + else: + ordered_policy_dict[key] = value + + return ordered_policy_dict + + +def map_complex_type(complex_type, type_map): + """ + Allows to cast elements within a dictionary to a specific type + Example of usage: + + DEPLOYMENT_CONFIGURATION_TYPE_MAP = { + 'maximum_percent': 'int', + 'minimum_healthy_percent': 'int' + } + + deployment_configuration = map_complex_type(module.params['deployment_configuration'], + DEPLOYMENT_CONFIGURATION_TYPE_MAP) + + This ensures all keys within the root element are casted and valid integers + """ + + if complex_type is None: + return + new_type = type(complex_type)() + if isinstance(complex_type, dict): + for key in complex_type: + if key in type_map: + if isinstance(type_map[key], list): + new_type[key] = map_complex_type( + complex_type[key], + type_map[key][0]) + else: + new_type[key] = map_complex_type( + complex_type[key], + type_map[key]) + else: + return complex_type + elif isinstance(complex_type, list): + for i in range(len(complex_type)): + new_type.append(map_complex_type( + complex_type[i], + type_map)) + elif type_map: + return globals()['__builtins__'][type_map](complex_type) + return new_type + + +def compare_aws_tags(current_tags_dict, new_tags_dict, purge_tags=True): + """ + Compare two dicts of AWS tags. Dicts are expected to of been created using 'boto3_tag_list_to_ansible_dict' helper function. + Two dicts are returned - the first is tags to be set, the second is any tags to remove. Since the AWS APIs differ + these may not be able to be used out of the box. + + :param current_tags_dict: + :param new_tags_dict: + :param purge_tags: + :return: tag_key_value_pairs_to_set: a dict of key value pairs that need to be set in AWS. If all tags are identical this dict will be empty + :return: tag_keys_to_unset: a list of key names (type str) that need to be unset in AWS. If no tags need to be unset this list will be empty + """ + + tag_key_value_pairs_to_set = {} + tag_keys_to_unset = [] + + for key in current_tags_dict.keys(): + if key not in new_tags_dict and purge_tags: + tag_keys_to_unset.append(key) + + for key in set(new_tags_dict.keys()) - set(tag_keys_to_unset): + if to_text(new_tags_dict[key]) != current_tags_dict.get(key): + tag_key_value_pairs_to_set[key] = new_tags_dict[key] + + return tag_key_value_pairs_to_set, tag_keys_to_unset diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/elb_utils.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/elb_utils.py new file mode 100644 index 00000000..218052d2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/elb_utils.py @@ -0,0 +1,109 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass + +from .core import is_boto3_error_code +from .ec2 import AWSRetry + + +def get_elb(connection, module, elb_name): + """ + Get an ELB based on name. If not found, return None. + + :param connection: AWS boto3 elbv2 connection + :param module: Ansible module + :param elb_name: Name of load balancer to get + :return: boto3 ELB dict or None if not found + """ + try: + return _get_elb(connection, module, elb_name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e) + + +@AWSRetry.jittered_backoff() +def _get_elb(connection, module, elb_name): + """ + Get an ELB based on name using AWSRetry. If not found, return None. + + :param connection: AWS boto3 elbv2 connection + :param module: Ansible module + :param elb_name: Name of load balancer to get + :return: boto3 ELB dict or None if not found + """ + + try: + load_balancer_paginator = connection.get_paginator('describe_load_balancers') + return (load_balancer_paginator.paginate(Names=[elb_name]).build_full_result())['LoadBalancers'][0] + except is_boto3_error_code('LoadBalancerNotFound'): + return None + + +def get_elb_listener(connection, module, elb_arn, listener_port): + """ + Get an ELB listener based on the port provided. If not found, return None. + + :param connection: AWS boto3 elbv2 connection + :param module: Ansible module + :param elb_arn: ARN of the ELB to look at + :param listener_port: Port of the listener to look for + :return: boto3 ELB listener dict or None if not found + """ + + try: + listener_paginator = connection.get_paginator('describe_listeners') + listeners = (AWSRetry.jittered_backoff()(listener_paginator.paginate)(LoadBalancerArn=elb_arn).build_full_result())['Listeners'] + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e) + + l = None + + for listener in listeners: + if listener['Port'] == listener_port: + l = listener + break + + return l + + +def get_elb_listener_rules(connection, module, listener_arn): + """ + Get rules for a particular ELB listener using the listener ARN. + + :param connection: AWS boto3 elbv2 connection + :param module: Ansible module + :param listener_arn: ARN of the ELB listener + :return: boto3 ELB rules list + """ + + try: + return AWSRetry.jittered_backoff()(connection.describe_rules)(ListenerArn=listener_arn)['Rules'] + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e) + + +def convert_tg_name_to_arn(connection, module, tg_name): + """ + Get ARN of a target group using the target group's name + + :param connection: AWS boto3 elbv2 connection + :param module: Ansible module + :param tg_name: Name of the target group + :return: target group ARN string + """ + + try: + response = AWSRetry.jittered_backoff()(connection.describe_target_groups)(Names=[tg_name]) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e) + + tg_arn = response['TargetGroups'][0]['TargetGroupArn'] + + return tg_arn diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py new file mode 100644 index 00000000..6078fba3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py @@ -0,0 +1,919 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import traceback +from copy import deepcopy + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass + +from .ec2 import AWSRetry +from .ec2 import ansible_dict_to_boto3_tag_list +from .ec2 import boto3_tag_list_to_ansible_dict +from .ec2 import get_ec2_security_group_ids_from_names +from .elb_utils import convert_tg_name_to_arn +from .elb_utils import get_elb +from .elb_utils import get_elb_listener + + +# ForwardConfig may be optional if we've got a single TargetGroupArn entry +def _prune_ForwardConfig(action): + if "ForwardConfig" in action and action['Type'] == 'forward': + if action["ForwardConfig"] == { + 'TargetGroupStickinessConfig': {'Enabled': False}, + 'TargetGroups': [{"TargetGroupArn": action["TargetGroupArn"], "Weight": 1}]}: + newAction = action.copy() + del(newAction["ForwardConfig"]) + return newAction + return action + + +# the AWS api won't return the client secret, so we'll have to remove it +# or the module will always see the new and current actions as different +# and try to apply the same config +def _prune_secret(action): + if action['Type'] == 'authenticate-oidc': + action['AuthenticateOidcConfig'].pop('ClientSecret') + return action + + +def _sort_actions(actions): + return sorted(actions, key=lambda x: x.get('Order', 0)) + + +class ElasticLoadBalancerV2(object): + + def __init__(self, connection, module): + + self.connection = connection + self.module = module + self.changed = False + self.new_load_balancer = False + self.scheme = module.params.get("scheme") + self.name = module.params.get("name") + self.subnet_mappings = module.params.get("subnet_mappings") + self.subnets = module.params.get("subnets") + self.deletion_protection = module.params.get("deletion_protection") + self.wait = module.params.get("wait") + + if module.params.get("tags") is not None: + self.tags = ansible_dict_to_boto3_tag_list(module.params.get("tags")) + else: + self.tags = None + self.purge_tags = module.params.get("purge_tags") + + self.elb = get_elb(connection, module, self.name) + if self.elb is not None: + self.elb_attributes = self.get_elb_attributes() + self.elb['tags'] = self.get_elb_tags() + else: + self.elb_attributes = None + + def wait_for_status(self, elb_arn): + """ + Wait for load balancer to reach 'active' status + + :param elb_arn: The load balancer ARN + :return: + """ + + try: + waiter = self.connection.get_waiter('load_balancer_available') + waiter.wait(LoadBalancerArns=[elb_arn]) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + def get_elb_attributes(self): + """ + Get load balancer attributes + + :return: + """ + + try: + attr_list = AWSRetry.jittered_backoff()( + self.connection.describe_load_balancer_attributes + )(LoadBalancerArn=self.elb['LoadBalancerArn'])['Attributes'] + + elb_attributes = boto3_tag_list_to_ansible_dict(attr_list) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + # Replace '.' with '_' in attribute key names to make it more Ansibley + return dict((k.replace('.', '_'), v) for k, v in elb_attributes.items()) + + def update_elb_attributes(self): + """ + Update the elb_attributes parameter + :return: + """ + self.elb_attributes = self.get_elb_attributes() + + def get_elb_tags(self): + """ + Get load balancer tags + + :return: + """ + + try: + return AWSRetry.jittered_backoff()( + self.connection.describe_tags + )(ResourceArns=[self.elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags'] + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + def delete_tags(self, tags_to_delete): + """ + Delete elb tags + + :return: + """ + + try: + AWSRetry.jittered_backoff()( + self.connection.remove_tags + )(ResourceArns=[self.elb['LoadBalancerArn']], TagKeys=tags_to_delete) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + self.changed = True + + def modify_tags(self): + """ + Modify elb tags + + :return: + """ + + try: + AWSRetry.jittered_backoff()( + self.connection.add_tags + )(ResourceArns=[self.elb['LoadBalancerArn']], Tags=self.tags) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + self.changed = True + + def delete(self): + """ + Delete elb + :return: + """ + + try: + AWSRetry.jittered_backoff()( + self.connection.delete_load_balancer + )(LoadBalancerArn=self.elb['LoadBalancerArn']) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + self.changed = True + + def compare_subnets(self): + """ + Compare user subnets with current ELB subnets + + :return: bool True if they match otherwise False + """ + + subnet_mapping_id_list = [] + subnet_mappings = [] + + # Check if we're dealing with subnets or subnet_mappings + if self.subnets is not None: + # Convert subnets to subnet_mappings format for comparison + for subnet in self.subnets: + subnet_mappings.append({'SubnetId': subnet}) + + if self.subnet_mappings is not None: + # Use this directly since we're comparing as a mapping + subnet_mappings = self.subnet_mappings + + # Build a subnet_mapping style struture of what's currently + # on the load balancer + for subnet in self.elb['AvailabilityZones']: + this_mapping = {'SubnetId': subnet['SubnetId']} + for address in subnet.get('LoadBalancerAddresses', []): + if 'AllocationId' in address: + this_mapping['AllocationId'] = address['AllocationId'] + break + + subnet_mapping_id_list.append(this_mapping) + + return set(frozenset(mapping.items()) for mapping in subnet_mapping_id_list) == set(frozenset(mapping.items()) for mapping in subnet_mappings) + + def modify_subnets(self): + """ + Modify elb subnets to match module parameters + :return: + """ + + try: + AWSRetry.jittered_backoff()( + self.connection.set_subnets + )(LoadBalancerArn=self.elb['LoadBalancerArn'], Subnets=self.subnets) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + self.changed = True + + def update(self): + """ + Update the elb from AWS + :return: + """ + + self.elb = get_elb(self.connection, self.module, self.module.params.get("name")) + self.elb['tags'] = self.get_elb_tags() + + +class ApplicationLoadBalancer(ElasticLoadBalancerV2): + + def __init__(self, connection, connection_ec2, module): + """ + + :param connection: boto3 connection + :param module: Ansible module + """ + super(ApplicationLoadBalancer, self).__init__(connection, module) + + self.connection_ec2 = connection_ec2 + + # Ansible module parameters specific to ALBs + self.type = 'application' + if module.params.get('security_groups') is not None: + try: + self.security_groups = AWSRetry.jittered_backoff()( + get_ec2_security_group_ids_from_names + )(module.params.get('security_groups'), self.connection_ec2, boto3=True) + except ValueError as e: + self.module.fail_json(msg=str(e), exception=traceback.format_exc()) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + else: + self.security_groups = module.params.get('security_groups') + self.access_logs_enabled = module.params.get("access_logs_enabled") + self.access_logs_s3_bucket = module.params.get("access_logs_s3_bucket") + self.access_logs_s3_prefix = module.params.get("access_logs_s3_prefix") + self.idle_timeout = module.params.get("idle_timeout") + self.http2 = module.params.get("http2") + + if self.elb is not None and self.elb['Type'] != 'application': + self.module.fail_json(msg="The load balancer type you are trying to manage is not application. Try elb_network_lb module instead.") + + def create_elb(self): + """ + Create a load balancer + :return: + """ + + # Required parameters + params = dict() + params['Name'] = self.name + params['Type'] = self.type + + # Other parameters + if self.subnets is not None: + params['Subnets'] = self.subnets + if self.subnet_mappings is not None: + params['SubnetMappings'] = self.subnet_mappings + if self.security_groups is not None: + params['SecurityGroups'] = self.security_groups + params['Scheme'] = self.scheme + if self.tags: + params['Tags'] = self.tags + + try: + self.elb = AWSRetry.jittered_backoff()(self.connection.create_load_balancer)(**params)['LoadBalancers'][0] + self.changed = True + self.new_load_balancer = True + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + if self.wait: + self.wait_for_status(self.elb['LoadBalancerArn']) + + def modify_elb_attributes(self): + """ + Update Application ELB attributes if required + + :return: + """ + + update_attributes = [] + + if self.access_logs_enabled is not None and str(self.access_logs_enabled).lower() != self.elb_attributes['access_logs_s3_enabled']: + update_attributes.append({'Key': 'access_logs.s3.enabled', 'Value': str(self.access_logs_enabled).lower()}) + if self.access_logs_s3_bucket is not None and self.access_logs_s3_bucket != self.elb_attributes['access_logs_s3_bucket']: + update_attributes.append({'Key': 'access_logs.s3.bucket', 'Value': self.access_logs_s3_bucket}) + if self.access_logs_s3_prefix is not None and self.access_logs_s3_prefix != self.elb_attributes['access_logs_s3_prefix']: + update_attributes.append({'Key': 'access_logs.s3.prefix', 'Value': self.access_logs_s3_prefix}) + if self.deletion_protection is not None and str(self.deletion_protection).lower() != self.elb_attributes['deletion_protection_enabled']: + update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': str(self.deletion_protection).lower()}) + if self.idle_timeout is not None and str(self.idle_timeout) != self.elb_attributes['idle_timeout_timeout_seconds']: + update_attributes.append({'Key': 'idle_timeout.timeout_seconds', 'Value': str(self.idle_timeout)}) + if self.http2 is not None and str(self.http2).lower() != self.elb_attributes['routing_http2_enabled']: + update_attributes.append({'Key': 'routing.http2.enabled', 'Value': str(self.http2).lower()}) + + if update_attributes: + try: + AWSRetry.jittered_backoff()( + self.connection.modify_load_balancer_attributes + )(LoadBalancerArn=self.elb['LoadBalancerArn'], Attributes=update_attributes) + self.changed = True + except (BotoCoreError, ClientError) as e: + # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state + if self.new_load_balancer: + AWSRetry.jittered_backoff()(self.connection.delete_load_balancer)(LoadBalancerArn=self.elb['LoadBalancerArn']) + self.module.fail_json_aws(e) + + def compare_security_groups(self): + """ + Compare user security groups with current ELB security groups + + :return: bool True if they match otherwise False + """ + + if set(self.elb['SecurityGroups']) != set(self.security_groups): + return False + else: + return True + + def modify_security_groups(self): + """ + Modify elb security groups to match module parameters + :return: + """ + + try: + AWSRetry.jittered_backoff()( + self.connection.set_security_groups + )(LoadBalancerArn=self.elb['LoadBalancerArn'], SecurityGroups=self.security_groups) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + self.changed = True + + +class NetworkLoadBalancer(ElasticLoadBalancerV2): + + def __init__(self, connection, connection_ec2, module): + + """ + + :param connection: boto3 connection + :param module: Ansible module + """ + super(NetworkLoadBalancer, self).__init__(connection, module) + + self.connection_ec2 = connection_ec2 + + # Ansible module parameters specific to NLBs + self.type = 'network' + self.cross_zone_load_balancing = module.params.get('cross_zone_load_balancing') + + if self.elb is not None and self.elb['Type'] != 'network': + self.module.fail_json(msg="The load balancer type you are trying to manage is not network. Try elb_application_lb module instead.") + + def create_elb(self): + """ + Create a load balancer + :return: + """ + + # Required parameters + params = dict() + params['Name'] = self.name + params['Type'] = self.type + + # Other parameters + if self.subnets is not None: + params['Subnets'] = self.subnets + if self.subnet_mappings is not None: + params['SubnetMappings'] = self.subnet_mappings + params['Scheme'] = self.scheme + if self.tags: + params['Tags'] = self.tags + + try: + self.elb = AWSRetry.jittered_backoff()(self.connection.create_load_balancer)(**params)['LoadBalancers'][0] + self.changed = True + self.new_load_balancer = True + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + if self.wait: + self.wait_for_status(self.elb['LoadBalancerArn']) + + def modify_elb_attributes(self): + """ + Update Network ELB attributes if required + + :return: + """ + + update_attributes = [] + + if self.cross_zone_load_balancing is not None and str(self.cross_zone_load_balancing).lower() != \ + self.elb_attributes['load_balancing_cross_zone_enabled']: + update_attributes.append({'Key': 'load_balancing.cross_zone.enabled', 'Value': str(self.cross_zone_load_balancing).lower()}) + if self.deletion_protection is not None and str(self.deletion_protection).lower() != self.elb_attributes['deletion_protection_enabled']: + update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': str(self.deletion_protection).lower()}) + + if update_attributes: + try: + AWSRetry.jittered_backoff()( + self.connection.modify_load_balancer_attributes + )(LoadBalancerArn=self.elb['LoadBalancerArn'], Attributes=update_attributes) + self.changed = True + except (BotoCoreError, ClientError) as e: + # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state + if self.new_load_balancer: + AWSRetry.jittered_backoff()(self.connection.delete_load_balancer)(LoadBalancerArn=self.elb['LoadBalancerArn']) + self.module.fail_json_aws(e) + + def modify_subnets(self): + """ + Modify elb subnets to match module parameters (unsupported for NLB) + :return: + """ + + self.module.fail_json(msg='Modifying subnets and elastic IPs is not supported for Network Load Balancer') + + +class ELBListeners(object): + + def __init__(self, connection, module, elb_arn): + + self.connection = connection + self.module = module + self.elb_arn = elb_arn + listeners = module.params.get("listeners") + if listeners is not None: + # Remove suboption argspec defaults of None from each listener + listeners = [dict((x, listener_dict[x]) for x in listener_dict if listener_dict[x] is not None) for listener_dict in listeners] + self.listeners = self._ensure_listeners_default_action_has_arn(listeners) + self.current_listeners = self._get_elb_listeners() + self.purge_listeners = module.params.get("purge_listeners") + self.changed = False + + def update(self): + """ + Update the listeners for the ELB + + :return: + """ + self.current_listeners = self._get_elb_listeners() + + def _get_elb_listeners(self): + """ + Get ELB listeners + + :return: + """ + + try: + listener_paginator = self.connection.get_paginator('describe_listeners') + return (AWSRetry.jittered_backoff()(listener_paginator.paginate)(LoadBalancerArn=self.elb_arn).build_full_result())['Listeners'] + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + def _ensure_listeners_default_action_has_arn(self, listeners): + """ + If a listener DefaultAction has been passed with a Target Group Name instead of ARN, lookup the ARN and + replace the name. + + :param listeners: a list of listener dicts + :return: the same list of dicts ensuring that each listener DefaultActions dict has TargetGroupArn key. If a TargetGroupName key exists, it is removed. + """ + + if not listeners: + listeners = [] + + fixed_listeners = [] + for listener in listeners: + fixed_actions = [] + for action in listener['DefaultActions']: + if 'TargetGroupName' in action: + action['TargetGroupArn'] = convert_tg_name_to_arn(self.connection, + self.module, + action['TargetGroupName']) + del action['TargetGroupName'] + fixed_actions.append(action) + listener['DefaultActions'] = fixed_actions + fixed_listeners.append(listener) + + return fixed_listeners + + def compare_listeners(self): + """ + + :return: + """ + listeners_to_modify = [] + listeners_to_delete = [] + listeners_to_add = deepcopy(self.listeners) + + # Check each current listener port to see if it's been passed to the module + for current_listener in self.current_listeners: + current_listener_passed_to_module = False + for new_listener in self.listeners[:]: + new_listener['Port'] = int(new_listener['Port']) + if current_listener['Port'] == new_listener['Port']: + current_listener_passed_to_module = True + # Remove what we match so that what is left can be marked as 'to be added' + listeners_to_add.remove(new_listener) + modified_listener = self._compare_listener(current_listener, new_listener) + if modified_listener: + modified_listener['Port'] = current_listener['Port'] + modified_listener['ListenerArn'] = current_listener['ListenerArn'] + listeners_to_modify.append(modified_listener) + break + + # If the current listener was not matched against passed listeners and purge is True, mark for removal + if not current_listener_passed_to_module and self.purge_listeners: + listeners_to_delete.append(current_listener['ListenerArn']) + + return listeners_to_add, listeners_to_modify, listeners_to_delete + + def _compare_listener(self, current_listener, new_listener): + """ + Compare two listeners. + + :param current_listener: + :param new_listener: + :return: + """ + + modified_listener = {} + + # Port + if current_listener['Port'] != new_listener['Port']: + modified_listener['Port'] = new_listener['Port'] + + # Protocol + if current_listener['Protocol'] != new_listener['Protocol']: + modified_listener['Protocol'] = new_listener['Protocol'] + + # If Protocol is HTTPS, check additional attributes + if current_listener['Protocol'] == 'HTTPS' and new_listener['Protocol'] == 'HTTPS': + # Cert + if current_listener['SslPolicy'] != new_listener['SslPolicy']: + modified_listener['SslPolicy'] = new_listener['SslPolicy'] + if current_listener['Certificates'][0]['CertificateArn'] != new_listener['Certificates'][0]['CertificateArn']: + modified_listener['Certificates'] = [] + modified_listener['Certificates'].append({}) + modified_listener['Certificates'][0]['CertificateArn'] = new_listener['Certificates'][0]['CertificateArn'] + elif current_listener['Protocol'] != 'HTTPS' and new_listener['Protocol'] == 'HTTPS': + modified_listener['SslPolicy'] = new_listener['SslPolicy'] + modified_listener['Certificates'] = [] + modified_listener['Certificates'].append({}) + modified_listener['Certificates'][0]['CertificateArn'] = new_listener['Certificates'][0]['CertificateArn'] + + # Default action + + # Check proper rule format on current listener + if len(current_listener['DefaultActions']) > 1: + for action in current_listener['DefaultActions']: + if 'Order' not in action: + self.module.fail_json(msg="'Order' key not found in actions. " + "installed version of botocore does not support " + "multiple actions, please upgrade botocore to version " + "1.10.30 or higher") + + # If the lengths of the actions are the same, we'll have to verify that the + # contents of those actions are the same + if len(current_listener['DefaultActions']) == len(new_listener['DefaultActions']): + current_actions_sorted = _sort_actions(current_listener['DefaultActions']) + new_actions_sorted = _sort_actions(new_listener['DefaultActions']) + + new_actions_sorted_no_secret = [_prune_secret(i) for i in new_actions_sorted] + + if [_prune_ForwardConfig(i) for i in current_actions_sorted] != [_prune_ForwardConfig(i) for i in new_actions_sorted_no_secret]: + modified_listener['DefaultActions'] = new_listener['DefaultActions'] + # If the action lengths are different, then replace with the new actions + else: + modified_listener['DefaultActions'] = new_listener['DefaultActions'] + + if modified_listener: + return modified_listener + else: + return None + + +class ELBListener(object): + + def __init__(self, connection, module, listener, elb_arn): + """ + + :param connection: + :param module: + :param listener: + :param elb_arn: + """ + + self.connection = connection + self.module = module + self.listener = listener + self.elb_arn = elb_arn + + def add(self): + + try: + # Rules is not a valid parameter for create_listener + if 'Rules' in self.listener: + self.listener.pop('Rules') + AWSRetry.jittered_backoff()(self.connection.create_listener)(LoadBalancerArn=self.elb_arn, **self.listener) + except (BotoCoreError, ClientError) as e: + if '"Order", must be one of: Type, TargetGroupArn' in str(e): + self.module.fail_json(msg="installed version of botocore does not support " + "multiple actions, please upgrade botocore to version " + "1.10.30 or higher") + else: + self.module.fail_json_aws(e) + + def modify(self): + + try: + # Rules is not a valid parameter for modify_listener + if 'Rules' in self.listener: + self.listener.pop('Rules') + AWSRetry.jittered_backoff()(self.connection.modify_listener)(**self.listener) + except (BotoCoreError, ClientError) as e: + if '"Order", must be one of: Type, TargetGroupArn' in str(e): + self.module.fail_json(msg="installed version of botocore does not support " + "multiple actions, please upgrade botocore to version " + "1.10.30 or higher") + else: + self.module.fail_json_aws(e) + + def delete(self): + + try: + AWSRetry.jittered_backoff()(self.connection.delete_listener)(ListenerArn=self.listener) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + +class ELBListenerRules(object): + + def __init__(self, connection, module, elb_arn, listener_rules, listener_port): + + self.connection = connection + self.module = module + self.elb_arn = elb_arn + self.rules = self._ensure_rules_action_has_arn(listener_rules) + self.changed = False + + # Get listener based on port so we can use ARN + self.current_listener = get_elb_listener(connection, module, elb_arn, listener_port) + self.listener_arn = self.current_listener['ListenerArn'] + self.rules_to_add = deepcopy(self.rules) + self.rules_to_modify = [] + self.rules_to_delete = [] + + # If the listener exists (i.e. has an ARN) get rules for the listener + if 'ListenerArn' in self.current_listener: + self.current_rules = self._get_elb_listener_rules() + else: + self.current_rules = [] + + def _ensure_rules_action_has_arn(self, rules): + """ + If a rule Action has been passed with a Target Group Name instead of ARN, lookup the ARN and + replace the name. + + :param rules: a list of rule dicts + :return: the same list of dicts ensuring that each rule Actions dict has TargetGroupArn key. If a TargetGroupName key exists, it is removed. + """ + + fixed_rules = [] + for rule in rules: + fixed_actions = [] + for action in rule['Actions']: + if 'TargetGroupName' in action: + action['TargetGroupArn'] = convert_tg_name_to_arn(self.connection, self.module, action['TargetGroupName']) + del action['TargetGroupName'] + fixed_actions.append(action) + rule['Actions'] = fixed_actions + fixed_rules.append(rule) + + return fixed_rules + + def _get_elb_listener_rules(self): + + try: + return AWSRetry.jittered_backoff()(self.connection.describe_rules)(ListenerArn=self.current_listener['ListenerArn'])['Rules'] + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + def _compare_condition(self, current_conditions, condition): + """ + + :param current_conditions: + :param condition: + :return: + """ + + condition_found = False + + for current_condition in current_conditions: + # host-header: current_condition includes both HostHeaderConfig AND Values while + # condition can be defined with either HostHeaderConfig OR Values. Only use + # HostHeaderConfig['Values'] comparison if both conditions includes HostHeaderConfig. + if current_condition.get('HostHeaderConfig') and condition.get('HostHeaderConfig'): + if (current_condition['Field'] == condition['Field'] and + sorted(current_condition['HostHeaderConfig']['Values']) == sorted(condition['HostHeaderConfig']['Values'])): + condition_found = True + break + elif current_condition.get('HttpHeaderConfig'): + if (current_condition['Field'] == condition['Field'] and + sorted(current_condition['HttpHeaderConfig']['Values']) == sorted(condition['HttpHeaderConfig']['Values']) and + current_condition['HttpHeaderConfig']['HttpHeaderName'] == condition['HttpHeaderConfig']['HttpHeaderName']): + condition_found = True + break + elif current_condition.get('HttpRequestMethodConfig'): + if (current_condition['Field'] == condition['Field'] and + sorted(current_condition['HttpRequestMethodConfig']['Values']) == sorted(condition['HttpRequestMethodConfig']['Values'])): + condition_found = True + break + # path-pattern: current_condition includes both PathPatternConfig AND Values while + # condition can be defined with either PathPatternConfig OR Values. Only use + # PathPatternConfig['Values'] comparison if both conditions includes PathPatternConfig. + elif current_condition.get('PathPatternConfig') and condition.get('PathPatternConfig'): + if (current_condition['Field'] == condition['Field'] and + sorted(current_condition['PathPatternConfig']['Values']) == sorted(condition['PathPatternConfig']['Values'])): + condition_found = True + break + elif current_condition.get('QueryStringConfig'): + # QueryString Values is not sorted as it is the only list of dicts (not strings). + if (current_condition['Field'] == condition['Field'] and + current_condition['QueryStringConfig']['Values'] == condition['QueryStringConfig']['Values']): + condition_found = True + break + elif current_condition.get('SourceIpConfig'): + if (current_condition['Field'] == condition['Field'] and + sorted(current_condition['SourceIpConfig']['Values']) == sorted(condition['SourceIpConfig']['Values'])): + condition_found = True + break + # Not all fields are required to have Values list nested within a *Config dict + # e.g. fields host-header/path-pattern can directly list Values + elif current_condition['Field'] == condition['Field'] and sorted(current_condition['Values']) == sorted(condition['Values']): + condition_found = True + break + + return condition_found + + def _compare_rule(self, current_rule, new_rule): + """ + + :return: + """ + + modified_rule = {} + + # Priority + if int(current_rule['Priority']) != int(new_rule['Priority']): + modified_rule['Priority'] = new_rule['Priority'] + + # Actions + + # Check proper rule format on current listener + if len(current_rule['Actions']) > 1: + for action in current_rule['Actions']: + if 'Order' not in action: + self.module.fail_json(msg="'Order' key not found in actions. " + "installed version of botocore does not support " + "multiple actions, please upgrade botocore to version " + "1.10.30 or higher") + + # If the lengths of the actions are the same, we'll have to verify that the + # contents of those actions are the same + if len(current_rule['Actions']) == len(new_rule['Actions']): + # if actions have just one element, compare the contents and then update if + # they're different + current_actions_sorted = _sort_actions(current_rule['Actions']) + new_actions_sorted = _sort_actions(new_rule['Actions']) + + new_actions_sorted_no_secret = [_prune_secret(i) for i in new_actions_sorted] + + if [_prune_ForwardConfig(i) for i in current_actions_sorted] != [_prune_ForwardConfig(i) for i in new_actions_sorted_no_secret]: + modified_rule['Actions'] = new_rule['Actions'] + # If the action lengths are different, then replace with the new actions + else: + modified_rule['Actions'] = new_rule['Actions'] + + # Conditions + modified_conditions = [] + for condition in new_rule['Conditions']: + if not self._compare_condition(current_rule['Conditions'], condition): + modified_conditions.append(condition) + + if modified_conditions: + modified_rule['Conditions'] = modified_conditions + + return modified_rule + + def compare_rules(self): + """ + + :return: + """ + + rules_to_modify = [] + rules_to_delete = [] + rules_to_add = deepcopy(self.rules) + + for current_rule in self.current_rules: + current_rule_passed_to_module = False + for new_rule in self.rules[:]: + if current_rule['Priority'] == str(new_rule['Priority']): + current_rule_passed_to_module = True + # Remove what we match so that what is left can be marked as 'to be added' + rules_to_add.remove(new_rule) + modified_rule = self._compare_rule(current_rule, new_rule) + if modified_rule: + modified_rule['Priority'] = int(current_rule['Priority']) + modified_rule['RuleArn'] = current_rule['RuleArn'] + modified_rule['Actions'] = new_rule['Actions'] + modified_rule['Conditions'] = new_rule['Conditions'] + rules_to_modify.append(modified_rule) + break + + # If the current rule was not matched against passed rules, mark for removal + if not current_rule_passed_to_module and not current_rule['IsDefault']: + rules_to_delete.append(current_rule['RuleArn']) + + return rules_to_add, rules_to_modify, rules_to_delete + + +class ELBListenerRule(object): + + def __init__(self, connection, module, rule, listener_arn): + + self.connection = connection + self.module = module + self.rule = rule + self.listener_arn = listener_arn + self.changed = False + + def create(self): + """ + Create a listener rule + + :return: + """ + + try: + self.rule['ListenerArn'] = self.listener_arn + self.rule['Priority'] = int(self.rule['Priority']) + AWSRetry.jittered_backoff()(self.connection.create_rule)(**self.rule) + except (BotoCoreError, ClientError) as e: + if '"Order", must be one of: Type, TargetGroupArn' in str(e): + self.module.fail_json(msg="installed version of botocore does not support " + "multiple actions, please upgrade botocore to version " + "1.10.30 or higher") + else: + self.module.fail_json_aws(e) + + self.changed = True + + def modify(self): + """ + Modify a listener rule + + :return: + """ + + try: + del self.rule['Priority'] + AWSRetry.jittered_backoff()(self.connection.modify_rule)(**self.rule) + except (BotoCoreError, ClientError) as e: + if '"Order", must be one of: Type, TargetGroupArn' in str(e): + self.module.fail_json(msg="installed version of botocore does not support " + "multiple actions, please upgrade botocore to version " + "1.10.30 or higher") + else: + self.module.fail_json_aws(e) + + self.changed = True + + def delete(self): + """ + Delete a listener rule + + :return: + """ + + try: + AWSRetry.jittered_backoff()(self.connection.delete_rule)(RuleArn=self.rule['RuleArn']) + except (BotoCoreError, ClientError) as e: + self.module.fail_json_aws(e) + + self.changed = True diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/iam.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/iam.py new file mode 100644 index 00000000..7e6aba78 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/iam.py @@ -0,0 +1,76 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +try: + import botocore +except ImportError: + pass + +from ansible.module_utils._text import to_native + +from .ec2 import AWSRetry +from .core import is_boto3_error_code + + +def get_aws_account_id(module): + """ Given an AnsibleAWSModule instance, get the active AWS account ID + """ + + return get_aws_account_info(module)[0] + + +def get_aws_account_info(module): + """Given an AnsibleAWSModule instance, return the account information + (account id and partition) we are currently working on + + get_account_info tries too find out the account that we are working + on. It's not guaranteed that this will be easy so we try in + several different ways. Giving either IAM or STS privileges to + the account should be enough to permit this. + + Tries: + - sts:GetCallerIdentity + - iam:GetUser + - sts:DecodeAuthorizationMessage + """ + account_id = None + partition = None + try: + sts_client = module.client('sts', retry_decorator=AWSRetry.jittered_backoff()) + caller_id = sts_client.get_caller_identity(aws_retry=True) + account_id = caller_id.get('Account') + partition = caller_id.get('Arn').split(':')[1] + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError): + try: + iam_client = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) + arn, partition, service, reg, account_id, resource = iam_client.get_user(aws_retry=True)['User']['Arn'].split(':') + except is_boto3_error_code('AccessDenied') as e: + try: + except_msg = to_native(e.message) + except AttributeError: + except_msg = to_native(e) + m = re.search(r"arn:(aws(-([a-z\-]+))?):iam::([0-9]{12,32}):\w+/", except_msg) + if m is None: + module.fail_json_aws( + e, + msg="Failed to get AWS account information, Try allowing sts:GetCallerIdentity or iam:GetUser permissions." + ) + account_id = m.group(4) + partition = m.group(1) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws( + e, + msg="Failed to get AWS account information, Try allowing sts:GetCallerIdentity or iam:GetUser permissions." + ) + + if account_id is None or partition is None: + module.fail_json( + msg="Failed to get AWS account information, Try allowing sts:GetCallerIdentity or iam:GetUser permissions." + ) + + return (to_native(account_id), to_native(partition)) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/rds.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/rds.py new file mode 100644 index 00000000..221b92ef --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/rds.py @@ -0,0 +1,235 @@ +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from collections import namedtuple +from time import sleep + +try: + from botocore.exceptions import BotoCoreError, ClientError, WaiterError +except ImportError: + pass + +from ansible.module_utils._text import to_text +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + +from .ec2 import AWSRetry +from .ec2 import ansible_dict_to_boto3_tag_list +from .ec2 import boto3_tag_list_to_ansible_dict +from .ec2 import compare_aws_tags +from .waiters import get_waiter + +Boto3ClientMethod = namedtuple('Boto3ClientMethod', ['name', 'waiter', 'operation_description', 'cluster', 'instance']) +# Whitelist boto3 client methods for cluster and instance resources +cluster_method_names = [ + 'create_db_cluster', 'restore_db_cluster_from_db_snapshot', 'restore_db_cluster_from_s3', + 'restore_db_cluster_to_point_in_time', 'modify_db_cluster', 'delete_db_cluster', 'add_tags_to_resource', + 'remove_tags_from_resource', 'list_tags_for_resource', 'promote_read_replica_db_cluster' +] +instance_method_names = [ + 'create_db_instance', 'restore_db_instance_to_point_in_time', 'restore_db_instance_from_s3', + 'restore_db_instance_from_db_snapshot', 'create_db_instance_read_replica', 'modify_db_instance', + 'delete_db_instance', 'add_tags_to_resource', 'remove_tags_from_resource', 'list_tags_for_resource', + 'promote_read_replica', 'stop_db_instance', 'start_db_instance', 'reboot_db_instance' +] + + +def get_rds_method_attribute(method_name, module): + readable_op = method_name.replace('_', ' ').replace('db', 'DB') + if method_name in cluster_method_names and 'new_db_cluster_identifier' in module.params: + cluster = True + instance = False + if method_name == 'delete_db_cluster': + waiter = 'cluster_deleted' + else: + waiter = 'cluster_available' + elif method_name in instance_method_names and 'new_db_instance_identifier' in module.params: + cluster = False + instance = True + if method_name == 'delete_db_instance': + waiter = 'db_instance_deleted' + elif method_name == 'stop_db_instance': + waiter = 'db_instance_stopped' + else: + waiter = 'db_instance_available' + else: + raise NotImplementedError("method {0} hasn't been added to the list of accepted methods to use a waiter in module_utils/rds.py".format(method_name)) + + return Boto3ClientMethod(name=method_name, waiter=waiter, operation_description=readable_op, cluster=cluster, instance=instance) + + +def get_final_identifier(method_name, module): + apply_immediately = module.params['apply_immediately'] + if get_rds_method_attribute(method_name, module).cluster: + identifier = module.params['db_cluster_identifier'] + updated_identifier = module.params['new_db_cluster_identifier'] + elif get_rds_method_attribute(method_name, module).instance: + identifier = module.params['db_instance_identifier'] + updated_identifier = module.params['new_db_instance_identifier'] + else: + raise NotImplementedError("method {0} hasn't been added to the list of accepted methods in module_utils/rds.py".format(method_name)) + if not module.check_mode and updated_identifier and apply_immediately: + identifier = updated_identifier + return identifier + + +def handle_errors(module, exception, method_name, parameters): + + if not isinstance(exception, ClientError): + module.fail_json_aws(exception, msg="Unexpected failure for method {0} with parameters {1}".format(method_name, parameters)) + + changed = True + error_code = exception.response['Error']['Code'] + if method_name == 'modify_db_instance' and error_code == 'InvalidParameterCombination': + if 'No modifications were requested' in to_text(exception): + changed = False + elif 'ModifyDbCluster API' in to_text(exception): + module.fail_json_aws(exception, msg='It appears you are trying to modify attributes that are managed at the cluster level. Please see rds_cluster') + else: + module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description)) + elif method_name == 'promote_read_replica' and error_code == 'InvalidDBInstanceState': + if 'DB Instance is not a read replica' in to_text(exception): + changed = False + else: + module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description)) + elif method_name == 'create_db_instance' and exception.response['Error']['Code'] == 'InvalidParameterValue': + accepted_engines = [ + 'aurora', 'aurora-mysql', 'aurora-postgresql', 'mariadb', 'mysql', 'oracle-ee', 'oracle-se', + 'oracle-se1', 'oracle-se2', 'postgres', 'sqlserver-ee', 'sqlserver-ex', 'sqlserver-se', 'sqlserver-web' + ] + if parameters.get('Engine') not in accepted_engines: + module.fail_json_aws(exception, msg='DB engine {0} should be one of {1}'.format(parameters.get('Engine'), accepted_engines)) + else: + module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description)) + else: + module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description)) + + return changed + + +def call_method(client, module, method_name, parameters): + result = {} + changed = True + if not module.check_mode: + wait = module.params['wait'] + # TODO: stabilize by adding get_rds_method_attribute(method_name).extra_retry_codes + method = getattr(client, method_name) + try: + if method_name == 'modify_db_instance': + # check if instance is in an available state first, if possible + if wait: + wait_for_status(client, module, module.params['db_instance_identifier'], method_name) + result = AWSRetry.jittered_backoff(catch_extra_error_codes=['InvalidDBInstanceState'])(method)(**parameters) + else: + result = AWSRetry.jittered_backoff()(method)(**parameters) + except (BotoCoreError, ClientError) as e: + changed = handle_errors(module, e, method_name, parameters) + + if wait and changed: + identifier = get_final_identifier(method_name, module) + wait_for_status(client, module, identifier, method_name) + return result, changed + + +def wait_for_instance_status(client, module, db_instance_id, waiter_name): + def wait(client, db_instance_id, waiter_name, extra_retry_codes): + retry = AWSRetry.jittered_backoff(catch_extra_error_codes=extra_retry_codes) + try: + waiter = client.get_waiter(waiter_name) + except ValueError: + # using a waiter in module_utils/waiters.py + waiter = get_waiter(client, waiter_name) + waiter.wait(WaiterConfig={'Delay': 60, 'MaxAttempts': 60}, DBInstanceIdentifier=db_instance_id) + + waiter_expected_status = { + 'db_instance_deleted': 'deleted', + 'db_instance_stopped': 'stopped', + } + expected_status = waiter_expected_status.get(waiter_name, 'available') + if expected_status == 'available': + extra_retry_codes = ['DBInstanceNotFound'] + else: + extra_retry_codes = [] + for attempt_to_wait in range(0, 10): + try: + wait(client, db_instance_id, waiter_name, extra_retry_codes) + break + except WaiterError as e: + # Instance may be renamed and AWSRetry doesn't handle WaiterError + if e.last_response.get('Error', {}).get('Code') == 'DBInstanceNotFound': + sleep(10) + continue + module.fail_json_aws(e, msg='Error while waiting for DB instance {0} to be {1}'.format(db_instance_id, expected_status)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Unexpected error while waiting for DB instance {0} to be {1}'.format( + db_instance_id, expected_status) + ) + + +def wait_for_cluster_status(client, module, db_cluster_id, waiter_name): + try: + waiter = get_waiter(client, waiter_name).wait(DBClusterIdentifier=db_cluster_id) + except WaiterError as e: + if waiter_name == 'cluster_deleted': + msg = "Failed to wait for DB cluster {0} to be deleted".format(db_cluster_id) + else: + msg = "Failed to wait for DB cluster {0} to be available".format(db_cluster_id) + module.fail_json_aws(e, msg=msg) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed with an unexpected error while waiting for the DB cluster {0}".format(db_cluster_id)) + + +def wait_for_status(client, module, identifier, method_name): + waiter_name = get_rds_method_attribute(method_name, module).waiter + if get_rds_method_attribute(method_name, module).cluster: + wait_for_cluster_status(client, module, identifier, waiter_name) + elif get_rds_method_attribute(method_name, module).instance: + wait_for_instance_status(client, module, identifier, waiter_name) + else: + raise NotImplementedError("method {0} hasn't been added to the whitelist of handled methods".format(method_name)) + + +def get_tags(client, module, cluster_arn): + try: + return boto3_tag_list_to_ansible_dict( + client.list_tags_for_resource(ResourceName=cluster_arn)['TagList'] + ) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to describe tags") + + +def arg_spec_to_rds_params(options_dict): + tags = options_dict.pop('tags') + has_processor_features = False + if 'processor_features' in options_dict: + has_processor_features = True + processor_features = options_dict.pop('processor_features') + camel_options = snake_dict_to_camel_dict(options_dict, capitalize_first=True) + for key in list(camel_options.keys()): + for old, new in (('Db', 'DB'), ('Iam', 'IAM'), ('Az', 'AZ')): + if old in key: + camel_options[key.replace(old, new)] = camel_options.pop(key) + camel_options['Tags'] = tags + if has_processor_features: + camel_options['ProcessorFeatures'] = processor_features + return camel_options + + +def ensure_tags(client, module, resource_arn, existing_tags, tags, purge_tags): + if tags is None: + return False + tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, tags, purge_tags) + changed = bool(tags_to_add or tags_to_remove) + if tags_to_add: + call_method( + client, module, method_name='add_tags_to_resource', + parameters={'ResourceName': resource_arn, 'Tags': ansible_dict_to_boto3_tag_list(tags_to_add)} + ) + if tags_to_remove: + call_method( + client, module, method_name='remove_tags_from_resource', + parameters={'ResourceName': resource_arn, 'TagKeys': tags_to_remove} + ) + return changed diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/s3.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/s3.py new file mode 100644 index 00000000..32e0e822 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/s3.py @@ -0,0 +1,83 @@ +# Copyright (c) 2018 Red Hat, Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # Handled by the calling module + +HAS_MD5 = True +try: + from hashlib import md5 +except ImportError: + try: + from md5 import md5 + except ImportError: + HAS_MD5 = False + + +def calculate_etag(module, filename, etag, s3, bucket, obj, version=None): + if not HAS_MD5: + return None + + if '-' in etag: + # Multi-part ETag; a hash of the hashes of each part. + parts = int(etag[1:-1].split('-')[1]) + digests = [] + + s3_kwargs = dict( + Bucket=bucket, + Key=obj, + ) + if version: + s3_kwargs['VersionId'] = version + + with open(filename, 'rb') as f: + for part_num in range(1, parts + 1): + s3_kwargs['PartNumber'] = part_num + try: + head = s3.head_object(**s3_kwargs) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to get head object") + digests.append(md5(f.read(int(head['ContentLength'])))) + + digest_squared = md5(b''.join(m.digest() for m in digests)) + return '"{0}-{1}"'.format(digest_squared.hexdigest(), len(digests)) + else: # Compute the MD5 sum normally + return '"{0}"'.format(module.md5(filename)) + + +def calculate_etag_content(module, content, etag, s3, bucket, obj, version=None): + if not HAS_MD5: + return None + + if '-' in etag: + # Multi-part ETag; a hash of the hashes of each part. + parts = int(etag[1:-1].split('-')[1]) + digests = [] + offset = 0 + + s3_kwargs = dict( + Bucket=bucket, + Key=obj, + ) + if version: + s3_kwargs['VersionId'] = version + + for part_num in range(1, parts + 1): + s3_kwargs['PartNumber'] = part_num + try: + head = s3.head_object(**s3_kwargs) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to get head object") + length = int(head['ContentLength']) + digests.append(md5(content[offset:offset + length])) + offset += length + + digest_squared = md5(b''.join(m.digest() for m in digests)) + return '"{0}-{1}"'.format(digest_squared.hexdigest(), len(digests)) + else: # Compute the MD5 sum normally + return '"{0}"'.format(md5(content).hexdigest()) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/urls.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/urls.py new file mode 100644 index 00000000..e00f485c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/urls.py @@ -0,0 +1,212 @@ +# Copyright: (c) 2018, Aaron Haaf <aabonh@gmail.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import datetime +import hashlib +import hmac +import operator + +try: + from boto3 import session +except ImportError: + pass + +from ansible.module_utils.six.moves.urllib.parse import urlencode +from ansible.module_utils.urls import open_url + +from .ec2 import HAS_BOTO3 +from .ec2 import get_aws_connection_info + + +def hexdigest(s): + """ + Returns the sha256 hexdigest of a string after encoding. + """ + + return hashlib.sha256(s.encode("utf-8")).hexdigest() + + +def format_querystring(params=None): + """ + Returns properly url-encoded query string from the provided params dict. + + It's specially sorted for cannonical requests + """ + + if not params: + return "" + + # Query string values must be URL-encoded (space=%20). The parameters must be sorted by name. + return urlencode(sorted(params.items(), operator.itemgetter(0))) + + +# Key derivation functions. See: +# http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python +def sign(key, msg): + ''' + Return digest for key applied to msg + ''' + + return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest() + + +def get_signature_key(key, dateStamp, regionName, serviceName): + ''' + Returns signature key for AWS resource + ''' + + kDate = sign(("AWS4" + key).encode("utf-8"), dateStamp) + kRegion = sign(kDate, regionName) + kService = sign(kRegion, serviceName) + kSigning = sign(kService, "aws4_request") + return kSigning + + +def get_aws_credentials_object(module): + ''' + Returns aws_access_key_id, aws_secret_access_key, session_token for a module. + ''' + + if not HAS_BOTO3: + module.fail_json("get_aws_credentials_object requires boto3") + + dummy, dummy, boto_params = get_aws_connection_info(module, boto3=True) + s = session.Session(**boto_params) + + return s.get_credentials() + + +# Reference: https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html +def signed_request( + module=None, + method="GET", service=None, host=None, uri=None, + query=None, body="", headers=None, + session_in_header=True, session_in_query=False +): + """Generate a SigV4 request to an AWS resource for a module + + This is used if you wish to authenticate with AWS credentials to a secure endpoint like an elastisearch domain. + + Returns :class:`HTTPResponse` object. + + Example: + result = signed_request( + module=this, + service="es", + host="search-recipes1-xxxxxxxxx.us-west-2.es.amazonaws.com", + ) + + :kwarg host: endpoint to talk to + :kwarg service: AWS id of service (like `ec2` or `es`) + :kwarg module: An AnsibleAWSModule to gather connection info from + + :kwarg body: (optional) Payload to send + :kwarg method: (optional) HTTP verb to use + :kwarg query: (optional) dict of query params to handle + :kwarg uri: (optional) Resource path without query parameters + + :kwarg session_in_header: (optional) Add the session token to the headers + :kwarg session_in_query: (optional) Add the session token to the query parameters + + :returns: HTTPResponse + """ + + if not HAS_BOTO3: + module.fail_json("A sigv4 signed_request requires boto3") + + # "Constants" + + t = datetime.datetime.utcnow() + amz_date = t.strftime("%Y%m%dT%H%M%SZ") + datestamp = t.strftime("%Y%m%d") # Date w/o time, used in credential scope + algorithm = "AWS4-HMAC-SHA256" + + # AWS stuff + + region, dummy, dummy = get_aws_connection_info(module, boto3=True) + credentials = get_aws_credentials_object(module) + access_key = credentials.access_key + secret_key = credentials.secret_key + session_token = credentials.token + + if not access_key: + module.fail_json(msg="aws_access_key_id is missing") + if not secret_key: + module.fail_json(msg="aws_secret_access_key is missing") + + credential_scope = "/".join([datestamp, region, service, "aws4_request"]) + + # Argument Defaults + + uri = uri or "/" + query_string = format_querystring(query) if query else "" + + headers = headers or dict() + query = query or dict() + + headers.update({ + "host": host, + "x-amz-date": amz_date, + }) + + # Handle adding of session_token if present + if session_token: + if session_in_header: + headers["X-Amz-Security-Token"] = session_token + if session_in_query: + query["X-Amz-Security-Token"] = session_token + + if method == "GET": + body = "" + + # Derived data + + body_hash = hexdigest(body) + signed_headers = ";".join(sorted(headers.keys())) + + # Setup Cannonical request to generate auth token + + cannonical_headers = "\n".join([ + key.lower().strip() + ":" + value for key, value in headers.items() + ]) + "\n" # Note additional trailing newline + + cannonical_request = "\n".join([ + method, + uri, + query_string, + cannonical_headers, + signed_headers, + body_hash, + ]) + + string_to_sign = "\n".join([algorithm, amz_date, credential_scope, hexdigest(cannonical_request)]) + + # Sign the Cannonical request + + signing_key = get_signature_key(secret_key, datestamp, region, service) + signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest() + + # Make auth header with that info + + authorization_header = "{0} Credential={1}/{2}, SignedHeaders={3}, Signature={4}".format( + algorithm, access_key, credential_scope, signed_headers, signature + ) + + # PERFORM THE REQUEST! + + url = "https://" + host + uri + + if query_string != "": + url = url + "?" + query_string + + final_headers = { + "x-amz-date": amz_date, + "Authorization": authorization_header, + } + + final_headers.update(headers) + + return open_url(url, method=method, data=body, headers=final_headers) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/waf.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/waf.py new file mode 100644 index 00000000..3ecc645c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/waf.py @@ -0,0 +1,224 @@ +# Copyright (c) 2017 Will Thames +# +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +This module adds shared support for Web Application Firewall modules +""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + import botocore +except ImportError: + pass # caught by imported HAS_BOTO3 + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from .ec2 import AWSRetry +from .waiters import get_waiter + + +MATCH_LOOKUP = { + 'byte': { + 'method': 'byte_match_set', + 'conditionset': 'ByteMatchSet', + 'conditiontuple': 'ByteMatchTuple', + 'type': 'ByteMatch' + }, + 'geo': { + 'method': 'geo_match_set', + 'conditionset': 'GeoMatchSet', + 'conditiontuple': 'GeoMatchConstraint', + 'type': 'GeoMatch' + }, + 'ip': { + 'method': 'ip_set', + 'conditionset': 'IPSet', + 'conditiontuple': 'IPSetDescriptor', + 'type': 'IPMatch' + }, + 'regex': { + 'method': 'regex_match_set', + 'conditionset': 'RegexMatchSet', + 'conditiontuple': 'RegexMatchTuple', + 'type': 'RegexMatch' + }, + 'size': { + 'method': 'size_constraint_set', + 'conditionset': 'SizeConstraintSet', + 'conditiontuple': 'SizeConstraint', + 'type': 'SizeConstraint' + }, + 'sql': { + 'method': 'sql_injection_match_set', + 'conditionset': 'SqlInjectionMatchSet', + 'conditiontuple': 'SqlInjectionMatchTuple', + 'type': 'SqlInjectionMatch', + }, + 'xss': { + 'method': 'xss_match_set', + 'conditionset': 'XssMatchSet', + 'conditiontuple': 'XssMatchTuple', + 'type': 'XssMatch' + }, +} + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_rule_with_backoff(client, rule_id): + return client.get_rule(RuleId=rule_id)['Rule'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_byte_match_set_with_backoff(client, byte_match_set_id): + return client.get_byte_match_set(ByteMatchSetId=byte_match_set_id)['ByteMatchSet'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_ip_set_with_backoff(client, ip_set_id): + return client.get_ip_set(IPSetId=ip_set_id)['IPSet'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_size_constraint_set_with_backoff(client, size_constraint_set_id): + return client.get_size_constraint_set(SizeConstraintSetId=size_constraint_set_id)['SizeConstraintSet'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_sql_injection_match_set_with_backoff(client, sql_injection_match_set_id): + return client.get_sql_injection_match_set(SqlInjectionMatchSetId=sql_injection_match_set_id)['SqlInjectionMatchSet'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_xss_match_set_with_backoff(client, xss_match_set_id): + return client.get_xss_match_set(XssMatchSetId=xss_match_set_id)['XssMatchSet'] + + +def get_rule(client, module, rule_id): + try: + rule = get_rule_with_backoff(client, rule_id) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't obtain waf rule") + + match_sets = { + 'ByteMatch': get_byte_match_set_with_backoff, + 'IPMatch': get_ip_set_with_backoff, + 'SizeConstraint': get_size_constraint_set_with_backoff, + 'SqlInjectionMatch': get_sql_injection_match_set_with_backoff, + 'XssMatch': get_xss_match_set_with_backoff + } + if 'Predicates' in rule: + for predicate in rule['Predicates']: + if predicate['Type'] in match_sets: + predicate.update(match_sets[predicate['Type']](client, predicate['DataId'])) + # replaced by Id from the relevant MatchSet + del(predicate['DataId']) + return rule + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def get_web_acl_with_backoff(client, web_acl_id): + return client.get_web_acl(WebACLId=web_acl_id)['WebACL'] + + +def get_web_acl(client, module, web_acl_id): + try: + web_acl = get_web_acl_with_backoff(client, web_acl_id) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't obtain web acl") + + if web_acl: + try: + for rule in web_acl['Rules']: + rule.update(get_rule(client, module, rule['RuleId'])) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't obtain web acl rule") + return camel_dict_to_snake_dict(web_acl) + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_rules_with_backoff(client): + paginator = client.get_paginator('list_rules') + return paginator.paginate().build_full_result()['Rules'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_regional_rules_with_backoff(client): + resp = client.list_rules() + rules = [] + while resp: + rules += resp['Rules'] + resp = client.list_rules(NextMarker=resp['NextMarker']) if 'NextMarker' in resp else None + return rules + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_web_acls_with_backoff(client): + paginator = client.get_paginator('list_web_acls') + return paginator.paginate().build_full_result()['WebACLs'] + + +@AWSRetry.backoff(tries=5, delay=5, backoff=2.0) +def list_regional_web_acls_with_backoff(client): + resp = client.list_web_acls() + acls = [] + while resp: + acls += resp['WebACLs'] + resp = client.list_web_acls(NextMarker=resp['NextMarker']) if 'NextMarker' in resp else None + return acls + + +def list_web_acls(client, module): + try: + if client.__class__.__name__ == 'WAF': + return list_web_acls_with_backoff(client) + elif client.__class__.__name__ == 'WAFRegional': + return list_regional_web_acls_with_backoff(client) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't obtain web acls") + + +def get_change_token(client, module): + try: + token = client.get_change_token() + return token['ChangeToken'] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't obtain change token") + + +@AWSRetry.backoff(tries=10, delay=2, backoff=2.0, catch_extra_error_codes=['WAFStaleDataException']) +def run_func_with_change_token_backoff(client, module, params, func, wait=False): + params['ChangeToken'] = get_change_token(client, module) + result = func(**params) + if wait: + get_waiter( + client, 'change_token_in_sync', + ).wait( + ChangeToken=result['ChangeToken'] + ) + return result diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/waiters.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/waiters.py new file mode 100644 index 00000000..ff1aac88 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/module_utils/waiters.py @@ -0,0 +1,551 @@ +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import copy + +try: + import botocore.waiter as core_waiter +except ImportError: + pass # caught by HAS_BOTO3 + +import ansible_collections.amazon.aws.plugins.module_utils.core as aws_core + + +ec2_data = { + "version": 2, + "waiters": { + "ImageAvailable": { + "operation": "DescribeImages", + "maxAttempts": 80, + "delay": 15, + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "Images[].State", + "expected": "available" + }, + { + "state": "failure", + "matcher": "pathAny", + "argument": "Images[].State", + "expected": "failed" + } + ] + }, + "InternetGatewayExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeInternetGateways", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(InternetGateways) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidInternetGatewayID.NotFound", + "state": "retry" + }, + ] + }, + "NetworkInterfaceAttached": { + "operation": "DescribeNetworkInterfaces", + "delay": 5, + "maxAttempts": 40, + "acceptors": [ + { + "expected": "attached", + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Attachment.Status" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + }, + ] + }, + "NetworkInterfaceAvailable": { + "operation": "DescribeNetworkInterfaces", + "delay": 5, + "maxAttempts": 40, + "acceptors": [ + { + "expected": "available", + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Status" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "retry" + }, + ] + }, + "NetworkInterfaceDeleteOnTerminate": { + "operation": "DescribeNetworkInterfaces", + "delay": 5, + "maxAttempts": 10, + "acceptors": [ + { + "expected": True, + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Attachment.DeleteOnTermination" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + }, + ] + }, + "NetworkInterfaceNoDeleteOnTerminate": { + "operation": "DescribeNetworkInterfaces", + "delay": 5, + "maxAttempts": 10, + "acceptors": [ + { + "expected": False, + "matcher": "pathAll", + "state": "success", + "argument": "NetworkInterfaces[].Attachment.DeleteOnTermination" + }, + { + "expected": "InvalidNetworkInterfaceID.NotFound", + "matcher": "error", + "state": "failure" + }, + ] + }, + "RouteTableExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeRouteTables", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(RouteTables[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidRouteTableID.NotFound", + "state": "retry" + }, + ] + }, + "SecurityGroupExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSecurityGroups", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(SecurityGroups[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidGroup.NotFound", + "state": "retry" + }, + ] + }, + "SubnetExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSubnets", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(Subnets[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidSubnetID.NotFound", + "state": "retry" + }, + ] + }, + "SubnetHasMapPublic": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSubnets", + "acceptors": [ + { + "matcher": "pathAll", + "expected": True, + "argument": "Subnets[].MapPublicIpOnLaunch", + "state": "success" + }, + ] + }, + "SubnetNoMapPublic": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSubnets", + "acceptors": [ + { + "matcher": "pathAll", + "expected": False, + "argument": "Subnets[].MapPublicIpOnLaunch", + "state": "success" + }, + ] + }, + "SubnetHasAssignIpv6": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSubnets", + "acceptors": [ + { + "matcher": "pathAll", + "expected": True, + "argument": "Subnets[].AssignIpv6AddressOnCreation", + "state": "success" + }, + ] + }, + "SubnetNoAssignIpv6": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSubnets", + "acceptors": [ + { + "matcher": "pathAll", + "expected": False, + "argument": "Subnets[].AssignIpv6AddressOnCreation", + "state": "success" + }, + ] + }, + "SubnetDeleted": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeSubnets", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(Subnets[]) > `0`", + "state": "retry" + }, + { + "matcher": "error", + "expected": "InvalidSubnetID.NotFound", + "state": "success" + }, + ] + }, + "VpnGatewayExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeVpnGateways", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(VpnGateways[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidVpnGatewayID.NotFound", + "state": "retry" + }, + ] + }, + "VpnGatewayDetached": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeVpnGateways", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "VpnGateways[0].State == 'available'", + "state": "success" + }, + ] + }, + } +} + + +waf_data = { + "version": 2, + "waiters": { + "ChangeTokenInSync": { + "delay": 20, + "maxAttempts": 60, + "operation": "GetChangeTokenStatus", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "ChangeTokenStatus == 'INSYNC'", + "state": "success" + }, + { + "matcher": "error", + "expected": "WAFInternalErrorException", + "state": "retry" + } + ] + } + } +} + +eks_data = { + "version": 2, + "waiters": { + "ClusterActive": { + "delay": 20, + "maxAttempts": 60, + "operation": "DescribeCluster", + "acceptors": [ + { + "state": "success", + "matcher": "path", + "argument": "cluster.status", + "expected": "ACTIVE" + }, + { + "state": "retry", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + }, + "ClusterDeleted": { + "delay": 20, + "maxAttempts": 60, + "operation": "DescribeCluster", + "acceptors": [ + { + "state": "retry", + "matcher": "path", + "argument": "cluster.status != 'DELETED'", + "expected": True + }, + { + "state": "success", + "matcher": "error", + "expected": "ResourceNotFoundException" + } + ] + } + } +} + + +rds_data = { + "version": 2, + "waiters": { + "DBInstanceStopped": { + "delay": 20, + "maxAttempts": 60, + "operation": "DescribeDBInstances", + "acceptors": [ + { + "state": "success", + "matcher": "pathAll", + "argument": "DBInstances[].DBInstanceStatus", + "expected": "stopped" + }, + ] + } + } +} + + +def _inject_limit_retries(model): + + extra_retries = [ + 'RequestLimitExceeded', 'Unavailable', 'ServiceUnavailable', + 'InternalFailure', 'InternalError', 'TooManyRequestsException', + 'Throttling'] + + acceptors = [] + for error in extra_retries: + acceptors.append({"state": "success", "matcher": "error", "expected": error}) + + _model = copy.deepcopy(model) + + for waiter in model["waiters"]: + _model["waiters"][waiter]["acceptors"].extend(acceptors) + + return _model + + +def ec2_model(name): + ec2_models = core_waiter.WaiterModel(waiter_config=_inject_limit_retries(ec2_data)) + return ec2_models.get_waiter(name) + + +def waf_model(name): + waf_models = core_waiter.WaiterModel(waiter_config=_inject_limit_retries(waf_data)) + return waf_models.get_waiter(name) + + +def eks_model(name): + eks_models = core_waiter.WaiterModel(waiter_config=_inject_limit_retries(eks_data)) + return eks_models.get_waiter(name) + + +def rds_model(name): + rds_models = core_waiter.WaiterModel(waiter_config=_inject_limit_retries(rds_data)) + return rds_models.get_waiter(name) + + +waiters_by_name = { + ('EC2', 'image_available'): lambda ec2: core_waiter.Waiter( + 'image_available', + ec2_model('ImageAvailable'), + core_waiter.NormalizedOperationMethod( + ec2.describe_images + )), + ('EC2', 'internet_gateway_exists'): lambda ec2: core_waiter.Waiter( + 'internet_gateway_exists', + ec2_model('InternetGatewayExists'), + core_waiter.NormalizedOperationMethod( + ec2.describe_internet_gateways + )), + ('EC2', 'network_interface_attached'): lambda ec2: core_waiter.Waiter( + 'network_interface_attached', + ec2_model('NetworkInterfaceAttached'), + core_waiter.NormalizedOperationMethod( + ec2.describe_network_interfaces + )), + ('EC2', 'network_interface_available'): lambda ec2: core_waiter.Waiter( + 'network_interface_available', + ec2_model('NetworkInterfaceAvailable'), + core_waiter.NormalizedOperationMethod( + ec2.describe_network_interfaces + )), + ('EC2', 'network_interface_delete_on_terminate'): lambda ec2: core_waiter.Waiter( + 'network_interface_delete_on_terminate', + ec2_model('NetworkInterfaceDeleteOnTerminate'), + core_waiter.NormalizedOperationMethod( + ec2.describe_network_interfaces + )), + ('EC2', 'network_interface_no_delete_on_terminate'): lambda ec2: core_waiter.Waiter( + 'network_interface_no_delete_on_terminate', + ec2_model('NetworkInterfaceNoDeleteOnTerminate'), + core_waiter.NormalizedOperationMethod( + ec2.describe_network_interfaces + )), + ('EC2', 'route_table_exists'): lambda ec2: core_waiter.Waiter( + 'route_table_exists', + ec2_model('RouteTableExists'), + core_waiter.NormalizedOperationMethod( + ec2.describe_route_tables + )), + ('EC2', 'security_group_exists'): lambda ec2: core_waiter.Waiter( + 'security_group_exists', + ec2_model('SecurityGroupExists'), + core_waiter.NormalizedOperationMethod( + ec2.describe_security_groups + )), + ('EC2', 'subnet_exists'): lambda ec2: core_waiter.Waiter( + 'subnet_exists', + ec2_model('SubnetExists'), + core_waiter.NormalizedOperationMethod( + ec2.describe_subnets + )), + ('EC2', 'subnet_has_map_public'): lambda ec2: core_waiter.Waiter( + 'subnet_has_map_public', + ec2_model('SubnetHasMapPublic'), + core_waiter.NormalizedOperationMethod( + ec2.describe_subnets + )), + ('EC2', 'subnet_no_map_public'): lambda ec2: core_waiter.Waiter( + 'subnet_no_map_public', + ec2_model('SubnetNoMapPublic'), + core_waiter.NormalizedOperationMethod( + ec2.describe_subnets + )), + ('EC2', 'subnet_has_assign_ipv6'): lambda ec2: core_waiter.Waiter( + 'subnet_has_assign_ipv6', + ec2_model('SubnetHasAssignIpv6'), + core_waiter.NormalizedOperationMethod( + ec2.describe_subnets + )), + ('EC2', 'subnet_no_assign_ipv6'): lambda ec2: core_waiter.Waiter( + 'subnet_no_assign_ipv6', + ec2_model('SubnetNoAssignIpv6'), + core_waiter.NormalizedOperationMethod( + ec2.describe_subnets + )), + ('EC2', 'subnet_deleted'): lambda ec2: core_waiter.Waiter( + 'subnet_deleted', + ec2_model('SubnetDeleted'), + core_waiter.NormalizedOperationMethod( + ec2.describe_subnets + )), + ('EC2', 'vpn_gateway_exists'): lambda ec2: core_waiter.Waiter( + 'vpn_gateway_exists', + ec2_model('VpnGatewayExists'), + core_waiter.NormalizedOperationMethod( + ec2.describe_vpn_gateways + )), + ('EC2', 'vpn_gateway_detached'): lambda ec2: core_waiter.Waiter( + 'vpn_gateway_detached', + ec2_model('VpnGatewayDetached'), + core_waiter.NormalizedOperationMethod( + ec2.describe_vpn_gateways + )), + ('WAF', 'change_token_in_sync'): lambda waf: core_waiter.Waiter( + 'change_token_in_sync', + waf_model('ChangeTokenInSync'), + core_waiter.NormalizedOperationMethod( + waf.get_change_token_status + )), + ('WAFRegional', 'change_token_in_sync'): lambda waf: core_waiter.Waiter( + 'change_token_in_sync', + waf_model('ChangeTokenInSync'), + core_waiter.NormalizedOperationMethod( + waf.get_change_token_status + )), + ('EKS', 'cluster_active'): lambda eks: core_waiter.Waiter( + 'cluster_active', + eks_model('ClusterActive'), + core_waiter.NormalizedOperationMethod( + eks.describe_cluster + )), + ('EKS', 'cluster_deleted'): lambda eks: core_waiter.Waiter( + 'cluster_deleted', + eks_model('ClusterDeleted'), + core_waiter.NormalizedOperationMethod( + eks.describe_cluster + )), + ('RDS', 'db_instance_stopped'): lambda rds: core_waiter.Waiter( + 'db_instance_stopped', + rds_model('DBInstanceStopped'), + core_waiter.NormalizedOperationMethod( + rds.describe_db_instances + )), +} + + +def get_waiter(client, waiter_name): + if isinstance(client, aws_core._RetryingBotoClientWrapper): + return get_waiter(client.client, waiter_name) + try: + return waiters_by_name[(client.__class__.__name__, waiter_name)](client) + except KeyError: + raise NotImplementedError("Waiter {0} could not be found for client {1}. Available waiters: {2}".format( + waiter_name, type(client), ', '.join(repr(k) for k in waiters_by_name.keys()))) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_az_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_az_facts.py new file mode 100644 index 00000000..42f12323 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_az_facts.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +module: aws_az_info +short_description: Gather information about availability zones in AWS. +version_added: 1.0.0 +description: + - Gather information about availability zones in AWS. + - This module was called M(amazon.aws.aws_az_facts) before Ansible 2.9. The usage did not change. +author: 'Henrique Rodrigues (@Sodki)' +options: + filters: + description: + - A dict of filters to apply. + - Each dict item consists of a filter key and a filter value. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAvailabilityZones.html) for possible filters. + - Filter names and values are case sensitive. + - You can use underscores instead of dashes (-) in the filter keys. + - Filter keys with underscores will take precedence in case of conflict. + required: false + default: {} + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +requirements: [botocore, boto3] +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Gather information about all availability zones + amazon.aws.aws_az_info: + +- name: Gather information about a single availability zone + amazon.aws.aws_az_info: + filters: + zone-name: eu-west-1a +''' + +RETURN = ''' +availability_zones: + returned: on success + description: > + Availability zones that match the provided filters. Each element consists of a dict with all the information + related to that available zone. + type: list + sample: "[ + { + 'messages': [], + 'region_name': 'us-west-1', + 'state': 'available', + 'zone_name': 'us-west-1b' + }, + { + 'messages': [], + 'region_name': 'us-west-1', + 'state': 'available', + 'zone_name': 'us-west-1c' + } + ]" +''' + +try: + from botocore.exceptions import ClientError, BotoCoreError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list + + +def main(): + argument_spec = dict( + filters=dict(default={}, type='dict') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'aws_az_facts': + module.deprecate("The 'aws_az_facts' module has been renamed to 'aws_az_info'", date='2022-06-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + # Replace filter key underscores with dashes, for compatibility + sanitized_filters = dict(module.params.get('filters')) + for k in module.params.get('filters').keys(): + if "_" in k: + sanitized_filters[k.replace('_', '-')] = sanitized_filters[k] + del sanitized_filters[k] + + try: + availability_zones = connection.describe_availability_zones(aws_retry=True, Filters=ansible_dict_to_boto3_filter_list(sanitized_filters)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to describe availability zones.") + + # Turn the boto3 result into ansible_friendly_snaked_names + snaked_availability_zones = [camel_dict_to_snake_dict(az) for az in availability_zones['AvailabilityZones']] + + module.exit_json(availability_zones=snaked_availability_zones) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_az_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_az_info.py new file mode 100644 index 00000000..42f12323 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_az_info.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +module: aws_az_info +short_description: Gather information about availability zones in AWS. +version_added: 1.0.0 +description: + - Gather information about availability zones in AWS. + - This module was called M(amazon.aws.aws_az_facts) before Ansible 2.9. The usage did not change. +author: 'Henrique Rodrigues (@Sodki)' +options: + filters: + description: + - A dict of filters to apply. + - Each dict item consists of a filter key and a filter value. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAvailabilityZones.html) for possible filters. + - Filter names and values are case sensitive. + - You can use underscores instead of dashes (-) in the filter keys. + - Filter keys with underscores will take precedence in case of conflict. + required: false + default: {} + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +requirements: [botocore, boto3] +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Gather information about all availability zones + amazon.aws.aws_az_info: + +- name: Gather information about a single availability zone + amazon.aws.aws_az_info: + filters: + zone-name: eu-west-1a +''' + +RETURN = ''' +availability_zones: + returned: on success + description: > + Availability zones that match the provided filters. Each element consists of a dict with all the information + related to that available zone. + type: list + sample: "[ + { + 'messages': [], + 'region_name': 'us-west-1', + 'state': 'available', + 'zone_name': 'us-west-1b' + }, + { + 'messages': [], + 'region_name': 'us-west-1', + 'state': 'available', + 'zone_name': 'us-west-1c' + } + ]" +''' + +try: + from botocore.exceptions import ClientError, BotoCoreError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list + + +def main(): + argument_spec = dict( + filters=dict(default={}, type='dict') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'aws_az_facts': + module.deprecate("The 'aws_az_facts' module has been renamed to 'aws_az_info'", date='2022-06-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + # Replace filter key underscores with dashes, for compatibility + sanitized_filters = dict(module.params.get('filters')) + for k in module.params.get('filters').keys(): + if "_" in k: + sanitized_filters[k.replace('_', '-')] = sanitized_filters[k] + del sanitized_filters[k] + + try: + availability_zones = connection.describe_availability_zones(aws_retry=True, Filters=ansible_dict_to_boto3_filter_list(sanitized_filters)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to describe availability zones.") + + # Turn the boto3 result into ansible_friendly_snaked_names + snaked_availability_zones = [camel_dict_to_snake_dict(az) for az in availability_zones['AvailabilityZones']] + + module.exit_json(availability_zones=snaked_availability_zones) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_caller_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_caller_facts.py new file mode 100644 index 00000000..91880fdb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_caller_facts.py @@ -0,0 +1,112 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: aws_caller_info +version_added: 1.0.0 +short_description: Get information about the user and account being used to make AWS calls. +description: + - This module returns information about the account and user / role from which the AWS access tokens originate. + - The primary use of this is to get the account id for templating into ARNs or similar to avoid needing to specify this information in inventory. + - This module was called M(amazon.aws.aws_caller_facts) before Ansible 2.9. The usage did not change. + +author: + - Ed Costello (@orthanc) + - Stijn Dubrul (@sdubrul) + +requirements: [ 'botocore', 'boto3' ] +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Get the current caller identity information + amazon.aws.aws_caller_info: + register: caller_info +''' + +RETURN = ''' +account: + description: The account id the access credentials are associated with. + returned: success + type: str + sample: "123456789012" +account_alias: + description: The account alias the access credentials are associated with. + returned: when caller has the iam:ListAccountAliases permission + type: str + sample: "acme-production" +arn: + description: The arn identifying the user the credentials are associated with. + returned: success + type: str + sample: arn:aws:sts::123456789012:federated-user/my-federated-user-name +user_id: + description: | + The user id the access credentials are associated with. Note that this may not correspond to + anything you can look up in the case of roles or federated identities. + returned: success + type: str + sample: 123456789012:my-federated-user-name +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry + + +def main(): + module = AnsibleAWSModule( + argument_spec={}, + supports_check_mode=True, + ) + if module._name == 'aws_caller_facts': + module.deprecate("The 'aws_caller_facts' module has been renamed to 'aws_caller_info'", date='2021-12-01', collection_name='amazon.aws') + + client = module.client('sts', retry_decorator=AWSRetry.jittered_backoff()) + + try: + caller_info = client.get_caller_identity(aws_retry=True) + caller_info.pop('ResponseMetadata', None) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to retrieve caller identity') + + iam_client = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) + + try: + # Although a list is returned by list_account_aliases AWS supports maximum one alias per account. + # If an alias is defined it will be returned otherwise a blank string is filled in as account_alias. + # see https://docs.aws.amazon.com/cli/latest/reference/iam/list-account-aliases.html#output + response = iam_client.list_account_aliases(aws_retry=True) + if response and response['AccountAliases']: + caller_info['account_alias'] = response['AccountAliases'][0] + else: + caller_info['account_alias'] = '' + except (BotoCoreError, ClientError) as e: + # The iam:ListAccountAliases permission is required for this operation to succeed. + # Lacking this permission is handled gracefully by not returning the account_alias. + pass + + module.exit_json( + changed=False, + **camel_dict_to_snake_dict(caller_info)) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_caller_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_caller_info.py new file mode 100644 index 00000000..91880fdb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_caller_info.py @@ -0,0 +1,112 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: aws_caller_info +version_added: 1.0.0 +short_description: Get information about the user and account being used to make AWS calls. +description: + - This module returns information about the account and user / role from which the AWS access tokens originate. + - The primary use of this is to get the account id for templating into ARNs or similar to avoid needing to specify this information in inventory. + - This module was called M(amazon.aws.aws_caller_facts) before Ansible 2.9. The usage did not change. + +author: + - Ed Costello (@orthanc) + - Stijn Dubrul (@sdubrul) + +requirements: [ 'botocore', 'boto3' ] +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Get the current caller identity information + amazon.aws.aws_caller_info: + register: caller_info +''' + +RETURN = ''' +account: + description: The account id the access credentials are associated with. + returned: success + type: str + sample: "123456789012" +account_alias: + description: The account alias the access credentials are associated with. + returned: when caller has the iam:ListAccountAliases permission + type: str + sample: "acme-production" +arn: + description: The arn identifying the user the credentials are associated with. + returned: success + type: str + sample: arn:aws:sts::123456789012:federated-user/my-federated-user-name +user_id: + description: | + The user id the access credentials are associated with. Note that this may not correspond to + anything you can look up in the case of roles or federated identities. + returned: success + type: str + sample: 123456789012:my-federated-user-name +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry + + +def main(): + module = AnsibleAWSModule( + argument_spec={}, + supports_check_mode=True, + ) + if module._name == 'aws_caller_facts': + module.deprecate("The 'aws_caller_facts' module has been renamed to 'aws_caller_info'", date='2021-12-01', collection_name='amazon.aws') + + client = module.client('sts', retry_decorator=AWSRetry.jittered_backoff()) + + try: + caller_info = client.get_caller_identity(aws_retry=True) + caller_info.pop('ResponseMetadata', None) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to retrieve caller identity') + + iam_client = module.client('iam', retry_decorator=AWSRetry.jittered_backoff()) + + try: + # Although a list is returned by list_account_aliases AWS supports maximum one alias per account. + # If an alias is defined it will be returned otherwise a blank string is filled in as account_alias. + # see https://docs.aws.amazon.com/cli/latest/reference/iam/list-account-aliases.html#output + response = iam_client.list_account_aliases(aws_retry=True) + if response and response['AccountAliases']: + caller_info['account_alias'] = response['AccountAliases'][0] + else: + caller_info['account_alias'] = '' + except (BotoCoreError, ClientError) as e: + # The iam:ListAccountAliases permission is required for this operation to succeed. + # Lacking this permission is handled gracefully by not returning the account_alias. + pass + + module.exit_json( + changed=False, + **camel_dict_to_snake_dict(caller_info)) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_s3.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_s3.py new file mode 100644 index 00000000..eb6d8b90 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/aws_s3.py @@ -0,0 +1,947 @@ +#!/usr/bin/python +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: aws_s3 +version_added: 1.0.0 +short_description: manage objects in S3. +description: + - This module allows the user to manage S3 buckets and the objects within them. Includes support for creating and + deleting both objects and buckets, retrieving objects as files or strings and generating download links. + This module has a dependency on boto3 and botocore. +options: + bucket: + description: + - Bucket name. + required: true + type: str + dest: + description: + - The destination file path when downloading an object/key with a GET operation. + type: path + encrypt: + description: + - When set for PUT mode, asks for server-side encryption. + default: true + type: bool + encryption_mode: + description: + - What encryption mode to use if I(encrypt=true). + default: AES256 + choices: + - AES256 + - aws:kms + type: str + expiry: + description: + - Time limit (in seconds) for the URL generated and returned by S3/Walrus when performing a I(mode=put) or I(mode=geturl) operation. + default: 600 + aliases: ['expiration'] + type: int + headers: + description: + - Custom headers for PUT operation, as a dictionary of C(key=value) and C(key=value,key=value). + type: dict + marker: + description: + - Specifies the key to start with when using list mode. Object keys are returned in alphabetical order, starting with key after the marker in order. + type: str + max_keys: + description: + - Max number of results to return in list mode, set this if you want to retrieve fewer than the default 1000 keys. + default: 1000 + type: int + metadata: + description: + - Metadata for PUT operation, as a dictionary of C(key=value) and C(key=value,key=value). + type: dict + mode: + description: + - Switches the module behaviour between C(put) (upload), C(get) (download), C(geturl) (return download url, Ansible 1.3+), + C(getstr) (download object as string (1.3+)), C(list) (list keys, Ansible 2.0+), C(create) (bucket), C(delete) (bucket), + and delobj (delete object, Ansible 2.0+). + required: true + choices: ['get', 'put', 'delete', 'create', 'geturl', 'getstr', 'delobj', 'list'] + type: str + object: + description: + - Keyname of the object inside the bucket. Can be used to create "virtual directories", see examples. + type: str + permission: + description: + - This option lets the user set the canned permissions on the object/bucket that are created. + The permissions that can be set are C(private), C(public-read), C(public-read-write), C(authenticated-read) for a bucket or + C(private), C(public-read), C(public-read-write), C(aws-exec-read), C(authenticated-read), C(bucket-owner-read), + C(bucket-owner-full-control) for an object. Multiple permissions can be specified as a list. + default: ['private'] + type: list + elements: str + prefix: + description: + - Limits the response to keys that begin with the specified prefix for list mode. + default: "" + type: str + version: + description: + - Version ID of the object inside the bucket. Can be used to get a specific version of a file if versioning is enabled in the target bucket. + type: str + overwrite: + description: + - Force overwrite either locally on the filesystem or remotely with the object/key. Used with PUT and GET operations. + - Must be a Boolean, C(always), C(never) or C(different). + - C(true) is the same as C(always). + - C(false) is equal to C(never). + - When this is set to C(different) the MD5 sum of the local file is compared with the 'ETag' of the object/key in S3. + The ETag may or may not be an MD5 digest of the object data. See the ETag response header here + U(https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html). + default: 'always' + aliases: ['force'] + type: str + retries: + description: + - On recoverable failure, how many times to retry before actually failing. + default: 0 + type: int + aliases: ['retry'] + s3_url: + description: + - S3 URL endpoint for usage with Ceph, Eucalyptus and fakes3 etc. Otherwise assumes AWS. + aliases: [ S3_URL ] + type: str + dualstack: + description: + - Enables Amazon S3 Dual-Stack Endpoints, allowing S3 communications using both IPv4 and IPv6. + - Requires at least botocore version 1.4.45. + type: bool + default: false + rgw: + description: + - Enable Ceph RGW S3 support. This option requires an explicit url via I(s3_url). + default: false + type: bool + src: + description: + - The source file path when performing a PUT operation. + - Either I(content), I(content_base64) or I(src) must be specified for a PUT operation. Ignored otherwise. + type: path + content: + description: + - The content to PUT into an object. + - The parameter value will be treated as a string and converted to UTF-8 before sending it to S3. + To send binary data, use the I(content_base64) parameter instead. + - Either I(content), I(content_base64) or I(src) must be specified for a PUT operation. Ignored otherwise. + version_added: "1.3.0" + type: str + content_base64: + description: + - The base64-encoded binary data to PUT into an object. + - Use this if you need to put raw binary data, and don't forget to encode in base64. + - Either I(content), I(content_base64) or I(src) must be specified for a PUT operation. Ignored otherwise. + version_added: "1.3.0" + type: str + ignore_nonexistent_bucket: + description: + - "Overrides initial bucket lookups in case bucket or iam policies are restrictive. Example: a user may have the + GetObject permission but no other permissions. In this case using the option mode: get will fail without specifying + I(ignore_nonexistent_bucket=true)." + type: bool + default: false + encryption_kms_key_id: + description: + - KMS key id to use when encrypting objects using I(encrypting=aws:kms). Ignored if I(encryption) is not C(aws:kms). + type: str +requirements: [ "boto3", "botocore" ] +author: + - "Lester Wade (@lwade)" + - "Sloane Hertel (@s-hertel)" +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +- name: Simple PUT operation + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + +- name: PUT operation from a rendered template + amazon.aws.aws_s3: + bucket: mybucket + object: /object.yaml + content: "{{ lookup('template', 'templates/object.yaml.j2') }}" + mode: put + +- name: Simple PUT operation in Ceph RGW S3 + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + rgw: true + s3_url: "http://localhost:8000" + +- name: Simple GET operation + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + dest: /usr/local/myfile.txt + mode: get + +- name: Get a specific version of an object. + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + version: 48c9ee5131af7a716edc22df9772aa6f + dest: /usr/local/myfile.txt + mode: get + +- name: PUT/upload with metadata + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + metadata: 'Content-Encoding=gzip,Cache-Control=no-cache' + +- name: PUT/upload with custom headers + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + src: /usr/local/myfile.txt + mode: put + headers: 'x-amz-grant-full-control=emailAddress=owner@example.com' + +- name: List keys simple + amazon.aws.aws_s3: + bucket: mybucket + mode: list + +- name: List keys all options + amazon.aws.aws_s3: + bucket: mybucket + mode: list + prefix: /my/desired/ + marker: /my/desired/0023.txt + max_keys: 472 + +- name: Create an empty bucket + amazon.aws.aws_s3: + bucket: mybucket + mode: create + permission: public-read + +- name: Create a bucket with key as directory, in the EU region + amazon.aws.aws_s3: + bucket: mybucket + object: /my/directory/path + mode: create + region: eu-west-1 + +- name: Delete a bucket and all contents + amazon.aws.aws_s3: + bucket: mybucket + mode: delete + +- name: GET an object but don't download if the file checksums match. New in 2.0 + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + dest: /usr/local/myfile.txt + mode: get + overwrite: different + +- name: Delete an object from a bucket + amazon.aws.aws_s3: + bucket: mybucket + object: /my/desired/key.txt + mode: delobj +''' + +RETURN = ''' +msg: + description: Message indicating the status of the operation. + returned: always + type: str + sample: PUT operation complete +url: + description: URL of the object. + returned: (for put and geturl operations) + type: str + sample: https://my-bucket.s3.amazonaws.com/my-key.txt?AWSAccessKeyId=<access-key>&Expires=1506888865&Signature=<signature> +expiry: + description: Number of seconds the presigned url is valid for. + returned: (for geturl operation) + type: int + sample: 600 +contents: + description: Contents of the object as string. + returned: (for getstr operation) + type: str + sample: "Hello, world!" +s3_keys: + description: List of object keys. + returned: (for list operation) + type: list + elements: str + sample: + - prefix1/ + - prefix1/key1 + - prefix1/key2 +''' + +import mimetypes +import os +import io +from ssl import SSLError +import base64 + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.basic import to_text +from ansible.module_utils.basic import to_native +from ansible.module_utils.six.moves.urllib.parse import urlparse + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.core import is_boto3_error_message +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import boto3_conn +from ..module_utils.ec2 import get_aws_connection_info +from ..module_utils.s3 import HAS_MD5 +from ..module_utils.s3 import calculate_etag +from ..module_utils.s3 import calculate_etag_content + +IGNORE_S3_DROP_IN_EXCEPTIONS = ['XNotImplemented', 'NotImplemented'] + + +class Sigv4Required(Exception): + pass + + +def key_check(module, s3, bucket, obj, version=None, validate=True): + try: + if version: + s3.head_object(Bucket=bucket, Key=obj, VersionId=version) + else: + s3.head_object(Bucket=bucket, Key=obj) + except is_boto3_error_code('404'): + return False + except is_boto3_error_code('403') as e: + if validate is True: + module.fail_json_aws(e, msg="Failed while looking up object (during key check) %s." % obj) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Failed while looking up object (during key check) %s." % obj) + + return True + + +def etag_compare(module, s3, bucket, obj, version=None, local_file=None, content=None): + s3_etag = get_etag(s3, bucket, obj, version=version) + if local_file is not None: + local_etag = calculate_etag(module, local_file, s3_etag, s3, bucket, obj, version) + else: + local_etag = calculate_etag_content(module, content, s3_etag, s3, bucket, obj, version) + + return s3_etag == local_etag + + +def get_etag(s3, bucket, obj, version=None): + if version: + key_check = s3.head_object(Bucket=bucket, Key=obj, VersionId=version) + else: + key_check = s3.head_object(Bucket=bucket, Key=obj) + if not key_check: + return None + return key_check['ETag'] + + +def bucket_check(module, s3, bucket, validate=True): + exists = True + try: + s3.head_bucket(Bucket=bucket) + except is_boto3_error_code('404'): + return False + except is_boto3_error_code('403') as e: + if validate is True: + module.fail_json_aws(e, msg="Failed while looking up bucket (during bucket_check) %s." % bucket) + except botocore.exceptions.EndpointConnectionError as e: + module.fail_json_aws(e, msg="Invalid endpoint provided") + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Failed while looking up bucket (during bucket_check) %s." % bucket) + return exists + + +def create_bucket(module, s3, bucket, location=None): + if module.check_mode: + module.exit_json(msg="CREATE operation skipped - running in check mode", changed=True) + configuration = {} + if location not in ('us-east-1', None): + configuration['LocationConstraint'] = location + try: + if len(configuration) > 0: + s3.create_bucket(Bucket=bucket, CreateBucketConfiguration=configuration) + else: + s3.create_bucket(Bucket=bucket) + if module.params.get('permission'): + # Wait for the bucket to exist before setting ACLs + s3.get_waiter('bucket_exists').wait(Bucket=bucket) + for acl in module.params.get('permission'): + AWSRetry.jittered_backoff( + max_delay=120, catch_extra_error_codes=['NoSuchBucket'] + )(s3.put_bucket_acl)(ACL=acl, Bucket=bucket) + except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS): + module.warn("PutBucketAcl is not implemented by your storage provider. Set the permission parameters to the empty list to avoid this warning") + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Failed while creating bucket or setting acl (check that you have CreateBucket and PutBucketAcl permission).") + + if bucket: + return True + + +def paginated_list(s3, **pagination_params): + pg = s3.get_paginator('list_objects_v2') + for page in pg.paginate(**pagination_params): + yield [data['Key'] for data in page.get('Contents', [])] + + +def paginated_versioned_list_with_fallback(s3, **pagination_params): + try: + versioned_pg = s3.get_paginator('list_object_versions') + for page in versioned_pg.paginate(**pagination_params): + delete_markers = [{'Key': data['Key'], 'VersionId': data['VersionId']} for data in page.get('DeleteMarkers', [])] + current_objects = [{'Key': data['Key'], 'VersionId': data['VersionId']} for data in page.get('Versions', [])] + yield delete_markers + current_objects + except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS + ['AccessDenied']): + for page in paginated_list(s3, **pagination_params): + yield [{'Key': data['Key']} for data in page] + + +def list_keys(module, s3, bucket, prefix, marker, max_keys): + pagination_params = {'Bucket': bucket} + for param_name, param_value in (('Prefix', prefix), ('StartAfter', marker), ('MaxKeys', max_keys)): + pagination_params[param_name] = param_value + try: + keys = sum(paginated_list(s3, **pagination_params), []) + module.exit_json(msg="LIST operation complete", s3_keys=keys) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed while listing the keys in the bucket {0}".format(bucket)) + + +def delete_bucket(module, s3, bucket): + if module.check_mode: + module.exit_json(msg="DELETE operation skipped - running in check mode", changed=True) + try: + exists = bucket_check(module, s3, bucket) + if exists is False: + return False + # if there are contents then we need to delete them before we can delete the bucket + for keys in paginated_versioned_list_with_fallback(s3, Bucket=bucket): + if keys: + s3.delete_objects(Bucket=bucket, Delete={'Objects': keys}) + s3.delete_bucket(Bucket=bucket) + return True + except is_boto3_error_code('NoSuchBucket'): + return False + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed while deleting bucket %s." % bucket) + + +def delete_key(module, s3, bucket, obj): + if module.check_mode: + module.exit_json(msg="DELETE operation skipped - running in check mode", changed=True) + try: + s3.delete_object(Bucket=bucket, Key=obj) + module.exit_json(msg="Object deleted from bucket %s." % (bucket), changed=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed while trying to delete %s." % obj) + + +def create_dirkey(module, s3, bucket, obj, encrypt): + if module.check_mode: + module.exit_json(msg="PUT operation skipped - running in check mode", changed=True) + try: + params = {'Bucket': bucket, 'Key': obj, 'Body': b''} + if encrypt: + params['ServerSideEncryption'] = module.params['encryption_mode'] + if module.params['encryption_kms_key_id'] and module.params['encryption_mode'] == 'aws:kms': + params['SSEKMSKeyId'] = module.params['encryption_kms_key_id'] + + s3.put_object(**params) + for acl in module.params.get('permission'): + s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj) + except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS): + module.warn("PutObjectAcl is not implemented by your storage provider. Set the permissions parameters to the empty list to avoid this warning") + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Failed while creating object %s." % obj) + module.exit_json(msg="Virtual directory %s created in bucket %s" % (obj, bucket), changed=True) + + +def path_check(path): + if os.path.exists(path): + return True + else: + return False + + +def option_in_extra_args(option): + temp_option = option.replace('-', '').lower() + + allowed_extra_args = {'acl': 'ACL', 'cachecontrol': 'CacheControl', 'contentdisposition': 'ContentDisposition', + 'contentencoding': 'ContentEncoding', 'contentlanguage': 'ContentLanguage', + 'contenttype': 'ContentType', 'expires': 'Expires', 'grantfullcontrol': 'GrantFullControl', + 'grantread': 'GrantRead', 'grantreadacp': 'GrantReadACP', 'grantwriteacp': 'GrantWriteACP', + 'metadata': 'Metadata', 'requestpayer': 'RequestPayer', 'serversideencryption': 'ServerSideEncryption', + 'storageclass': 'StorageClass', 'ssecustomeralgorithm': 'SSECustomerAlgorithm', 'ssecustomerkey': 'SSECustomerKey', + 'ssecustomerkeymd5': 'SSECustomerKeyMD5', 'ssekmskeyid': 'SSEKMSKeyId', 'websiteredirectlocation': 'WebsiteRedirectLocation'} + + if temp_option in allowed_extra_args: + return allowed_extra_args[temp_option] + + +def upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, src=None, content=None): + if module.check_mode: + module.exit_json(msg="PUT operation skipped - running in check mode", changed=True) + try: + extra = {} + if encrypt: + extra['ServerSideEncryption'] = module.params['encryption_mode'] + if module.params['encryption_kms_key_id'] and module.params['encryption_mode'] == 'aws:kms': + extra['SSEKMSKeyId'] = module.params['encryption_kms_key_id'] + if metadata: + extra['Metadata'] = {} + + # determine object metadata and extra arguments + for option in metadata: + extra_args_option = option_in_extra_args(option) + if extra_args_option is not None: + extra[extra_args_option] = metadata[option] + else: + extra['Metadata'][option] = metadata[option] + + if 'ContentType' not in extra: + content_type = None + if src is not None: + content_type = mimetypes.guess_type(src)[0] + if content_type is None: + # s3 default content type + content_type = 'binary/octet-stream' + extra['ContentType'] = content_type + + if src is not None: + s3.upload_file(Filename=src, Bucket=bucket, Key=obj, ExtraArgs=extra) + else: + f = io.BytesIO(content) + s3.upload_fileobj(Fileobj=f, Bucket=bucket, Key=obj, ExtraArgs=extra) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to complete PUT operation.") + try: + for acl in module.params.get('permission'): + s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj) + except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS): + module.warn("PutObjectAcl is not implemented by your storage provider. Set the permission parameters to the empty list to avoid this warning") + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Unable to set object ACL") + try: + url = s3.generate_presigned_url(ClientMethod='put_object', + Params={'Bucket': bucket, 'Key': obj}, + ExpiresIn=expiry) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to generate presigned URL") + module.exit_json(msg="PUT operation complete", url=url, changed=True) + + +def download_s3file(module, s3, bucket, obj, dest, retries, version=None): + if module.check_mode: + module.exit_json(msg="GET operation skipped - running in check mode", changed=True) + # retries is the number of loops; range/xrange needs to be one + # more to get that count of loops. + try: + if version: + key = s3.get_object(Bucket=bucket, Key=obj, VersionId=version) + else: + key = s3.get_object(Bucket=bucket, Key=obj) + except is_boto3_error_code(['404', '403']) as e: + # AccessDenied errors may be triggered if 1) file does not exist or 2) file exists but + # user does not have the s3:GetObject permission. 404 errors are handled by download_file(). + module.fail_json_aws(e, msg="Could not find the key %s." % obj) + except is_boto3_error_message('require AWS Signature Version 4'): + raise Sigv4Required() + except is_boto3_error_code('InvalidArgument') as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Could not find the key %s." % obj) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Could not find the key %s." % obj) + + optional_kwargs = {'ExtraArgs': {'VersionId': version}} if version else {} + for x in range(0, retries + 1): + try: + s3.download_file(bucket, obj, dest, **optional_kwargs) + module.exit_json(msg="GET operation complete", changed=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + # actually fail on last pass through the loop. + if x >= retries: + module.fail_json_aws(e, msg="Failed while downloading %s." % obj) + # otherwise, try again, this may be a transient timeout. + except SSLError as e: # will ClientError catch SSLError? + # actually fail on last pass through the loop. + if x >= retries: + module.fail_json_aws(e, msg="s3 download failed") + # otherwise, try again, this may be a transient timeout. + + +def download_s3str(module, s3, bucket, obj, version=None, validate=True): + if module.check_mode: + module.exit_json(msg="GET operation skipped - running in check mode", changed=True) + try: + if version: + contents = to_native(s3.get_object(Bucket=bucket, Key=obj, VersionId=version)["Body"].read()) + else: + contents = to_native(s3.get_object(Bucket=bucket, Key=obj)["Body"].read()) + module.exit_json(msg="GET operation complete", contents=contents, changed=True) + except is_boto3_error_message('require AWS Signature Version 4'): + raise Sigv4Required() + except is_boto3_error_code('InvalidArgument') as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Failed while getting contents of object %s as a string." % obj) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Failed while getting contents of object %s as a string." % obj) + + +def get_download_url(module, s3, bucket, obj, expiry, changed=True): + try: + url = s3.generate_presigned_url(ClientMethod='get_object', + Params={'Bucket': bucket, 'Key': obj}, + ExpiresIn=expiry) + module.exit_json(msg="Download url:", url=url, expiry=expiry, changed=changed) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed while getting download url.") + + +def is_fakes3(s3_url): + """ Return True if s3_url has scheme fakes3:// """ + if s3_url is not None: + return urlparse(s3_url).scheme in ('fakes3', 'fakes3s') + else: + return False + + +def get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url, sig_4=False): + if s3_url and rgw: # TODO - test this + rgw = urlparse(s3_url) + params = dict(module=module, conn_type='client', resource='s3', use_ssl=rgw.scheme == 'https', region=location, endpoint=s3_url, **aws_connect_kwargs) + elif is_fakes3(s3_url): + fakes3 = urlparse(s3_url) + port = fakes3.port + if fakes3.scheme == 'fakes3s': + protocol = "https" + if port is None: + port = 443 + else: + protocol = "http" + if port is None: + port = 80 + params = dict(module=module, conn_type='client', resource='s3', region=location, + endpoint="%s://%s:%s" % (protocol, fakes3.hostname, to_text(port)), + use_ssl=fakes3.scheme == 'fakes3s', **aws_connect_kwargs) + else: + params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=s3_url, **aws_connect_kwargs) + if module.params['mode'] == 'put' and module.params['encryption_mode'] == 'aws:kms': + params['config'] = botocore.client.Config(signature_version='s3v4') + elif module.params['mode'] in ('get', 'getstr') and sig_4: + params['config'] = botocore.client.Config(signature_version='s3v4') + if module.params['dualstack']: + dualconf = botocore.client.Config(s3={'use_dualstack_endpoint': True}) + if 'config' in params: + params['config'] = params['config'].merge(dualconf) + else: + params['config'] = dualconf + return boto3_conn(**params) + + +def main(): + argument_spec = dict( + bucket=dict(required=True), + dest=dict(default=None, type='path'), + encrypt=dict(default=True, type='bool'), + encryption_mode=dict(choices=['AES256', 'aws:kms'], default='AES256'), + expiry=dict(default=600, type='int', aliases=['expiration']), + headers=dict(type='dict'), + marker=dict(default=""), + max_keys=dict(default=1000, type='int'), + metadata=dict(type='dict'), + mode=dict(choices=['get', 'put', 'delete', 'create', 'geturl', 'getstr', 'delobj', 'list'], required=True), + object=dict(), + permission=dict(type='list', elements='str', default=['private']), + version=dict(default=None), + overwrite=dict(aliases=['force'], default='always'), + prefix=dict(default=""), + retries=dict(aliases=['retry'], type='int', default=0), + s3_url=dict(aliases=['S3_URL']), + dualstack=dict(default='no', type='bool'), + rgw=dict(default='no', type='bool'), + src=dict(type='path'), + content=dict(), + content_base64=dict(), + ignore_nonexistent_bucket=dict(default=False, type='bool'), + encryption_kms_key_id=dict() + ) + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[['mode', 'put', ['object']], + ['mode', 'get', ['dest', 'object']], + ['mode', 'getstr', ['object']], + ['mode', 'geturl', ['object']]], + mutually_exclusive=[['content', 'content_base64', 'src']], + ) + + bucket = module.params.get('bucket') + encrypt = module.params.get('encrypt') + expiry = module.params.get('expiry') + dest = module.params.get('dest', '') + headers = module.params.get('headers') + marker = module.params.get('marker') + max_keys = module.params.get('max_keys') + metadata = module.params.get('metadata') + mode = module.params.get('mode') + obj = module.params.get('object') + version = module.params.get('version') + overwrite = module.params.get('overwrite') + prefix = module.params.get('prefix') + retries = module.params.get('retries') + s3_url = module.params.get('s3_url') + dualstack = module.params.get('dualstack') + rgw = module.params.get('rgw') + src = module.params.get('src') + content = module.params.get('content') + content_base64 = module.params.get('content_base64') + ignore_nonexistent_bucket = module.params.get('ignore_nonexistent_bucket') + + object_canned_acl = ["private", "public-read", "public-read-write", "aws-exec-read", "authenticated-read", "bucket-owner-read", "bucket-owner-full-control"] + bucket_canned_acl = ["private", "public-read", "public-read-write", "authenticated-read"] + + if overwrite not in ['always', 'never', 'different']: + if module.boolean(overwrite): + overwrite = 'always' + else: + overwrite = 'never' + + if overwrite == 'different' and not HAS_MD5: + module.fail_json(msg='overwrite=different is unavailable: ETag calculation requires MD5 support') + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + + if region in ('us-east-1', '', None): + # default to US Standard region + location = 'us-east-1' + else: + # Boto uses symbolic names for locations but region strings will + # actually work fine for everything except us-east-1 (US Standard) + location = region + + if module.params.get('object'): + obj = module.params['object'] + # If there is a top level object, do nothing - if the object starts with / + # remove the leading character to maintain compatibility with Ansible versions < 2.4 + if obj.startswith('/'): + obj = obj[1:] + + # Bucket deletion does not require obj. Prevents ambiguity with delobj. + if obj and mode == "delete": + module.fail_json(msg='Parameter obj cannot be used with mode=delete') + + # allow eucarc environment variables to be used if ansible vars aren't set + if not s3_url and 'S3_URL' in os.environ: + s3_url = os.environ['S3_URL'] + + if dualstack and s3_url is not None and 'amazonaws.com' not in s3_url: + module.fail_json(msg='dualstack only applies to AWS S3') + + if dualstack and not module.botocore_at_least('1.4.45'): + module.fail_json(msg='dualstack requires botocore >= 1.4.45') + + # rgw requires an explicit url + if rgw and not s3_url: + module.fail_json(msg='rgw flavour requires s3_url') + + # Look at s3_url and tweak connection settings + # if connecting to RGW, Walrus or fakes3 + if s3_url: + for key in ['validate_certs', 'security_token', 'profile_name']: + aws_connect_kwargs.pop(key, None) + s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url) + + validate = not ignore_nonexistent_bucket + + # separate types of ACLs + bucket_acl = [acl for acl in module.params.get('permission') if acl in bucket_canned_acl] + object_acl = [acl for acl in module.params.get('permission') if acl in object_canned_acl] + error_acl = [acl for acl in module.params.get('permission') if acl not in bucket_canned_acl and acl not in object_canned_acl] + if error_acl: + module.fail_json(msg='Unknown permission specified: %s' % error_acl) + + # First, we check to see if the bucket exists, we get "bucket" returned. + bucketrtn = bucket_check(module, s3, bucket, validate=validate) + + if validate and mode not in ('create', 'put', 'delete') and not bucketrtn: + module.fail_json(msg="Source bucket cannot be found.") + + if mode == 'get': + keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) + if keyrtn is False: + if version: + module.fail_json(msg="Key %s with version id %s does not exist." % (obj, version)) + else: + module.fail_json(msg="Key %s does not exist." % obj) + + if dest and path_check(dest) and overwrite != 'always': + if overwrite == 'never': + module.exit_json(msg="Local object already exists and overwrite is disabled.", changed=False) + if etag_compare(module, s3, bucket, obj, version=version, local_file=dest): + module.exit_json(msg="Local and remote object are identical, ignoring. Use overwrite=always parameter to force.", changed=False) + + try: + download_s3file(module, s3, bucket, obj, dest, retries, version=version) + except Sigv4Required: + s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url, sig_4=True) + download_s3file(module, s3, bucket, obj, dest, retries, version=version) + + if mode == 'put': + + # if putting an object in a bucket yet to be created, acls for the bucket and/or the object may be specified + # these were separated into the variables bucket_acl and object_acl above + + if content is None and content_base64 is None and src is None: + module.fail_json('Either content, content_base64 or src must be specified for PUT operations') + if src is not None and not path_check(src): + module.fail_json('Local object "%s" does not exist for PUT operation' % (src)) + + if bucketrtn: + keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) + else: + # If the bucket doesn't exist we should create it. + # only use valid bucket acls for create_bucket function + module.params['permission'] = bucket_acl + create_bucket(module, s3, bucket, location) + + # the content will be uploaded as a byte string, so we must encode it first + bincontent = None + if content is not None: + bincontent = content.encode('utf-8') + if content_base64 is not None: + bincontent = base64.standard_b64decode(content_base64) + + if keyrtn and overwrite != 'always': + if overwrite == 'never' or etag_compare(module, s3, bucket, obj, version=version, local_file=src, content=bincontent): + # Return the download URL for the existing object + get_download_url(module, s3, bucket, obj, expiry, changed=False) + + # only use valid object acls for the upload_s3file function + module.params['permission'] = object_acl + upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, src=src, content=bincontent) + + # Delete an object from a bucket, not the entire bucket + if mode == 'delobj': + if obj is None: + module.fail_json(msg="object parameter is required") + if bucket: + deletertn = delete_key(module, s3, bucket, obj) + if deletertn is True: + module.exit_json(msg="Object deleted from bucket %s." % bucket, changed=True) + else: + module.fail_json(msg="Bucket parameter is required.") + + # Delete an entire bucket, including all objects in the bucket + if mode == 'delete': + if bucket: + deletertn = delete_bucket(module, s3, bucket) + if deletertn is True: + module.exit_json(msg="Bucket %s and all keys have been deleted." % bucket, changed=True) + else: + module.fail_json(msg="Bucket parameter is required.") + + # Support for listing a set of keys + if mode == 'list': + exists = bucket_check(module, s3, bucket) + + # If the bucket does not exist then bail out + if not exists: + module.fail_json(msg="Target bucket (%s) cannot be found" % bucket) + + list_keys(module, s3, bucket, prefix, marker, max_keys) + + # Need to research how to create directories without "populating" a key, so this should just do bucket creation for now. + # WE SHOULD ENABLE SOME WAY OF CREATING AN EMPTY KEY TO CREATE "DIRECTORY" STRUCTURE, AWS CONSOLE DOES THIS. + if mode == 'create': + + # if both creating a bucket and putting an object in it, acls for the bucket and/or the object may be specified + # these were separated above into the variables bucket_acl and object_acl + + if bucket and not obj: + if bucketrtn: + module.exit_json(msg="Bucket already exists.", changed=False) + else: + # only use valid bucket acls when creating the bucket + module.params['permission'] = bucket_acl + module.exit_json(msg="Bucket created successfully", changed=create_bucket(module, s3, bucket, location)) + if bucket and obj: + if obj.endswith('/'): + dirobj = obj + else: + dirobj = obj + "/" + if bucketrtn: + if key_check(module, s3, bucket, dirobj): + module.exit_json(msg="Bucket %s and key %s already exists." % (bucket, obj), changed=False) + else: + # setting valid object acls for the create_dirkey function + module.params['permission'] = object_acl + create_dirkey(module, s3, bucket, dirobj, encrypt) + else: + # only use valid bucket acls for the create_bucket function + module.params['permission'] = bucket_acl + created = create_bucket(module, s3, bucket, location) + # only use valid object acls for the create_dirkey function + module.params['permission'] = object_acl + create_dirkey(module, s3, bucket, dirobj, encrypt) + + # Support for grabbing the time-expired URL for an object in S3/Walrus. + if mode == 'geturl': + if not bucket and not obj: + module.fail_json(msg="Bucket and Object parameters must be set") + + keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) + if keyrtn: + get_download_url(module, s3, bucket, obj, expiry) + else: + module.fail_json(msg="Key %s does not exist." % obj) + + if mode == 'getstr': + if bucket and obj: + keyrtn = key_check(module, s3, bucket, obj, version=version, validate=validate) + if keyrtn: + try: + download_s3str(module, s3, bucket, obj, version=version) + except Sigv4Required: + s3 = get_s3_connection(module, aws_connect_kwargs, location, rgw, s3_url, sig_4=True) + download_s3str(module, s3, bucket, obj, version=version) + elif version is not None: + module.fail_json(msg="Key %s with version id %s does not exist." % (obj, version)) + else: + module.fail_json(msg="Key %s does not exist." % obj) + + module.exit_json(failed=False) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation.py new file mode 100644 index 00000000..030bfc45 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation.py @@ -0,0 +1,808 @@ +#!/usr/bin/python + +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: cloudformation +version_added: 1.0.0 +short_description: Create or delete an AWS CloudFormation stack +description: + - Launches or updates an AWS CloudFormation stack and waits for it complete. +notes: + - CloudFormation features change often, and this module tries to keep up. That means your botocore version should be fresh. + The version listed in the requirements is the oldest version that works with the module as a whole. + Some features may require recent versions, and we do not pinpoint a minimum version for each feature. + Instead of relying on the minimum version, keep botocore up to date. AWS is always releasing features and fixing bugs. +options: + stack_name: + description: + - Name of the CloudFormation stack. + required: true + type: str + disable_rollback: + description: + - If a stacks fails to form, rollback will remove the stack. + default: false + type: bool + on_create_failure: + description: + - Action to take upon failure of stack creation. Incompatible with the I(disable_rollback) option. + choices: + - DO_NOTHING + - ROLLBACK + - DELETE + type: str + create_timeout: + description: + - The amount of time (in minutes) that can pass before the stack status becomes CREATE_FAILED + type: int + template_parameters: + description: + - A list of hashes of all the template variables for the stack. The value can be a string or a dict. + - Dict can be used to set additional template parameter attributes like UsePreviousValue (see example). + default: {} + type: dict + state: + description: + - If I(state=present), stack will be created. + - If I(state=present) and if stack exists and template has changed, it will be updated. + - If I(state=absent), stack will be removed. + default: present + choices: [ present, absent ] + type: str + template: + description: + - The local path of the CloudFormation template. + - This must be the full path to the file, relative to the working directory. If using roles this may look + like C(roles/cloudformation/files/cloudformation-example.json). + - If I(state=present) and the stack does not exist yet, either I(template), I(template_body) or I(template_url) + must be specified (but only one of them). + - If I(state=present), the stack does exist, and neither I(template), + I(template_body) nor I(template_url) are specified, the previous template will be reused. + type: path + notification_arns: + description: + - A comma separated list of Simple Notification Service (SNS) topic ARNs to publish stack related events. + type: str + stack_policy: + description: + - The path of the CloudFormation stack policy. A policy cannot be removed once placed, but it can be modified. + for instance, allow all updates U(https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/protect-stack-resources.html#d0e9051) + type: str + tags: + description: + - Dictionary of tags to associate with stack and its resources during stack creation. + - Can be updated later, updating tags removes previous entries. + type: dict + template_url: + description: + - Location of file containing the template body. The URL must point to a template (max size 307,200 bytes) located in an + S3 bucket in the same region as the stack. + - If I(state=present) and the stack does not exist yet, either I(template), I(template_body) or I(template_url) + must be specified (but only one of them). + - If I(state=present), the stack does exist, and neither I(template), I(template_body) nor I(template_url) are specified, + the previous template will be reused. + type: str + create_changeset: + description: + - "If stack already exists create a changeset instead of directly applying changes. See the AWS Change Sets docs + U(https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html)." + - "WARNING: if the stack does not exist, it will be created without changeset. If I(state=absent), the stack will be + deleted immediately with no changeset." + type: bool + default: false + changeset_name: + description: + - Name given to the changeset when creating a changeset. + - Only used when I(create_changeset=true). + - By default a name prefixed with Ansible-STACKNAME is generated based on input parameters. + See the AWS Change Sets docs for more information + U(https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html) + type: str + template_format: + description: + - This parameter is ignored since Ansible 2.3 and will be removed after 2022-06-01. + - Templates are now passed raw to CloudFormation regardless of format. + type: str + role_arn: + description: + - The role that AWS CloudFormation assumes to create the stack. See the AWS CloudFormation Service Role + docs U(https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-servicerole.html) + type: str + termination_protection: + description: + - Enable or disable termination protection on the stack. Only works with botocore >= 1.7.18. + type: bool + template_body: + description: + - Template body. Use this to pass in the actual body of the CloudFormation template. + - If I(state=present) and the stack does not exist yet, either I(template), I(template_body) or I(template_url) + must be specified (but only one of them). + - If I(state=present), the stack does exist, and neither I(template), I(template_body) nor I(template_url) + are specified, the previous template will be reused. + type: str + events_limit: + description: + - Maximum number of CloudFormation events to fetch from a stack when creating or updating it. + default: 200 + type: int + backoff_delay: + description: + - Number of seconds to wait for the next retry. + default: 3 + type: int + required: False + backoff_max_delay: + description: + - Maximum amount of time to wait between retries. + default: 30 + type: int + required: False + backoff_retries: + description: + - Number of times to retry operation. + - AWS API throttling mechanism fails CloudFormation module so we have to retry a couple of times. + default: 10 + type: int + required: False + capabilities: + description: + - Specify capabilities that stack template contains. + - Valid values are C(CAPABILITY_IAM), C(CAPABILITY_NAMED_IAM) and C(CAPABILITY_AUTO_EXPAND). + type: list + elements: str + default: [ CAPABILITY_IAM, CAPABILITY_NAMED_IAM ] + +author: "James S. Martin (@jsmartin)" +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +requirements: [ boto3, botocore>=1.5.45 ] +''' + +EXAMPLES = ''' +- name: create a cloudformation stack + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: "present" + region: "us-east-1" + disable_rollback: true + template: "files/cloudformation-example.json" + template_parameters: + KeyName: "jmartin" + DiskType: "ephemeral" + InstanceType: "m1.small" + ClusterSize: 3 + tags: + Stack: "ansible-cloudformation" + +# Basic role example +- name: create a stack, specify role that cloudformation assumes + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: "present" + region: "us-east-1" + disable_rollback: true + template: "roles/cloudformation/files/cloudformation-example.json" + role_arn: 'arn:aws:iam::123456789012:role/cloudformation-iam-role' + +- name: delete a stack + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation-old" + state: "absent" + +# Create a stack, pass in template from a URL, disable rollback if stack creation fails, +# pass in some parameters to the template, provide tags for resources created +- name: create a stack, pass in the template via an URL + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: present + region: us-east-1 + disable_rollback: true + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + template_parameters: + KeyName: jmartin + DiskType: ephemeral + InstanceType: m1.small + ClusterSize: 3 + tags: + Stack: ansible-cloudformation + +# Create a stack, passing in template body using lookup of Jinja2 template, disable rollback if stack creation fails, +# pass in some parameters to the template, provide tags for resources created +- name: create a stack, pass in the template body via lookup template + amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: present + region: us-east-1 + disable_rollback: true + template_body: "{{ lookup('template', 'cloudformation.j2') }}" + template_parameters: + KeyName: jmartin + DiskType: ephemeral + InstanceType: m1.small + ClusterSize: 3 + tags: + Stack: ansible-cloudformation + +# Pass a template parameter which uses CloudFormation's UsePreviousValue attribute +# When use_previous_value is set to True, the given value will be ignored and +# CloudFormation will use the value from a previously submitted template. +# If use_previous_value is set to False (default) the given value is used. +- amazon.aws.cloudformation: + stack_name: "ansible-cloudformation" + state: "present" + region: "us-east-1" + template: "files/cloudformation-example.json" + template_parameters: + DBSnapshotIdentifier: + use_previous_value: True + value: arn:aws:rds:es-east-1:000000000000:snapshot:rds:my-db-snapshot + DBName: + use_previous_value: True + tags: + Stack: "ansible-cloudformation" + +# Enable termination protection on a stack. +# If the stack already exists, this will update its termination protection +- name: enable termination protection during stack creation + amazon.aws.cloudformation: + stack_name: my_stack + state: present + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + termination_protection: yes + +# Configure TimeoutInMinutes before the stack status becomes CREATE_FAILED +# In this case, if disable_rollback is not set or is set to false, the stack will be rolled back. +- name: enable termination protection during stack creation + amazon.aws.cloudformation: + stack_name: my_stack + state: present + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + create_timeout: 5 + +# Configure rollback behaviour on the unsuccessful creation of a stack allowing +# CloudFormation to clean up, or do nothing in the event of an unsuccessful +# deployment +# In this case, if on_create_failure is set to "DELETE", it will clean up the stack if +# it fails to create +- name: create stack which will delete on creation failure + amazon.aws.cloudformation: + stack_name: my_stack + state: present + template_url: https://s3.amazonaws.com/my-bucket/cloudformation.template + on_create_failure: DELETE +''' + +RETURN = ''' +events: + type: list + description: Most recent events in CloudFormation's event log. This may be from a previous run in some cases. + returned: always + sample: ["StackEvent AWS::CloudFormation::Stack stackname UPDATE_COMPLETE", "StackEvent AWS::CloudFormation::Stack stackname UPDATE_COMPLETE_CLEANUP_IN_PROGRESS"] +log: + description: Debugging logs. Useful when modifying or finding an error. + returned: always + type: list + sample: ["updating stack"] +change_set_id: + description: The ID of the stack change set if one was created + returned: I(state=present) and I(create_changeset=true) + type: str + sample: "arn:aws:cloudformation:us-east-1:012345678901:changeSet/Ansible-StackName-f4496805bd1b2be824d1e315c6884247ede41eb0" +stack_resources: + description: AWS stack resources and their status. List of dictionaries, one dict per resource. + returned: state == present + type: list + sample: [ + { + "last_updated_time": "2016-10-11T19:40:14.979000+00:00", + "logical_resource_id": "CFTestSg", + "physical_resource_id": "cloudformation2-CFTestSg-16UQ4CYQ57O9F", + "resource_type": "AWS::EC2::SecurityGroup", + "status": "UPDATE_COMPLETE", + "status_reason": null + } + ] +stack_outputs: + type: dict + description: A key:value dictionary of all the stack outputs currently defined. If there are no stack outputs, it is an empty dictionary. + returned: state == present + sample: {"MySg": "AnsibleModuleTestYAML-CFTestSg-C8UVS567B6NS"} +''' # NOQA + +import json +import time +import traceback +import uuid +from hashlib import sha1 + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils._text import to_bytes +from ansible.module_utils._text import to_native + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto_exception + + +def get_stack_events(cfn, stack_name, events_limit, token_filter=None): + '''This event data was never correct, it worked as a side effect. So the v2.3 format is different.''' + ret = {'events': [], 'log': []} + + try: + pg = cfn.get_paginator( + 'describe_stack_events' + ).paginate( + StackName=stack_name, + PaginationConfig={'MaxItems': events_limit} + ) + if token_filter is not None: + events = list(pg.search( + "StackEvents[?ClientRequestToken == '{0}']".format(token_filter) + )) + else: + events = list(pg.search("StackEvents[*]")) + except (botocore.exceptions.ValidationError, botocore.exceptions.ClientError) as err: + error_msg = boto_exception(err) + if 'does not exist' in error_msg: + # missing stack, don't bail. + ret['log'].append('Stack does not exist.') + return ret + ret['log'].append('Unknown error: ' + str(error_msg)) + return ret + + for e in events: + eventline = 'StackEvent {ResourceType} {LogicalResourceId} {ResourceStatus}'.format(**e) + ret['events'].append(eventline) + + if e['ResourceStatus'].endswith('FAILED'): + failline = '{ResourceType} {LogicalResourceId} {ResourceStatus}: {ResourceStatusReason}'.format(**e) + ret['log'].append(failline) + + return ret + + +def create_stack(module, stack_params, cfn, events_limit): + if 'TemplateBody' not in stack_params and 'TemplateURL' not in stack_params: + module.fail_json(msg="Either 'template', 'template_body' or 'template_url' is required when the stack does not exist.") + + # 'DisableRollback', 'TimeoutInMinutes', 'EnableTerminationProtection' and + # 'OnFailure' only apply on creation, not update. + if module.params.get('on_create_failure') is not None: + stack_params['OnFailure'] = module.params['on_create_failure'] + else: + stack_params['DisableRollback'] = module.params['disable_rollback'] + + if module.params.get('create_timeout') is not None: + stack_params['TimeoutInMinutes'] = module.params['create_timeout'] + if module.params.get('termination_protection') is not None: + if boto_supports_termination_protection(cfn): + stack_params['EnableTerminationProtection'] = bool(module.params.get('termination_protection')) + else: + module.fail_json(msg="termination_protection parameter requires botocore >= 1.7.18") + + try: + response = cfn.create_stack(**stack_params) + # Use stack ID to follow stack state in case of on_create_failure = DELETE + result = stack_operation(cfn, response['StackId'], 'CREATE', events_limit, stack_params.get('ClientRequestToken', None)) + except Exception as err: + module.fail_json_aws(err, msg="Failed to create stack {0}".format(stack_params.get('StackName'))) + if not result: + module.fail_json(msg="empty result") + return result + + +def list_changesets(cfn, stack_name): + res = cfn.list_change_sets(StackName=stack_name) + return [cs['ChangeSetName'] for cs in res['Summaries']] + + +def create_changeset(module, stack_params, cfn, events_limit): + if 'TemplateBody' not in stack_params and 'TemplateURL' not in stack_params: + module.fail_json(msg="Either 'template' or 'template_url' is required.") + if module.params['changeset_name'] is not None: + stack_params['ChangeSetName'] = module.params['changeset_name'] + + # changesets don't accept ClientRequestToken parameters + stack_params.pop('ClientRequestToken', None) + + try: + changeset_name = build_changeset_name(stack_params) + stack_params['ChangeSetName'] = changeset_name + + # Determine if this changeset already exists + pending_changesets = list_changesets(cfn, stack_params['StackName']) + if changeset_name in pending_changesets: + warning = 'WARNING: %d pending changeset(s) exist(s) for this stack!' % len(pending_changesets) + result = dict(changed=False, output='ChangeSet %s already exists.' % changeset_name, warnings=[warning]) + else: + cs = cfn.create_change_set(**stack_params) + # Make sure we don't enter an infinite loop + time_end = time.time() + 600 + while time.time() < time_end: + try: + newcs = cfn.describe_change_set(ChangeSetName=cs['Id']) + except botocore.exceptions.BotoCoreError as err: + module.fail_json_aws(err) + if newcs['Status'] == 'CREATE_PENDING' or newcs['Status'] == 'CREATE_IN_PROGRESS': + time.sleep(1) + elif newcs['Status'] == 'FAILED' and "The submitted information didn't contain changes" in newcs['StatusReason']: + cfn.delete_change_set(ChangeSetName=cs['Id']) + result = dict(changed=False, + output='The created Change Set did not contain any changes to this stack and was deleted.') + # a failed change set does not trigger any stack events so we just want to + # skip any further processing of result and just return it directly + return result + else: + break + # Lets not hog the cpu/spam the AWS API + time.sleep(1) + result = stack_operation(cfn, stack_params['StackName'], 'CREATE_CHANGESET', events_limit) + result['change_set_id'] = cs['Id'] + result['warnings'] = ['Created changeset named %s for stack %s' % (changeset_name, stack_params['StackName']), + 'You can execute it using: aws cloudformation execute-change-set --change-set-name %s' % cs['Id'], + 'NOTE that dependencies on this stack might fail due to pending changes!'] + except Exception as err: + error_msg = boto_exception(err) + if 'No updates are to be performed.' in error_msg: + result = dict(changed=False, output='Stack is already up-to-date.') + else: + module.fail_json_aws(err, msg='Failed to create change set') + + if not result: + module.fail_json(msg="empty result") + return result + + +def update_stack(module, stack_params, cfn, events_limit): + if 'TemplateBody' not in stack_params and 'TemplateURL' not in stack_params: + stack_params['UsePreviousTemplate'] = True + + # if the state is present and the stack already exists, we try to update it. + # AWS will tell us if the stack template and parameters are the same and + # don't need to be updated. + try: + cfn.update_stack(**stack_params) + result = stack_operation(cfn, stack_params['StackName'], 'UPDATE', events_limit, stack_params.get('ClientRequestToken', None)) + except Exception as err: + error_msg = boto_exception(err) + if 'No updates are to be performed.' in error_msg: + result = dict(changed=False, output='Stack is already up-to-date.') + else: + module.fail_json_aws(err, msg="Failed to update stack {0}".format(stack_params.get('StackName'))) + if not result: + module.fail_json(msg="empty result") + return result + + +def update_termination_protection(module, cfn, stack_name, desired_termination_protection_state): + '''updates termination protection of a stack''' + if not boto_supports_termination_protection(cfn): + module.fail_json(msg="termination_protection parameter requires botocore >= 1.7.18") + stack = get_stack_facts(cfn, stack_name) + if stack: + if stack['EnableTerminationProtection'] is not desired_termination_protection_state: + try: + cfn.update_termination_protection( + EnableTerminationProtection=desired_termination_protection_state, + StackName=stack_name) + except botocore.exceptions.ClientError as e: + module.fail_json_aws(e) + + +def boto_supports_termination_protection(cfn): + '''termination protection was added in botocore 1.7.18''' + return hasattr(cfn, "update_termination_protection") + + +def stack_operation(cfn, stack_name, operation, events_limit, op_token=None): + '''gets the status of a stack while it is created/updated/deleted''' + existed = [] + while True: + try: + stack = get_stack_facts(cfn, stack_name) + existed.append('yes') + except Exception: + # If the stack previously existed, and now can't be found then it's + # been deleted successfully. + if 'yes' in existed or operation == 'DELETE': # stacks may delete fast, look in a few ways. + ret = get_stack_events(cfn, stack_name, events_limit, op_token) + ret.update({'changed': True, 'output': 'Stack Deleted'}) + return ret + else: + return {'changed': True, 'failed': True, 'output': 'Stack Not Found', 'exception': traceback.format_exc()} + ret = get_stack_events(cfn, stack_name, events_limit, op_token) + if not stack: + if 'yes' in existed or operation == 'DELETE': # stacks may delete fast, look in a few ways. + ret = get_stack_events(cfn, stack_name, events_limit, op_token) + ret.update({'changed': True, 'output': 'Stack Deleted'}) + return ret + else: + ret.update({'changed': False, 'failed': True, 'output': 'Stack not found.'}) + return ret + # it covers ROLLBACK_COMPLETE and UPDATE_ROLLBACK_COMPLETE + # Possible states: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html#w1ab2c15c17c21c13 + elif stack['StackStatus'].endswith('ROLLBACK_COMPLETE') and operation != 'CREATE_CHANGESET': + ret.update({'changed': True, 'failed': True, 'output': 'Problem with %s. Rollback complete' % operation}) + return ret + elif stack['StackStatus'] == 'DELETE_COMPLETE' and operation == 'CREATE': + ret.update({'changed': True, 'failed': True, 'output': 'Stack create failed. Delete complete.'}) + return ret + # note the ordering of ROLLBACK_COMPLETE, DELETE_COMPLETE, and COMPLETE, because otherwise COMPLETE will match all cases. + elif stack['StackStatus'].endswith('_COMPLETE'): + ret.update({'changed': True, 'output': 'Stack %s complete' % operation}) + return ret + elif stack['StackStatus'].endswith('_ROLLBACK_FAILED'): + ret.update({'changed': True, 'failed': True, 'output': 'Stack %s rollback failed' % operation}) + return ret + # note the ordering of ROLLBACK_FAILED and FAILED, because otherwise FAILED will match both cases. + elif stack['StackStatus'].endswith('_FAILED'): + ret.update({'changed': True, 'failed': True, 'output': 'Stack %s failed' % operation}) + return ret + else: + # this can loop forever :/ + time.sleep(5) + return {'failed': True, 'output': 'Failed for unknown reasons.'} + + +def build_changeset_name(stack_params): + if 'ChangeSetName' in stack_params: + return stack_params['ChangeSetName'] + + json_params = json.dumps(stack_params, sort_keys=True) + + return 'Ansible-{0}-{1}'.format( + stack_params['StackName'], + sha1(to_bytes(json_params, errors='surrogate_or_strict')).hexdigest() + ) + + +def check_mode_changeset(module, stack_params, cfn): + """Create a change set, describe it and delete it before returning check mode outputs.""" + stack_params['ChangeSetName'] = build_changeset_name(stack_params) + # changesets don't accept ClientRequestToken parameters + stack_params.pop('ClientRequestToken', None) + + try: + change_set = cfn.create_change_set(**stack_params) + for i in range(60): # total time 5 min + description = cfn.describe_change_set(ChangeSetName=change_set['Id']) + if description['Status'] in ('CREATE_COMPLETE', 'FAILED'): + break + time.sleep(5) + else: + # if the changeset doesn't finish in 5 mins, this `else` will trigger and fail + module.fail_json(msg="Failed to create change set %s" % stack_params['ChangeSetName']) + + cfn.delete_change_set(ChangeSetName=change_set['Id']) + + reason = description.get('StatusReason') + + if description['Status'] == 'FAILED' and "didn't contain changes" in description['StatusReason']: + return {'changed': False, 'msg': reason, 'meta': description['StatusReason']} + return {'changed': True, 'msg': reason, 'meta': description['Changes']} + + except (botocore.exceptions.ValidationError, botocore.exceptions.ClientError) as err: + module.fail_json_aws(err) + + +def get_stack_facts(cfn, stack_name): + try: + stack_response = cfn.describe_stacks(StackName=stack_name) + stack_info = stack_response['Stacks'][0] + except (botocore.exceptions.ValidationError, botocore.exceptions.ClientError) as err: + error_msg = boto_exception(err) + if 'does not exist' in error_msg: + # missing stack, don't bail. + return None + + # other error, bail. + raise err + + if stack_response and stack_response.get('Stacks', None): + stacks = stack_response['Stacks'] + if len(stacks): + stack_info = stacks[0] + + return stack_info + + +def main(): + argument_spec = dict( + stack_name=dict(required=True), + template_parameters=dict(required=False, type='dict', default={}), + state=dict(default='present', choices=['present', 'absent']), + template=dict(default=None, required=False, type='path'), + notification_arns=dict(default=None, required=False), + stack_policy=dict(default=None, required=False), + disable_rollback=dict(default=False, type='bool'), + on_create_failure=dict(default=None, required=False, choices=['DO_NOTHING', 'ROLLBACK', 'DELETE']), + create_timeout=dict(default=None, type='int'), + template_url=dict(default=None, required=False), + template_body=dict(default=None, required=False), + template_format=dict(removed_at_date='2022-06-01', removed_from_collection='amazon.aws'), + create_changeset=dict(default=False, type='bool'), + changeset_name=dict(default=None, required=False), + role_arn=dict(default=None, required=False), + tags=dict(default=None, type='dict'), + termination_protection=dict(default=None, type='bool'), + events_limit=dict(default=200, type='int'), + backoff_retries=dict(type='int', default=10, required=False), + backoff_delay=dict(type='int', default=3, required=False), + backoff_max_delay=dict(type='int', default=30, required=False), + capabilities=dict(type='list', elements='str', default=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + mutually_exclusive=[['template_url', 'template', 'template_body'], + ['disable_rollback', 'on_create_failure']], + supports_check_mode=True + ) + + invalid_capabilities = [] + user_capabilities = module.params.get('capabilities') + for user_cap in user_capabilities: + if user_cap not in ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND']: + invalid_capabilities.append(user_cap) + + if invalid_capabilities: + module.fail_json(msg="Specified capabilities are invalid : %r," + " please check documentation for valid capabilities" % invalid_capabilities) + + # collect the parameters that are passed to boto3. Keeps us from having so many scalars floating around. + stack_params = { + 'Capabilities': user_capabilities, + 'ClientRequestToken': to_native(uuid.uuid4()), + } + state = module.params['state'] + stack_params['StackName'] = module.params['stack_name'] + + if module.params['template'] is not None: + with open(module.params['template'], 'r') as template_fh: + stack_params['TemplateBody'] = template_fh.read() + elif module.params['template_body'] is not None: + stack_params['TemplateBody'] = module.params['template_body'] + elif module.params['template_url'] is not None: + stack_params['TemplateURL'] = module.params['template_url'] + + if module.params.get('notification_arns'): + stack_params['NotificationARNs'] = module.params['notification_arns'].split(',') + else: + stack_params['NotificationARNs'] = [] + + # can't check the policy when verifying. + if module.params['stack_policy'] is not None and not module.check_mode and not module.params['create_changeset']: + with open(module.params['stack_policy'], 'r') as stack_policy_fh: + stack_params['StackPolicyBody'] = stack_policy_fh.read() + + template_parameters = module.params['template_parameters'] + + stack_params['Parameters'] = [] + for k, v in template_parameters.items(): + if isinstance(v, dict): + # set parameter based on a dict to allow additional CFN Parameter Attributes + param = dict(ParameterKey=k) + + if 'value' in v: + param['ParameterValue'] = str(v['value']) + + if 'use_previous_value' in v and bool(v['use_previous_value']): + param['UsePreviousValue'] = True + param.pop('ParameterValue', None) + + stack_params['Parameters'].append(param) + else: + # allow default k/v configuration to set a template parameter + stack_params['Parameters'].append({'ParameterKey': k, 'ParameterValue': str(v)}) + + if isinstance(module.params.get('tags'), dict): + stack_params['Tags'] = ansible_dict_to_boto3_tag_list(module.params['tags']) + + if module.params.get('role_arn'): + stack_params['RoleARN'] = module.params['role_arn'] + + result = {} + + cfn = module.client('cloudformation') + + # Wrap the cloudformation client methods that this module uses with + # automatic backoff / retry for throttling error codes + backoff_wrapper = AWSRetry.jittered_backoff( + retries=module.params.get('backoff_retries'), + delay=module.params.get('backoff_delay'), + max_delay=module.params.get('backoff_max_delay') + ) + cfn.describe_stack_events = backoff_wrapper(cfn.describe_stack_events) + cfn.create_stack = backoff_wrapper(cfn.create_stack) + cfn.list_change_sets = backoff_wrapper(cfn.list_change_sets) + cfn.create_change_set = backoff_wrapper(cfn.create_change_set) + cfn.update_stack = backoff_wrapper(cfn.update_stack) + cfn.describe_stacks = backoff_wrapper(cfn.describe_stacks) + cfn.list_stack_resources = backoff_wrapper(cfn.list_stack_resources) + cfn.delete_stack = backoff_wrapper(cfn.delete_stack) + if boto_supports_termination_protection(cfn): + cfn.update_termination_protection = backoff_wrapper(cfn.update_termination_protection) + + stack_info = get_stack_facts(cfn, stack_params['StackName']) + + if module.check_mode: + if state == 'absent' and stack_info: + module.exit_json(changed=True, msg='Stack would be deleted', meta=[]) + elif state == 'absent' and not stack_info: + module.exit_json(changed=False, msg='Stack doesn\'t exist', meta=[]) + elif state == 'present' and not stack_info: + module.exit_json(changed=True, msg='New stack would be created', meta=[]) + else: + module.exit_json(**check_mode_changeset(module, stack_params, cfn)) + + if state == 'present': + if not stack_info: + result = create_stack(module, stack_params, cfn, module.params.get('events_limit')) + elif module.params.get('create_changeset'): + result = create_changeset(module, stack_params, cfn, module.params.get('events_limit')) + else: + if module.params.get('termination_protection') is not None: + update_termination_protection(module, cfn, stack_params['StackName'], + bool(module.params.get('termination_protection'))) + result = update_stack(module, stack_params, cfn, module.params.get('events_limit')) + + # format the stack output + + stack = get_stack_facts(cfn, stack_params['StackName']) + if stack is not None: + if result.get('stack_outputs') is None: + # always define stack_outputs, but it may be empty + result['stack_outputs'] = {} + for output in stack.get('Outputs', []): + result['stack_outputs'][output['OutputKey']] = output['OutputValue'] + stack_resources = [] + reslist = cfn.list_stack_resources(StackName=stack_params['StackName']) + for res in reslist.get('StackResourceSummaries', []): + stack_resources.append({ + "logical_resource_id": res['LogicalResourceId'], + "physical_resource_id": res.get('PhysicalResourceId', ''), + "resource_type": res['ResourceType'], + "last_updated_time": res['LastUpdatedTimestamp'], + "status": res['ResourceStatus'], + "status_reason": res.get('ResourceStatusReason') # can be blank, apparently + }) + result['stack_resources'] = stack_resources + + elif state == 'absent': + # absent state is different because of the way delete_stack works. + # problem is it it doesn't give an error if stack isn't found + # so must describe the stack first + + try: + stack = get_stack_facts(cfn, stack_params['StackName']) + if not stack: + result = {'changed': False, 'output': 'Stack not found.'} + else: + if stack_params.get('RoleARN') is None: + cfn.delete_stack(StackName=stack_params['StackName']) + else: + cfn.delete_stack(StackName=stack_params['StackName'], RoleARN=stack_params['RoleARN']) + result = stack_operation(cfn, stack_params['StackName'], 'DELETE', module.params.get('events_limit'), + stack_params.get('ClientRequestToken', None)) + except Exception as err: + module.fail_json_aws(err) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation_facts.py new file mode 100644 index 00000000..0c34e8b1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation_facts.py @@ -0,0 +1,349 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: cloudformation_info +version_added: 1.0.0 +short_description: Obtain information about an AWS CloudFormation stack +description: + - Gets information about an AWS CloudFormation stack. + - This module was called C(amazon.aws.cloudformation_facts) before Ansible 2.9, returning C(ansible_facts). + Note that the M(amazon.aws.cloudformation_info) module no longer returns C(ansible_facts)! +requirements: + - boto3 >= 1.0.0 + - python >= 2.6 +author: + - Justin Menga (@jmenga) + - Kevin Coming (@waffie1) +options: + stack_name: + description: + - The name or id of the CloudFormation stack. Gathers information on all stacks by default. + type: str + all_facts: + description: + - Get all stack information for the stack. + type: bool + default: false + stack_events: + description: + - Get stack events for the stack. + type: bool + default: false + stack_template: + description: + - Get stack template body for the stack. + type: bool + default: false + stack_resources: + description: + - Get stack resources for the stack. + type: bool + default: false + stack_policy: + description: + - Get stack policy for the stack. + type: bool + default: false + stack_change_sets: + description: + - Get stack change sets for the stack + type: bool + default: false +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Get summary information about a stack + amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + register: output + +- debug: + msg: "{{ output['cloudformation']['my-cloudformation-stack'] }}" + +# When the module is called as cloudformation_facts, return values are published +# in ansible_facts['cloudformation'][<stack_name>] and can be used as follows. +# Note that this is deprecated and will stop working in Ansible after 2021-12-01. + +- amazon.aws.cloudformation_facts: + stack_name: my-cloudformation-stack + +- debug: + msg: "{{ ansible_facts['cloudformation']['my-cloudformation-stack'] }}" + +# Get stack outputs, when you have the stack name available as a fact +- set_fact: + stack_name: my-awesome-stack + +- amazon.aws.cloudformation_info: + stack_name: "{{ stack_name }}" + register: my_stack + +- debug: + msg: "{{ my_stack.cloudformation[stack_name].stack_outputs }}" + +# Get all stack information about a stack +- amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + all_facts: true + +# Get stack resource and stack policy information about a stack +- amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + stack_resources: true + stack_policy: true + +# Fail if the stack doesn't exist +- name: try to get facts about a stack but fail if it doesn't exist + amazon.aws.cloudformation_info: + stack_name: nonexistent-stack + all_facts: yes + failed_when: cloudformation['nonexistent-stack'] is undefined +''' + +RETURN = ''' +stack_description: + description: Summary facts about the stack + returned: if the stack exists + type: dict +stack_outputs: + description: Dictionary of stack outputs keyed by the value of each output 'OutputKey' parameter and corresponding value of each + output 'OutputValue' parameter + returned: if the stack exists + type: dict + sample: + ApplicationDatabaseName: dazvlpr01xj55a.ap-southeast-2.rds.amazonaws.com +stack_parameters: + description: Dictionary of stack parameters keyed by the value of each parameter 'ParameterKey' parameter and corresponding value of + each parameter 'ParameterValue' parameter + returned: if the stack exists + type: dict + sample: + DatabaseEngine: mysql + DatabasePassword: "***" +stack_events: + description: All stack events for the stack + returned: only if all_facts or stack_events is true and the stack exists + type: list +stack_policy: + description: Describes the stack policy for the stack + returned: only if all_facts or stack_policy is true and the stack exists + type: dict +stack_template: + description: Describes the stack template for the stack + returned: only if all_facts or stack_template is true and the stack exists + type: dict +stack_resource_list: + description: Describes stack resources for the stack + returned: only if all_facts or stack_resources is true and the stack exists + type: list +stack_resources: + description: Dictionary of stack resources keyed by the value of each resource 'LogicalResourceId' parameter and corresponding value of each + resource 'PhysicalResourceId' parameter + returned: only if all_facts or stack_resources is true and the stack exists + type: dict + sample: + AutoScalingGroup: "dev-someapp-AutoscalingGroup-1SKEXXBCAN0S7" + AutoScalingSecurityGroup: "sg-abcd1234" + ApplicationDatabase: "dazvlpr01xj55a" +stack_change_sets: + description: A list of stack change sets. Each item in the list represents the details of a specific changeset + + returned: only if all_facts or stack_change_sets is true and the stack exists + type: list +''' + +import json + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_message +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +class CloudFormationServiceManager: + """Handles CloudFormation Services""" + + def __init__(self, module): + self.module = module + self.client = module.client('cloudformation') + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def describe_stacks_with_backoff(self, **kwargs): + paginator = self.client.get_paginator('describe_stacks') + return paginator.paginate(**kwargs).build_full_result()['Stacks'] + + def describe_stacks(self, stack_name=None): + try: + kwargs = {'StackName': stack_name} if stack_name else {} + response = self.describe_stacks_with_backoff(**kwargs) + if response is not None: + return response + self.module.fail_json(msg="Error describing stack(s) - an empty response was returned") + except is_boto3_error_message('does not exist'): + return {} + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + self.module.fail_json_aws(e, msg="Error describing stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def list_stack_resources_with_backoff(self, stack_name): + paginator = self.client.get_paginator('list_stack_resources') + return paginator.paginate(StackName=stack_name).build_full_result()['StackResourceSummaries'] + + def list_stack_resources(self, stack_name): + try: + return self.list_stack_resources_with_backoff(stack_name) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error listing stack resources for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def describe_stack_events_with_backoff(self, stack_name): + paginator = self.client.get_paginator('describe_stack_events') + return paginator.paginate(StackName=stack_name).build_full_result()['StackEvents'] + + def describe_stack_events(self, stack_name): + try: + return self.describe_stack_events_with_backoff(stack_name) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error listing stack events for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def list_stack_change_sets_with_backoff(self, stack_name): + paginator = self.client.get_paginator('list_change_sets') + return paginator.paginate(StackName=stack_name).build_full_result()['Summaries'] + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def describe_stack_change_set_with_backoff(self, **kwargs): + paginator = self.client.get_paginator('describe_change_set') + return paginator.paginate(**kwargs).build_full_result() + + def describe_stack_change_sets(self, stack_name): + changes = [] + try: + change_sets = self.list_stack_change_sets_with_backoff(stack_name) + for item in change_sets: + changes.append(self.describe_stack_change_set_with_backoff( + StackName=stack_name, + ChangeSetName=item['ChangeSetName'])) + return changes + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error describing stack change sets for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def get_stack_policy_with_backoff(self, stack_name): + return self.client.get_stack_policy(StackName=stack_name) + + def get_stack_policy(self, stack_name): + try: + response = self.get_stack_policy_with_backoff(stack_name) + stack_policy = response.get('StackPolicyBody') + if stack_policy: + return json.loads(stack_policy) + return dict() + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error getting stack policy for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def get_template_with_backoff(self, stack_name): + return self.client.get_template(StackName=stack_name) + + def get_template(self, stack_name): + try: + response = self.get_template_with_backoff(stack_name) + return response.get('TemplateBody') + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error getting stack template for stack " + stack_name) + + +def to_dict(items, key, value): + ''' Transforms a list of items to a Key/Value dictionary ''' + if items: + return dict(zip([i.get(key) for i in items], [i.get(value) for i in items])) + else: + return dict() + + +def main(): + argument_spec = dict( + stack_name=dict(), + all_facts=dict(required=False, default=False, type='bool'), + stack_policy=dict(required=False, default=False, type='bool'), + stack_events=dict(required=False, default=False, type='bool'), + stack_resources=dict(required=False, default=False, type='bool'), + stack_template=dict(required=False, default=False, type='bool'), + stack_change_sets=dict(required=False, default=False, type='bool'), + ) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + + is_old_facts = module._name == 'cloudformation_facts' + if is_old_facts: + module.deprecate("The 'cloudformation_facts' module has been renamed to 'cloudformation_info', " + "and the renamed one no longer returns ansible_facts", date='2021-12-01', collection_name='amazon.aws') + + service_mgr = CloudFormationServiceManager(module) + + if is_old_facts: + result = {'ansible_facts': {'cloudformation': {}}} + else: + result = {'cloudformation': {}} + + for stack_description in service_mgr.describe_stacks(module.params.get('stack_name')): + facts = {'stack_description': stack_description} + stack_name = stack_description.get('StackName') + + # Create stack output and stack parameter dictionaries + if facts['stack_description']: + facts['stack_outputs'] = to_dict(facts['stack_description'].get('Outputs'), 'OutputKey', 'OutputValue') + facts['stack_parameters'] = to_dict(facts['stack_description'].get('Parameters'), + 'ParameterKey', 'ParameterValue') + facts['stack_tags'] = boto3_tag_list_to_ansible_dict(facts['stack_description'].get('Tags')) + + # Create optional stack outputs + all_facts = module.params.get('all_facts') + if all_facts or module.params.get('stack_resources'): + facts['stack_resource_list'] = service_mgr.list_stack_resources(stack_name) + facts['stack_resources'] = to_dict(facts.get('stack_resource_list'), + 'LogicalResourceId', 'PhysicalResourceId') + if all_facts or module.params.get('stack_template'): + facts['stack_template'] = service_mgr.get_template(stack_name) + if all_facts or module.params.get('stack_policy'): + facts['stack_policy'] = service_mgr.get_stack_policy(stack_name) + if all_facts or module.params.get('stack_events'): + facts['stack_events'] = service_mgr.describe_stack_events(stack_name) + if all_facts or module.params.get('stack_change_sets'): + facts['stack_change_sets'] = service_mgr.describe_stack_change_sets(stack_name) + + if is_old_facts: + result['ansible_facts']['cloudformation'][stack_name] = facts + else: + result['cloudformation'][stack_name] = camel_dict_to_snake_dict(facts, ignore_list=('stack_outputs', + 'stack_parameters', + 'stack_policy', + 'stack_resources', + 'stack_tags', + 'stack_template')) + + module.exit_json(changed=False, **result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation_info.py new file mode 100644 index 00000000..0c34e8b1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/cloudformation_info.py @@ -0,0 +1,349 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: cloudformation_info +version_added: 1.0.0 +short_description: Obtain information about an AWS CloudFormation stack +description: + - Gets information about an AWS CloudFormation stack. + - This module was called C(amazon.aws.cloudformation_facts) before Ansible 2.9, returning C(ansible_facts). + Note that the M(amazon.aws.cloudformation_info) module no longer returns C(ansible_facts)! +requirements: + - boto3 >= 1.0.0 + - python >= 2.6 +author: + - Justin Menga (@jmenga) + - Kevin Coming (@waffie1) +options: + stack_name: + description: + - The name or id of the CloudFormation stack. Gathers information on all stacks by default. + type: str + all_facts: + description: + - Get all stack information for the stack. + type: bool + default: false + stack_events: + description: + - Get stack events for the stack. + type: bool + default: false + stack_template: + description: + - Get stack template body for the stack. + type: bool + default: false + stack_resources: + description: + - Get stack resources for the stack. + type: bool + default: false + stack_policy: + description: + - Get stack policy for the stack. + type: bool + default: false + stack_change_sets: + description: + - Get stack change sets for the stack + type: bool + default: false +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Get summary information about a stack + amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + register: output + +- debug: + msg: "{{ output['cloudformation']['my-cloudformation-stack'] }}" + +# When the module is called as cloudformation_facts, return values are published +# in ansible_facts['cloudformation'][<stack_name>] and can be used as follows. +# Note that this is deprecated and will stop working in Ansible after 2021-12-01. + +- amazon.aws.cloudformation_facts: + stack_name: my-cloudformation-stack + +- debug: + msg: "{{ ansible_facts['cloudformation']['my-cloudformation-stack'] }}" + +# Get stack outputs, when you have the stack name available as a fact +- set_fact: + stack_name: my-awesome-stack + +- amazon.aws.cloudformation_info: + stack_name: "{{ stack_name }}" + register: my_stack + +- debug: + msg: "{{ my_stack.cloudformation[stack_name].stack_outputs }}" + +# Get all stack information about a stack +- amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + all_facts: true + +# Get stack resource and stack policy information about a stack +- amazon.aws.cloudformation_info: + stack_name: my-cloudformation-stack + stack_resources: true + stack_policy: true + +# Fail if the stack doesn't exist +- name: try to get facts about a stack but fail if it doesn't exist + amazon.aws.cloudformation_info: + stack_name: nonexistent-stack + all_facts: yes + failed_when: cloudformation['nonexistent-stack'] is undefined +''' + +RETURN = ''' +stack_description: + description: Summary facts about the stack + returned: if the stack exists + type: dict +stack_outputs: + description: Dictionary of stack outputs keyed by the value of each output 'OutputKey' parameter and corresponding value of each + output 'OutputValue' parameter + returned: if the stack exists + type: dict + sample: + ApplicationDatabaseName: dazvlpr01xj55a.ap-southeast-2.rds.amazonaws.com +stack_parameters: + description: Dictionary of stack parameters keyed by the value of each parameter 'ParameterKey' parameter and corresponding value of + each parameter 'ParameterValue' parameter + returned: if the stack exists + type: dict + sample: + DatabaseEngine: mysql + DatabasePassword: "***" +stack_events: + description: All stack events for the stack + returned: only if all_facts or stack_events is true and the stack exists + type: list +stack_policy: + description: Describes the stack policy for the stack + returned: only if all_facts or stack_policy is true and the stack exists + type: dict +stack_template: + description: Describes the stack template for the stack + returned: only if all_facts or stack_template is true and the stack exists + type: dict +stack_resource_list: + description: Describes stack resources for the stack + returned: only if all_facts or stack_resources is true and the stack exists + type: list +stack_resources: + description: Dictionary of stack resources keyed by the value of each resource 'LogicalResourceId' parameter and corresponding value of each + resource 'PhysicalResourceId' parameter + returned: only if all_facts or stack_resources is true and the stack exists + type: dict + sample: + AutoScalingGroup: "dev-someapp-AutoscalingGroup-1SKEXXBCAN0S7" + AutoScalingSecurityGroup: "sg-abcd1234" + ApplicationDatabase: "dazvlpr01xj55a" +stack_change_sets: + description: A list of stack change sets. Each item in the list represents the details of a specific changeset + + returned: only if all_facts or stack_change_sets is true and the stack exists + type: list +''' + +import json + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_message +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +class CloudFormationServiceManager: + """Handles CloudFormation Services""" + + def __init__(self, module): + self.module = module + self.client = module.client('cloudformation') + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def describe_stacks_with_backoff(self, **kwargs): + paginator = self.client.get_paginator('describe_stacks') + return paginator.paginate(**kwargs).build_full_result()['Stacks'] + + def describe_stacks(self, stack_name=None): + try: + kwargs = {'StackName': stack_name} if stack_name else {} + response = self.describe_stacks_with_backoff(**kwargs) + if response is not None: + return response + self.module.fail_json(msg="Error describing stack(s) - an empty response was returned") + except is_boto3_error_message('does not exist'): + return {} + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + self.module.fail_json_aws(e, msg="Error describing stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def list_stack_resources_with_backoff(self, stack_name): + paginator = self.client.get_paginator('list_stack_resources') + return paginator.paginate(StackName=stack_name).build_full_result()['StackResourceSummaries'] + + def list_stack_resources(self, stack_name): + try: + return self.list_stack_resources_with_backoff(stack_name) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error listing stack resources for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def describe_stack_events_with_backoff(self, stack_name): + paginator = self.client.get_paginator('describe_stack_events') + return paginator.paginate(StackName=stack_name).build_full_result()['StackEvents'] + + def describe_stack_events(self, stack_name): + try: + return self.describe_stack_events_with_backoff(stack_name) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error listing stack events for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def list_stack_change_sets_with_backoff(self, stack_name): + paginator = self.client.get_paginator('list_change_sets') + return paginator.paginate(StackName=stack_name).build_full_result()['Summaries'] + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def describe_stack_change_set_with_backoff(self, **kwargs): + paginator = self.client.get_paginator('describe_change_set') + return paginator.paginate(**kwargs).build_full_result() + + def describe_stack_change_sets(self, stack_name): + changes = [] + try: + change_sets = self.list_stack_change_sets_with_backoff(stack_name) + for item in change_sets: + changes.append(self.describe_stack_change_set_with_backoff( + StackName=stack_name, + ChangeSetName=item['ChangeSetName'])) + return changes + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error describing stack change sets for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def get_stack_policy_with_backoff(self, stack_name): + return self.client.get_stack_policy(StackName=stack_name) + + def get_stack_policy(self, stack_name): + try: + response = self.get_stack_policy_with_backoff(stack_name) + stack_policy = response.get('StackPolicyBody') + if stack_policy: + return json.loads(stack_policy) + return dict() + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error getting stack policy for stack " + stack_name) + + @AWSRetry.exponential_backoff(retries=5, delay=5) + def get_template_with_backoff(self, stack_name): + return self.client.get_template(StackName=stack_name) + + def get_template(self, stack_name): + try: + response = self.get_template_with_backoff(stack_name) + return response.get('TemplateBody') + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + self.module.fail_json_aws(e, msg="Error getting stack template for stack " + stack_name) + + +def to_dict(items, key, value): + ''' Transforms a list of items to a Key/Value dictionary ''' + if items: + return dict(zip([i.get(key) for i in items], [i.get(value) for i in items])) + else: + return dict() + + +def main(): + argument_spec = dict( + stack_name=dict(), + all_facts=dict(required=False, default=False, type='bool'), + stack_policy=dict(required=False, default=False, type='bool'), + stack_events=dict(required=False, default=False, type='bool'), + stack_resources=dict(required=False, default=False, type='bool'), + stack_template=dict(required=False, default=False, type='bool'), + stack_change_sets=dict(required=False, default=False, type='bool'), + ) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + + is_old_facts = module._name == 'cloudformation_facts' + if is_old_facts: + module.deprecate("The 'cloudformation_facts' module has been renamed to 'cloudformation_info', " + "and the renamed one no longer returns ansible_facts", date='2021-12-01', collection_name='amazon.aws') + + service_mgr = CloudFormationServiceManager(module) + + if is_old_facts: + result = {'ansible_facts': {'cloudformation': {}}} + else: + result = {'cloudformation': {}} + + for stack_description in service_mgr.describe_stacks(module.params.get('stack_name')): + facts = {'stack_description': stack_description} + stack_name = stack_description.get('StackName') + + # Create stack output and stack parameter dictionaries + if facts['stack_description']: + facts['stack_outputs'] = to_dict(facts['stack_description'].get('Outputs'), 'OutputKey', 'OutputValue') + facts['stack_parameters'] = to_dict(facts['stack_description'].get('Parameters'), + 'ParameterKey', 'ParameterValue') + facts['stack_tags'] = boto3_tag_list_to_ansible_dict(facts['stack_description'].get('Tags')) + + # Create optional stack outputs + all_facts = module.params.get('all_facts') + if all_facts or module.params.get('stack_resources'): + facts['stack_resource_list'] = service_mgr.list_stack_resources(stack_name) + facts['stack_resources'] = to_dict(facts.get('stack_resource_list'), + 'LogicalResourceId', 'PhysicalResourceId') + if all_facts or module.params.get('stack_template'): + facts['stack_template'] = service_mgr.get_template(stack_name) + if all_facts or module.params.get('stack_policy'): + facts['stack_policy'] = service_mgr.get_stack_policy(stack_name) + if all_facts or module.params.get('stack_events'): + facts['stack_events'] = service_mgr.describe_stack_events(stack_name) + if all_facts or module.params.get('stack_change_sets'): + facts['stack_change_sets'] = service_mgr.describe_stack_change_sets(stack_name) + + if is_old_facts: + result['ansible_facts']['cloudformation'][stack_name] = facts + else: + result['cloudformation'][stack_name] = camel_dict_to_snake_dict(facts, ignore_list=('stack_outputs', + 'stack_parameters', + 'stack_policy', + 'stack_resources', + 'stack_tags', + 'stack_template')) + + module.exit_json(changed=False, **result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2.py new file mode 100644 index 00000000..990a7e69 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2.py @@ -0,0 +1,1740 @@ +#!/usr/bin/python +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2 +version_added: 1.0.0 +short_description: create, terminate, start or stop an instance in ec2 +description: + - Creates or terminates ec2 instances. + - > + Note: This module uses the older boto Python module to interact with the EC2 API. + M(amazon.aws.ec2) will still receive bug fixes, but no new features. + Consider using the M(amazon.aws.ec2_instance) module instead. + If M(amazon.aws.ec2_instance) does not support a feature you need that is available in M(amazon.aws.ec2), please + file a feature request. +options: + key_name: + description: + - Key pair to use on the instance. + - The SSH key must already exist in AWS in order to use this argument. + - Keys can be created / deleted using the M(amazon.aws.ec2_key) module. + aliases: ['keypair'] + type: str + id: + description: + - Identifier for this instance or set of instances, so that the module will be idempotent with respect to EC2 instances. + - This identifier is valid for at least 24 hours after the termination of the instance, and should not be reused for another call later on. + - For details, see the description of client token at U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Run_Instance_Idempotency.html). + type: str + group: + description: + - Security group (or list of groups) to use with the instance. + aliases: [ 'groups' ] + type: list + elements: str + group_id: + description: + - Security group id (or list of ids) to use with the instance. + type: list + elements: str + zone: + description: + - AWS availability zone in which to launch the instance. + aliases: [ 'aws_zone', 'ec2_zone' ] + type: str + instance_type: + description: + - Instance type to use for the instance, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html). + - Required when creating a new instance. + type: str + aliases: ['type'] + tenancy: + description: + - An instance with a tenancy of C(dedicated) runs on single-tenant hardware and can only be launched into a VPC. + - Note that to use dedicated tenancy you MUST specify a I(vpc_subnet_id) as well. + - Dedicated tenancy is not available for EC2 "micro" instances. + default: default + choices: [ "default", "dedicated" ] + type: str + spot_price: + description: + - Maximum spot price to bid. If not set, a regular on-demand instance is requested. + - A spot request is made with this maximum bid. When it is filled, the instance is started. + type: str + spot_type: + description: + - The type of spot request. + - After being interrupted a C(persistent) spot instance will be started once there is capacity to fill the request again. + default: "one-time" + choices: [ "one-time", "persistent" ] + type: str + image: + description: + - I(ami) ID to use for the instance. + - Required when I(state=present). + type: str + kernel: + description: + - Kernel eki to use for the instance. + type: str + ramdisk: + description: + - Ramdisk eri to use for the instance. + type: str + wait: + description: + - Wait for the instance to reach its desired state before returning. + - Does not wait for SSH, see the 'wait_for_connection' example for details. + type: bool + default: false + wait_timeout: + description: + - How long before wait gives up, in seconds. + default: 300 + type: int + spot_wait_timeout: + description: + - How long to wait for the spot instance request to be fulfilled. Affects 'Request valid until' for setting spot request lifespan. + default: 600 + type: int + count: + description: + - Number of instances to launch. + default: 1 + type: int + monitoring: + description: + - Enable detailed monitoring (CloudWatch) for the instance. + type: bool + default: false + user_data: + description: + - Opaque blob of data which is made available to the EC2 instance. + type: str + instance_tags: + description: + - > + A hash/dictionary of tags to add to the new instance or for + instances to start/stop by tag. For example C({"key":"value"}) or + C({"key":"value","key2":"value2"}). + type: dict + placement_group: + description: + - Placement group for the instance when using EC2 Clustered Compute. + type: str + vpc_subnet_id: + description: + - The subnet ID in which to launch the instance (VPC). + type: str + assign_public_ip: + description: + - When provisioning within vpc, assign a public IP address. Boto library must be 2.13.0+. + type: bool + private_ip: + description: + - The private ip address to assign the instance (from the vpc subnet). + type: str + instance_profile_name: + description: + - Name of the IAM instance profile (i.e. what the EC2 console refers to as an "IAM Role") to use. Boto library must be 2.5.0+. + type: str + instance_ids: + description: + - "list of instance ids, currently used for states: absent, running, stopped" + aliases: ['instance_id'] + type: list + elements: str + source_dest_check: + description: + - Enable or Disable the Source/Destination checks (for NAT instances and Virtual Routers). + When initially creating an instance the EC2 API defaults this to C(True). + type: bool + termination_protection: + description: + - Enable or Disable the Termination Protection. + - Defaults to C(false). + type: bool + instance_initiated_shutdown_behavior: + description: + - Set whether AWS will Stop or Terminate an instance on shutdown. This parameter is ignored when using instance-store. + images (which require termination on shutdown). + default: 'stop' + choices: [ "stop", "terminate" ] + type: str + state: + description: + - Create, terminate, start, stop or restart instances. The state 'restarted' was added in Ansible 2.2. + - When I(state=absent), I(instance_ids) is required. + - When I(state=running), I(state=stopped) or I(state=restarted) then either I(instance_ids) or I(instance_tags) is required. + default: 'present' + choices: ['absent', 'present', 'restarted', 'running', 'stopped'] + type: str + volumes: + description: + - A list of hash/dictionaries of volumes to add to the new instance. + type: list + elements: dict + suboptions: + device_name: + type: str + required: true + description: + - A name for the device (For example C(/dev/sda)). + delete_on_termination: + type: bool + default: false + description: + - Whether the volume should be automatically deleted when the instance is terminated. + ephemeral: + type: str + description: + - Whether the volume should be ephemeral. + - Data on ephemeral volumes is lost when the instance is stopped. + - Mutually exclusive with the I(snapshot) parameter. + encrypted: + type: bool + default: false + description: + - Whether the volume should be encrypted using the 'aws/ebs' KMS CMK. + snapshot: + type: str + description: + - The ID of an EBS snapshot to copy when creating the volume. + - Mutually exclusive with the I(ephemeral) parameter. + volume_type: + type: str + description: + - The type of volume to create. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html) for more information on the available volume types. + volume_size: + type: int + description: + - The size of the volume (in GiB). + iops: + type: int + description: + - The number of IOPS per second to provision for the volume. + - Required when I(volume_type=io1). + ebs_optimized: + description: + - Whether instance is using optimized EBS volumes, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html). + default: false + type: bool + exact_count: + description: + - An integer value which indicates how many instances that match the 'count_tag' parameter should be running. + Instances are either created or terminated based on this value. + type: int + count_tag: + description: + - Used with I(exact_count) to determine how many nodes based on a specific tag criteria should be running. + This can be expressed in multiple ways and is shown in the EXAMPLES section. For instance, one can request 25 servers + that are tagged with C(class=webserver). The specified tag must already exist or be passed in as the I(instance_tags) option. + type: raw + network_interfaces: + description: + - A list of existing network interfaces to attach to the instance at launch. When specifying existing network interfaces, + none of the I(assign_public_ip), I(private_ip), I(vpc_subnet_id), I(group), or I(group_id) parameters may be used. (Those parameters are + for creating a new network interface at launch.) + aliases: ['network_interface'] + type: list + elements: str + spot_launch_group: + description: + - Launch group for spot requests, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-spot-instances-work.html#spot-launch-group). + type: str +author: + - "Tim Gerla (@tgerla)" + - "Lester Wade (@lwade)" + - "Seth Vidal (@skvidal)" +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Basic provisioning example +- amazon.aws.ec2: + key_name: mykey + instance_type: t2.micro + image: ami-123456 + wait: yes + group: webserver + count: 3 + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# Advanced example with tagging and CloudWatch +- amazon.aws.ec2: + key_name: mykey + group: databases + instance_type: t2.micro + image: ami-123456 + wait: yes + wait_timeout: 500 + count: 5 + instance_tags: + db: postgres + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# Single instance with additional IOPS volume from snapshot and volume delete on termination +- amazon.aws.ec2: + key_name: mykey + group: webserver + instance_type: c3.medium + image: ami-123456 + wait: yes + wait_timeout: 500 + volumes: + - device_name: /dev/sdb + snapshot: snap-abcdef12 + volume_type: io1 + iops: 1000 + volume_size: 100 + delete_on_termination: true + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# Single instance with ssd gp2 root volume +- amazon.aws.ec2: + key_name: mykey + group: webserver + instance_type: c3.medium + image: ami-123456 + wait: yes + wait_timeout: 500 + volumes: + - device_name: /dev/xvda + volume_type: gp2 + volume_size: 8 + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + count_tag: + Name: dbserver + exact_count: 1 + +# Multiple groups example +- amazon.aws.ec2: + key_name: mykey + group: ['databases', 'internal-services', 'sshable', 'and-so-forth'] + instance_type: m1.large + image: ami-6e649707 + wait: yes + wait_timeout: 500 + count: 5 + instance_tags: + db: postgres + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# Multiple instances with additional volume from snapshot +- amazon.aws.ec2: + key_name: mykey + group: webserver + instance_type: m1.large + image: ami-6e649707 + wait: yes + wait_timeout: 500 + count: 5 + volumes: + - device_name: /dev/sdb + snapshot: snap-abcdef12 + volume_size: 10 + monitoring: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# Dedicated tenancy example +- amazon.aws.ec2: + assign_public_ip: yes + group_id: sg-1dc53f72 + key_name: mykey + image: ami-6e649707 + instance_type: m1.small + tenancy: dedicated + vpc_subnet_id: subnet-29e63245 + wait: yes + +# Spot instance example +- amazon.aws.ec2: + spot_price: 0.24 + spot_wait_timeout: 600 + keypair: mykey + group_id: sg-1dc53f72 + instance_type: m1.small + image: ami-6e649707 + wait: yes + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + spot_launch_group: report_generators + instance_initiated_shutdown_behavior: terminate + +# Examples using pre-existing network interfaces +- amazon.aws.ec2: + key_name: mykey + instance_type: t2.small + image: ami-f005ba11 + network_interface: eni-deadbeef + +- amazon.aws.ec2: + key_name: mykey + instance_type: t2.small + image: ami-f005ba11 + network_interfaces: ['eni-deadbeef', 'eni-5ca1ab1e'] + +# Launch instances, runs some tasks +# and then terminate them + +- name: Create a sandbox instance + hosts: localhost + gather_facts: False + vars: + keypair: my_keypair + instance_type: m1.small + security_group: my_securitygroup + image: my_ami_id + region: us-east-1 + tasks: + - name: Launch instance + amazon.aws.ec2: + key_name: "{{ keypair }}" + group: "{{ security_group }}" + instance_type: "{{ instance_type }}" + image: "{{ image }}" + wait: true + region: "{{ region }}" + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + register: ec2 + + - name: Add new instance to host group + add_host: + hostname: "{{ item.public_ip }}" + groupname: launched + loop: "{{ ec2.instances }}" + + - name: Wait for SSH to come up + delegate_to: "{{ item.public_dns_name }}" + wait_for_connection: + delay: 60 + timeout: 320 + loop: "{{ ec2.instances }}" + +- name: Configure instance(s) + hosts: launched + become: True + gather_facts: True + roles: + - my_awesome_role + - my_awesome_test + +- name: Terminate instances + hosts: localhost + tasks: + - name: Terminate instances that were previously launched + amazon.aws.ec2: + state: 'absent' + instance_ids: '{{ ec2.instance_ids }}' + +# Start a few existing instances, run some tasks +# and stop the instances + +- name: Start sandbox instances + hosts: localhost + gather_facts: false + vars: + instance_ids: + - 'i-xxxxxx' + - 'i-xxxxxx' + - 'i-xxxxxx' + region: us-east-1 + tasks: + - name: Start the sandbox instances + amazon.aws.ec2: + instance_ids: '{{ instance_ids }}' + region: '{{ region }}' + state: running + wait: True + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + roles: + - do_neat_stuff + - do_more_neat_stuff + +- name: Stop sandbox instances + hosts: localhost + gather_facts: false + vars: + instance_ids: + - 'i-xxxxxx' + - 'i-xxxxxx' + - 'i-xxxxxx' + region: us-east-1 + tasks: + - name: Stop the sandbox instances + amazon.aws.ec2: + instance_ids: '{{ instance_ids }}' + region: '{{ region }}' + state: stopped + wait: True + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# +# Start stopped instances specified by tag +# +- amazon.aws.ec2: + instance_tags: + Name: ExtraPower + state: running + +# +# Restart instances specified by tag +# +- amazon.aws.ec2: + instance_tags: + Name: ExtraPower + state: restarted + +# +# Enforce that 5 instances with a tag "foo" are running +# (Highly recommended!) +# + +- amazon.aws.ec2: + key_name: mykey + instance_type: c1.medium + image: ami-40603AD1 + wait: yes + group: webserver + instance_tags: + foo: bar + exact_count: 5 + count_tag: foo + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# +# Enforce that 5 running instances named "database" with a "dbtype" of "postgres" +# + +- amazon.aws.ec2: + key_name: mykey + instance_type: c1.medium + image: ami-40603AD1 + wait: yes + group: webserver + instance_tags: + Name: database + dbtype: postgres + exact_count: 5 + count_tag: + Name: database + dbtype: postgres + vpc_subnet_id: subnet-29e63245 + assign_public_ip: yes + +# +# count_tag complex argument examples +# + + # instances with tag foo +- amazon.aws.ec2: + count_tag: + foo: + + # instances with tag foo=bar +- amazon.aws.ec2: + count_tag: + foo: bar + + # instances with tags foo=bar & baz +- amazon.aws.ec2: + count_tag: + foo: bar + baz: + + # instances with tags foo & bar & baz=bang +- amazon.aws.ec2: + count_tag: + - foo + - bar + - baz: bang + +''' + +import time +import datetime +from ast import literal_eval +from distutils.version import LooseVersion + +try: + import boto.ec2 + from boto.ec2.blockdevicemapping import BlockDeviceType + from boto.ec2.blockdevicemapping import BlockDeviceMapping + from boto.exception import EC2ResponseError + from boto import connect_ec2_endpoint + from boto import connect_vpc +except ImportError: + pass # Taken care of by ec2.HAS_BOTO + +from ansible.module_utils.six import get_function_code +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils._text import to_text + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import HAS_BOTO +from ..module_utils.ec2 import ec2_connect +from ..module_utils.ec2 import get_aws_connection_info + + +def find_running_instances_by_count_tag(module, ec2, vpc, count_tag, zone=None): + + # get reservations for instances that match tag(s) and are in the desired state + state = module.params.get('state') + if state not in ['running', 'stopped']: + state = None + reservations = get_reservations(module, ec2, vpc, tags=count_tag, state=state, zone=zone) + + instances = [] + for res in reservations: + if hasattr(res, 'instances'): + for inst in res.instances: + if inst.state == 'terminated' or inst.state == 'shutting-down': + continue + instances.append(inst) + + return reservations, instances + + +def _set_none_to_blank(dictionary): + result = dictionary + for k in result: + if isinstance(result[k], dict): + result[k] = _set_none_to_blank(result[k]) + elif not result[k]: + result[k] = "" + return result + + +def get_reservations(module, ec2, vpc, tags=None, state=None, zone=None): + # TODO: filters do not work with tags that have underscores + filters = dict() + + vpc_subnet_id = module.params.get('vpc_subnet_id') + vpc_id = None + if vpc_subnet_id: + filters.update({"subnet-id": vpc_subnet_id}) + if vpc: + vpc_id = vpc.get_all_subnets(subnet_ids=[vpc_subnet_id])[0].vpc_id + + if vpc_id: + filters.update({"vpc-id": vpc_id}) + + if tags is not None: + + if isinstance(tags, str): + try: + tags = literal_eval(tags) + except Exception: + pass + + # if not a string type, convert and make sure it's a text string + if isinstance(tags, int): + tags = to_text(tags) + + # if string, we only care that a tag of that name exists + if isinstance(tags, str): + filters.update({"tag-key": tags}) + + # if list, append each item to filters + if isinstance(tags, list): + for x in tags: + if isinstance(x, dict): + x = _set_none_to_blank(x) + filters.update(dict(("tag:" + tn, tv) for (tn, tv) in x.items())) + else: + filters.update({"tag-key": x}) + + # if dict, add the key and value to the filter + if isinstance(tags, dict): + tags = _set_none_to_blank(tags) + filters.update(dict(("tag:" + tn, tv) for (tn, tv) in tags.items())) + + # lets check to see if the filters dict is empty, if so then stop + if not filters: + module.fail_json(msg="Filters based on tag is empty => tags: %s" % (tags)) + + if state: + # http://stackoverflow.com/questions/437511/what-are-the-valid-instancestates-for-the-amazon-ec2-api + filters.update({'instance-state-name': state}) + + if zone: + filters.update({'availability-zone': zone}) + + if module.params.get('id'): + filters['client-token'] = module.params['id'] + + results = ec2.get_all_instances(filters=filters) + + return results + + +def get_instance_info(inst): + """ + Retrieves instance information from an instance + ID and returns it as a dictionary + """ + instance_info = {'id': inst.id, + 'ami_launch_index': inst.ami_launch_index, + 'private_ip': inst.private_ip_address, + 'private_dns_name': inst.private_dns_name, + 'public_ip': inst.ip_address, + 'dns_name': inst.dns_name, + 'public_dns_name': inst.public_dns_name, + 'state_code': inst.state_code, + 'architecture': inst.architecture, + 'image_id': inst.image_id, + 'key_name': inst.key_name, + 'placement': inst.placement, + 'region': inst.placement[:-1], + 'kernel': inst.kernel, + 'ramdisk': inst.ramdisk, + 'launch_time': inst.launch_time, + 'instance_type': inst.instance_type, + 'root_device_type': inst.root_device_type, + 'root_device_name': inst.root_device_name, + 'state': inst.state, + 'hypervisor': inst.hypervisor, + 'tags': inst.tags, + 'groups': dict((group.id, group.name) for group in inst.groups), + } + try: + instance_info['virtualization_type'] = getattr(inst, 'virtualization_type') + except AttributeError: + instance_info['virtualization_type'] = None + + try: + instance_info['ebs_optimized'] = getattr(inst, 'ebs_optimized') + except AttributeError: + instance_info['ebs_optimized'] = False + + try: + bdm_dict = {} + bdm = getattr(inst, 'block_device_mapping') + for device_name in bdm.keys(): + bdm_dict[device_name] = { + 'status': bdm[device_name].status, + 'volume_id': bdm[device_name].volume_id, + 'delete_on_termination': bdm[device_name].delete_on_termination + } + instance_info['block_device_mapping'] = bdm_dict + except AttributeError: + instance_info['block_device_mapping'] = False + + try: + instance_info['tenancy'] = getattr(inst, 'placement_tenancy') + except AttributeError: + instance_info['tenancy'] = 'default' + + return instance_info + + +def boto_supports_associate_public_ip_address(ec2): + """ + Check if Boto library has associate_public_ip_address in the NetworkInterfaceSpecification + class. Added in Boto 2.13.0 + + ec2: authenticated ec2 connection object + + Returns: + True if Boto library accepts associate_public_ip_address argument, else false + """ + + try: + network_interface = boto.ec2.networkinterface.NetworkInterfaceSpecification() + getattr(network_interface, "associate_public_ip_address") + return True + except AttributeError: + return False + + +def boto_supports_profile_name_arg(ec2): + """ + Check if Boto library has instance_profile_name argument. instance_profile_name has been added in Boto 2.5.0 + + ec2: authenticated ec2 connection object + + Returns: + True if Boto library accept instance_profile_name argument, else false + """ + run_instances_method = getattr(ec2, 'run_instances') + return 'instance_profile_name' in get_function_code(run_instances_method).co_varnames + + +def boto_supports_volume_encryption(): + """ + Check if Boto library supports encryption of EBS volumes (added in 2.29.0) + + Returns: + True if boto library has the named param as an argument on the request_spot_instances method, else False + """ + return hasattr(boto, 'Version') and LooseVersion(boto.Version) >= LooseVersion('2.29.0') + + +def create_block_device(module, ec2, volume): + # Not aware of a way to determine this programatically + # http://aws.amazon.com/about-aws/whats-new/2013/10/09/ebs-provisioned-iops-maximum-iops-gb-ratio-increased-to-30-1/ + MAX_IOPS_TO_SIZE_RATIO = 30 + + volume_type = volume.get('volume_type') + + if 'snapshot' not in volume and 'ephemeral' not in volume: + if 'volume_size' not in volume: + module.fail_json(msg='Size must be specified when creating a new volume or modifying the root volume') + if 'snapshot' in volume: + if volume_type == 'io1' and 'iops' not in volume: + module.fail_json(msg='io1 volumes must have an iops value set') + if 'iops' in volume: + snapshot = ec2.get_all_snapshots(snapshot_ids=[volume['snapshot']])[0] + size = volume.get('volume_size', snapshot.volume_size) + if int(volume['iops']) > MAX_IOPS_TO_SIZE_RATIO * int(size): + module.fail_json(msg='IOPS must be at most %d times greater than size' % MAX_IOPS_TO_SIZE_RATIO) + if 'ephemeral' in volume: + if 'snapshot' in volume: + module.fail_json(msg='Cannot set both ephemeral and snapshot') + if boto_supports_volume_encryption(): + return BlockDeviceType(snapshot_id=volume.get('snapshot'), + ephemeral_name=volume.get('ephemeral'), + size=volume.get('volume_size'), + volume_type=volume_type, + delete_on_termination=volume.get('delete_on_termination', False), + iops=volume.get('iops'), + encrypted=volume.get('encrypted', None)) + else: + return BlockDeviceType(snapshot_id=volume.get('snapshot'), + ephemeral_name=volume.get('ephemeral'), + size=volume.get('volume_size'), + volume_type=volume_type, + delete_on_termination=volume.get('delete_on_termination', False), + iops=volume.get('iops')) + + +def boto_supports_param_in_spot_request(ec2, param): + """ + Check if Boto library has a <param> in its request_spot_instances() method. For example, the placement_group parameter wasn't added until 2.3.0. + + ec2: authenticated ec2 connection object + + Returns: + True if boto library has the named param as an argument on the request_spot_instances method, else False + """ + method = getattr(ec2, 'request_spot_instances') + return param in get_function_code(method).co_varnames + + +def await_spot_requests(module, ec2, spot_requests, count): + """ + Wait for a group of spot requests to be fulfilled, or fail. + + module: Ansible module object + ec2: authenticated ec2 connection object + spot_requests: boto.ec2.spotinstancerequest.SpotInstanceRequest object returned by ec2.request_spot_instances + count: Total number of instances to be created by the spot requests + + Returns: + list of instance ID's created by the spot request(s) + """ + spot_wait_timeout = int(module.params.get('spot_wait_timeout')) + wait_complete = time.time() + spot_wait_timeout + + spot_req_inst_ids = dict() + while time.time() < wait_complete: + reqs = ec2.get_all_spot_instance_requests() + for sirb in spot_requests: + if sirb.id in spot_req_inst_ids: + continue + for sir in reqs: + if sir.id != sirb.id: + continue # this is not our spot instance + if sir.instance_id is not None: + spot_req_inst_ids[sirb.id] = sir.instance_id + elif sir.state == 'open': + continue # still waiting, nothing to do here + elif sir.state == 'active': + continue # Instance is created already, nothing to do here + elif sir.state == 'failed': + module.fail_json(msg="Spot instance request %s failed with status %s and fault %s:%s" % ( + sir.id, sir.status.code, sir.fault.code, sir.fault.message)) + elif sir.state == 'cancelled': + module.fail_json(msg="Spot instance request %s was cancelled before it could be fulfilled." % sir.id) + elif sir.state == 'closed': + # instance is terminating or marked for termination + # this may be intentional on the part of the operator, + # or it may have been terminated by AWS due to capacity, + # price, or group constraints in this case, we'll fail + # the module if the reason for the state is anything + # other than termination by user. Codes are documented at + # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html + if sir.status.code == 'instance-terminated-by-user': + # do nothing, since the user likely did this on purpose + pass + else: + spot_msg = "Spot instance request %s was closed by AWS with the status %s and fault %s:%s" + module.fail_json(msg=spot_msg % (sir.id, sir.status.code, sir.fault.code, sir.fault.message)) + + if len(spot_req_inst_ids) < count: + time.sleep(5) + else: + return list(spot_req_inst_ids.values()) + module.fail_json(msg="wait for spot requests timeout on %s" % time.asctime()) + + +def enforce_count(module, ec2, vpc): + + exact_count = module.params.get('exact_count') + count_tag = module.params.get('count_tag') + zone = module.params.get('zone') + + # fail here if the exact count was specified without filtering + # on a tag, as this may lead to a undesired removal of instances + if exact_count and count_tag is None: + module.fail_json(msg="you must use the 'count_tag' option with exact_count") + + reservations, instances = find_running_instances_by_count_tag(module, ec2, vpc, count_tag, zone) + + changed = None + checkmode = False + instance_dict_array = [] + changed_instance_ids = None + + if len(instances) == exact_count: + changed = False + elif len(instances) < exact_count: + changed = True + to_create = exact_count - len(instances) + if not checkmode: + (instance_dict_array, changed_instance_ids, changed) \ + = create_instances(module, ec2, vpc, override_count=to_create) + + for inst in instance_dict_array: + instances.append(inst) + elif len(instances) > exact_count: + changed = True + to_remove = len(instances) - exact_count + if not checkmode: + all_instance_ids = sorted([x.id for x in instances]) + remove_ids = all_instance_ids[0:to_remove] + + instances = [x for x in instances if x.id not in remove_ids] + + (changed, instance_dict_array, changed_instance_ids) \ + = terminate_instances(module, ec2, remove_ids) + terminated_list = [] + for inst in instance_dict_array: + inst['state'] = "terminated" + terminated_list.append(inst) + instance_dict_array = terminated_list + + # ensure all instances are dictionaries + all_instances = [] + for inst in instances: + + if not isinstance(inst, dict): + warn_if_public_ip_assignment_changed(module, inst) + inst = get_instance_info(inst) + all_instances.append(inst) + + return (all_instances, instance_dict_array, changed_instance_ids, changed) + + +def create_instances(module, ec2, vpc, override_count=None): + """ + Creates new instances + + module : AnsibleAWSModule object + ec2: authenticated ec2 connection object + + Returns: + A list of dictionaries with instance information + about the instances that were launched + """ + + key_name = module.params.get('key_name') + id = module.params.get('id') + group_name = module.params.get('group') + group_id = module.params.get('group_id') + zone = module.params.get('zone') + instance_type = module.params.get('instance_type') + tenancy = module.params.get('tenancy') + spot_price = module.params.get('spot_price') + spot_type = module.params.get('spot_type') + image = module.params.get('image') + if override_count: + count = override_count + else: + count = module.params.get('count') + monitoring = module.params.get('monitoring') + kernel = module.params.get('kernel') + ramdisk = module.params.get('ramdisk') + wait = module.params.get('wait') + wait_timeout = int(module.params.get('wait_timeout')) + spot_wait_timeout = int(module.params.get('spot_wait_timeout')) + placement_group = module.params.get('placement_group') + user_data = module.params.get('user_data') + instance_tags = module.params.get('instance_tags') + vpc_subnet_id = module.params.get('vpc_subnet_id') + assign_public_ip = module.boolean(module.params.get('assign_public_ip')) + private_ip = module.params.get('private_ip') + instance_profile_name = module.params.get('instance_profile_name') + volumes = module.params.get('volumes') + ebs_optimized = module.params.get('ebs_optimized') + exact_count = module.params.get('exact_count') + count_tag = module.params.get('count_tag') + source_dest_check = module.boolean(module.params.get('source_dest_check')) + termination_protection = module.boolean(module.params.get('termination_protection')) + network_interfaces = module.params.get('network_interfaces') + spot_launch_group = module.params.get('spot_launch_group') + instance_initiated_shutdown_behavior = module.params.get('instance_initiated_shutdown_behavior') + + vpc_id = None + if vpc_subnet_id: + if not vpc: + module.fail_json(msg="region must be specified") + else: + vpc_id = vpc.get_all_subnets(subnet_ids=[vpc_subnet_id])[0].vpc_id + else: + vpc_id = None + + try: + # Here we try to lookup the group id from the security group name - if group is set. + if group_name: + if vpc_id: + grp_details = ec2.get_all_security_groups(filters={'vpc_id': vpc_id}) + else: + grp_details = ec2.get_all_security_groups() + if isinstance(group_name, string_types): + group_name = [group_name] + unmatched = set(group_name).difference(str(grp.name) for grp in grp_details) + if len(unmatched) > 0: + module.fail_json(msg="The following group names are not valid: %s" % ', '.join(unmatched)) + group_id = [str(grp.id) for grp in grp_details if str(grp.name) in group_name] + # Now we try to lookup the group id testing if group exists. + elif group_id: + # wrap the group_id in a list if it's not one already + if isinstance(group_id, string_types): + group_id = [group_id] + grp_details = ec2.get_all_security_groups(group_ids=group_id) + group_name = [grp_item.name for grp_item in grp_details] + except boto.exception.NoAuthHandlerFound as e: + module.fail_json_aws(e, msg='Unable to authenticate to AWS') + + # Lookup any instances that much our run id. + + running_instances = [] + count_remaining = int(count) + + if id is not None: + filter_dict = {'client-token': id, 'instance-state-name': 'running'} + previous_reservations = ec2.get_all_instances(None, filter_dict) + for res in previous_reservations: + for prev_instance in res.instances: + running_instances.append(prev_instance) + count_remaining = count_remaining - len(running_instances) + + # Both min_count and max_count equal count parameter. This means the launch request is explicit (we want count, or fail) in how many instances we want. + + if count_remaining == 0: + changed = False + else: + changed = True + try: + params = {'image_id': image, + 'key_name': key_name, + 'monitoring_enabled': monitoring, + 'placement': zone, + 'instance_type': instance_type, + 'kernel_id': kernel, + 'ramdisk_id': ramdisk} + if user_data is not None: + params['user_data'] = to_bytes(user_data, errors='surrogate_or_strict') + + if ebs_optimized: + params['ebs_optimized'] = ebs_optimized + + # 'tenancy' always has a default value, but it is not a valid parameter for spot instance request + if not spot_price: + params['tenancy'] = tenancy + + if boto_supports_profile_name_arg(ec2): + params['instance_profile_name'] = instance_profile_name + else: + if instance_profile_name is not None: + module.fail_json( + msg="instance_profile_name parameter requires Boto version 2.5.0 or higher") + + if assign_public_ip is not None: + if not boto_supports_associate_public_ip_address(ec2): + module.fail_json( + msg="assign_public_ip parameter requires Boto version 2.13.0 or higher.") + elif not vpc_subnet_id: + module.fail_json( + msg="assign_public_ip only available with vpc_subnet_id") + + else: + if private_ip: + interface = boto.ec2.networkinterface.NetworkInterfaceSpecification( + subnet_id=vpc_subnet_id, + private_ip_address=private_ip, + groups=group_id, + associate_public_ip_address=assign_public_ip) + else: + interface = boto.ec2.networkinterface.NetworkInterfaceSpecification( + subnet_id=vpc_subnet_id, + groups=group_id, + associate_public_ip_address=assign_public_ip) + interfaces = boto.ec2.networkinterface.NetworkInterfaceCollection(interface) + params['network_interfaces'] = interfaces + else: + if network_interfaces: + if isinstance(network_interfaces, string_types): + network_interfaces = [network_interfaces] + interfaces = [] + for i, network_interface_id in enumerate(network_interfaces): + interface = boto.ec2.networkinterface.NetworkInterfaceSpecification( + network_interface_id=network_interface_id, + device_index=i) + interfaces.append(interface) + params['network_interfaces'] = \ + boto.ec2.networkinterface.NetworkInterfaceCollection(*interfaces) + else: + params['subnet_id'] = vpc_subnet_id + if vpc_subnet_id: + params['security_group_ids'] = group_id + else: + params['security_groups'] = group_name + + if volumes: + bdm = BlockDeviceMapping() + for volume in volumes: + if 'device_name' not in volume: + module.fail_json(msg='Device name must be set for volume') + # Minimum volume size is 1GiB. We'll use volume size explicitly set to 0 + # to be a signal not to create this volume + if 'volume_size' not in volume or int(volume['volume_size']) > 0: + bdm[volume['device_name']] = create_block_device(module, ec2, volume) + + params['block_device_map'] = bdm + + # check to see if we're using spot pricing first before starting instances + if not spot_price: + if assign_public_ip is not None and private_ip: + params.update( + dict( + min_count=count_remaining, + max_count=count_remaining, + client_token=id, + placement_group=placement_group, + ) + ) + else: + params.update( + dict( + min_count=count_remaining, + max_count=count_remaining, + client_token=id, + placement_group=placement_group, + private_ip_address=private_ip, + ) + ) + + # For ordinary (not spot) instances, we can select 'stop' + # (the default) or 'terminate' here. + params['instance_initiated_shutdown_behavior'] = instance_initiated_shutdown_behavior or 'stop' + + try: + res = ec2.run_instances(**params) + except boto.exception.EC2ResponseError as e: + if (params['instance_initiated_shutdown_behavior'] != 'terminate' and + "InvalidParameterCombination" == e.error_code): + params['instance_initiated_shutdown_behavior'] = 'terminate' + res = ec2.run_instances(**params) + else: + raise + + instids = [i.id for i in res.instances] + while True: + try: + ec2.get_all_instances(instids) + break + except boto.exception.EC2ResponseError as e: + if e.error_code == 'InvalidInstanceID.NotFound': + # there's a race between start and get an instance + continue + else: + module.fail_json_aws(e) + + # The instances returned through ec2.run_instances above can be in + # terminated state due to idempotency. See commit 7f11c3d for a complete + # explanation. + terminated_instances = [ + str(instance.id) for instance in res.instances if instance.state == 'terminated' + ] + if terminated_instances: + module.fail_json(msg="Instances with id(s) %s " % terminated_instances + + "were created previously but have since been terminated - " + + "use a (possibly different) 'instanceid' parameter") + + else: + if private_ip: + module.fail_json( + msg='private_ip only available with on-demand (non-spot) instances') + if boto_supports_param_in_spot_request(ec2, 'placement_group'): + params['placement_group'] = placement_group + elif placement_group: + module.fail_json( + msg="placement_group parameter requires Boto version 2.3.0 or higher.") + + # You can't tell spot instances to 'stop'; they will always be + # 'terminate'd. For convenience, we'll ignore the latter value. + if instance_initiated_shutdown_behavior and instance_initiated_shutdown_behavior != 'terminate': + module.fail_json( + msg="instance_initiated_shutdown_behavior=stop is not supported for spot instances.") + + if spot_launch_group and isinstance(spot_launch_group, string_types): + params['launch_group'] = spot_launch_group + + params.update(dict( + count=count_remaining, + type=spot_type, + )) + + # Set spot ValidUntil + # ValidUntil -> (timestamp). The end date of the request, in + # UTC format (for example, YYYY -MM -DD T*HH* :MM :SS Z). + utc_valid_until = ( + datetime.datetime.utcnow() + + datetime.timedelta(seconds=spot_wait_timeout)) + params['valid_until'] = utc_valid_until.strftime('%Y-%m-%dT%H:%M:%S.000Z') + + res = ec2.request_spot_instances(spot_price, **params) + + # Now we have to do the intermediate waiting + if wait: + instids = await_spot_requests(module, ec2, res, count) + else: + instids = [] + except boto.exception.BotoServerError as e: + module.fail_json_aws(e, msg='Instance creation failed') + + # wait here until the instances are up + num_running = 0 + wait_timeout = time.time() + wait_timeout + res_list = () + while wait_timeout > time.time() and num_running < len(instids): + try: + res_list = ec2.get_all_instances(instids) + except boto.exception.BotoServerError as e: + if e.error_code == 'InvalidInstanceID.NotFound': + time.sleep(1) + continue + else: + raise + + num_running = 0 + for res in res_list: + num_running += len([i for i in res.instances if i.state == 'running']) + if len(res_list) <= 0: + # got a bad response of some sort, possibly due to + # stale/cached data. Wait a second and then try again + time.sleep(1) + continue + if wait and num_running < len(instids): + time.sleep(5) + else: + break + + if wait and wait_timeout <= time.time(): + # waiting took too long + module.fail_json(msg="wait for instances running timeout on %s" % time.asctime()) + + # We do this after the loop ends so that we end up with one list + for res in res_list: + running_instances.extend(res.instances) + + # Enabled by default by AWS + if source_dest_check is False: + for inst in res.instances: + inst.modify_attribute('sourceDestCheck', False) + + # Disabled by default by AWS + if termination_protection is True: + for inst in res.instances: + inst.modify_attribute('disableApiTermination', True) + + # Leave this as late as possible to try and avoid InvalidInstanceID.NotFound + if instance_tags and instids: + try: + ec2.create_tags(instids, instance_tags) + except boto.exception.EC2ResponseError as e: + module.fail_json_aws(e, msg='Instance tagging failed') + + instance_dict_array = [] + created_instance_ids = [] + for inst in running_instances: + inst.update() + d = get_instance_info(inst) + created_instance_ids.append(inst.id) + instance_dict_array.append(d) + + return (instance_dict_array, created_instance_ids, changed) + + +def terminate_instances(module, ec2, instance_ids): + """ + Terminates a list of instances + + module: Ansible module object + ec2: authenticated ec2 connection object + termination_list: a list of instances to terminate in the form of + [ {id: <inst-id>}, ..] + + Returns a dictionary of instance information + about the instances terminated. + + If the instance to be terminated is running + "changed" will be set to False. + + """ + + # Whether to wait for termination to complete before returning + wait = module.params.get('wait') + wait_timeout = int(module.params.get('wait_timeout')) + + changed = False + instance_dict_array = [] + + if not isinstance(instance_ids, list) or len(instance_ids) < 1: + module.fail_json(msg='instance_ids should be a list of instances, aborting') + + terminated_instance_ids = [] + for res in ec2.get_all_instances(instance_ids): + for inst in res.instances: + if inst.state == 'running' or inst.state == 'stopped': + terminated_instance_ids.append(inst.id) + instance_dict_array.append(get_instance_info(inst)) + try: + ec2.terminate_instances([inst.id]) + except EC2ResponseError as e: + module.fail_json_aws(e, msg='Unable to terminate instance {0}'.format(inst.id)) + changed = True + + # wait here until the instances are 'terminated' + if wait: + num_terminated = 0 + wait_timeout = time.time() + wait_timeout + while wait_timeout > time.time() and num_terminated < len(terminated_instance_ids): + response = ec2.get_all_instances(instance_ids=terminated_instance_ids, + filters={'instance-state-name': 'terminated'}) + try: + num_terminated = sum([len(res.instances) for res in response]) + except Exception as e: + # got a bad response of some sort, possibly due to + # stale/cached data. Wait a second and then try again + time.sleep(1) + continue + + if num_terminated < len(terminated_instance_ids): + time.sleep(5) + + # waiting took too long + if wait_timeout < time.time() and num_terminated < len(terminated_instance_ids): + module.fail_json(msg="wait for instance termination timeout on %s" % time.asctime()) + # Lets get the current state of the instances after terminating - issue600 + instance_dict_array = [] + for res in ec2.get_all_instances(instance_ids=terminated_instance_ids, filters={'instance-state-name': 'terminated'}): + for inst in res.instances: + instance_dict_array.append(get_instance_info(inst)) + + return (changed, instance_dict_array, terminated_instance_ids) + + +def startstop_instances(module, ec2, instance_ids, state, instance_tags): + """ + Starts or stops a list of existing instances + + module: Ansible module object + ec2: authenticated ec2 connection object + instance_ids: The list of instances to start in the form of + [ {id: <inst-id>}, ..] + instance_tags: A dict of tag keys and values in the form of + {key: value, ... } + state: Intended state ("running" or "stopped") + + Returns a dictionary of instance information + about the instances started/stopped. + + If the instance was not able to change state, + "changed" will be set to False. + + Note that if instance_ids and instance_tags are both non-empty, + this method will process the intersection of the two + """ + + wait = module.params.get('wait') + wait_timeout = int(module.params.get('wait_timeout')) + group_id = module.params.get('group_id') + group_name = module.params.get('group') + changed = False + instance_dict_array = [] + + if not isinstance(instance_ids, list) or len(instance_ids) < 1: + # Fail unless the user defined instance tags + if not instance_tags: + module.fail_json(msg='instance_ids should be a list of instances, aborting') + + # To make an EC2 tag filter, we need to prepend 'tag:' to each key. + # An empty filter does no filtering, so it's safe to pass it to the + # get_all_instances method even if the user did not specify instance_tags + filters = {} + if instance_tags: + for key, value in instance_tags.items(): + filters["tag:" + key] = value + + filters['instance-state-name'] = ["pending", "running", "stopping", "stopped"] + + if module.params.get('id'): + filters['client-token'] = module.params['id'] + # Check that our instances are not in the state we want to take + + # Check (and eventually change) instances attributes and instances state + existing_instances_array = [] + for res in ec2.get_all_instances(instance_ids, filters=filters): + for inst in res.instances: + + warn_if_public_ip_assignment_changed(module, inst) + + changed = (check_source_dest_attr(module, inst, ec2) or + check_termination_protection(module, inst) or changed) + + # Check security groups and if we're using ec2-vpc; ec2-classic security groups may not be modified + if inst.vpc_id and group_name: + grp_details = ec2.get_all_security_groups(filters={'vpc_id': inst.vpc_id}) + if isinstance(group_name, string_types): + group_name = [group_name] + unmatched = set(group_name) - set(to_text(grp.name) for grp in grp_details) + if unmatched: + module.fail_json(msg="The following group names are not valid: %s" % ', '.join(unmatched)) + group_ids = [to_text(grp.id) for grp in grp_details if to_text(grp.name) in group_name] + elif inst.vpc_id and group_id: + if isinstance(group_id, string_types): + group_id = [group_id] + grp_details = ec2.get_all_security_groups(group_ids=group_id) + group_ids = [grp_item.id for grp_item in grp_details] + if inst.vpc_id and (group_name or group_id): + if set(sg.id for sg in inst.groups) != set(group_ids): + changed = inst.modify_attribute('groupSet', group_ids) + + # Check instance state + if inst.state != state: + instance_dict_array.append(get_instance_info(inst)) + try: + if state == 'running': + inst.start() + else: + inst.stop() + except EC2ResponseError as e: + module.fail_json_aws(e, 'Unable to change state for instance {0}'.format(inst.id)) + changed = True + existing_instances_array.append(inst.id) + + instance_ids = list(set(existing_instances_array + (instance_ids or []))) + # Wait for all the instances to finish starting or stopping + wait_timeout = time.time() + wait_timeout + while wait and wait_timeout > time.time(): + instance_dict_array = [] + matched_instances = [] + for res in ec2.get_all_instances(instance_ids): + for i in res.instances: + if i.state == state: + instance_dict_array.append(get_instance_info(i)) + matched_instances.append(i) + if len(matched_instances) < len(instance_ids): + time.sleep(5) + else: + break + + if wait and wait_timeout <= time.time(): + # waiting took too long + module.fail_json(msg="wait for instances running timeout on %s" % time.asctime()) + + return (changed, instance_dict_array, instance_ids) + + +def restart_instances(module, ec2, instance_ids, state, instance_tags): + """ + Restarts a list of existing instances + + module: Ansible module object + ec2: authenticated ec2 connection object + instance_ids: The list of instances to start in the form of + [ {id: <inst-id>}, ..] + instance_tags: A dict of tag keys and values in the form of + {key: value, ... } + state: Intended state ("restarted") + + Returns a dictionary of instance information + about the instances. + + If the instance was not able to change state, + "changed" will be set to False. + + Wait will not apply here as this is a OS level operation. + + Note that if instance_ids and instance_tags are both non-empty, + this method will process the intersection of the two. + """ + + changed = False + instance_dict_array = [] + + if not isinstance(instance_ids, list) or len(instance_ids) < 1: + # Fail unless the user defined instance tags + if not instance_tags: + module.fail_json(msg='instance_ids should be a list of instances, aborting') + + # To make an EC2 tag filter, we need to prepend 'tag:' to each key. + # An empty filter does no filtering, so it's safe to pass it to the + # get_all_instances method even if the user did not specify instance_tags + filters = {} + if instance_tags: + for key, value in instance_tags.items(): + filters["tag:" + key] = value + if module.params.get('id'): + filters['client-token'] = module.params['id'] + + # Check that our instances are not in the state we want to take + + # Check (and eventually change) instances attributes and instances state + for res in ec2.get_all_instances(instance_ids, filters=filters): + for inst in res.instances: + + warn_if_public_ip_assignment_changed(module, inst) + + changed = (check_source_dest_attr(module, inst, ec2) or + check_termination_protection(module, inst) or changed) + + # Check instance state + if inst.state != state: + instance_dict_array.append(get_instance_info(inst)) + try: + inst.reboot() + except EC2ResponseError as e: + module.fail_json_aws(e, msg='Unable to change state for instance {0}'.format(inst.id)) + changed = True + + return (changed, instance_dict_array, instance_ids) + + +def check_termination_protection(module, inst): + """ + Check the instance disableApiTermination attribute. + + module: Ansible module object + inst: EC2 instance object + + returns: True if state changed None otherwise + """ + + termination_protection = module.params.get('termination_protection') + + if (inst.get_attribute('disableApiTermination')['disableApiTermination'] != termination_protection and termination_protection is not None): + inst.modify_attribute('disableApiTermination', termination_protection) + return True + + +def check_source_dest_attr(module, inst, ec2): + """ + Check the instance sourceDestCheck attribute. + + module: Ansible module object + inst: EC2 instance object + + returns: True if state changed None otherwise + """ + + source_dest_check = module.params.get('source_dest_check') + + if source_dest_check is not None: + try: + if inst.vpc_id is not None and inst.get_attribute('sourceDestCheck')['sourceDestCheck'] != source_dest_check: + inst.modify_attribute('sourceDestCheck', source_dest_check) + return True + except boto.exception.EC2ResponseError as exc: + # instances with more than one Elastic Network Interface will + # fail, because they have the sourceDestCheck attribute defined + # per-interface + if exc.code == 'InvalidInstanceID': + for interface in inst.interfaces: + if interface.source_dest_check != source_dest_check: + ec2.modify_network_interface_attribute(interface.id, "sourceDestCheck", source_dest_check) + return True + else: + module.fail_json_aws(exc, msg='Failed to handle source_dest_check state for instance {0}'.format(inst.id)) + + +def warn_if_public_ip_assignment_changed(module, instance): + # This is a non-modifiable attribute. + assign_public_ip = module.params.get('assign_public_ip') + + # Check that public ip assignment is the same and warn if not + public_dns_name = getattr(instance, 'public_dns_name', None) + if (assign_public_ip or public_dns_name) and (not public_dns_name or assign_public_ip is False): + module.warn("Unable to modify public ip assignment to {0} for instance {1}. " + "Whether or not to assign a public IP is determined during instance creation.".format(assign_public_ip, instance.id)) + + +def main(): + argument_spec = dict( + key_name=dict(aliases=['keypair']), + id=dict(), + group=dict(type='list', elements='str', aliases=['groups']), + group_id=dict(type='list', elements='str'), + zone=dict(aliases=['aws_zone', 'ec2_zone']), + instance_type=dict(aliases=['type']), + spot_price=dict(), + spot_type=dict(default='one-time', choices=["one-time", "persistent"]), + spot_launch_group=dict(), + image=dict(), + kernel=dict(), + count=dict(type='int', default='1'), + monitoring=dict(type='bool', default=False), + ramdisk=dict(), + wait=dict(type='bool', default=False), + wait_timeout=dict(type='int', default=300), + spot_wait_timeout=dict(type='int', default=600), + placement_group=dict(), + user_data=dict(), + instance_tags=dict(type='dict'), + vpc_subnet_id=dict(), + assign_public_ip=dict(type='bool'), + private_ip=dict(), + instance_profile_name=dict(), + instance_ids=dict(type='list', elements='str', aliases=['instance_id']), + source_dest_check=dict(type='bool', default=None), + termination_protection=dict(type='bool', default=None), + state=dict(default='present', choices=['present', 'absent', 'running', 'restarted', 'stopped']), + instance_initiated_shutdown_behavior=dict(default='stop', choices=['stop', 'terminate']), + exact_count=dict(type='int', default=None), + count_tag=dict(type='raw'), + volumes=dict(type='list', elements='dict',), + ebs_optimized=dict(type='bool', default=False), + tenancy=dict(default='default', choices=['default', 'dedicated']), + network_interfaces=dict(type='list', elements='str', aliases=['network_interface']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + check_boto3=False, + mutually_exclusive=[ + # Can be uncommented when we finish the deprecation cycle. + # ['group', 'group_id'], + ['exact_count', 'count'], + ['exact_count', 'state'], + ['exact_count', 'instance_ids'], + ['network_interfaces', 'assign_public_ip'], + ['network_interfaces', 'group'], + ['network_interfaces', 'group_id'], + ['network_interfaces', 'private_ip'], + ['network_interfaces', 'vpc_subnet_id'], + ], + ) + + if module.params.get('group') and module.params.get('group_id'): + module.deprecate( + msg='Support for passing both group and group_id has been deprecated. ' + 'Currently group_id is ignored, in future passing both will result in an error', + date='2022-06-01', collection_name='amazon.aws') + + if not HAS_BOTO: + module.fail_json(msg='boto required for this module') + + try: + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) + if module.params.get('region') or not module.params.get('ec2_url'): + ec2 = ec2_connect(module) + elif module.params.get('ec2_url'): + ec2 = connect_ec2_endpoint(ec2_url, **aws_connect_kwargs) + + if 'region' not in aws_connect_kwargs: + aws_connect_kwargs['region'] = ec2.region + + vpc = connect_vpc(**aws_connect_kwargs) + except boto.exception.NoAuthHandlerFound as e: + module.fail_json_aws(e, msg='Failed to get connection') + + tagged_instances = [] + + state = module.params['state'] + + if state == 'absent': + instance_ids = module.params['instance_ids'] + if not instance_ids: + module.fail_json(msg='instance_ids list is required for absent state') + + (changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids) + + elif state in ('running', 'stopped'): + instance_ids = module.params.get('instance_ids') + instance_tags = module.params.get('instance_tags') + if not (isinstance(instance_ids, list) or isinstance(instance_tags, dict)): + module.fail_json(msg='running list needs to be a list of instances or set of tags to run: %s' % instance_ids) + + (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state, instance_tags) + + elif state in ('restarted'): + instance_ids = module.params.get('instance_ids') + instance_tags = module.params.get('instance_tags') + if not (isinstance(instance_ids, list) or isinstance(instance_tags, dict)): + module.fail_json(msg='running list needs to be a list of instances or set of tags to run: %s' % instance_ids) + + (changed, instance_dict_array, new_instance_ids) = restart_instances(module, ec2, instance_ids, state, instance_tags) + + elif state == 'present': + # Changed is always set to true when provisioning new instances + if not module.params.get('image'): + module.fail_json(msg='image parameter is required for new instance') + + if module.params.get('exact_count') is None: + (instance_dict_array, new_instance_ids, changed) = create_instances(module, ec2, vpc) + else: + (tagged_instances, instance_dict_array, new_instance_ids, changed) = enforce_count(module, ec2, vpc) + + # Always return instances in the same order + if new_instance_ids: + new_instance_ids.sort() + if instance_dict_array: + instance_dict_array.sort(key=lambda x: x['id']) + if tagged_instances: + tagged_instances.sort(key=lambda x: x['id']) + + module.exit_json(changed=changed, instance_ids=new_instance_ids, instances=instance_dict_array, tagged_instances=tagged_instances) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami.py new file mode 100644 index 00000000..86364f78 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami.py @@ -0,0 +1,761 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_ami +version_added: 1.0.0 +short_description: Create or destroy an image (AMI) in ec2 +description: + - Registers or deregisters ec2 images. +options: + instance_id: + description: + - Instance ID to create the AMI from. + type: str + name: + description: + - The name of the new AMI. + type: str + architecture: + description: + - The target architecture of the image to register + default: "x86_64" + type: str + kernel_id: + description: + - The target kernel id of the image to register. + type: str + virtualization_type: + description: + - The virtualization type of the image to register. + default: "hvm" + type: str + root_device_name: + description: + - The root device name of the image to register. + type: str + wait: + description: + - Wait for the AMI to be in state 'available' before returning. + default: false + type: bool + wait_timeout: + description: + - How long before wait gives up, in seconds. + default: 1200 + type: int + state: + description: + - Register or deregister an AMI. + default: 'present' + choices: [ "absent", "present" ] + type: str + description: + description: + - Human-readable string describing the contents and purpose of the AMI. + type: str + no_reboot: + description: + - Flag indicating that the bundling process should not attempt to shutdown the instance before bundling. If this flag is True, the + responsibility of maintaining file system integrity is left to the owner of the instance. + default: false + type: bool + image_id: + description: + - Image ID to be deregistered. + type: str + device_mapping: + description: + - List of device hashes/dictionaries with custom configurations (same block-device-mapping parameters). + type: list + elements: dict + suboptions: + device_name: + type: str + description: + - The device name. For example C(/dev/sda). + required: yes + aliases: ['DeviceName'] + virtual_name: + type: str + description: + - The virtual name for the device. + - See the AWS documentation for more detail U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html). + - Alias C(VirtualName) has been deprecated and will be removed after 2022-06-01. + aliases: ['VirtualName'] + no_device: + type: bool + description: + - Suppresses the specified device included in the block device mapping of the AMI. + - Alias C(NoDevice) has been deprecated and will be removed after 2022-06-01. + aliases: ['NoDevice'] + volume_type: + type: str + description: The volume type. Defaults to C(gp2) when not set. + delete_on_termination: + type: bool + description: Whether the device should be automatically deleted when the Instance is terminated. + snapshot_id: + type: str + description: The ID of the Snapshot. + iops: + type: int + description: When using an C(io1) I(volume_type) this sets the number of IOPS provisioned for the volume + encrypted: + type: bool + description: Whether the volume should be encrypted. + volume_size: + aliases: ['size'] + type: int + description: The size of the volume (in GiB) + delete_snapshot: + description: + - Delete snapshots when deregistering the AMI. + default: false + type: bool + tags: + description: + - A dictionary of tags to add to the new image; '{"key":"value"}' and '{"key":"value","key":"value"}' + type: dict + purge_tags: + description: Whether to remove existing tags that aren't passed in the C(tags) parameter + default: false + type: bool + launch_permissions: + description: + - Users and groups that should be able to launch the AMI. Expects dictionary with a key of user_ids and/or group_names. user_ids should + be a list of account ids. group_name should be a list of groups, "all" is the only acceptable value currently. + - You must pass all desired launch permissions if you wish to modify existing launch permissions (passing just groups will remove all users) + type: dict + image_location: + description: + - The s3 location of an image to use for the AMI. + type: str + enhanced_networking: + description: + - A boolean representing whether enhanced networking with ENA is enabled or not. + type: bool + billing_products: + description: + - A list of valid billing codes. To be used with valid accounts by aws marketplace vendors. + type: list + elements: str + ramdisk_id: + description: + - The ID of the RAM disk. + type: str + sriov_net_support: + description: + - Set to simple to enable enhanced networking with the Intel 82599 Virtual Function interface for the AMI and any instances that you launch from the AMI. + type: str +author: + - "Evan Duffield (@scicoin-project) <eduffield@iacquire.com>" + - "Constantin Bugneac (@Constantin07) <constantin.bugneac@endava.com>" + - "Ross Williams (@gunzy83) <gunzy83au@gmail.com>" + - "Willem van Ketwich (@wilvk) <willvk@gmail.com>" +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +# Thank you to iAcquire for sponsoring development of this module. + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Basic AMI Creation + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + wait: yes + name: newtest + tags: + Name: newtest + Service: TestService + +- name: Basic AMI Creation, without waiting + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + wait: no + name: newtest + +- name: AMI Registration from EBS Snapshot + amazon.aws.ec2_ami: + name: newtest + state: present + architecture: x86_64 + virtualization_type: hvm + root_device_name: /dev/xvda + device_mapping: + - device_name: /dev/xvda + volume_size: 8 + snapshot_id: snap-xxxxxxxx + delete_on_termination: true + volume_type: gp2 + +- name: AMI Creation, with a custom root-device size and another EBS attached + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + name: newtest + device_mapping: + - device_name: /dev/sda1 + size: XXX + delete_on_termination: true + volume_type: gp2 + - device_name: /dev/sdb + size: YYY + delete_on_termination: false + volume_type: gp2 + +- name: AMI Creation, excluding a volume attached at /dev/sdb + amazon.aws.ec2_ami: + instance_id: i-xxxxxx + name: newtest + device_mapping: + - device_name: /dev/sda1 + size: XXX + delete_on_termination: true + volume_type: gp2 + - device_name: /dev/sdb + no_device: yes + +- name: Deregister/Delete AMI (keep associated snapshots) + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + delete_snapshot: False + state: absent + +- name: Deregister AMI (delete associated snapshots too) + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + delete_snapshot: True + state: absent + +- name: Update AMI Launch Permissions, making it public + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + state: present + launch_permissions: + group_names: ['all'] + +- name: Allow AMI to be launched by another account + amazon.aws.ec2_ami: + image_id: "{{ instance.image_id }}" + state: present + launch_permissions: + user_ids: ['123456789012'] +''' + +RETURN = ''' +architecture: + description: Architecture of image. + returned: when AMI is created or already exists + type: str + sample: "x86_64" +block_device_mapping: + description: Block device mapping associated with image. + returned: when AMI is created or already exists + type: dict + sample: { + "/dev/sda1": { + "delete_on_termination": true, + "encrypted": false, + "size": 10, + "snapshot_id": "snap-1a03b80e7", + "volume_type": "standard" + } + } +creationDate: + description: Creation date of image. + returned: when AMI is created or already exists + type: str + sample: "2015-10-15T22:43:44.000Z" +description: + description: Description of image. + returned: when AMI is created or already exists + type: str + sample: "nat-server" +hypervisor: + description: Type of hypervisor. + returned: when AMI is created or already exists + type: str + sample: "xen" +image_id: + description: ID of the image. + returned: when AMI is created or already exists + type: str + sample: "ami-1234abcd" +is_public: + description: Whether image is public. + returned: when AMI is created or already exists + type: bool + sample: false +launch_permission: + description: Permissions allowing other accounts to access the AMI. + returned: when AMI is created or already exists + type: list + sample: + - group: "all" +location: + description: Location of image. + returned: when AMI is created or already exists + type: str + sample: "315210894379/nat-server" +name: + description: AMI name of image. + returned: when AMI is created or already exists + type: str + sample: "nat-server" +ownerId: + description: Owner of image. + returned: when AMI is created or already exists + type: str + sample: "435210894375" +platform: + description: Platform of image. + returned: when AMI is created or already exists + type: str + sample: null +root_device_name: + description: Root device name of image. + returned: when AMI is created or already exists + type: str + sample: "/dev/sda1" +root_device_type: + description: Root device type of image. + returned: when AMI is created or already exists + type: str + sample: "ebs" +state: + description: State of image. + returned: when AMI is created or already exists + type: str + sample: "available" +tags: + description: A dictionary of tags assigned to image. + returned: when AMI is created or already exists + type: dict + sample: { + "Env": "devel", + "Name": "nat-server" + } +virtualization_type: + description: Image virtualization type. + returned: when AMI is created or already exists + type: str + sample: "hvm" +snapshots_deleted: + description: A list of snapshot ids deleted after deregistering image. + returned: after AMI is deregistered, if I(delete_snapshot=true) + type: list + sample: [ + "snap-fbcccb8f", + "snap-cfe7cdb4" + ] +''' + +import time + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_aws_tags +from ..module_utils.waiters import get_waiter + + +def get_block_device_mapping(image): + bdm_dict = dict() + if image is not None and image.get('block_device_mappings') is not None: + bdm = image.get('block_device_mappings') + for device in bdm: + device_name = device.get('device_name') + if 'ebs' in device: + ebs = device.get("ebs") + bdm_dict_item = { + 'size': ebs.get("volume_size"), + 'snapshot_id': ebs.get("snapshot_id"), + 'volume_type': ebs.get("volume_type"), + 'encrypted': ebs.get("encrypted"), + 'delete_on_termination': ebs.get("delete_on_termination") + } + elif 'virtual_name' in device: + bdm_dict_item = dict(virtual_name=device['virtual_name']) + bdm_dict[device_name] = bdm_dict_item + return bdm_dict + + +def get_ami_info(camel_image): + image = camel_dict_to_snake_dict(camel_image) + return dict( + image_id=image.get("image_id"), + state=image.get("state"), + architecture=image.get("architecture"), + block_device_mapping=get_block_device_mapping(image), + creationDate=image.get("creation_date"), + description=image.get("description"), + hypervisor=image.get("hypervisor"), + is_public=image.get("public"), + location=image.get("image_location"), + ownerId=image.get("owner_id"), + root_device_name=image.get("root_device_name"), + root_device_type=image.get("root_device_type"), + virtualization_type=image.get("virtualization_type"), + name=image.get("name"), + tags=boto3_tag_list_to_ansible_dict(image.get('tags')), + platform=image.get("platform"), + enhanced_networking=image.get("ena_support"), + image_owner_alias=image.get("image_owner_alias"), + image_type=image.get("image_type"), + kernel_id=image.get("kernel_id"), + product_codes=image.get("product_codes"), + ramdisk_id=image.get("ramdisk_id"), + sriov_net_support=image.get("sriov_net_support"), + state_reason=image.get("state_reason"), + launch_permissions=image.get('launch_permissions') + ) + + +def create_image(module, connection): + instance_id = module.params.get('instance_id') + name = module.params.get('name') + wait = module.params.get('wait') + wait_timeout = module.params.get('wait_timeout') + description = module.params.get('description') + architecture = module.params.get('architecture') + kernel_id = module.params.get('kernel_id') + root_device_name = module.params.get('root_device_name') + virtualization_type = module.params.get('virtualization_type') + no_reboot = module.params.get('no_reboot') + device_mapping = module.params.get('device_mapping') + tags = module.params.get('tags') + launch_permissions = module.params.get('launch_permissions') + image_location = module.params.get('image_location') + enhanced_networking = module.params.get('enhanced_networking') + billing_products = module.params.get('billing_products') + ramdisk_id = module.params.get('ramdisk_id') + sriov_net_support = module.params.get('sriov_net_support') + + try: + params = { + 'Name': name, + 'Description': description + } + + block_device_mapping = None + + # Remove empty values injected by using options + if device_mapping: + block_device_mapping = [] + for device in device_mapping: + device = dict((k, v) for k, v in device.items() if v is not None) + device['Ebs'] = {} + device = rename_item_if_exists(device, 'device_name', 'DeviceName') + device = rename_item_if_exists(device, 'virtual_name', 'VirtualName') + device = rename_item_if_exists(device, 'no_device', 'NoDevice') + device = rename_item_if_exists(device, 'volume_type', 'VolumeType', 'Ebs') + device = rename_item_if_exists(device, 'snapshot_id', 'SnapshotId', 'Ebs') + device = rename_item_if_exists(device, 'delete_on_termination', 'DeleteOnTermination', 'Ebs') + device = rename_item_if_exists(device, 'size', 'VolumeSize', 'Ebs', attribute_type=int) + device = rename_item_if_exists(device, 'volume_size', 'VolumeSize', 'Ebs', attribute_type=int) + device = rename_item_if_exists(device, 'iops', 'Iops', 'Ebs') + device = rename_item_if_exists(device, 'encrypted', 'Encrypted', 'Ebs') + block_device_mapping.append(device) + if block_device_mapping: + params['BlockDeviceMappings'] = block_device_mapping + if instance_id: + params['InstanceId'] = instance_id + params['NoReboot'] = no_reboot + image_id = connection.create_image(aws_retry=True, **params).get('ImageId') + else: + if architecture: + params['Architecture'] = architecture + if virtualization_type: + params['VirtualizationType'] = virtualization_type + if image_location: + params['ImageLocation'] = image_location + if enhanced_networking: + params['EnaSupport'] = enhanced_networking + if billing_products: + params['BillingProducts'] = billing_products + if ramdisk_id: + params['RamdiskId'] = ramdisk_id + if sriov_net_support: + params['SriovNetSupport'] = sriov_net_support + if kernel_id: + params['KernelId'] = kernel_id + if root_device_name: + params['RootDeviceName'] = root_device_name + image_id = connection.register_image(aws_retry=True, **params).get('ImageId') + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error registering image") + + if wait: + delay = 15 + max_attempts = wait_timeout // delay + waiter = get_waiter(connection, 'image_available') + waiter.wait(ImageIds=[image_id], WaiterConfig=dict(Delay=delay, MaxAttempts=max_attempts)) + + if tags: + try: + connection.create_tags(aws_retry=True, Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags)) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error tagging image") + + if launch_permissions: + try: + params = dict(Attribute='LaunchPermission', ImageId=image_id, LaunchPermission=dict(Add=list())) + for group_name in launch_permissions.get('group_names', []): + params['LaunchPermission']['Add'].append(dict(Group=group_name)) + for user_id in launch_permissions.get('user_ids', []): + params['LaunchPermission']['Add'].append(dict(UserId=str(user_id))) + if params['LaunchPermission']['Add']: + connection.modify_image_attribute(aws_retry=True, **params) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error setting launch permissions for image %s" % image_id) + + module.exit_json(msg="AMI creation operation complete.", changed=True, + **get_ami_info(get_image_by_id(module, connection, image_id))) + + +def deregister_image(module, connection): + image_id = module.params.get('image_id') + delete_snapshot = module.params.get('delete_snapshot') + wait = module.params.get('wait') + wait_timeout = module.params.get('wait_timeout') + image = get_image_by_id(module, connection, image_id) + + if image is None: + module.exit_json(changed=False) + + # Get all associated snapshot ids before deregistering image otherwise this information becomes unavailable. + snapshots = [] + if 'BlockDeviceMappings' in image: + for mapping in image.get('BlockDeviceMappings'): + snapshot_id = mapping.get('Ebs', {}).get('SnapshotId') + if snapshot_id is not None: + snapshots.append(snapshot_id) + + # When trying to re-deregister an already deregistered image it doesn't raise an exception, it just returns an object without image attributes. + if 'ImageId' in image: + try: + connection.deregister_image(aws_retry=True, ImageId=image_id) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error deregistering image") + else: + module.exit_json(msg="Image %s has already been deregistered." % image_id, changed=False) + + image = get_image_by_id(module, connection, image_id) + wait_timeout = time.time() + wait_timeout + + while wait and wait_timeout > time.time() and image is not None: + image = get_image_by_id(module, connection, image_id) + time.sleep(3) + + if wait and wait_timeout <= time.time(): + module.fail_json(msg="Timed out waiting for image to be deregistered.") + + exit_params = {'msg': "AMI deregister operation complete.", 'changed': True} + + if delete_snapshot: + for snapshot_id in snapshots: + try: + connection.delete_snapshot(aws_retry=True, SnapshotId=snapshot_id) + # Don't error out if root volume snapshot was already deregistered as part of deregister_image + except is_boto3_error_code('InvalidSnapshot.NotFound'): + pass + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to delete snapshot.') + exit_params['snapshots_deleted'] = snapshots + + module.exit_json(**exit_params) + + +def update_image(module, connection, image_id): + launch_permissions = module.params.get('launch_permissions') + image = get_image_by_id(module, connection, image_id) + if image is None: + module.fail_json(msg="Image %s does not exist" % image_id, changed=False) + changed = False + + if launch_permissions is not None: + current_permissions = image['LaunchPermissions'] + + current_users = set(permission['UserId'] for permission in current_permissions if 'UserId' in permission) + desired_users = set(str(user_id) for user_id in launch_permissions.get('user_ids', [])) + current_groups = set(permission['Group'] for permission in current_permissions if 'Group' in permission) + desired_groups = set(launch_permissions.get('group_names', [])) + + to_add_users = desired_users - current_users + to_remove_users = current_users - desired_users + to_add_groups = desired_groups - current_groups + to_remove_groups = current_groups - desired_groups + + to_add = [dict(Group=group) for group in to_add_groups] + [dict(UserId=user_id) for user_id in to_add_users] + to_remove = [dict(Group=group) for group in to_remove_groups] + [dict(UserId=user_id) for user_id in to_remove_users] + + if to_add or to_remove: + try: + connection.modify_image_attribute(aws_retry=True, + ImageId=image_id, Attribute='launchPermission', + LaunchPermission=dict(Add=to_add, Remove=to_remove)) + changed = True + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error updating launch permissions of image %s" % image_id) + + desired_tags = module.params.get('tags') + if desired_tags is not None: + current_tags = boto3_tag_list_to_ansible_dict(image.get('Tags')) + tags_to_add, tags_to_remove = compare_aws_tags(current_tags, desired_tags, purge_tags=module.params.get('purge_tags')) + + if tags_to_remove: + try: + connection.delete_tags(aws_retry=True, Resources=[image_id], Tags=[dict(Key=tagkey) for tagkey in tags_to_remove]) + changed = True + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error updating tags") + + if tags_to_add: + try: + connection.create_tags(aws_retry=True, Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags_to_add)) + changed = True + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error updating tags") + + description = module.params.get('description') + if description and description != image['Description']: + try: + connection.modify_image_attribute(aws_retry=True, Attribute='Description ', ImageId=image_id, Description=dict(Value=description)) + changed = True + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error setting description for image %s" % image_id) + + if changed: + module.exit_json(msg="AMI updated.", changed=True, + **get_ami_info(get_image_by_id(module, connection, image_id))) + else: + module.exit_json(msg="AMI not updated.", changed=False, + **get_ami_info(get_image_by_id(module, connection, image_id))) + + +def get_image_by_id(module, connection, image_id): + try: + try: + images_response = connection.describe_images(aws_retry=True, ImageIds=[image_id]) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error retrieving image %s" % image_id) + images = images_response.get('Images') + no_images = len(images) + if no_images == 0: + return None + if no_images == 1: + result = images[0] + try: + result['LaunchPermissions'] = connection.describe_image_attribute(aws_retry=True, Attribute='launchPermission', + ImageId=image_id)['LaunchPermissions'] + result['ProductCodes'] = connection.describe_image_attribute(aws_retry=True, Attribute='productCodes', + ImageId=image_id)['ProductCodes'] + except is_boto3_error_code('InvalidAMIID.Unavailable'): + pass + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error retrieving image attributes for image %s" % image_id) + return result + module.fail_json(msg="Invalid number of instances (%s) found for image_id: %s." % (str(len(images)), image_id)) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Error retrieving image by image_id") + + +def rename_item_if_exists(dict_object, attribute, new_attribute, child_node=None, attribute_type=None): + new_item = dict_object.get(attribute) + if new_item is not None: + if attribute_type is not None: + new_item = attribute_type(new_item) + if child_node is None: + dict_object[new_attribute] = new_item + else: + dict_object[child_node][new_attribute] = new_item + dict_object.pop(attribute) + return dict_object + + +def main(): + mapping_options = dict( + device_name=dict(type='str', aliases=['DeviceName'], required=True), + virtual_name=dict( + type='str', aliases=['VirtualName'], + deprecated_aliases=[dict(name='VirtualName', date='2022-06-01', collection_name='amazon.aws')]), + no_device=dict( + type='bool', aliases=['NoDevice'], + deprecated_aliases=[dict(name='NoDevice', date='2022-06-01', collection_name='amazon.aws')]), + volume_type=dict(type='str'), + delete_on_termination=dict(type='bool'), + snapshot_id=dict(type='str'), + iops=dict(type='int'), + encrypted=dict(type='bool'), + volume_size=dict(type='int', aliases=['size']), + ) + argument_spec = dict( + instance_id=dict(), + image_id=dict(), + architecture=dict(default='x86_64'), + kernel_id=dict(), + virtualization_type=dict(default='hvm'), + root_device_name=dict(), + delete_snapshot=dict(default=False, type='bool'), + name=dict(), + wait=dict(type='bool', default=False), + wait_timeout=dict(default=1200, type='int'), + description=dict(default=''), + no_reboot=dict(default=False, type='bool'), + state=dict(default='present', choices=['present', 'absent']), + device_mapping=dict(type='list', elements='dict', options=mapping_options), + tags=dict(type='dict'), + launch_permissions=dict(type='dict'), + image_location=dict(), + enhanced_networking=dict(type='bool'), + billing_products=dict(type='list', elements='str',), + ramdisk_id=dict(), + sriov_net_support=dict(), + purge_tags=dict(type='bool', default=False) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + required_if=[ + ['state', 'absent', ['image_id']], + ] + ) + + # Using a required_one_of=[['name', 'image_id']] overrides the message that should be provided by + # the required_if for state=absent, so check manually instead + if not any([module.params['image_id'], module.params['name']]): + module.fail_json(msg="one of the following is required: name, image_id") + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + if module.params.get('state') == 'absent': + deregister_image(module, connection) + elif module.params.get('state') == 'present': + if module.params.get('image_id'): + update_image(module, connection, module.params.get('image_id')) + if not module.params.get('instance_id') and not module.params.get('device_mapping'): + module.fail_json(msg="The parameters instance_id or device_mapping (register from EBS snapshot) are required for a new image.") + create_image(module, connection) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami_facts.py new file mode 100644 index 00000000..f2b52556 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami_facts.py @@ -0,0 +1,287 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_ami_info +version_added: 1.0.0 +short_description: Gather information about ec2 AMIs +description: + - Gather information about ec2 AMIs + - This module was called C(amazon.aws.ec2_ami_facts) before Ansible 2.9. The usage did not change. +author: + - Prasad Katti (@prasadkatti) +requirements: [ boto3 ] +options: + image_ids: + description: One or more image IDs. + aliases: [image_id] + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) for possible filters. + - Filter names and values are case sensitive. + type: dict + owners: + description: + - Filter the images by the owner. Valid options are an AWS account ID, self, + or an AWS owner alias ( amazon | aws-marketplace | microsoft ). + aliases: [owner] + type: list + elements: str + executable_users: + description: + - Filter images by users with explicit launch permissions. Valid options are an AWS account ID, self, or all (public AMIs). + aliases: [executable_user] + type: list + elements: str + describe_image_attributes: + description: + - Describe attributes (like launchPermission) of the images found. + default: no + type: bool + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: gather information about an AMI using ami-id + amazon.aws.ec2_ami_info: + image_ids: ami-5b488823 + +- name: gather information about all AMIs with tag key Name and value webapp + amazon.aws.ec2_ami_info: + filters: + "tag:Name": webapp + +- name: gather information about an AMI with 'AMI Name' equal to foobar + amazon.aws.ec2_ami_info: + filters: + name: foobar + +- name: gather information about Ubuntu 17.04 AMIs published by Canonical (099720109477) + amazon.aws.ec2_ami_info: + owners: 099720109477 + filters: + name: "ubuntu/images/ubuntu-zesty-17.04-*" +''' + +RETURN = ''' +images: + description: A list of images. + returned: always + type: list + elements: dict + contains: + architecture: + description: The architecture of the image. + returned: always + type: str + sample: x86_64 + block_device_mappings: + description: Any block device mapping entries. + returned: always + type: list + elements: dict + contains: + device_name: + description: The device name exposed to the instance. + returned: always + type: str + sample: /dev/sda1 + ebs: + description: EBS volumes + returned: always + type: complex + creation_date: + description: The date and time the image was created. + returned: always + type: str + sample: '2017-10-16T19:22:13.000Z' + description: + description: The description of the AMI. + returned: always + type: str + sample: '' + ena_support: + description: Whether enhanced networking with ENA is enabled. + returned: always + type: bool + sample: true + hypervisor: + description: The hypervisor type of the image. + returned: always + type: str + sample: xen + image_id: + description: The ID of the AMI. + returned: always + type: str + sample: ami-5b466623 + image_location: + description: The location of the AMI. + returned: always + type: str + sample: 408466080000/Webapp + image_type: + description: The type of image. + returned: always + type: str + sample: machine + launch_permissions: + description: A List of AWS accounts may launch the AMI. + returned: When image is owned by calling account and I(describe_image_attributes) is yes. + type: list + elements: dict + contains: + group: + description: A value of 'all' means the AMI is public. + type: str + user_id: + description: An AWS account ID with permissions to launch the AMI. + type: str + sample: [{"group": "all"}, {"user_id": "408466080000"}] + name: + description: The name of the AMI that was provided during image creation. + returned: always + type: str + sample: Webapp + owner_id: + description: The AWS account ID of the image owner. + returned: always + type: str + sample: '408466080000' + public: + description: Whether the image has public launch permissions. + returned: always + type: bool + sample: true + root_device_name: + description: The device name of the root device. + returned: always + type: str + sample: /dev/sda1 + root_device_type: + description: The type of root device used by the AMI. + returned: always + type: str + sample: ebs + sriov_net_support: + description: Whether enhanced networking is enabled. + returned: always + type: str + sample: simple + state: + description: The current state of the AMI. + returned: always + type: str + sample: available + tags: + description: Any tags assigned to the image. + returned: always + type: dict + virtualization_type: + description: The type of virtualization of the AMI. + returned: always + type: str + sample: hvm +''' + +try: + from botocore.exceptions import ClientError, BotoCoreError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def list_ec2_images(ec2_client, module): + + image_ids = module.params.get("image_ids") + owners = module.params.get("owners") + executable_users = module.params.get("executable_users") + filters = module.params.get("filters") + owner_param = [] + + # describe_images is *very* slow if you pass the `Owners` + # param (unless it's self), for some reason. + # Converting the owners to filters and removing from the + # owners param greatly speeds things up. + # Implementation based on aioue's suggestion in #24886 + for owner in owners: + if owner.isdigit(): + if 'owner-id' not in filters: + filters['owner-id'] = list() + filters['owner-id'].append(owner) + elif owner == 'self': + # self not a valid owner-alias filter (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) + owner_param.append(owner) + else: + if 'owner-alias' not in filters: + filters['owner-alias'] = list() + filters['owner-alias'].append(owner) + + filters = ansible_dict_to_boto3_filter_list(filters) + + try: + images = ec2_client.describe_images(aws_retry=True, ImageIds=image_ids, Filters=filters, Owners=owner_param, + ExecutableUsers=executable_users) + images = [camel_dict_to_snake_dict(image) for image in images["Images"]] + except (ClientError, BotoCoreError) as err: + module.fail_json_aws(err, msg="error describing images") + for image in images: + try: + image['tags'] = boto3_tag_list_to_ansible_dict(image.get('tags', [])) + if module.params.get("describe_image_attributes"): + launch_permissions = ec2_client.describe_image_attribute(aws_retry=True, Attribute='launchPermission', + ImageId=image['image_id'])['LaunchPermissions'] + image['launch_permissions'] = [camel_dict_to_snake_dict(perm) for perm in launch_permissions] + except is_boto3_error_code('AuthFailure'): + # describing launch permissions of images owned by others is not permitted, but shouldn't cause failures + pass + except (ClientError, BotoCoreError) as err: + module.fail_json_aws(err, 'Failed to describe AMI') + + images.sort(key=lambda e: e.get('creation_date', '')) # it may be possible that creation_date does not always exist + module.exit_json(images=images) + + +def main(): + + argument_spec = dict( + image_ids=dict(default=[], type='list', elements='str', aliases=['image_id']), + filters=dict(default={}, type='dict'), + owners=dict(default=[], type='list', elements='str', aliases=['owner']), + executable_users=dict(default=[], type='list', elements='str', aliases=['executable_user']), + describe_image_attributes=dict(default=False, type='bool') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._module._name == 'ec2_ami_facts': + module._module.deprecate("The 'ec2_ami_facts' module has been renamed to 'ec2_ami_info'", date='2021-12-01', collection_name='amazon.aws') + + ec2_client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + list_ec2_images(ec2_client, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami_info.py new file mode 100644 index 00000000..f2b52556 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_ami_info.py @@ -0,0 +1,287 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_ami_info +version_added: 1.0.0 +short_description: Gather information about ec2 AMIs +description: + - Gather information about ec2 AMIs + - This module was called C(amazon.aws.ec2_ami_facts) before Ansible 2.9. The usage did not change. +author: + - Prasad Katti (@prasadkatti) +requirements: [ boto3 ] +options: + image_ids: + description: One or more image IDs. + aliases: [image_id] + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) for possible filters. + - Filter names and values are case sensitive. + type: dict + owners: + description: + - Filter the images by the owner. Valid options are an AWS account ID, self, + or an AWS owner alias ( amazon | aws-marketplace | microsoft ). + aliases: [owner] + type: list + elements: str + executable_users: + description: + - Filter images by users with explicit launch permissions. Valid options are an AWS account ID, self, or all (public AMIs). + aliases: [executable_user] + type: list + elements: str + describe_image_attributes: + description: + - Describe attributes (like launchPermission) of the images found. + default: no + type: bool + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: gather information about an AMI using ami-id + amazon.aws.ec2_ami_info: + image_ids: ami-5b488823 + +- name: gather information about all AMIs with tag key Name and value webapp + amazon.aws.ec2_ami_info: + filters: + "tag:Name": webapp + +- name: gather information about an AMI with 'AMI Name' equal to foobar + amazon.aws.ec2_ami_info: + filters: + name: foobar + +- name: gather information about Ubuntu 17.04 AMIs published by Canonical (099720109477) + amazon.aws.ec2_ami_info: + owners: 099720109477 + filters: + name: "ubuntu/images/ubuntu-zesty-17.04-*" +''' + +RETURN = ''' +images: + description: A list of images. + returned: always + type: list + elements: dict + contains: + architecture: + description: The architecture of the image. + returned: always + type: str + sample: x86_64 + block_device_mappings: + description: Any block device mapping entries. + returned: always + type: list + elements: dict + contains: + device_name: + description: The device name exposed to the instance. + returned: always + type: str + sample: /dev/sda1 + ebs: + description: EBS volumes + returned: always + type: complex + creation_date: + description: The date and time the image was created. + returned: always + type: str + sample: '2017-10-16T19:22:13.000Z' + description: + description: The description of the AMI. + returned: always + type: str + sample: '' + ena_support: + description: Whether enhanced networking with ENA is enabled. + returned: always + type: bool + sample: true + hypervisor: + description: The hypervisor type of the image. + returned: always + type: str + sample: xen + image_id: + description: The ID of the AMI. + returned: always + type: str + sample: ami-5b466623 + image_location: + description: The location of the AMI. + returned: always + type: str + sample: 408466080000/Webapp + image_type: + description: The type of image. + returned: always + type: str + sample: machine + launch_permissions: + description: A List of AWS accounts may launch the AMI. + returned: When image is owned by calling account and I(describe_image_attributes) is yes. + type: list + elements: dict + contains: + group: + description: A value of 'all' means the AMI is public. + type: str + user_id: + description: An AWS account ID with permissions to launch the AMI. + type: str + sample: [{"group": "all"}, {"user_id": "408466080000"}] + name: + description: The name of the AMI that was provided during image creation. + returned: always + type: str + sample: Webapp + owner_id: + description: The AWS account ID of the image owner. + returned: always + type: str + sample: '408466080000' + public: + description: Whether the image has public launch permissions. + returned: always + type: bool + sample: true + root_device_name: + description: The device name of the root device. + returned: always + type: str + sample: /dev/sda1 + root_device_type: + description: The type of root device used by the AMI. + returned: always + type: str + sample: ebs + sriov_net_support: + description: Whether enhanced networking is enabled. + returned: always + type: str + sample: simple + state: + description: The current state of the AMI. + returned: always + type: str + sample: available + tags: + description: Any tags assigned to the image. + returned: always + type: dict + virtualization_type: + description: The type of virtualization of the AMI. + returned: always + type: str + sample: hvm +''' + +try: + from botocore.exceptions import ClientError, BotoCoreError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def list_ec2_images(ec2_client, module): + + image_ids = module.params.get("image_ids") + owners = module.params.get("owners") + executable_users = module.params.get("executable_users") + filters = module.params.get("filters") + owner_param = [] + + # describe_images is *very* slow if you pass the `Owners` + # param (unless it's self), for some reason. + # Converting the owners to filters and removing from the + # owners param greatly speeds things up. + # Implementation based on aioue's suggestion in #24886 + for owner in owners: + if owner.isdigit(): + if 'owner-id' not in filters: + filters['owner-id'] = list() + filters['owner-id'].append(owner) + elif owner == 'self': + # self not a valid owner-alias filter (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html) + owner_param.append(owner) + else: + if 'owner-alias' not in filters: + filters['owner-alias'] = list() + filters['owner-alias'].append(owner) + + filters = ansible_dict_to_boto3_filter_list(filters) + + try: + images = ec2_client.describe_images(aws_retry=True, ImageIds=image_ids, Filters=filters, Owners=owner_param, + ExecutableUsers=executable_users) + images = [camel_dict_to_snake_dict(image) for image in images["Images"]] + except (ClientError, BotoCoreError) as err: + module.fail_json_aws(err, msg="error describing images") + for image in images: + try: + image['tags'] = boto3_tag_list_to_ansible_dict(image.get('tags', [])) + if module.params.get("describe_image_attributes"): + launch_permissions = ec2_client.describe_image_attribute(aws_retry=True, Attribute='launchPermission', + ImageId=image['image_id'])['LaunchPermissions'] + image['launch_permissions'] = [camel_dict_to_snake_dict(perm) for perm in launch_permissions] + except is_boto3_error_code('AuthFailure'): + # describing launch permissions of images owned by others is not permitted, but shouldn't cause failures + pass + except (ClientError, BotoCoreError) as err: + module.fail_json_aws(err, 'Failed to describe AMI') + + images.sort(key=lambda e: e.get('creation_date', '')) # it may be possible that creation_date does not always exist + module.exit_json(images=images) + + +def main(): + + argument_spec = dict( + image_ids=dict(default=[], type='list', elements='str', aliases=['image_id']), + filters=dict(default={}, type='dict'), + owners=dict(default=[], type='list', elements='str', aliases=['owner']), + executable_users=dict(default=[], type='list', elements='str', aliases=['executable_user']), + describe_image_attributes=dict(default=False, type='bool') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._module._name == 'ec2_ami_facts': + module._module.deprecate("The 'ec2_ami_facts' module has been renamed to 'ec2_ami_info'", date='2021-12-01', collection_name='amazon.aws') + + ec2_client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + list_ec2_images(ec2_client, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_elb_lb.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_elb_lb.py new file mode 100644 index 00000000..a1e732e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_elb_lb.py @@ -0,0 +1,1338 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_elb_lb +version_added: 1.0.0 +description: + - Returns information about the load balancer. + - Will be marked changed when called only if state is changed. +short_description: Creates, updates or destroys an Amazon ELB. +author: + - "Jim Dalton (@jsdalton)" +options: + state: + description: + - Create or destroy the ELB. + type: str + choices: [ absent, present ] + required: true + name: + description: + - The name of the ELB. + type: str + required: true + listeners: + description: + - List of ports/protocols for this ELB to listen on (see examples). + type: list + elements: dict + purge_listeners: + description: + - Purge existing listeners on ELB that are not found in listeners. + type: bool + default: yes + instance_ids: + description: + - List of instance ids to attach to this ELB. + type: list + elements: str + purge_instance_ids: + description: + - Purge existing instance ids on ELB that are not found in instance_ids. + type: bool + default: no + zones: + description: + - List of availability zones to enable on this ELB. + type: list + elements: str + purge_zones: + description: + - Purge existing availability zones on ELB that are not found in zones. + type: bool + default: no + security_group_ids: + description: + - A list of security groups to apply to the ELB. + type: list + elements: str + security_group_names: + description: + - A list of security group names to apply to the ELB. + type: list + elements: str + health_check: + description: + - An associative array of health check configuration settings (see examples). + type: dict + access_logs: + description: + - An associative array of access logs configuration settings (see examples). + type: dict + subnets: + description: + - A list of VPC subnets to use when creating ELB. Zones should be empty if using this. + type: list + elements: str + purge_subnets: + description: + - Purge existing subnet on ELB that are not found in subnets. + type: bool + default: no + scheme: + description: + - The scheme to use when creating the ELB. For a private VPC-visible ELB use C(internal). + - If you choose to update your scheme with a different value the ELB will be destroyed and + recreated. To update scheme you must use the option I(wait). + type: str + choices: ["internal", "internet-facing"] + default: 'internet-facing' + validate_certs: + description: + - When set to C(no), SSL certificates will not be validated for boto versions >= 2.6.0. + type: bool + default: yes + connection_draining_timeout: + description: + - Wait a specified timeout allowing connections to drain before terminating an instance. + type: int + idle_timeout: + description: + - ELB connections from clients and to servers are timed out after this amount of time. + type: int + cross_az_load_balancing: + description: + - Distribute load across all configured Availability Zones. + - Defaults to C(false). + type: bool + stickiness: + description: + - An associative array of stickiness policy settings. Policy will be applied to all listeners (see examples). + type: dict + wait: + description: + - When specified, Ansible will check the status of the load balancer to ensure it has been successfully + removed from AWS. + type: bool + default: no + wait_timeout: + description: + - Used in conjunction with wait. Number of seconds to wait for the ELB to be terminated. + - A maximum of 600 seconds (10 minutes) is allowed. + type: int + default: 60 + tags: + description: + - An associative array of tags. To delete all tags, supply an empty dict (C({})). + type: dict + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = """ +# Note: None of these examples set aws_access_key, aws_secret_key, or region. +# It is assumed that their matching environment variables are set. + +# Basic provisioning example (non-VPC) + +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http # options are http, https, ssl, tcp + load_balancer_port: 80 + instance_port: 80 + proxy_protocol: True + - protocol: https + load_balancer_port: 443 + instance_protocol: http # optional, defaults to value of protocol setting + instance_port: 80 + # ssl certificate required for https or ssl + ssl_certificate_id: "arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert" + +# Internal ELB example + +- amazon.aws.ec2_elb_lb: + name: "test-vpc" + scheme: internal + state: present + instance_ids: + - i-abcd1234 + purge_instance_ids: true + subnets: + - subnet-abcd1234 + - subnet-1a2b3c4d + listeners: + - protocol: http # options are http, https, ssl, tcp + load_balancer_port: 80 + instance_port: 80 + +# Configure a health check and the access logs +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + health_check: + ping_protocol: http # options are http, https, ssl, tcp + ping_port: 80 + ping_path: "/index.html" # not required for tcp or ssl + response_timeout: 5 # seconds + interval: 30 # seconds + unhealthy_threshold: 2 + healthy_threshold: 10 + access_logs: + interval: 5 # minutes (defaults to 60) + s3_location: "my-bucket" # This value is required if access_logs is set + s3_prefix: "logs" + +# Ensure ELB is gone +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: absent + +# Ensure ELB is gone and wait for check (for default timeout) +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: absent + wait: yes + +# Ensure ELB is gone and wait for check with timeout value +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: absent + wait: yes + wait_timeout: 600 + +# Normally, this module will purge any listeners that exist on the ELB +# but aren't specified in the listeners parameter. If purge_listeners is +# false it leaves them alone +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_listeners: no + +# Normally, this module will leave availability zones that are enabled +# on the ELB alone. If purge_zones is true, then any extraneous zones +# will be removed +- amazon.aws.ec2_elb_lb: + name: "test-please-delete" + state: present + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_zones: yes + +# Creates a ELB and assigns a list of subnets to it. +- amazon.aws.ec2_elb_lb: + state: present + name: 'New ELB' + security_group_ids: 'sg-123456, sg-67890' + region: us-west-2 + subnets: 'subnet-123456,subnet-67890' + purge_subnets: yes + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + +# Create an ELB with connection draining, increased idle timeout and cross availability +# zone load balancing +- amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + connection_draining_timeout: 60 + idle_timeout: 300 + cross_az_load_balancing: "yes" + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + +# Create an ELB with load balancer stickiness enabled +- amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + stickiness: + type: loadbalancer + enabled: yes + expiration: 300 + +# Create an ELB with application stickiness enabled +- amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + stickiness: + type: application + enabled: yes + cookie: SESSIONID + +# Create an ELB and add tags +- amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + tags: + Name: "New ELB" + stack: "production" + client: "Bob" + +# Delete all tags from an ELB +- amazon.aws.ec2_elb_lb: + name: "New ELB" + state: present + region: us-east-1 + zones: + - us-east-1a + - us-east-1d + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + tags: {} +""" + +import random +import time + +try: + import boto + import boto.ec2.elb + import boto.ec2.elb.attributes + import boto.vpc + from boto.ec2.elb.healthcheck import HealthCheck + from boto.ec2.tag import Tag +except ImportError: + pass # Taken care of by ec2.HAS_BOTO + +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_native + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AnsibleAWSError +from ..module_utils.ec2 import HAS_BOTO +from ..module_utils.ec2 import connect_to_aws +from ..module_utils.ec2 import get_aws_connection_info + + +def _throttleable_operation(max_retries): + def _operation_wrapper(op): + def _do_op(*args, **kwargs): + retry = 0 + while True: + try: + return op(*args, **kwargs) + except boto.exception.BotoServerError as e: + if retry < max_retries and e.code in \ + ("Throttling", "RequestLimitExceeded"): + retry = retry + 1 + time.sleep(min(random.random() * (2 ** retry), 300)) + continue + else: + raise + return _do_op + return _operation_wrapper + + +def _get_vpc_connection(module, region, aws_connect_params): + try: + return connect_to_aws(boto.vpc, region, **aws_connect_params) + except (boto.exception.NoAuthHandlerFound, AnsibleAWSError) as e: + module.fail_json_aws(e, 'Failed to connect to AWS') + + +_THROTTLING_RETRIES = 5 + + +class ElbManager(object): + """Handles ELB creation and destruction""" + + def __init__(self, module, name, listeners=None, purge_listeners=None, + zones=None, purge_zones=None, security_group_ids=None, + health_check=None, subnets=None, purge_subnets=None, + scheme="internet-facing", connection_draining_timeout=None, + idle_timeout=None, + cross_az_load_balancing=None, access_logs=None, + stickiness=None, wait=None, wait_timeout=None, tags=None, + region=None, + instance_ids=None, purge_instance_ids=None, **aws_connect_params): + + self.module = module + self.name = name + self.listeners = listeners + self.purge_listeners = purge_listeners + self.instance_ids = instance_ids + self.purge_instance_ids = purge_instance_ids + self.zones = zones + self.purge_zones = purge_zones + self.security_group_ids = security_group_ids + self.health_check = health_check + self.subnets = subnets + self.purge_subnets = purge_subnets + self.scheme = scheme + self.connection_draining_timeout = connection_draining_timeout + self.idle_timeout = idle_timeout + self.cross_az_load_balancing = cross_az_load_balancing + self.access_logs = access_logs + self.stickiness = stickiness + self.wait = wait + self.wait_timeout = wait_timeout + self.tags = tags + + self.aws_connect_params = aws_connect_params + self.region = region + + self.changed = False + self.status = 'gone' + self.elb_conn = self._get_elb_connection() + + try: + self.elb = self._get_elb() + except boto.exception.BotoServerError as e: + module.fail_json_aws(e, msg='Unable to get all load balancers') + + self.ec2_conn = self._get_ec2_connection() + + @_throttleable_operation(_THROTTLING_RETRIES) + def ensure_ok(self): + """Create the ELB""" + if not self.elb: + # Zones and listeners will be added at creation + self._create_elb() + else: + if self._get_scheme(): + # the only way to change the scheme is by recreating the resource + self.ensure_gone() + self._create_elb() + else: + self._set_zones() + self._set_security_groups() + self._set_elb_listeners() + self._set_subnets() + self._set_health_check() + # boto has introduced support for some ELB attributes in + # different versions, so we check first before trying to + # set them to avoid errors + if self._check_attribute_support('connection_draining'): + self._set_connection_draining_timeout() + if self._check_attribute_support('connecting_settings'): + self._set_idle_timeout() + if self._check_attribute_support('cross_zone_load_balancing'): + self._set_cross_az_load_balancing() + if self._check_attribute_support('access_log'): + self._set_access_log() + # add sticky options + self.select_stickiness_policy() + + # ensure backend server policies are correct + self._set_backend_policies() + # set/remove instance ids + self._set_instance_ids() + + self._set_tags() + + def ensure_gone(self): + """Destroy the ELB""" + if self.elb: + self._delete_elb() + if self.wait: + elb_removed = self._wait_for_elb_removed() + # Unfortunately even though the ELB itself is removed quickly + # the interfaces take longer so reliant security groups cannot + # be deleted until the interface has registered as removed. + elb_interface_removed = self._wait_for_elb_interface_removed() + if not (elb_removed and elb_interface_removed): + self.module.fail_json(msg='Timed out waiting for removal of load balancer.') + + def get_info(self): + try: + check_elb = self.elb_conn.get_all_load_balancers(self.name)[0] + except Exception: + check_elb = None + + if not check_elb: + info = { + 'name': self.name, + 'status': self.status, + 'region': self.region + } + else: + try: + lb_cookie_policy = check_elb.policies.lb_cookie_stickiness_policies[0].__dict__['policy_name'] + except Exception: + lb_cookie_policy = None + try: + app_cookie_policy = check_elb.policies.app_cookie_stickiness_policies[0].__dict__['policy_name'] + except Exception: + app_cookie_policy = None + + info = { + 'name': check_elb.name, + 'dns_name': check_elb.dns_name, + 'zones': check_elb.availability_zones, + 'security_group_ids': check_elb.security_groups, + 'status': self.status, + 'subnets': self.subnets, + 'scheme': check_elb.scheme, + 'hosted_zone_name': check_elb.canonical_hosted_zone_name, + 'hosted_zone_id': check_elb.canonical_hosted_zone_name_id, + 'lb_cookie_policy': lb_cookie_policy, + 'app_cookie_policy': app_cookie_policy, + 'proxy_policy': self._get_proxy_protocol_policy(), + 'backends': self._get_backend_policies(), + 'instances': [instance.id for instance in check_elb.instances], + 'out_of_service_count': 0, + 'in_service_count': 0, + 'unknown_instance_state_count': 0, + 'region': self.region + } + + # status of instances behind the ELB + if info['instances']: + info['instance_health'] = [dict( + instance_id=instance_state.instance_id, + reason_code=instance_state.reason_code, + state=instance_state.state + ) for instance_state in self.elb_conn.describe_instance_health(self.name)] + else: + info['instance_health'] = [] + + # instance state counts: InService or OutOfService + if info['instance_health']: + for instance_state in info['instance_health']: + if instance_state['state'] == "InService": + info['in_service_count'] += 1 + elif instance_state['state'] == "OutOfService": + info['out_of_service_count'] += 1 + else: + info['unknown_instance_state_count'] += 1 + + if check_elb.health_check: + info['health_check'] = { + 'target': check_elb.health_check.target, + 'interval': check_elb.health_check.interval, + 'timeout': check_elb.health_check.timeout, + 'healthy_threshold': check_elb.health_check.healthy_threshold, + 'unhealthy_threshold': check_elb.health_check.unhealthy_threshold, + } + + if check_elb.listeners: + info['listeners'] = [self._api_listener_as_tuple(l) + for l in check_elb.listeners] + elif self.status == 'created': + # When creating a new ELB, listeners don't show in the + # immediately returned result, so just include the + # ones that were added + info['listeners'] = [self._listener_as_tuple(l) + for l in self.listeners] + else: + info['listeners'] = [] + + if self._check_attribute_support('connection_draining'): + info['connection_draining_timeout'] = int(self.elb_conn.get_lb_attribute(self.name, 'ConnectionDraining').timeout) + + if self._check_attribute_support('connecting_settings'): + info['idle_timeout'] = self.elb_conn.get_lb_attribute(self.name, 'ConnectingSettings').idle_timeout + + if self._check_attribute_support('cross_zone_load_balancing'): + is_cross_az_lb_enabled = self.elb_conn.get_lb_attribute(self.name, 'CrossZoneLoadBalancing') + if is_cross_az_lb_enabled: + info['cross_az_load_balancing'] = 'yes' + else: + info['cross_az_load_balancing'] = 'no' + + # return stickiness info? + + info['tags'] = self.tags + + return info + + @_throttleable_operation(_THROTTLING_RETRIES) + def _wait_for_elb_removed(self): + polling_increment_secs = 15 + max_retries = (self.wait_timeout // polling_increment_secs) + status_achieved = False + + for x in range(0, max_retries): + try: + self.elb_conn.get_all_lb_attributes(self.name) + except (boto.exception.BotoServerError, Exception) as e: + if "LoadBalancerNotFound" in e.code: + status_achieved = True + break + else: + time.sleep(polling_increment_secs) + + return status_achieved + + @_throttleable_operation(_THROTTLING_RETRIES) + def _wait_for_elb_interface_removed(self): + polling_increment_secs = 15 + max_retries = (self.wait_timeout // polling_increment_secs) + status_achieved = False + + elb_interfaces = self.ec2_conn.get_all_network_interfaces( + filters={'attachment.instance-owner-id': 'amazon-elb', + 'description': 'ELB {0}'.format(self.name)}) + + for x in range(0, max_retries): + for interface in elb_interfaces: + try: + result = self.ec2_conn.get_all_network_interfaces(interface.id) + if result == []: + status_achieved = True + break + else: + time.sleep(polling_increment_secs) + except (boto.exception.BotoServerError, Exception) as e: + if 'InvalidNetworkInterfaceID' in e.code: + status_achieved = True + break + else: + self.module.fail_json_aws(e, 'Failure while waiting for interface to be removed') + + return status_achieved + + @_throttleable_operation(_THROTTLING_RETRIES) + def _get_elb(self): + elbs = self.elb_conn.get_all_load_balancers() + for elb in elbs: + if self.name == elb.name: + self.status = 'ok' + return elb + + def _get_elb_connection(self): + try: + return connect_to_aws(boto.ec2.elb, self.region, + **self.aws_connect_params) + except (boto.exception.NoAuthHandlerFound, AnsibleAWSError) as e: + self.module.fail_json_aws(e, 'Failure while connecting to AWS') + + def _get_ec2_connection(self): + try: + return connect_to_aws(boto.ec2, self.region, + **self.aws_connect_params) + except (boto.exception.NoAuthHandlerFound, Exception) as e: + self.module.fail_json_aws(e, 'Failure while connecting to AWS') + + @_throttleable_operation(_THROTTLING_RETRIES) + def _delete_elb(self): + # True if succeeds, exception raised if not + result = self.elb_conn.delete_load_balancer(name=self.name) + if result: + self.changed = True + self.status = 'deleted' + + def _create_elb(self): + listeners = [self._listener_as_tuple(l) for l in self.listeners] + self.elb = self.elb_conn.create_load_balancer(name=self.name, + zones=self.zones, + security_groups=self.security_group_ids, + complex_listeners=listeners, + subnets=self.subnets, + scheme=self.scheme) + if self.elb: + # HACK: Work around a boto bug in which the listeners attribute is + # always set to the listeners argument to create_load_balancer, and + # not the complex_listeners + # We're not doing a self.elb = self._get_elb here because there + # might be eventual consistency issues and it doesn't necessarily + # make sense to wait until the ELB gets returned from the EC2 API. + # This is necessary in the event we hit the throttling errors and + # need to retry ensure_ok + # See https://github.com/boto/boto/issues/3526 + self.elb.listeners = self.listeners + self.changed = True + self.status = 'created' + + def _create_elb_listeners(self, listeners): + """Takes a list of listener tuples and creates them""" + # True if succeeds, exception raised if not + self.changed = self.elb_conn.create_load_balancer_listeners(self.name, + complex_listeners=listeners) + + def _delete_elb_listeners(self, listeners): + """Takes a list of listener tuples and deletes them from the elb""" + ports = [l[0] for l in listeners] + + # True if succeeds, exception raised if not + self.changed = self.elb_conn.delete_load_balancer_listeners(self.name, + ports) + + def _set_elb_listeners(self): + """ + Creates listeners specified by self.listeners; overwrites existing + listeners on these ports; removes extraneous listeners + """ + listeners_to_add = [] + listeners_to_remove = [] + listeners_to_keep = [] + + # Check for any listeners we need to create or overwrite + for listener in self.listeners: + listener_as_tuple = self._listener_as_tuple(listener) + + # First we loop through existing listeners to see if one is + # already specified for this port + existing_listener_found = None + for existing_listener in self.elb.listeners: + # Since ELB allows only one listener on each incoming port, a + # single match on the incoming port is all we're looking for + if existing_listener[0] == int(listener['load_balancer_port']): + existing_listener_found = self._api_listener_as_tuple(existing_listener) + break + + if existing_listener_found: + # Does it match exactly? + if listener_as_tuple != existing_listener_found: + # The ports are the same but something else is different, + # so we'll remove the existing one and add the new one + listeners_to_remove.append(existing_listener_found) + listeners_to_add.append(listener_as_tuple) + else: + # We already have this listener, so we're going to keep it + listeners_to_keep.append(existing_listener_found) + else: + # We didn't find an existing listener, so just add the new one + listeners_to_add.append(listener_as_tuple) + + # Check for any extraneous listeners we need to remove, if desired + if self.purge_listeners: + for existing_listener in self.elb.listeners: + existing_listener_tuple = self._api_listener_as_tuple(existing_listener) + if existing_listener_tuple in listeners_to_remove: + # Already queued for removal + continue + if existing_listener_tuple in listeners_to_keep: + # Keep this one around + continue + # Since we're not already removing it and we don't need to keep + # it, let's get rid of it + listeners_to_remove.append(existing_listener_tuple) + + if listeners_to_remove: + self._delete_elb_listeners(listeners_to_remove) + + if listeners_to_add: + self._create_elb_listeners(listeners_to_add) + + def _api_listener_as_tuple(self, listener): + """Adds ssl_certificate_id to ELB API tuple if present""" + base_tuple = listener.get_complex_tuple() + if listener.ssl_certificate_id and len(base_tuple) < 5: + return base_tuple + (listener.ssl_certificate_id,) + return base_tuple + + def _listener_as_tuple(self, listener): + """Formats listener as a 4- or 5-tuples, in the order specified by the + ELB API""" + # N.B. string manipulations on protocols below (str(), upper()) is to + # ensure format matches output from ELB API + listener_list = [ + int(listener['load_balancer_port']), + int(listener['instance_port']), + str(listener['protocol'].upper()), + ] + + # Instance protocol is not required by ELB API; it defaults to match + # load balancer protocol. We'll mimic that behavior here + if 'instance_protocol' in listener: + listener_list.append(str(listener['instance_protocol'].upper())) + else: + listener_list.append(str(listener['protocol'].upper())) + + if 'ssl_certificate_id' in listener: + listener_list.append(str(listener['ssl_certificate_id'])) + + return tuple(listener_list) + + def _enable_zones(self, zones): + try: + self.elb.enable_zones(zones) + except boto.exception.BotoServerError as e: + self.module.fail_json_aws(e, msg='unable to enable zones') + + self.changed = True + + def _disable_zones(self, zones): + try: + self.elb.disable_zones(zones) + except boto.exception.BotoServerError as e: + self.module.fail_json_aws(e, msg='unable to disable zones') + self.changed = True + + def _attach_subnets(self, subnets): + self.elb_conn.attach_lb_to_subnets(self.name, subnets) + self.changed = True + + def _detach_subnets(self, subnets): + self.elb_conn.detach_lb_from_subnets(self.name, subnets) + self.changed = True + + def _set_subnets(self): + """Determine which subnets need to be attached or detached on the ELB""" + if self.subnets: + if self.purge_subnets: + subnets_to_detach = list(set(self.elb.subnets) - set(self.subnets)) + subnets_to_attach = list(set(self.subnets) - set(self.elb.subnets)) + else: + subnets_to_detach = None + subnets_to_attach = list(set(self.subnets) - set(self.elb.subnets)) + + if subnets_to_attach: + self._attach_subnets(subnets_to_attach) + if subnets_to_detach: + self._detach_subnets(subnets_to_detach) + + def _get_scheme(self): + """Determine if the current scheme is different than the scheme of the ELB""" + if self.scheme: + if self.elb.scheme != self.scheme: + if not self.wait: + self.module.fail_json(msg="Unable to modify scheme without using the wait option") + return True + return False + + def _set_zones(self): + """Determine which zones need to be enabled or disabled on the ELB""" + if self.zones: + if self.purge_zones: + zones_to_disable = list(set(self.elb.availability_zones) - + set(self.zones)) + zones_to_enable = list(set(self.zones) - + set(self.elb.availability_zones)) + else: + zones_to_disable = None + zones_to_enable = list(set(self.zones) - + set(self.elb.availability_zones)) + if zones_to_enable: + self._enable_zones(zones_to_enable) + # N.B. This must come second, in case it would have removed all zones + if zones_to_disable: + self._disable_zones(zones_to_disable) + + def _set_security_groups(self): + if self.security_group_ids is not None and set(self.elb.security_groups) != set(self.security_group_ids): + self.elb_conn.apply_security_groups_to_lb(self.name, self.security_group_ids) + self.changed = True + + def _set_health_check(self): + """Set health check values on ELB as needed""" + if self.health_check: + # This just makes it easier to compare each of the attributes + # and look for changes. Keys are attributes of the current + # health_check; values are desired values of new health_check + health_check_config = { + "target": self._get_health_check_target(), + "timeout": self.health_check['response_timeout'], + "interval": self.health_check['interval'], + "unhealthy_threshold": self.health_check['unhealthy_threshold'], + "healthy_threshold": self.health_check['healthy_threshold'], + } + + update_health_check = False + + # The health_check attribute is *not* set on newly created + # ELBs! So we have to create our own. + if not self.elb.health_check: + self.elb.health_check = HealthCheck() + + for attr, desired_value in health_check_config.items(): + if getattr(self.elb.health_check, attr) != desired_value: + setattr(self.elb.health_check, attr, desired_value) + update_health_check = True + + if update_health_check: + self.elb.configure_health_check(self.elb.health_check) + self.changed = True + + def _check_attribute_support(self, attr): + return hasattr(boto.ec2.elb.attributes.LbAttributes(), attr) + + def _set_cross_az_load_balancing(self): + attributes = self.elb.get_attributes() + if self.cross_az_load_balancing: + if not attributes.cross_zone_load_balancing.enabled: + self.changed = True + attributes.cross_zone_load_balancing.enabled = True + else: + if attributes.cross_zone_load_balancing.enabled: + self.changed = True + attributes.cross_zone_load_balancing.enabled = False + self.elb_conn.modify_lb_attribute(self.name, 'CrossZoneLoadBalancing', + attributes.cross_zone_load_balancing.enabled) + + def _set_access_log(self): + attributes = self.elb.get_attributes() + if self.access_logs: + if 's3_location' not in self.access_logs: + self.module.fail_json(msg='s3_location information required') + + access_logs_config = { + "enabled": True, + "s3_bucket_name": self.access_logs['s3_location'], + "s3_bucket_prefix": self.access_logs.get('s3_prefix', ''), + "emit_interval": self.access_logs.get('interval', 60), + } + + update_access_logs_config = False + for attr, desired_value in access_logs_config.items(): + if getattr(attributes.access_log, attr) != desired_value: + setattr(attributes.access_log, attr, desired_value) + update_access_logs_config = True + if update_access_logs_config: + self.elb_conn.modify_lb_attribute(self.name, 'AccessLog', attributes.access_log) + self.changed = True + elif attributes.access_log.enabled: + attributes.access_log.enabled = False + self.changed = True + self.elb_conn.modify_lb_attribute(self.name, 'AccessLog', attributes.access_log) + + def _set_connection_draining_timeout(self): + attributes = self.elb.get_attributes() + if self.connection_draining_timeout is not None: + if not attributes.connection_draining.enabled or \ + attributes.connection_draining.timeout != self.connection_draining_timeout: + self.changed = True + attributes.connection_draining.enabled = True + attributes.connection_draining.timeout = self.connection_draining_timeout + self.elb_conn.modify_lb_attribute(self.name, 'ConnectionDraining', attributes.connection_draining) + else: + if attributes.connection_draining.enabled: + self.changed = True + attributes.connection_draining.enabled = False + self.elb_conn.modify_lb_attribute(self.name, 'ConnectionDraining', attributes.connection_draining) + + def _set_idle_timeout(self): + attributes = self.elb.get_attributes() + if self.idle_timeout is not None: + if attributes.connecting_settings.idle_timeout != self.idle_timeout: + self.changed = True + attributes.connecting_settings.idle_timeout = self.idle_timeout + self.elb_conn.modify_lb_attribute(self.name, 'ConnectingSettings', attributes.connecting_settings) + + def _policy_name(self, policy_type): + return 'ec2-elb-lb-{0}'.format(to_native(policy_type, errors='surrogate_or_strict')) + + def _create_policy(self, policy_param, policy_meth, policy): + getattr(self.elb_conn, policy_meth)(policy_param, self.elb.name, policy) + + def _delete_policy(self, elb_name, policy): + self.elb_conn.delete_lb_policy(elb_name, policy) + + def _update_policy(self, policy_param, policy_meth, policy_attr, policy): + self._delete_policy(self.elb.name, policy) + self._create_policy(policy_param, policy_meth, policy) + + def _set_listener_policy(self, listeners_dict, policy=None): + policy = [] if policy is None else policy + + for listener_port in listeners_dict: + if listeners_dict[listener_port].startswith('HTTP'): + self.elb_conn.set_lb_policies_of_listener(self.elb.name, listener_port, policy) + + def _set_stickiness_policy(self, elb_info, listeners_dict, policy, **policy_attrs): + for p in getattr(elb_info.policies, policy_attrs['attr']): + if str(p.__dict__['policy_name']) == str(policy[0]): + if str(p.__dict__[policy_attrs['dict_key']]) != str(policy_attrs['param_value'] or 0): + self._set_listener_policy(listeners_dict) + self._update_policy(policy_attrs['param_value'], policy_attrs['method'], policy_attrs['attr'], policy[0]) + self.changed = True + break + else: + self._create_policy(policy_attrs['param_value'], policy_attrs['method'], policy[0]) + self.changed = True + + self._set_listener_policy(listeners_dict, policy) + + def select_stickiness_policy(self): + if self.stickiness: + + if 'cookie' in self.stickiness and 'expiration' in self.stickiness: + self.module.fail_json(msg='\'cookie\' and \'expiration\' can not be set at the same time') + + elb_info = self.elb_conn.get_all_load_balancers(self.elb.name)[0] + d = {} + for listener in elb_info.listeners: + d[listener[0]] = listener[2] + listeners_dict = d + + if self.stickiness['type'] == 'loadbalancer': + policy = [] + policy_type = 'LBCookieStickinessPolicyType' + + if self.module.boolean(self.stickiness['enabled']): + + if 'expiration' not in self.stickiness: + self.module.fail_json(msg='expiration must be set when type is loadbalancer') + + try: + expiration = self.stickiness['expiration'] if int(self.stickiness['expiration']) else None + except ValueError: + self.module.fail_json(msg='expiration must be set to an integer') + + policy_attrs = { + 'type': policy_type, + 'attr': 'lb_cookie_stickiness_policies', + 'method': 'create_lb_cookie_stickiness_policy', + 'dict_key': 'cookie_expiration_period', + 'param_value': expiration + } + policy.append(self._policy_name(policy_attrs['type'])) + + self._set_stickiness_policy(elb_info, listeners_dict, policy, **policy_attrs) + elif not self.module.boolean(self.stickiness['enabled']): + if len(elb_info.policies.lb_cookie_stickiness_policies): + if elb_info.policies.lb_cookie_stickiness_policies[0].policy_name == self._policy_name(policy_type): + self.changed = True + else: + self.changed = False + self._set_listener_policy(listeners_dict) + self._delete_policy(self.elb.name, self._policy_name(policy_type)) + + elif self.stickiness['type'] == 'application': + policy = [] + policy_type = 'AppCookieStickinessPolicyType' + if self.module.boolean(self.stickiness['enabled']): + + if 'cookie' not in self.stickiness: + self.module.fail_json(msg='cookie must be set when type is application') + + policy_attrs = { + 'type': policy_type, + 'attr': 'app_cookie_stickiness_policies', + 'method': 'create_app_cookie_stickiness_policy', + 'dict_key': 'cookie_name', + 'param_value': self.stickiness['cookie'] + } + policy.append(self._policy_name(policy_attrs['type'])) + self._set_stickiness_policy(elb_info, listeners_dict, policy, **policy_attrs) + elif not self.module.boolean(self.stickiness['enabled']): + if len(elb_info.policies.app_cookie_stickiness_policies): + if elb_info.policies.app_cookie_stickiness_policies[0].policy_name == self._policy_name(policy_type): + self.changed = True + self._set_listener_policy(listeners_dict) + self._delete_policy(self.elb.name, self._policy_name(policy_type)) + + else: + self._set_listener_policy(listeners_dict) + + def _get_backend_policies(self): + """Get a list of backend policies""" + policies = [] + if self.elb.backends is not None: + for backend in self.elb.backends: + if backend.policies is not None: + for policy in backend.policies: + policies.append(str(backend.instance_port) + ':' + policy.policy_name) + + return policies + + def _set_backend_policies(self): + """Sets policies for all backends""" + ensure_proxy_protocol = False + replace = [] + backend_policies = self._get_backend_policies() + + # Find out what needs to be changed + for listener in self.listeners: + want = False + + if 'proxy_protocol' in listener and listener['proxy_protocol']: + ensure_proxy_protocol = True + want = True + + if str(listener['instance_port']) + ':ProxyProtocol-policy' in backend_policies: + if not want: + replace.append({'port': listener['instance_port'], 'policies': []}) + elif want: + replace.append({'port': listener['instance_port'], 'policies': ['ProxyProtocol-policy']}) + + # enable or disable proxy protocol + if ensure_proxy_protocol: + self._set_proxy_protocol_policy() + + # Make the backend policies so + for item in replace: + self.elb_conn.set_lb_policies_of_backend_server(self.elb.name, item['port'], item['policies']) + self.changed = True + + def _get_proxy_protocol_policy(self): + """Find out if the elb has a proxy protocol enabled""" + if self.elb.policies is not None and self.elb.policies.other_policies is not None: + for policy in self.elb.policies.other_policies: + if policy.policy_name == 'ProxyProtocol-policy': + return policy.policy_name + + return None + + def _set_proxy_protocol_policy(self): + """Install a proxy protocol policy if needed""" + proxy_policy = self._get_proxy_protocol_policy() + + if proxy_policy is None: + self.elb_conn.create_lb_policy( + self.elb.name, 'ProxyProtocol-policy', 'ProxyProtocolPolicyType', {'ProxyProtocol': True} + ) + self.changed = True + + # TODO: remove proxy protocol policy if not needed anymore? There is no side effect to leaving it there + + def _diff_list(self, a, b): + """Find the entries in list a that are not in list b""" + b = set(b) + return [aa for aa in a if aa not in b] + + def _get_instance_ids(self): + """Get the current list of instance ids installed in the elb""" + instances = [] + if self.elb.instances is not None: + for instance in self.elb.instances: + instances.append(instance.id) + + return instances + + def _set_instance_ids(self): + """Register or deregister instances from an lb instance""" + assert_instances = self.instance_ids or [] + + has_instances = self._get_instance_ids() + + add_instances = self._diff_list(assert_instances, has_instances) + if add_instances: + self.elb_conn.register_instances(self.elb.name, add_instances) + self.changed = True + + if self.purge_instance_ids: + remove_instances = self._diff_list(has_instances, assert_instances) + if remove_instances: + self.elb_conn.deregister_instances(self.elb.name, remove_instances) + self.changed = True + + def _set_tags(self): + """Add/Delete tags""" + if self.tags is None: + return + + params = {'LoadBalancerNames.member.1': self.name} + + tagdict = dict() + + # get the current list of tags from the ELB, if ELB exists + if self.elb: + current_tags = self.elb_conn.get_list('DescribeTags', params, + [('member', Tag)]) + tagdict = dict((tag.Key, tag.Value) for tag in current_tags + if hasattr(tag, 'Key')) + + # Add missing tags + dictact = dict(set(self.tags.items()) - set(tagdict.items())) + if dictact: + for i, key in enumerate(dictact): + params['Tags.member.%d.Key' % (i + 1)] = key + params['Tags.member.%d.Value' % (i + 1)] = dictact[key] + + self.elb_conn.make_request('AddTags', params) + self.changed = True + + # Remove extra tags + dictact = dict(set(tagdict.items()) - set(self.tags.items())) + if dictact: + for i, key in enumerate(dictact): + params['Tags.member.%d.Key' % (i + 1)] = key + + self.elb_conn.make_request('RemoveTags', params) + self.changed = True + + def _get_health_check_target(self): + """Compose target string from healthcheck parameters""" + protocol = self.health_check['ping_protocol'].upper() + path = "" + + if protocol in ['HTTP', 'HTTPS'] and 'ping_path' in self.health_check: + path = self.health_check['ping_path'] + + return "%s:%s%s" % (protocol, self.health_check['ping_port'], path) + + +def main(): + argument_spec = dict( + state={'required': True, 'choices': ['present', 'absent']}, + name={'required': True}, + listeners={'default': None, 'required': False, 'type': 'list', 'elements': 'dict'}, + purge_listeners={'default': True, 'required': False, 'type': 'bool'}, + instance_ids={'default': None, 'required': False, 'type': 'list', 'elements': 'str'}, + purge_instance_ids={'default': False, 'required': False, 'type': 'bool'}, + zones={'default': None, 'required': False, 'type': 'list', 'elements': 'str'}, + purge_zones={'default': False, 'required': False, 'type': 'bool'}, + security_group_ids={'default': None, 'required': False, 'type': 'list', 'elements': 'str'}, + security_group_names={'default': None, 'required': False, 'type': 'list', 'elements': 'str'}, + health_check={'default': None, 'required': False, 'type': 'dict'}, + subnets={'default': None, 'required': False, 'type': 'list', 'elements': 'str'}, + purge_subnets={'default': False, 'required': False, 'type': 'bool'}, + scheme={'default': 'internet-facing', 'required': False, 'choices': ['internal', 'internet-facing']}, + connection_draining_timeout={'default': None, 'required': False, 'type': 'int'}, + idle_timeout={'default': None, 'type': 'int', 'required': False}, + cross_az_load_balancing={'default': None, 'type': 'bool', 'required': False}, + stickiness={'default': None, 'required': False, 'type': 'dict'}, + access_logs={'default': None, 'required': False, 'type': 'dict'}, + wait={'default': False, 'type': 'bool', 'required': False}, + wait_timeout={'default': 60, 'type': 'int', 'required': False}, + tags={'default': None, 'required': False, 'type': 'dict'} + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + check_boto3=False, + mutually_exclusive=[['security_group_ids', 'security_group_names']] + ) + + if not HAS_BOTO: + module.fail_json(msg='boto required for this module') + + region, ec2_url, aws_connect_params = get_aws_connection_info(module) + if not region: + module.fail_json(msg="Region must be specified as a parameter, in EC2_REGION or AWS_REGION environment variables or in boto configuration file") + + name = module.params['name'] + state = module.params['state'] + listeners = module.params['listeners'] + purge_listeners = module.params['purge_listeners'] + instance_ids = module.params['instance_ids'] + purge_instance_ids = module.params['purge_instance_ids'] + zones = module.params['zones'] + purge_zones = module.params['purge_zones'] + security_group_ids = module.params['security_group_ids'] + security_group_names = module.params['security_group_names'] + health_check = module.params['health_check'] + access_logs = module.params['access_logs'] + subnets = module.params['subnets'] + purge_subnets = module.params['purge_subnets'] + scheme = module.params['scheme'] + connection_draining_timeout = module.params['connection_draining_timeout'] + idle_timeout = module.params['idle_timeout'] + cross_az_load_balancing = module.params['cross_az_load_balancing'] + stickiness = module.params['stickiness'] + wait = module.params['wait'] + wait_timeout = module.params['wait_timeout'] + tags = module.params['tags'] + + if state == 'present' and not listeners: + module.fail_json(msg="At least one listener is required for ELB creation") + + if state == 'present' and not (zones or subnets): + module.fail_json(msg="At least one availability zone or subnet is required for ELB creation") + + if wait_timeout > 600: + module.fail_json(msg='wait_timeout maximum is 600 seconds') + + if security_group_names: + security_group_ids = [] + try: + ec2 = connect_to_aws(boto.ec2, region, **aws_connect_params) + if subnets: # We have at least one subnet, ergo this is a VPC + vpc_conn = _get_vpc_connection(module=module, region=region, aws_connect_params=aws_connect_params) + vpc_id = vpc_conn.get_all_subnets([subnets[0]])[0].vpc_id + filters = {'vpc_id': vpc_id} + else: + filters = None + grp_details = ec2.get_all_security_groups(filters=filters) + + for group_name in security_group_names: + if isinstance(group_name, string_types): + group_name = [group_name] + + group_id = [str(grp.id) for grp in grp_details if str(grp.name) in group_name] + security_group_ids.extend(group_id) + except boto.exception.NoAuthHandlerFound as e: + module.fail_json_aws(e) + + elb_man = ElbManager(module, name, listeners, purge_listeners, zones, + purge_zones, security_group_ids, health_check, + subnets, purge_subnets, scheme, + connection_draining_timeout, idle_timeout, + cross_az_load_balancing, + access_logs, stickiness, wait, wait_timeout, tags, + region=region, instance_ids=instance_ids, purge_instance_ids=purge_instance_ids, + **aws_connect_params) + + # check for unsupported attributes for this version of boto + if cross_az_load_balancing and not elb_man._check_attribute_support('cross_zone_load_balancing'): + module.fail_json(msg="You must install boto >= 2.18.0 to use the cross_az_load_balancing attribute") + + if connection_draining_timeout and not elb_man._check_attribute_support('connection_draining'): + module.fail_json(msg="You must install boto >= 2.28.0 to use the connection_draining_timeout attribute") + + if idle_timeout and not elb_man._check_attribute_support('connecting_settings'): + module.fail_json(msg="You must install boto >= 2.33.0 to use the idle_timeout attribute") + + if state == 'present': + elb_man.ensure_ok() + elif state == 'absent': + elb_man.ensure_gone() + + ansible_facts = {'ec2_elb': 'info'} + ec2_facts_result = dict(changed=elb_man.changed, + elb=elb_man.get_info(), + ansible_facts=ansible_facts) + + module.exit_json(**ec2_facts_result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni.py new file mode 100644 index 00000000..01a81f99 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni.py @@ -0,0 +1,882 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_eni +version_added: 1.0.0 +short_description: Create and optionally attach an Elastic Network Interface (ENI) to an instance +description: + - Create and optionally attach an Elastic Network Interface (ENI) to an instance. If an ENI ID or private_ip is + provided, the existing ENI (if any) will be modified. The 'attached' parameter controls the attachment status + of the network interface. +author: + - "Rob White (@wimnat)" + - "Mike Healey (@healem)" +options: + eni_id: + description: + - The ID of the ENI (to modify). + - If I(eni_id=None) and I(state=present), a new eni will be created. + type: str + instance_id: + description: + - Instance ID that you wish to attach ENI to. + - Since version 2.2, use the I(attached) parameter to attach or detach an ENI. Prior to 2.2, to detach an ENI from an instance, use C(None). + type: str + private_ip_address: + description: + - Private IP address. + type: str + subnet_id: + description: + - ID of subnet in which to create the ENI. + type: str + description: + description: + - Optional description of the ENI. + type: str + security_groups: + description: + - List of security groups associated with the interface. Only used when I(state=present). + - Since version 2.2, you can specify security groups by ID or by name or a combination of both. Prior to 2.2, you can specify only by ID. + type: list + elements: str + state: + description: + - Create or delete ENI. + default: present + choices: [ 'present', 'absent' ] + type: str + device_index: + description: + - The index of the device for the network interface attachment on the instance. + default: 0 + type: int + attached: + description: + - Specifies if network interface should be attached or detached from instance. If omitted, attachment status + won't change + type: bool + force_detach: + description: + - Force detachment of the interface. This applies either when explicitly detaching the interface by setting I(instance_id=None) + or when deleting an interface with I(state=absent). + default: false + type: bool + delete_on_termination: + description: + - Delete the interface when the instance it is attached to is terminated. You can only specify this flag when the + interface is being modified, not on creation. + required: false + type: bool + source_dest_check: + description: + - By default, interfaces perform source/destination checks. NAT instances however need this check to be disabled. + You can only specify this flag when the interface is being modified, not on creation. + required: false + type: bool + secondary_private_ip_addresses: + description: + - A list of IP addresses to assign as secondary IP addresses to the network interface. + This option is mutually exclusive of I(secondary_private_ip_address_count) + required: false + type: list + elements: str + purge_secondary_private_ip_addresses: + description: + - To be used with I(secondary_private_ip_addresses) to determine whether or not to remove any secondary IP addresses other than those specified. + - Set I(secondary_private_ip_addresses=[]) to purge all secondary addresses. + default: false + type: bool + secondary_private_ip_address_count: + description: + - The number of secondary IP addresses to assign to the network interface. This option is mutually exclusive of I(secondary_private_ip_addresses) + required: false + type: int + allow_reassignment: + description: + - Indicates whether to allow an IP address that is already assigned to another network interface or instance + to be reassigned to the specified network interface. + required: false + default: false + type: bool + name: + description: + - Name for the ENI. This will create a tag called "Name" with the value assigned here. + - This can be used in conjunction with I(subnet_id) as another means of identifiying a network interface. + - AWS does not enforce unique Name tags, so duplicate names are possible if you configure it that way. + If that is the case, you will need to provide other identifying information such as I(private_ip_address) or I(eni_id). + required: false + type: str + tags: + description: + - A hash/dictionary of tags to add to the new ENI or to add/remove from an existing one. Please note that + the name field sets the "Name" tag. + - To clear all tags, set this option to an empty dictionary to use in conjunction with I(purge_tags). + If you provide I(name), that tag will not be removed. + - To prevent removing any tags set I(purge_tags) to false. + type: dict + required: false + version_added: 1.3.0 + purge_tags: + description: + - Indicates whether to remove tags not specified in I(tags) or I(name). This means you have to specify all + the desired tags on each task affecting a network interface. + - If I(tags) is omitted or None this option is disregarded. + default: true + type: bool + version_added: 1.3.0 +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +notes: + - This module identifies and ENI based on either the I(eni_id), a combination of I(private_ip_address) and I(subnet_id), + or a combination of I(instance_id) and I(device_id). Any of these options will let you specify a particular ENI. +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Create an ENI. As no security group is defined, ENI will be created in default security group +- amazon.aws.ec2_eni: + private_ip_address: 172.31.0.20 + subnet_id: subnet-xxxxxxxx + state: present + +# Create an ENI and attach it to an instance +- amazon.aws.ec2_eni: + instance_id: i-xxxxxxx + device_index: 1 + private_ip_address: 172.31.0.20 + subnet_id: subnet-xxxxxxxx + state: present + +# Create an ENI with two secondary addresses +- amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxxx + state: present + secondary_private_ip_address_count: 2 + +# Assign a secondary IP address to an existing ENI +# This will purge any existing IPs +- amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxxx + eni_id: eni-yyyyyyyy + state: present + secondary_private_ip_addresses: + - 172.16.1.1 + +# Remove any secondary IP addresses from an existing ENI +- amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxxx + eni_id: eni-yyyyyyyy + state: present + secondary_private_ip_address_count: 0 + +# Destroy an ENI, detaching it from any instance if necessary +- amazon.aws.ec2_eni: + eni_id: eni-xxxxxxx + force_detach: true + state: absent + +# Update an ENI +- amazon.aws.ec2_eni: + eni_id: eni-xxxxxxx + description: "My new description" + state: present + +# Update an ENI using name and subnet_id +- amazon.aws.ec2_eni: + name: eni-20 + subnet_id: subnet-xxxxxxx + description: "My new description" + state: present + +# Update an ENI identifying it by private_ip_address and subnet_id +- amazon.aws.ec2_eni: + subnet_id: subnet-xxxxxxx + private_ip_address: 172.16.1.1 + description: "My new description" + +# Detach an ENI from an instance +- amazon.aws.ec2_eni: + eni_id: eni-xxxxxxx + instance_id: None + state: present + +### Delete an interface on termination +# First create the interface +- amazon.aws.ec2_eni: + instance_id: i-xxxxxxx + device_index: 1 + private_ip_address: 172.31.0.20 + subnet_id: subnet-xxxxxxxx + state: present + register: eni + +# Modify the interface to enable the delete_on_terminaton flag +- amazon.aws.ec2_eni: + eni_id: "{{ eni.interface.id }}" + delete_on_termination: true + +''' + + +RETURN = ''' +interface: + description: Network interface attributes + returned: when state != absent + type: complex + contains: + description: + description: interface description + type: str + sample: Firewall network interface + groups: + description: list of security groups + type: list + elements: dict + sample: [ { "sg-f8a8a9da": "default" } ] + id: + description: network interface id + type: str + sample: "eni-1d889198" + mac_address: + description: interface's physical address + type: str + sample: "00:00:5E:00:53:23" + name: + description: The name of the ENI + type: str + sample: "my-eni-20" + owner_id: + description: aws account id + type: str + sample: 812381371 + private_ip_address: + description: primary ip address of this interface + type: str + sample: 10.20.30.40 + private_ip_addresses: + description: list of all private ip addresses associated to this interface + type: list + elements: dict + sample: [ { "primary_address": true, "private_ip_address": "10.20.30.40" } ] + source_dest_check: + description: value of source/dest check flag + type: bool + sample: True + status: + description: network interface status + type: str + sample: "pending" + subnet_id: + description: which vpc subnet the interface is bound + type: str + sample: subnet-b0a0393c + tags: + description: The dictionary of tags associated with the ENI + type: dict + sample: { "Name": "my-eni", "group": "Finance" } + vpc_id: + description: which vpc this network interface is bound + type: str + sample: vpc-9a9a9da + +''' + +import time + +try: + import botocore.exceptions +except ImportError: + pass # Handled by AnsibleAWSModule + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import get_ec2_security_group_ids_from_names +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_aws_tags +from ..module_utils.waiters import get_waiter + + +def get_eni_info(interface): + + # Private addresses + private_addresses = [] + if "PrivateIpAddresses" in interface: + for ip in interface["PrivateIpAddresses"]: + private_addresses.append({'private_ip_address': ip["PrivateIpAddress"], 'primary_address': ip["Primary"]}) + + groups = {} + if "Groups" in interface: + for group in interface["Groups"]: + groups[group["GroupId"]] = group["GroupName"] + + interface_info = {'id': interface.get("NetworkInterfaceId"), + 'subnet_id': interface.get("SubnetId"), + 'vpc_id': interface.get("VpcId"), + 'description': interface.get("Description"), + 'owner_id': interface.get("OwnerId"), + 'status': interface.get("Status"), + 'mac_address': interface.get("MacAddress"), + 'private_ip_address': interface.get("PrivateIpAddress"), + 'source_dest_check': interface.get("SourceDestCheck"), + 'groups': groups, + 'private_ip_addresses': private_addresses + } + + if "TagSet" in interface: + tags = {} + name = None + for tag in interface["TagSet"]: + tags[tag["Key"]] = tag["Value"] + if tag["Key"] == "Name": + name = tag["Value"] + interface_info["tags"] = tags + + if name is not None: + interface_info["name"] = name + + if "Attachment" in interface: + interface_info['attachment'] = { + 'attachment_id': interface["Attachment"].get("AttachmentId"), + 'instance_id': interface["Attachment"].get("InstanceId"), + 'device_index': interface["Attachment"].get("DeviceIndex"), + 'status': interface["Attachment"].get("Status"), + 'attach_time': interface["Attachment"].get("AttachTime"), + 'delete_on_termination': interface["Attachment"].get("DeleteOnTermination"), + } + + return interface_info + + +def correct_ips(connection, ip_list, module, eni_id): + all_there = True + eni = describe_eni(connection, module, eni_id) + private_addresses = set() + if "PrivateIpAddresses" in eni: + for ip in eni["PrivateIpAddresses"]: + private_addresses.add(ip["PrivateIpAddress"]) + + ip_set = set(ip_list) + + return ip_set.issubset(private_addresses) + + +def absent_ips(connection, ip_list, module, eni_id): + all_there = True + eni = describe_eni(connection, module, eni_id) + private_addresses = set() + if "PrivateIpAddresses" in eni: + for ip in eni["PrivateIpAddresses"]: + private_addresses.add(ip["PrivateIpAddress"]) + + ip_set = set(ip_list) + + return not ip_set.union(private_addresses) + + +def correct_ip_count(connection, ip_count, module, eni_id): + eni = describe_eni(connection, module, eni_id) + private_addresses = set() + if "PrivateIpAddresses" in eni: + for ip in eni["PrivateIpAddresses"]: + private_addresses.add(ip["PrivateIpAddress"]) + + if len(private_addresses) == ip_count: + return True + else: + return False + + +def wait_for(function_pointer, *args): + max_wait = 30 + interval_time = 3 + current_wait = 0 + while current_wait < max_wait: + time.sleep(interval_time) + current_wait += interval_time + if function_pointer(*args): + break + + +def create_eni(connection, vpc_id, module): + + instance_id = module.params.get("instance_id") + attached = module.params.get("attached") + if instance_id == 'None': + instance_id = None + device_index = module.params.get("device_index") + subnet_id = module.params.get('subnet_id') + private_ip_address = module.params.get('private_ip_address') + description = module.params.get('description') + security_groups = get_ec2_security_group_ids_from_names( + module.params.get('security_groups'), + connection, + vpc_id=vpc_id, + boto3=True + ) + secondary_private_ip_addresses = module.params.get("secondary_private_ip_addresses") + secondary_private_ip_address_count = module.params.get("secondary_private_ip_address_count") + changed = False + tags = module.params.get("tags") + name = module.params.get("name") + purge_tags = module.params.get("purge_tags") + + try: + args = {"SubnetId": subnet_id} + if private_ip_address: + args["PrivateIpAddress"] = private_ip_address + if description: + args["Description"] = description + if len(security_groups) > 0: + args["Groups"] = security_groups + eni_dict = connection.create_network_interface(aws_retry=True, **args) + eni = eni_dict["NetworkInterface"] + # Once we have an ID make sure we're always modifying the same object + eni_id = eni["NetworkInterfaceId"] + get_waiter(connection, 'network_interface_available').wait(NetworkInterfaceIds=[eni_id]) + + if attached and instance_id is not None: + try: + connection.attach_network_interface( + aws_retry=True, + InstanceId=instance_id, + DeviceIndex=device_index, + NetworkInterfaceId=eni["NetworkInterfaceId"] + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError): + connection.delete_network_interface(aws_retry=True, NetworkInterfaceId=eni_id) + raise + get_waiter(connection, 'network_interface_attached').wait(NetworkInterfaceIds=[eni_id]) + + if secondary_private_ip_address_count is not None: + try: + connection.assign_private_ip_addresses( + aws_retry=True, + NetworkInterfaceId=eni["NetworkInterfaceId"], + SecondaryPrivateIpAddressCount=secondary_private_ip_address_count + ) + wait_for(correct_ip_count, connection, secondary_private_ip_address_count, module, eni_id) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError): + connection.delete_network_interface(aws_retry=True, NetworkInterfaceId=eni_id) + raise + + if secondary_private_ip_addresses is not None: + try: + connection.assign_private_ip_addresses( + NetworkInterfaceId=eni["NetworkInterfaceId"], + PrivateIpAddresses=secondary_private_ip_addresses + ) + wait_for(correct_ips, connection, secondary_private_ip_addresses, module, eni_id) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError): + connection.delete_network_interface(aws_retry=True, NetworkInterfaceId=eni_id) + raise + + manage_tags(eni, name, tags, purge_tags, connection) + + # Refresh the eni data + eni = describe_eni(connection, module, eni_id) + changed = True + + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws( + e, + "Failed to create eni {0} for {1} in {2} with {3}".format(name, subnet_id, vpc_id, private_ip_address) + ) + + module.exit_json(changed=changed, interface=get_eni_info(eni)) + + +def modify_eni(connection, module, eni): + + instance_id = module.params.get("instance_id") + attached = module.params.get("attached") + device_index = module.params.get("device_index") + description = module.params.get('description') + security_groups = module.params.get('security_groups') + force_detach = module.params.get("force_detach") + source_dest_check = module.params.get("source_dest_check") + delete_on_termination = module.params.get("delete_on_termination") + secondary_private_ip_addresses = module.params.get("secondary_private_ip_addresses") + purge_secondary_private_ip_addresses = module.params.get("purge_secondary_private_ip_addresses") + secondary_private_ip_address_count = module.params.get("secondary_private_ip_address_count") + allow_reassignment = module.params.get("allow_reassignment") + changed = False + tags = module.params.get("tags") + name = module.params.get("name") + purge_tags = module.params.get("purge_tags") + + eni = uniquely_find_eni(connection, module, eni) + eni_id = eni["NetworkInterfaceId"] + + try: + if description is not None: + if "Description" not in eni or eni["Description"] != description: + connection.modify_network_interface_attribute( + aws_retry=True, + NetworkInterfaceId=eni_id, + Description={'Value': description} + ) + changed = True + if len(security_groups) > 0: + groups = get_ec2_security_group_ids_from_names(security_groups, connection, vpc_id=eni["VpcId"], boto3=True) + if sorted(get_sec_group_list(eni["Groups"])) != sorted(groups): + connection.modify_network_interface_attribute( + aws_retry=True, + NetworkInterfaceId=eni_id, + Groups=groups + ) + changed = True + if source_dest_check is not None: + if "SourceDestCheck" not in eni or eni["SourceDestCheck"] != source_dest_check: + connection.modify_network_interface_attribute( + aws_retry=True, + NetworkInterfaceId=eni_id, + SourceDestCheck={'Value': source_dest_check} + ) + changed = True + if delete_on_termination is not None and "Attachment" in eni: + if eni["Attachment"]["DeleteOnTermination"] is not delete_on_termination: + connection.modify_network_interface_attribute( + aws_retry=True, + NetworkInterfaceId=eni_id, + Attachment={'AttachmentId': eni["Attachment"]["AttachmentId"], + 'DeleteOnTermination': delete_on_termination} + ) + changed = True + if delete_on_termination: + waiter = "network_interface_delete_on_terminate" + else: + waiter = "network_interface_no_delete_on_terminate" + get_waiter(connection, waiter).wait(NetworkInterfaceIds=[eni_id]) + + current_secondary_addresses = [] + if "PrivateIpAddresses" in eni: + current_secondary_addresses = [i["PrivateIpAddress"] for i in eni["PrivateIpAddresses"] if not i["Primary"]] + + if secondary_private_ip_addresses is not None: + secondary_addresses_to_remove = list(set(current_secondary_addresses) - set(secondary_private_ip_addresses)) + if secondary_addresses_to_remove and purge_secondary_private_ip_addresses: + connection.unassign_private_ip_addresses( + aws_retry=True, + NetworkInterfaceId=eni_id, + PrivateIpAddresses=list(set(current_secondary_addresses) - set(secondary_private_ip_addresses)), + ) + wait_for(absent_ips, connection, secondary_addresses_to_remove, module, eni_id) + changed = True + secondary_addresses_to_add = list(set(secondary_private_ip_addresses) - set(current_secondary_addresses)) + if secondary_addresses_to_add: + connection.assign_private_ip_addresses( + aws_retry=True, + NetworkInterfaceId=eni_id, + PrivateIpAddresses=secondary_addresses_to_add, + AllowReassignment=allow_reassignment + ) + wait_for(correct_ips, connection, secondary_addresses_to_add, module, eni_id) + changed = True + + if secondary_private_ip_address_count is not None: + current_secondary_address_count = len(current_secondary_addresses) + if secondary_private_ip_address_count > current_secondary_address_count: + connection.assign_private_ip_addresses( + aws_retry=True, + NetworkInterfaceId=eni_id, + SecondaryPrivateIpAddressCount=(secondary_private_ip_address_count - current_secondary_address_count), + AllowReassignment=allow_reassignment + ) + wait_for(correct_ip_count, connection, secondary_private_ip_address_count, module, eni_id) + changed = True + elif secondary_private_ip_address_count < current_secondary_address_count: + # How many of these addresses do we want to remove + secondary_addresses_to_remove_count = current_secondary_address_count - secondary_private_ip_address_count + connection.unassign_private_ip_addresses( + aws_retry=True, + NetworkInterfaceId=eni_id, + PrivateIpAddresses=current_secondary_addresses[:secondary_addresses_to_remove_count] + ) + wait_for(correct_ip_count, connection, secondary_private_ip_address_count, module, eni_id) + changed = True + + if attached is True: + if "Attachment" in eni and eni["Attachment"]["InstanceId"] != instance_id: + detach_eni(connection, eni, module) + connection.attach_network_interface( + aws_retry=True, + InstanceId=instance_id, + DeviceIndex=device_index, + NetworkInterfaceId=eni_id, + ) + get_waiter(connection, 'network_interface_attached').wait(NetworkInterfaceIds=[eni_id]) + changed = True + if "Attachment" not in eni: + connection.attach_network_interface( + aws_retry=True, + InstanceId=instance_id, + DeviceIndex=device_index, + NetworkInterfaceId=eni_id, + ) + get_waiter(connection, 'network_interface_attached').wait(NetworkInterfaceIds=[eni_id]) + changed = True + + elif attached is False: + changed |= detach_eni(connection, eni, module) + get_waiter(connection, 'network_interface_available').wait(NetworkInterfaceIds=[eni_id]) + + changed |= manage_tags(eni, name, tags, purge_tags, connection) + + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to modify eni {0}".format(eni_id)) + + eni = describe_eni(connection, module, eni_id) + module.exit_json(changed=changed, interface=get_eni_info(eni)) + + +def delete_eni(connection, module): + + eni = uniquely_find_eni(connection, module) + if not eni: + module.exit_json(changed=False) + + eni_id = eni["NetworkInterfaceId"] + force_detach = module.params.get("force_detach") + + try: + if force_detach is True: + if "Attachment" in eni: + connection.detach_network_interface( + aws_retry=True, + AttachmentId=eni["Attachment"]["AttachmentId"], + Force=True + ) + # Wait to allow detachment to finish + get_waiter(connection, 'network_interface_available').wait(NetworkInterfaceIds=[eni_id]) + connection.delete_network_interface(aws_retry=True, NetworkInterfaceId=eni_id) + changed = True + else: + connection.delete_network_interface(aws_retry=True, NetworkInterfaceId=eni_id) + changed = True + + module.exit_json(changed=changed) + except is_boto3_error_code('InvalidNetworkInterfaceID.NotFound'): + module.exit_json(changed=False) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, "Failure during delete of {0}".format(eni_id)) + + +def detach_eni(connection, eni, module): + + attached = module.params.get("attached") + eni_id = eni["NetworkInterfaceId"] + + force_detach = module.params.get("force_detach") + if "Attachment" in eni: + connection.detach_network_interface( + aws_retry=True, + AttachmentId=eni["Attachment"]["AttachmentId"], + Force=force_detach + ) + get_waiter(connection, 'network_interface_available').wait(NetworkInterfaceIds=[eni_id]) + return True + + return False + + +def describe_eni(connection, module, eni_id): + try: + eni_result = connection.describe_network_interfaces(aws_retry=True, NetworkInterfaceIds=[eni_id]) + if eni_result["NetworkInterfaces"]: + return eni_result["NetworkInterfaces"][0] + else: + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to describe eni with id: {0}".format(eni_id)) + + +def uniquely_find_eni(connection, module, eni=None): + + if eni: + # In the case of create, eni_id will not be a param but we can still get the eni_id after creation + if "NetworkInterfaceId" in eni: + eni_id = eni["NetworkInterfaceId"] + else: + eni_id = None + else: + eni_id = module.params.get("eni_id") + + private_ip_address = module.params.get('private_ip_address') + subnet_id = module.params.get('subnet_id') + instance_id = module.params.get('instance_id') + device_index = module.params.get('device_index') + attached = module.params.get('attached') + name = module.params.get("name") + + filters = [] + + # proceed only if we're unequivocally specifying an ENI + if eni_id is None and private_ip_address is None and (instance_id is None and device_index is None): + return None + + if eni_id: + filters.append({'Name': 'network-interface-id', + 'Values': [eni_id]}) + + if private_ip_address and subnet_id and not filters: + filters.append({'Name': 'private-ip-address', + 'Values': [private_ip_address]}) + filters.append({'Name': 'subnet-id', + 'Values': [subnet_id]}) + + if not attached and instance_id and device_index and not filters: + filters.append({'Name': 'attachment.instance-id', + 'Values': [instance_id]}) + filters.append({'Name': 'attachment.device-index', + 'Values': [device_index]}) + + if name and subnet_id and not filters: + filters.append({'Name': 'tag:Name', + 'Values': [name]}) + filters.append({'Name': 'subnet-id', + 'Values': [subnet_id]}) + + if not filters: + return None + + try: + eni_result = connection.describe_network_interfaces(aws_retry=True, Filters=filters)["NetworkInterfaces"] + if len(eni_result) == 1: + return eni_result[0] + else: + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to find unique eni with filters: {0}".format(filters)) + + return None + + +def get_sec_group_list(groups): + + # Build list of remote security groups + remote_security_groups = [] + for group in groups: + remote_security_groups.append(group["GroupId"].encode()) + + return remote_security_groups + + +def _get_vpc_id(connection, module, subnet_id): + + try: + subnets = connection.describe_subnets(aws_retry=True, SubnetIds=[subnet_id]) + return subnets["Subnets"][0]["VpcId"] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to get vpc_id for {0}".format(subnet_id)) + + +def manage_tags(eni, name, new_tags, purge_tags, connection): + changed = False + + if "TagSet" in eni: + old_tags = boto3_tag_list_to_ansible_dict(eni['TagSet']) + elif new_tags: + old_tags = {} + else: + # No new tags and nothing in TagSet + return False + + # Do not purge tags unless tags is not None + if new_tags is None: + purge_tags = False + new_tags = {} + + if name: + new_tags['Name'] = name + + tags_to_set, tags_to_delete = compare_aws_tags( + old_tags, new_tags, + purge_tags=purge_tags, + ) + if tags_to_set: + connection.create_tags( + aws_retry=True, + Resources=[eni['NetworkInterfaceId']], + Tags=ansible_dict_to_boto3_tag_list(tags_to_set)) + changed |= True + if tags_to_delete: + delete_with_current_values = dict((k, old_tags.get(k)) for k in tags_to_delete) + connection.delete_tags( + aws_retry=True, + Resources=[eni['NetworkInterfaceId']], + Tags=ansible_dict_to_boto3_tag_list(delete_with_current_values)) + changed |= True + return changed + + +def main(): + argument_spec = dict( + eni_id=dict(default=None, type='str'), + instance_id=dict(default=None, type='str'), + private_ip_address=dict(type='str'), + subnet_id=dict(type='str'), + description=dict(type='str'), + security_groups=dict(default=[], type='list', elements='str'), + device_index=dict(default=0, type='int'), + state=dict(default='present', choices=['present', 'absent']), + force_detach=dict(default='no', type='bool'), + source_dest_check=dict(default=None, type='bool'), + delete_on_termination=dict(default=None, type='bool'), + secondary_private_ip_addresses=dict(default=None, type='list', elements='str'), + purge_secondary_private_ip_addresses=dict(default=False, type='bool'), + secondary_private_ip_address_count=dict(default=None, type='int'), + allow_reassignment=dict(default=False, type='bool'), + attached=dict(default=None, type='bool'), + name=dict(default=None, type='str'), + tags=dict(type='dict'), + purge_tags=dict(default=True, type='bool') + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ['secondary_private_ip_addresses', 'secondary_private_ip_address_count'] + ], + required_if=([ + ('attached', True, ['instance_id']), + ('purge_secondary_private_ip_addresses', True, ['secondary_private_ip_addresses']) + ]) + ) + + retry_decorator = AWSRetry.jittered_backoff( + catch_extra_error_codes=['IncorrectState'], + ) + connection = module.client('ec2', retry_decorator=retry_decorator) + state = module.params.get("state") + + if state == 'present': + eni = uniquely_find_eni(connection, module) + if eni is None: + subnet_id = module.params.get("subnet_id") + if subnet_id is None: + module.fail_json(msg='subnet_id is required when creating a new ENI') + + vpc_id = _get_vpc_id(connection, module, subnet_id) + create_eni(connection, vpc_id, module) + else: + modify_eni(connection, module, eni) + + elif state == 'absent': + delete_eni(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni_facts.py new file mode 100644 index 00000000..4741dfbc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni_facts.py @@ -0,0 +1,298 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_eni_info +version_added: 1.0.0 +short_description: Gather information about ec2 ENI interfaces in AWS +description: + - Gather information about ec2 ENI interfaces in AWS. + - This module was called C(ec2_eni_facts) before Ansible 2.9. The usage did not change. +author: "Rob White (@wimnat)" +requirements: [ boto3 ] +options: + eni_id: + description: + - The ID of the ENI. + - This option is mutually exclusive of I(filters). + type: str + version_added: 1.3.0 + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html) for possible filters. + - This option is mutually exclusive of I(eni_id). + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all ENIs +- amazon.aws.ec2_eni_info: + +# Gather information about a particular ENI +- amazon.aws.ec2_eni_info: + filters: + network-interface-id: eni-xxxxxxx + +''' + +RETURN = ''' +network_interfaces: + description: List of matching elastic network interfaces + returned: always + type: complex + contains: + association: + description: Info of associated elastic IP (EIP) + returned: When an ENI is associated with an EIP + type: dict + sample: { + allocation_id: "eipalloc-5sdf123", + association_id: "eipassoc-8sdf123", + ip_owner_id: "4415120123456", + public_dns_name: "ec2-52-1-0-63.compute-1.amazonaws.com", + public_ip: "52.1.0.63" + } + attachment: + description: Info about attached ec2 instance + returned: When an ENI is attached to an ec2 instance + type: dict + sample: { + attach_time: "2017-08-05T15:25:47+00:00", + attachment_id: "eni-attach-149d21234", + delete_on_termination: false, + device_index: 1, + instance_id: "i-15b8d3cadbafa1234", + instance_owner_id: "4415120123456", + status: "attached" + } + availability_zone: + description: Availability zone of ENI + returned: always + type: str + sample: "us-east-1b" + description: + description: Description text for ENI + returned: always + type: str + sample: "My favourite network interface" + groups: + description: List of attached security groups + returned: always + type: list + sample: [ + { + group_id: "sg-26d0f1234", + group_name: "my_ec2_security_group" + } + ] + id: + description: The id of the ENI (alias for network_interface_id) + returned: always + type: str + sample: "eni-392fsdf" + interface_type: + description: Type of the network interface + returned: always + type: str + sample: "interface" + ipv6_addresses: + description: List of IPv6 addresses for this interface + returned: always + type: list + sample: [] + mac_address: + description: MAC address of the network interface + returned: always + type: str + sample: "0a:f8:10:2f:ab:a1" + name: + description: The Name tag of the ENI, often displayed in the AWS UIs as Name + returned: When a Name tag has been set + type: str + version_added: 1.3.0 + network_interface_id: + description: The id of the ENI + returned: always + type: str + sample: "eni-392fsdf" + owner_id: + description: AWS account id of the owner of the ENI + returned: always + type: str + sample: "4415120123456" + private_dns_name: + description: Private DNS name for the ENI + returned: always + type: str + sample: "ip-172-16-1-180.ec2.internal" + private_ip_address: + description: Private IP address for the ENI + returned: always + type: str + sample: "172.16.1.180" + private_ip_addresses: + description: List of private IP addresses attached to the ENI + returned: always + type: list + sample: [] + requester_id: + description: The ID of the entity that launched the ENI + returned: always + type: str + sample: "AIDAIONYVJQNIAZFT3ABC" + requester_managed: + description: Indicates whether the network interface is being managed by an AWS service. + returned: always + type: bool + sample: false + source_dest_check: + description: Indicates whether the network interface performs source/destination checking. + returned: always + type: bool + sample: false + status: + description: Indicates if the network interface is attached to an instance or not + returned: always + type: str + sample: "in-use" + subnet_id: + description: Subnet ID the ENI is in + returned: always + type: str + sample: "subnet-7bbf01234" + tags: + description: Dictionary of tags added to the ENI + returned: always + type: dict + sample: {} + version_added: 1.3.0 + tag_set: + description: Dictionary of tags added to the ENI + returned: always + type: dict + sample: {} + vpc_id: + description: ID of the VPC the network interface it part of + returned: always + type: str + sample: "vpc-b3f1f123" +''' + +try: + from botocore.exceptions import ClientError + from botocore.exceptions import NoCredentialsError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def list_eni(connection, module): + + params = {} + # Options are mutually exclusive + if module.params.get("eni_id"): + params['NetworkInterfaceIds'] = [module.params.get("eni_id")] + elif module.params.get("filters"): + params['Filters'] = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + else: + params['Filters'] = [] + + try: + network_interfaces_result = connection.describe_network_interfaces(aws_retry=True, **params)['NetworkInterfaces'] + except is_boto3_error_code('InvalidNetworkInterfaceID.NotFound'): + module.exit_json(network_interfaces=[]) + except (ClientError, NoCredentialsError) as e: + module.fail_json_aws(e) + + # Modify boto3 tags list to be ansible friendly dict and then camel_case + camel_network_interfaces = [] + for network_interface in network_interfaces_result: + network_interface['TagSet'] = boto3_tag_list_to_ansible_dict(network_interface['TagSet']) + network_interface['Tags'] = network_interface['TagSet'] + if 'Name' in network_interface['Tags']: + network_interface['Name'] = network_interface['Tags']['Name'] + # Added id to interface info to be compatible with return values of ec2_eni module: + network_interface['Id'] = network_interface['NetworkInterfaceId'] + camel_network_interfaces.append(camel_dict_to_snake_dict(network_interface, ignore_list=['Tags', 'TagSet'])) + + module.exit_json(network_interfaces=camel_network_interfaces) + + +def get_eni_info(interface): + + # Private addresses + private_addresses = [] + for ip in interface.private_ip_addresses: + private_addresses.append({'private_ip_address': ip.private_ip_address, 'primary_address': ip.primary}) + + interface_info = {'id': interface.id, + 'subnet_id': interface.subnet_id, + 'vpc_id': interface.vpc_id, + 'description': interface.description, + 'owner_id': interface.owner_id, + 'status': interface.status, + 'mac_address': interface.mac_address, + 'private_ip_address': interface.private_ip_address, + 'source_dest_check': interface.source_dest_check, + 'groups': dict((group.id, group.name) for group in interface.groups), + 'private_ip_addresses': private_addresses + } + + if hasattr(interface, 'publicDnsName'): + interface_info['association'] = {'public_ip_address': interface.publicIp, + 'public_dns_name': interface.publicDnsName, + 'ip_owner_id': interface.ipOwnerId + } + + if interface.attachment is not None: + interface_info['attachment'] = {'attachment_id': interface.attachment.id, + 'instance_id': interface.attachment.instance_id, + 'device_index': interface.attachment.device_index, + 'status': interface.attachment.status, + 'attach_time': interface.attachment.attach_time, + 'delete_on_termination': interface.attachment.delete_on_termination, + } + + return interface_info + + +def main(): + argument_spec = dict( + eni_id=dict(type='str'), + filters=dict(default=None, type='dict') + ) + mutually_exclusive = [ + ['eni_id', 'filters'] + ] + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_eni_facts': + module.deprecate("The 'ec2_eni_facts' module has been renamed to 'ec2_eni_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + list_eni(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni_info.py new file mode 100644 index 00000000..4741dfbc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_eni_info.py @@ -0,0 +1,298 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_eni_info +version_added: 1.0.0 +short_description: Gather information about ec2 ENI interfaces in AWS +description: + - Gather information about ec2 ENI interfaces in AWS. + - This module was called C(ec2_eni_facts) before Ansible 2.9. The usage did not change. +author: "Rob White (@wimnat)" +requirements: [ boto3 ] +options: + eni_id: + description: + - The ID of the ENI. + - This option is mutually exclusive of I(filters). + type: str + version_added: 1.3.0 + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html) for possible filters. + - This option is mutually exclusive of I(eni_id). + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all ENIs +- amazon.aws.ec2_eni_info: + +# Gather information about a particular ENI +- amazon.aws.ec2_eni_info: + filters: + network-interface-id: eni-xxxxxxx + +''' + +RETURN = ''' +network_interfaces: + description: List of matching elastic network interfaces + returned: always + type: complex + contains: + association: + description: Info of associated elastic IP (EIP) + returned: When an ENI is associated with an EIP + type: dict + sample: { + allocation_id: "eipalloc-5sdf123", + association_id: "eipassoc-8sdf123", + ip_owner_id: "4415120123456", + public_dns_name: "ec2-52-1-0-63.compute-1.amazonaws.com", + public_ip: "52.1.0.63" + } + attachment: + description: Info about attached ec2 instance + returned: When an ENI is attached to an ec2 instance + type: dict + sample: { + attach_time: "2017-08-05T15:25:47+00:00", + attachment_id: "eni-attach-149d21234", + delete_on_termination: false, + device_index: 1, + instance_id: "i-15b8d3cadbafa1234", + instance_owner_id: "4415120123456", + status: "attached" + } + availability_zone: + description: Availability zone of ENI + returned: always + type: str + sample: "us-east-1b" + description: + description: Description text for ENI + returned: always + type: str + sample: "My favourite network interface" + groups: + description: List of attached security groups + returned: always + type: list + sample: [ + { + group_id: "sg-26d0f1234", + group_name: "my_ec2_security_group" + } + ] + id: + description: The id of the ENI (alias for network_interface_id) + returned: always + type: str + sample: "eni-392fsdf" + interface_type: + description: Type of the network interface + returned: always + type: str + sample: "interface" + ipv6_addresses: + description: List of IPv6 addresses for this interface + returned: always + type: list + sample: [] + mac_address: + description: MAC address of the network interface + returned: always + type: str + sample: "0a:f8:10:2f:ab:a1" + name: + description: The Name tag of the ENI, often displayed in the AWS UIs as Name + returned: When a Name tag has been set + type: str + version_added: 1.3.0 + network_interface_id: + description: The id of the ENI + returned: always + type: str + sample: "eni-392fsdf" + owner_id: + description: AWS account id of the owner of the ENI + returned: always + type: str + sample: "4415120123456" + private_dns_name: + description: Private DNS name for the ENI + returned: always + type: str + sample: "ip-172-16-1-180.ec2.internal" + private_ip_address: + description: Private IP address for the ENI + returned: always + type: str + sample: "172.16.1.180" + private_ip_addresses: + description: List of private IP addresses attached to the ENI + returned: always + type: list + sample: [] + requester_id: + description: The ID of the entity that launched the ENI + returned: always + type: str + sample: "AIDAIONYVJQNIAZFT3ABC" + requester_managed: + description: Indicates whether the network interface is being managed by an AWS service. + returned: always + type: bool + sample: false + source_dest_check: + description: Indicates whether the network interface performs source/destination checking. + returned: always + type: bool + sample: false + status: + description: Indicates if the network interface is attached to an instance or not + returned: always + type: str + sample: "in-use" + subnet_id: + description: Subnet ID the ENI is in + returned: always + type: str + sample: "subnet-7bbf01234" + tags: + description: Dictionary of tags added to the ENI + returned: always + type: dict + sample: {} + version_added: 1.3.0 + tag_set: + description: Dictionary of tags added to the ENI + returned: always + type: dict + sample: {} + vpc_id: + description: ID of the VPC the network interface it part of + returned: always + type: str + sample: "vpc-b3f1f123" +''' + +try: + from botocore.exceptions import ClientError + from botocore.exceptions import NoCredentialsError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def list_eni(connection, module): + + params = {} + # Options are mutually exclusive + if module.params.get("eni_id"): + params['NetworkInterfaceIds'] = [module.params.get("eni_id")] + elif module.params.get("filters"): + params['Filters'] = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + else: + params['Filters'] = [] + + try: + network_interfaces_result = connection.describe_network_interfaces(aws_retry=True, **params)['NetworkInterfaces'] + except is_boto3_error_code('InvalidNetworkInterfaceID.NotFound'): + module.exit_json(network_interfaces=[]) + except (ClientError, NoCredentialsError) as e: + module.fail_json_aws(e) + + # Modify boto3 tags list to be ansible friendly dict and then camel_case + camel_network_interfaces = [] + for network_interface in network_interfaces_result: + network_interface['TagSet'] = boto3_tag_list_to_ansible_dict(network_interface['TagSet']) + network_interface['Tags'] = network_interface['TagSet'] + if 'Name' in network_interface['Tags']: + network_interface['Name'] = network_interface['Tags']['Name'] + # Added id to interface info to be compatible with return values of ec2_eni module: + network_interface['Id'] = network_interface['NetworkInterfaceId'] + camel_network_interfaces.append(camel_dict_to_snake_dict(network_interface, ignore_list=['Tags', 'TagSet'])) + + module.exit_json(network_interfaces=camel_network_interfaces) + + +def get_eni_info(interface): + + # Private addresses + private_addresses = [] + for ip in interface.private_ip_addresses: + private_addresses.append({'private_ip_address': ip.private_ip_address, 'primary_address': ip.primary}) + + interface_info = {'id': interface.id, + 'subnet_id': interface.subnet_id, + 'vpc_id': interface.vpc_id, + 'description': interface.description, + 'owner_id': interface.owner_id, + 'status': interface.status, + 'mac_address': interface.mac_address, + 'private_ip_address': interface.private_ip_address, + 'source_dest_check': interface.source_dest_check, + 'groups': dict((group.id, group.name) for group in interface.groups), + 'private_ip_addresses': private_addresses + } + + if hasattr(interface, 'publicDnsName'): + interface_info['association'] = {'public_ip_address': interface.publicIp, + 'public_dns_name': interface.publicDnsName, + 'ip_owner_id': interface.ipOwnerId + } + + if interface.attachment is not None: + interface_info['attachment'] = {'attachment_id': interface.attachment.id, + 'instance_id': interface.attachment.instance_id, + 'device_index': interface.attachment.device_index, + 'status': interface.attachment.status, + 'attach_time': interface.attachment.attach_time, + 'delete_on_termination': interface.attachment.delete_on_termination, + } + + return interface_info + + +def main(): + argument_spec = dict( + eni_id=dict(type='str'), + filters=dict(default=None, type='dict') + ) + mutually_exclusive = [ + ['eni_id', 'filters'] + ] + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_eni_facts': + module.deprecate("The 'ec2_eni_facts' module has been renamed to 'ec2_eni_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + list_eni(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group.py new file mode 100644 index 00000000..2338aa69 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group.py @@ -0,0 +1,1380 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_group +version_added: 1.0.0 +author: "Andrew de Quincey (@adq)" +requirements: [ boto3 ] +short_description: maintain an ec2 VPC security group. +description: + - Maintains ec2 security groups. This module has a dependency on python-boto >= 2.5. +options: + name: + description: + - Name of the security group. + - One of and only one of I(name) or I(group_id) is required. + - Required if I(state=present). + required: false + type: str + group_id: + description: + - Id of group to delete (works only with absent). + - One of and only one of I(name) or I(group_id) is required. + required: false + type: str + description: + description: + - Description of the security group. Required when C(state) is C(present). + required: false + type: str + vpc_id: + description: + - ID of the VPC to create the group in. + required: false + type: str + rules: + description: + - List of firewall inbound rules to enforce in this group (see example). If none are supplied, + no inbound rules will be enabled. Rules list may include its own name in `group_name`. + This allows idempotent loopback additions (e.g. allow group to access itself). + Rule sources list support was added in version 2.4. This allows to define multiple sources per + source type as well as multiple source types per rule. Prior to 2.4 an individual source is allowed. + In version 2.5 support for rule descriptions was added. + required: false + type: list + elements: dict + suboptions: + cidr_ip: + type: str + description: + - The IPv4 CIDR range traffic is coming from. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + cidr_ipv6: + type: str + description: + - The IPv6 CIDR range traffic is coming from. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + ip_prefix: + type: str + description: + - The IP Prefix U(https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-prefix-lists.html) + that traffic is coming from. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + group_id: + type: str + description: + - The ID of the Security Group that traffic is coming from. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + group_name: + type: str + description: + - Name of the Security Group that traffic is coming from. + - If the Security Group doesn't exist a new Security Group will be + created with I(group_desc) as the description. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + group_desc: + type: str + description: + - If the I(group_name) is set and the Security Group doesn't exist a new Security Group will be + created with I(group_desc) as the description. + proto: + type: str + description: + - The IP protocol name (C(tcp), C(udp), C(icmp), C(icmpv6)) or number (U(https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers)) + from_port: + type: int + description: The start of the range of ports that traffic is coming from. A value of C(-1) indicates all ports. + to_port: + type: int + description: The end of the range of ports that traffic is coming from. A value of C(-1) indicates all ports. + rule_desc: + type: str + description: A description for the rule. + rules_egress: + description: + - List of firewall outbound rules to enforce in this group (see example). If none are supplied, + a default all-out rule is assumed. If an empty list is supplied, no outbound rules will be enabled. + Rule Egress sources list support was added in version 2.4. In version 2.5 support for rule descriptions + was added. + required: false + type: list + elements: dict + suboptions: + cidr_ip: + type: str + description: + - The IPv4 CIDR range traffic is going to. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + cidr_ipv6: + type: str + description: + - The IPv6 CIDR range traffic is going to. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + ip_prefix: + type: str + description: + - The IP Prefix U(https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-prefix-lists.html) + that traffic is going to. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + group_id: + type: str + description: + - The ID of the Security Group that traffic is going to. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + group_name: + type: str + description: + - Name of the Security Group that traffic is going to. + - If the Security Group doesn't exist a new Security Group will be + created with I(group_desc) as the description. + - You can specify only one of I(cidr_ip), I(cidr_ipv6), I(ip_prefix), I(group_id) + and I(group_name). + group_desc: + type: str + description: + - If the I(group_name) is set and the Security Group doesn't exist a new Security Group will be + created with I(group_desc) as the description. + proto: + type: str + description: + - The IP protocol name (C(tcp), C(udp), C(icmp), C(icmpv6)) or number (U(https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers)) + from_port: + type: int + description: The start of the range of ports that traffic is going to. A value of C(-1) indicates all ports. + to_port: + type: int + description: The end of the range of ports that traffic is going to. A value of C(-1) indicates all ports. + rule_desc: + type: str + description: A description for the rule. + state: + description: + - Create or delete a security group. + required: false + default: 'present' + choices: [ "present", "absent" ] + aliases: [] + type: str + purge_rules: + description: + - Purge existing rules on security group that are not found in rules. + required: false + default: 'true' + aliases: [] + type: bool + purge_rules_egress: + description: + - Purge existing rules_egress on security group that are not found in rules_egress. + required: false + default: 'true' + aliases: [] + type: bool + tags: + description: + - A dictionary of one or more tags to assign to the security group. + required: false + type: dict + aliases: ['resource_tags'] + purge_tags: + description: + - If yes, existing tags will be purged from the resource to match exactly what is defined by I(tags) parameter. If the I(tags) parameter is not set then + tags will not be modified. + required: false + default: yes + type: bool + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + + +notes: + - If a rule declares a group_name and that group doesn't exist, it will be + automatically created. In that case, group_desc should be provided as well. + The module will refuse to create a depended-on group without a description. + - Preview diff mode support is added in version 2.7. +''' + +EXAMPLES = ''' +- name: example using security group rule descriptions + amazon.aws.ec2_group: + name: "{{ name }}" + description: sg with rule descriptions + vpc_id: vpc-xxxxxxxx + profile: "{{ aws_profile }}" + region: us-east-1 + rules: + - proto: tcp + ports: + - 80 + cidr_ip: 0.0.0.0/0 + rule_desc: allow all on port 80 + +- name: example ec2 group + amazon.aws.ec2_group: + name: example + description: an example EC2 group + vpc_id: 12345 + region: eu-west-1 + aws_secret_key: SECRET + aws_access_key: ACCESS + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 10.0.0.0/8 + - proto: tcp + from_port: 443 + to_port: 443 + # this should only be needed for EC2 Classic security group rules + # because in a VPC an ELB will use a user-account security group + group_id: amazon-elb/sg-87654321/amazon-elb-sg + - proto: tcp + from_port: 3306 + to_port: 3306 + group_id: 123412341234/sg-87654321/exact-name-of-sg + - proto: udp + from_port: 10050 + to_port: 10050 + cidr_ip: 10.0.0.0/8 + - proto: udp + from_port: 10051 + to_port: 10051 + group_id: sg-12345678 + - proto: icmp + from_port: 8 # icmp type, -1 = any type + to_port: -1 # icmp subtype, -1 = any subtype + cidr_ip: 10.0.0.0/8 + - proto: all + # the containing group name may be specified here + group_name: example + - proto: all + # in the 'proto' attribute, if you specify -1, all, or a protocol number other than tcp, udp, icmp, or 58 (ICMPv6), + # traffic on all ports is allowed, regardless of any ports you specify + from_port: 10050 # this value is ignored + to_port: 10050 # this value is ignored + cidr_ip: 10.0.0.0/8 + + rules_egress: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + cidr_ipv6: 64:ff9b::/96 + group_name: example-other + # description to use if example-other needs to be created + group_desc: other example EC2 group + +- name: example2 ec2 group + amazon.aws.ec2_group: + name: example2 + description: an example2 EC2 group + vpc_id: 12345 + region: eu-west-1 + rules: + # 'ports' rule keyword was introduced in version 2.4. It accepts a single port value or a list of values including ranges (from_port-to_port). + - proto: tcp + ports: 22 + group_name: example-vpn + - proto: tcp + ports: + - 80 + - 443 + - 8080-8099 + cidr_ip: 0.0.0.0/0 + # Rule sources list support was added in version 2.4. This allows to define multiple sources per source type as well as multiple source types per rule. + - proto: tcp + ports: + - 6379 + - 26379 + group_name: + - example-vpn + - example-redis + - proto: tcp + ports: 5665 + group_name: example-vpn + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + cidr_ipv6: + - 2607:F8B0::/32 + - 64:ff9b::/96 + group_id: + - sg-edcd9784 + diff: True + +- name: "Delete group by its id" + amazon.aws.ec2_group: + region: eu-west-1 + group_id: sg-33b4ee5b + state: absent +''' + +RETURN = ''' +group_name: + description: Security group name + sample: My Security Group + type: str + returned: on create/update +group_id: + description: Security group id + sample: sg-abcd1234 + type: str + returned: on create/update +description: + description: Description of security group + sample: My Security Group + type: str + returned: on create/update +tags: + description: Tags associated with the security group + sample: + Name: My Security Group + Purpose: protecting stuff + type: dict + returned: on create/update +vpc_id: + description: ID of VPC to which the security group belongs + sample: vpc-abcd1234 + type: str + returned: on create/update +ip_permissions: + description: Inbound rules associated with the security group. + sample: + - from_port: 8182 + ip_protocol: tcp + ip_ranges: + - cidr_ip: "198.51.100.1/32" + ipv6_ranges: [] + prefix_list_ids: [] + to_port: 8182 + user_id_group_pairs: [] + type: list + returned: on create/update +ip_permissions_egress: + description: Outbound rules associated with the security group. + sample: + - ip_protocol: -1 + ip_ranges: + - cidr_ip: "0.0.0.0/0" + ipv6_ranges: [] + prefix_list_ids: [] + user_id_group_pairs: [] + type: list + returned: on create/update +owner_id: + description: AWS Account ID of the security group + sample: 123456789012 + type: int + returned: on create/update +''' + +import json +import re +import itertools +from copy import deepcopy +from time import sleep +from collections import namedtuple + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils._text import to_text +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.common.network import to_ipv6_subnet +from ansible.module_utils.common.network import to_subnet +from ansible.module_utils.six import string_types +from ansible_collections.ansible.netcommon.plugins.module_utils.compat.ipaddress import IPv6Network +from ansible_collections.ansible.netcommon.plugins.module_utils.compat.ipaddress import ip_network + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_aws_tags +from ..module_utils.iam import get_aws_account_id +from ..module_utils.waiters import get_waiter + + +Rule = namedtuple('Rule', ['port_range', 'protocol', 'target', 'target_type', 'description']) +valid_targets = set(['ipv4', 'ipv6', 'group', 'ip_prefix']) +current_account_id = None + + +def rule_cmp(a, b): + """Compare rules without descriptions""" + for prop in ['port_range', 'protocol', 'target', 'target_type']: + if prop == 'port_range' and to_text(a.protocol) == to_text(b.protocol): + # equal protocols can interchange `(-1, -1)` and `(None, None)` + if a.port_range in ((None, None), (-1, -1)) and b.port_range in ((None, None), (-1, -1)): + continue + elif getattr(a, prop) != getattr(b, prop): + return False + elif getattr(a, prop) != getattr(b, prop): + return False + return True + + +def rules_to_permissions(rules): + return [to_permission(rule) for rule in rules] + + +def to_permission(rule): + # take a Rule, output the serialized grant + perm = { + 'IpProtocol': rule.protocol, + } + perm['FromPort'], perm['ToPort'] = rule.port_range + if rule.target_type == 'ipv4': + perm['IpRanges'] = [{ + 'CidrIp': rule.target, + }] + if rule.description: + perm['IpRanges'][0]['Description'] = rule.description + elif rule.target_type == 'ipv6': + perm['Ipv6Ranges'] = [{ + 'CidrIpv6': rule.target, + }] + if rule.description: + perm['Ipv6Ranges'][0]['Description'] = rule.description + elif rule.target_type == 'group': + if isinstance(rule.target, tuple): + pair = {} + if rule.target[0]: + pair['UserId'] = rule.target[0] + # group_id/group_name are mutually exclusive - give group_id more precedence as it is more specific + if rule.target[1]: + pair['GroupId'] = rule.target[1] + elif rule.target[2]: + pair['GroupName'] = rule.target[2] + perm['UserIdGroupPairs'] = [pair] + else: + perm['UserIdGroupPairs'] = [{ + 'GroupId': rule.target + }] + if rule.description: + perm['UserIdGroupPairs'][0]['Description'] = rule.description + elif rule.target_type == 'ip_prefix': + perm['PrefixListIds'] = [{ + 'PrefixListId': rule.target, + }] + if rule.description: + perm['PrefixListIds'][0]['Description'] = rule.description + elif rule.target_type not in valid_targets: + raise ValueError('Invalid target type for rule {0}'.format(rule)) + return fix_port_and_protocol(perm) + + +def rule_from_group_permission(perm): + """ + Returns a rule dict from an existing security group. + + When using a security group as a target all 3 fields (OwnerId, GroupId, and + GroupName) need to exist in the target. This ensures consistency of the + values that will be compared to desired_ingress or desired_egress + in wait_for_rule_propagation(). + GroupId is preferred as it is more specific except when targeting 'amazon-' + prefixed security groups (such as EC2 Classic ELBs). + """ + def ports_from_permission(p): + if 'FromPort' not in p and 'ToPort' not in p: + return (None, None) + return (int(perm['FromPort']), int(perm['ToPort'])) + + # outputs a rule tuple + for target_key, target_subkey, target_type in [ + ('IpRanges', 'CidrIp', 'ipv4'), + ('Ipv6Ranges', 'CidrIpv6', 'ipv6'), + ('PrefixListIds', 'PrefixListId', 'ip_prefix'), + ]: + if target_key not in perm: + continue + for r in perm[target_key]: + # there may be several IP ranges here, which is ok + yield Rule( + ports_from_permission(perm), + to_text(perm['IpProtocol']), + r[target_subkey], + target_type, + r.get('Description') + ) + if 'UserIdGroupPairs' in perm and perm['UserIdGroupPairs']: + for pair in perm['UserIdGroupPairs']: + target = ( + pair.get('UserId', current_account_id), + pair.get('GroupId', None), + None, + ) + if pair.get('UserId', '').startswith('amazon-'): + # amazon-elb and amazon-prefix rules don't need + # group-id specified, so remove it when querying + # from permission + target = ( + pair.get('UserId', None), + None, + pair.get('GroupName', None), + ) + elif 'VpcPeeringConnectionId' not in pair and pair['UserId'] != current_account_id: + # EC2-Classic cross-account + pass + elif 'VpcPeeringConnectionId' in pair: + # EC2-VPC cross-account VPC peering + target = ( + pair.get('UserId', None), + pair.get('GroupId', None), + None, + ) + + yield Rule( + ports_from_permission(perm), + to_text(perm['IpProtocol']), + target, + 'group', + pair.get('Description') + ) + + +# Wrap just this method so we can retry on missing groups +@AWSRetry.jittered_backoff(retries=5, delay=5, catch_extra_error_codes=['InvalidGroup.NotFound']) +def get_security_groups_with_backoff(client, **kwargs): + return client.describe_security_groups(**kwargs) + + +def sg_exists_with_backoff(client, **kwargs): + try: + return client.describe_security_groups(aws_retry=True, **kwargs) + except is_boto3_error_code('InvalidGroup.NotFound'): + return {'SecurityGroups': []} + + +def deduplicate_rules_args(rules): + """Returns unique rules""" + if rules is None: + return None + return list(dict(zip((json.dumps(r, sort_keys=True) for r in rules), rules)).values()) + + +def validate_rule(module, rule): + VALID_PARAMS = ('cidr_ip', 'cidr_ipv6', 'ip_prefix', + 'group_id', 'group_name', 'group_desc', + 'proto', 'from_port', 'to_port', 'rule_desc') + if not isinstance(rule, dict): + module.fail_json(msg='Invalid rule parameter type [%s].' % type(rule)) + for k in rule: + if k not in VALID_PARAMS: + module.fail_json(msg='Invalid rule parameter \'{0}\' for rule: {1}'.format(k, rule)) + + if 'group_id' in rule and 'cidr_ip' in rule: + module.fail_json(msg='Specify group_id OR cidr_ip, not both') + elif 'group_name' in rule and 'cidr_ip' in rule: + module.fail_json(msg='Specify group_name OR cidr_ip, not both') + elif 'group_id' in rule and 'cidr_ipv6' in rule: + module.fail_json(msg="Specify group_id OR cidr_ipv6, not both") + elif 'group_name' in rule and 'cidr_ipv6' in rule: + module.fail_json(msg="Specify group_name OR cidr_ipv6, not both") + elif 'cidr_ip' in rule and 'cidr_ipv6' in rule: + module.fail_json(msg="Specify cidr_ip OR cidr_ipv6, not both") + elif 'group_id' in rule and 'group_name' in rule: + module.fail_json(msg='Specify group_id OR group_name, not both') + + +def get_target_from_rule(module, client, rule, name, group, groups, vpc_id): + """ + Returns tuple of (target_type, target, group_created) after validating rule params. + + rule: Dict describing a rule. + name: Name of the security group being managed. + groups: Dict of all available security groups. + + AWS accepts an ip range or a security group as target of a rule. This + function validate the rule specification and return either a non-None + group_id or a non-None ip range. + + When using a security group as a target all 3 fields (OwnerId, GroupId, and + GroupName) need to exist in the target. This ensures consistency of the + values that will be compared to current_rules (from current_ingress and + current_egress) in wait_for_rule_propagation(). + """ + FOREIGN_SECURITY_GROUP_REGEX = r'^([^/]+)/?(sg-\S+)?/(\S+)' + owner_id = current_account_id + group_id = None + group_name = None + target_group_created = False + + validate_rule(module, rule) + if rule.get('group_id') and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']): + # this is a foreign Security Group. Since you can't fetch it you must create an instance of it + # Matches on groups like amazon-elb/sg-5a9c116a/amazon-elb-sg, amazon-elb/amazon-elb-sg, + # and peer-VPC groups like 0987654321/sg-1234567890/example + owner_id, group_id, group_name = re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']).groups() + group_instance = dict(UserId=owner_id, GroupId=group_id, GroupName=group_name) + groups[group_id] = group_instance + groups[group_name] = group_instance + if group_id and group_name: + if group_name.startswith('amazon-'): + # amazon-elb and amazon-prefix rules don't need group_id specified, + group_id = None + else: + # group_id/group_name are mutually exclusive - give group_id more precedence as it is more specific + group_name = None + return 'group', (owner_id, group_id, group_name), False + elif 'group_id' in rule: + return 'group', (owner_id, rule['group_id'], None), False + elif 'group_name' in rule: + group_name = rule['group_name'] + if group_name == name: + group_id = group['GroupId'] + groups[group_id] = group + groups[group_name] = group + elif group_name in groups and group.get('VpcId') and groups[group_name].get('VpcId'): + # both are VPC groups, this is ok + group_id = groups[group_name]['GroupId'] + elif group_name in groups and not (group.get('VpcId') or groups[group_name].get('VpcId')): + # both are EC2 classic, this is ok + group_id = groups[group_name]['GroupId'] + else: + auto_group = None + filters = {'group-name': group_name} + if vpc_id: + filters['vpc-id'] = vpc_id + # if we got here, either the target group does not exist, or there + # is a mix of EC2 classic + VPC groups. Mixing of EC2 classic + VPC + # is bad, so we have to create a new SG because no compatible group + # exists + if not rule.get('group_desc', '').strip(): + # retry describing the group once + try: + auto_group = get_security_groups_with_backoff(client, Filters=ansible_dict_to_boto3_filter_list(filters)).get('SecurityGroups', [])[0] + except (is_boto3_error_code('InvalidGroup.NotFound'), IndexError): + module.fail_json(msg="group %s will be automatically created by rule %s but " + "no description was provided" % (group_name, rule)) + except ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e) + elif not module.check_mode: + params = dict(GroupName=group_name, Description=rule['group_desc']) + if vpc_id: + params['VpcId'] = vpc_id + try: + auto_group = client.create_security_group(aws_retry=True, **params) + get_waiter( + client, 'security_group_exists', + ).wait( + GroupIds=[auto_group['GroupId']], + ) + except is_boto3_error_code('InvalidGroup.Duplicate'): + # The group exists, but didn't show up in any of our describe-security-groups calls + # Try searching on a filter for the name, and allow a retry window for AWS to update + # the model on their end. + try: + auto_group = get_security_groups_with_backoff(client, Filters=ansible_dict_to_boto3_filter_list(filters)).get('SecurityGroups', [])[0] + except IndexError as e: + module.fail_json(msg="Could not create or use existing group '{0}' in rule. Make sure the group exists".format(group_name)) + except ClientError as e: + module.fail_json_aws( + e, + msg="Could not create or use existing group '{0}' in rule. Make sure the group exists".format(group_name)) + if auto_group is not None: + group_id = auto_group['GroupId'] + groups[group_id] = auto_group + groups[group_name] = auto_group + target_group_created = True + return 'group', (owner_id, group_id, None), target_group_created + elif 'cidr_ip' in rule: + return 'ipv4', validate_ip(module, rule['cidr_ip']), False + elif 'cidr_ipv6' in rule: + return 'ipv6', validate_ip(module, rule['cidr_ipv6']), False + elif 'ip_prefix' in rule: + return 'ip_prefix', rule['ip_prefix'], False + + module.fail_json(msg="Could not match target for rule {0}".format(rule), failed_rule=rule) + + +def ports_expand(ports): + # takes a list of ports and returns a list of (port_from, port_to) + ports_expanded = [] + for port in ports: + if not isinstance(port, string_types): + ports_expanded.append((port,) * 2) + elif '-' in port: + ports_expanded.append(tuple(int(p.strip()) for p in port.split('-', 1))) + else: + ports_expanded.append((int(port.strip()),) * 2) + + return ports_expanded + + +def rule_expand_ports(rule): + # takes a rule dict and returns a list of expanded rule dicts + if 'ports' not in rule: + if isinstance(rule.get('from_port'), string_types): + rule['from_port'] = int(rule.get('from_port')) + if isinstance(rule.get('to_port'), string_types): + rule['to_port'] = int(rule.get('to_port')) + return [rule] + + ports = rule['ports'] if isinstance(rule['ports'], list) else [rule['ports']] + + rule_expanded = [] + for from_to in ports_expand(ports): + temp_rule = rule.copy() + del temp_rule['ports'] + temp_rule['from_port'], temp_rule['to_port'] = sorted(from_to) + rule_expanded.append(temp_rule) + + return rule_expanded + + +def rules_expand_ports(rules): + # takes a list of rules and expands it based on 'ports' + if not rules: + return rules + + return [rule for rule_complex in rules + for rule in rule_expand_ports(rule_complex)] + + +def rule_expand_source(rule, source_type): + # takes a rule dict and returns a list of expanded rule dicts for specified source_type + sources = rule[source_type] if isinstance(rule[source_type], list) else [rule[source_type]] + source_types_all = ('cidr_ip', 'cidr_ipv6', 'group_id', 'group_name', 'ip_prefix') + + rule_expanded = [] + for source in sources: + temp_rule = rule.copy() + for s in source_types_all: + temp_rule.pop(s, None) + temp_rule[source_type] = source + rule_expanded.append(temp_rule) + + return rule_expanded + + +def rule_expand_sources(rule): + # takes a rule dict and returns a list of expanded rule discts + source_types = (stype for stype in ('cidr_ip', 'cidr_ipv6', 'group_id', 'group_name', 'ip_prefix') if stype in rule) + + return [r for stype in source_types + for r in rule_expand_source(rule, stype)] + + +def rules_expand_sources(rules): + # takes a list of rules and expands it based on 'cidr_ip', 'group_id', 'group_name' + if not rules: + return rules + + return [rule for rule_complex in rules + for rule in rule_expand_sources(rule_complex)] + + +def update_rules_description(module, client, rule_type, group_id, ip_permissions): + if module.check_mode: + return + try: + if rule_type == "in": + client.update_security_group_rule_descriptions_ingress( + aws_retry=True, GroupId=group_id, IpPermissions=ip_permissions) + if rule_type == "out": + client.update_security_group_rule_descriptions_egress( + aws_retry=True, GroupId=group_id, IpPermissions=ip_permissions) + except (ClientError, BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to update rule description for group %s" % group_id) + + +def fix_port_and_protocol(permission): + for key in ('FromPort', 'ToPort'): + if key in permission: + if permission[key] is None: + del permission[key] + else: + permission[key] = int(permission[key]) + + permission['IpProtocol'] = to_text(permission['IpProtocol']) + + return permission + + +def remove_old_permissions(client, module, revoke_ingress, revoke_egress, group_id): + if revoke_ingress: + revoke(client, module, revoke_ingress, group_id, 'in') + if revoke_egress: + revoke(client, module, revoke_egress, group_id, 'out') + return bool(revoke_ingress or revoke_egress) + + +def revoke(client, module, ip_permissions, group_id, rule_type): + if not module.check_mode: + try: + if rule_type == 'in': + client.revoke_security_group_ingress( + aws_retry=True, GroupId=group_id, IpPermissions=ip_permissions) + elif rule_type == 'out': + client.revoke_security_group_egress( + aws_retry=True, + GroupId=group_id, IpPermissions=ip_permissions) + except (BotoCoreError, ClientError) as e: + rules = 'ingress rules' if rule_type == 'in' else 'egress rules' + module.fail_json_aws(e, "Unable to revoke {0}: {1}".format(rules, ip_permissions)) + + +def add_new_permissions(client, module, new_ingress, new_egress, group_id): + if new_ingress: + authorize(client, module, new_ingress, group_id, 'in') + if new_egress: + authorize(client, module, new_egress, group_id, 'out') + return bool(new_ingress or new_egress) + + +def authorize(client, module, ip_permissions, group_id, rule_type): + if not module.check_mode: + try: + if rule_type == 'in': + client.authorize_security_group_ingress( + aws_retry=True, + GroupId=group_id, IpPermissions=ip_permissions) + elif rule_type == 'out': + client.authorize_security_group_egress( + aws_retry=True, + GroupId=group_id, IpPermissions=ip_permissions) + except (BotoCoreError, ClientError) as e: + rules = 'ingress rules' if rule_type == 'in' else 'egress rules' + module.fail_json_aws(e, "Unable to authorize {0}: {1}".format(rules, ip_permissions)) + + +def validate_ip(module, cidr_ip): + split_addr = cidr_ip.split('/') + if len(split_addr) == 2: + # this_ip is a IPv4 or IPv6 CIDR that may or may not have host bits set + # Get the network bits if IPv4, and validate if IPv6. + try: + ip = to_subnet(split_addr[0], split_addr[1]) + if ip != cidr_ip: + module.warn("One of your CIDR addresses ({0}) has host bits set. To get rid of this warning, " + "check the network mask and make sure that only network bits are set: {1}.".format( + cidr_ip, ip)) + except ValueError: + # to_subnet throws a ValueError on IPv6 networks, so we should be working with v6 if we get here + try: + isinstance(ip_network(to_text(cidr_ip)), IPv6Network) + ip = cidr_ip + except ValueError: + # If a host bit is set on something other than a /128, IPv6Network will throw a ValueError + # The ipv6_cidr in this case probably looks like "2001:DB8:A0B:12F0::1/64" and we just want the network bits + ip6 = to_ipv6_subnet(split_addr[0]) + "/" + split_addr[1] + if ip6 != cidr_ip: + module.warn("One of your IPv6 CIDR addresses ({0}) has host bits set. To get rid of this warning, " + "check the network mask and make sure that only network bits are set: {1}.".format(cidr_ip, ip6)) + return ip6 + return ip + return cidr_ip + + +def update_tags(client, module, group_id, current_tags, tags, purge_tags): + tags_need_modify, tags_to_delete = compare_aws_tags(current_tags, tags, purge_tags) + + if not module.check_mode: + if tags_to_delete: + try: + client.delete_tags(aws_retry=True, Resources=[group_id], Tags=[{'Key': tag} for tag in tags_to_delete]) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to delete tags {0}".format(tags_to_delete)) + + # Add/update tags + if tags_need_modify: + try: + client.create_tags(aws_retry=True, Resources=[group_id], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to add tags {0}".format(tags_need_modify)) + + return bool(tags_need_modify or tags_to_delete) + + +def update_rule_descriptions(module, client, group_id, present_ingress, named_tuple_ingress_list, present_egress, named_tuple_egress_list): + changed = False + ingress_needs_desc_update = [] + egress_needs_desc_update = [] + + for present_rule in present_egress: + needs_update = [r for r in named_tuple_egress_list if rule_cmp(r, present_rule) and r.description != present_rule.description] + for r in needs_update: + named_tuple_egress_list.remove(r) + egress_needs_desc_update.extend(needs_update) + for present_rule in present_ingress: + needs_update = [r for r in named_tuple_ingress_list if rule_cmp(r, present_rule) and r.description != present_rule.description] + for r in needs_update: + named_tuple_ingress_list.remove(r) + ingress_needs_desc_update.extend(needs_update) + + if ingress_needs_desc_update: + update_rules_description(module, client, 'in', group_id, rules_to_permissions(ingress_needs_desc_update)) + changed |= True + if egress_needs_desc_update: + update_rules_description(module, client, 'out', group_id, rules_to_permissions(egress_needs_desc_update)) + changed |= True + return changed + + +def create_security_group(client, module, name, description, vpc_id): + if not module.check_mode: + params = dict(GroupName=name, Description=description) + if vpc_id: + params['VpcId'] = vpc_id + try: + group = client.create_security_group(aws_retry=True, **params) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to create security group") + # When a group is created, an egress_rule ALLOW ALL + # to 0.0.0.0/0 is added automatically but it's not + # reflected in the object returned by the AWS API + # call. We re-read the group for getting an updated object + # amazon sometimes takes a couple seconds to update the security group so wait till it exists + while True: + sleep(3) + group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0] + if group.get('VpcId') and not group.get('IpPermissionsEgress'): + pass + else: + break + return group + return None + + +def wait_for_rule_propagation(module, client, group, desired_ingress, desired_egress, purge_ingress, purge_egress): + group_id = group['GroupId'] + tries = 6 + + def await_rules(group, desired_rules, purge, rule_key): + for i in range(tries): + current_rules = set(sum([list(rule_from_group_permission(p)) for p in group[rule_key]], [])) + if purge and len(current_rules ^ set(desired_rules)) == 0: + return group + elif purge: + conflicts = current_rules ^ set(desired_rules) + # For cases where set comparison is equivalent, but invalid port/proto exist + for a, b in itertools.combinations(conflicts, 2): + if rule_cmp(a, b): + conflicts.discard(a) + conflicts.discard(b) + if not len(conflicts): + return group + elif current_rules.issuperset(desired_rules) and not purge: + return group + sleep(10) + group = get_security_groups_with_backoff(client, GroupIds=[group_id])['SecurityGroups'][0] + module.warn("Ran out of time waiting for {0} {1}. Current: {2}, Desired: {3}".format(group_id, rule_key, current_rules, desired_rules)) + return group + + group = get_security_groups_with_backoff(client, GroupIds=[group_id])['SecurityGroups'][0] + if 'VpcId' in group and module.params.get('rules_egress') is not None: + group = await_rules(group, desired_egress, purge_egress, 'IpPermissionsEgress') + return await_rules(group, desired_ingress, purge_ingress, 'IpPermissions') + + +def group_exists(client, module, vpc_id, group_id, name): + params = {'Filters': []} + if group_id: + params['GroupIds'] = [group_id] + if name: + # Add name to filters rather than params['GroupNames'] + # because params['GroupNames'] only checks the default vpc if no vpc is provided + params['Filters'].append({'Name': 'group-name', 'Values': [name]}) + if vpc_id: + params['Filters'].append({'Name': 'vpc-id', 'Values': [vpc_id]}) + # Don't filter by description to maintain backwards compatibility + + try: + security_groups = sg_exists_with_backoff(client, **params).get('SecurityGroups', []) + all_groups = get_security_groups_with_backoff(client).get('SecurityGroups', []) + except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg="Error in describe_security_groups") + + if security_groups: + groups = dict((group['GroupId'], group) for group in all_groups) + groups.update(dict((group['GroupName'], group) for group in all_groups)) + if vpc_id: + vpc_wins = dict((group['GroupName'], group) for group in all_groups if group.get('VpcId') and group['VpcId'] == vpc_id) + groups.update(vpc_wins) + # maintain backwards compatibility by using the last matching group + return security_groups[-1], groups + return None, {} + + +def verify_rules_with_descriptions_permitted(client, module, rules, rules_egress): + if not hasattr(client, "update_security_group_rule_descriptions_egress"): + all_rules = rules if rules else [] + rules_egress if rules_egress else [] + if any('rule_desc' in rule for rule in all_rules): + module.fail_json(msg="Using rule descriptions requires botocore version >= 1.7.2.") + + +def get_diff_final_resource(client, module, security_group): + def get_account_id(security_group, module): + try: + owner_id = security_group.get('owner_id', current_account_id) + except (BotoCoreError, ClientError) as e: + owner_id = "Unable to determine owner_id: {0}".format(to_text(e)) + return owner_id + + def get_final_tags(security_group_tags, specified_tags, purge_tags): + if specified_tags is None: + return security_group_tags + tags_need_modify, tags_to_delete = compare_aws_tags(security_group_tags, specified_tags, purge_tags) + end_result_tags = dict((k, v) for k, v in specified_tags.items() if k not in tags_to_delete) + end_result_tags.update(dict((k, v) for k, v in security_group_tags.items() if k not in tags_to_delete)) + end_result_tags.update(tags_need_modify) + return end_result_tags + + def get_final_rules(client, module, security_group_rules, specified_rules, purge_rules): + if specified_rules is None: + return security_group_rules + if purge_rules: + final_rules = [] + else: + final_rules = list(security_group_rules) + specified_rules = flatten_nested_targets(module, deepcopy(specified_rules)) + for rule in specified_rules: + format_rule = { + 'from_port': None, 'to_port': None, 'ip_protocol': rule.get('proto', 'tcp'), + 'ip_ranges': [], 'ipv6_ranges': [], 'prefix_list_ids': [], 'user_id_group_pairs': [] + } + if rule.get('proto', 'tcp') in ('all', '-1', -1): + format_rule['ip_protocol'] = '-1' + format_rule.pop('from_port') + format_rule.pop('to_port') + elif rule.get('ports'): + if rule.get('ports') and (isinstance(rule['ports'], string_types) or isinstance(rule['ports'], int)): + rule['ports'] = [rule['ports']] + for port in rule.get('ports'): + if isinstance(port, string_types) and '-' in port: + format_rule['from_port'], format_rule['to_port'] = port.split('-') + else: + format_rule['from_port'] = format_rule['to_port'] = port + elif rule.get('from_port') or rule.get('to_port'): + format_rule['from_port'] = rule.get('from_port', rule.get('to_port')) + format_rule['to_port'] = rule.get('to_port', rule.get('from_port')) + for source_type in ('cidr_ip', 'cidr_ipv6', 'prefix_list_id'): + if rule.get(source_type): + rule_key = {'cidr_ip': 'ip_ranges', 'cidr_ipv6': 'ipv6_ranges', 'prefix_list_id': 'prefix_list_ids'}.get(source_type) + if rule.get('rule_desc'): + format_rule[rule_key] = [{source_type: rule[source_type], 'description': rule['rule_desc']}] + else: + if not isinstance(rule[source_type], list): + rule[source_type] = [rule[source_type]] + format_rule[rule_key] = [{source_type: target} for target in rule[source_type]] + if rule.get('group_id') or rule.get('group_name'): + rule_sg = camel_dict_to_snake_dict(group_exists(client, module, module.params['vpc_id'], rule.get('group_id'), rule.get('group_name'))[0]) + format_rule['user_id_group_pairs'] = [{ + 'description': rule_sg.get('description', rule_sg.get('group_desc')), + 'group_id': rule_sg.get('group_id', rule.get('group_id')), + 'group_name': rule_sg.get('group_name', rule.get('group_name')), + 'peering_status': rule_sg.get('peering_status'), + 'user_id': rule_sg.get('user_id', get_account_id(security_group, module)), + 'vpc_id': rule_sg.get('vpc_id', module.params['vpc_id']), + 'vpc_peering_connection_id': rule_sg.get('vpc_peering_connection_id') + }] + for k, v in list(format_rule['user_id_group_pairs'][0].items()): + if v is None: + format_rule['user_id_group_pairs'][0].pop(k) + final_rules.append(format_rule) + # Order final rules consistently + final_rules.sort(key=get_ip_permissions_sort_key) + return final_rules + security_group_ingress = security_group.get('ip_permissions', []) + specified_ingress = module.params['rules'] + purge_ingress = module.params['purge_rules'] + security_group_egress = security_group.get('ip_permissions_egress', []) + specified_egress = module.params['rules_egress'] + purge_egress = module.params['purge_rules_egress'] + return { + 'description': module.params['description'], + 'group_id': security_group.get('group_id', 'sg-xxxxxxxx'), + 'group_name': security_group.get('group_name', module.params['name']), + 'ip_permissions': get_final_rules(client, module, security_group_ingress, specified_ingress, purge_ingress), + 'ip_permissions_egress': get_final_rules(client, module, security_group_egress, specified_egress, purge_egress), + 'owner_id': get_account_id(security_group, module), + 'tags': get_final_tags(security_group.get('tags', {}), module.params['tags'], module.params['purge_tags']), + 'vpc_id': security_group.get('vpc_id', module.params['vpc_id'])} + + +def flatten_nested_targets(module, rules): + def _flatten(targets): + for target in targets: + if isinstance(target, list): + for t in _flatten(target): + yield t + elif isinstance(target, string_types): + yield target + + if rules is not None: + for rule in rules: + target_list_type = None + if isinstance(rule.get('cidr_ip'), list): + target_list_type = 'cidr_ip' + elif isinstance(rule.get('cidr_ipv6'), list): + target_list_type = 'cidr_ipv6' + if target_list_type is not None: + rule[target_list_type] = list(_flatten(rule[target_list_type])) + return rules + + +def get_rule_sort_key(dicts): + if dicts.get('cidr_ip'): + return dicts.get('cidr_ip') + elif dicts.get('cidr_ipv6'): + return dicts.get('cidr_ipv6') + elif dicts.get('prefix_list_id'): + return dicts.get('prefix_list_id') + elif dicts.get('group_id'): + return dicts.get('group_id') + return None + + +def get_ip_permissions_sort_key(rule): + if rule.get('ip_ranges'): + rule.get('ip_ranges').sort(key=get_rule_sort_key) + return rule.get('ip_ranges')[0]['cidr_ip'] + elif rule.get('ipv6_ranges'): + rule.get('ipv6_ranges').sort(key=get_rule_sort_key) + return rule.get('ipv6_ranges')[0]['cidr_ipv6'] + elif rule.get('prefix_list_ids'): + rule.get('prefix_list_ids').sort(key=get_rule_sort_key) + return rule.get('prefix_list_ids')[0]['prefix_list_id'] + elif rule.get('user_id_group_pairs'): + rule.get('user_id_group_pairs').sort(key=get_rule_sort_key) + return rule.get('user_id_group_pairs')[0]['group_id'] + return None + + +def main(): + argument_spec = dict( + name=dict(), + group_id=dict(), + description=dict(), + vpc_id=dict(), + rules=dict(type='list', elements='dict'), + rules_egress=dict(type='list', elements='dict'), + state=dict(default='present', type='str', choices=['present', 'absent']), + purge_rules=dict(default=True, required=False, type='bool'), + purge_rules_egress=dict(default=True, required=False, type='bool'), + tags=dict(required=False, type='dict', aliases=['resource_tags']), + purge_tags=dict(default=True, required=False, type='bool') + ) + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=[['name', 'group_id']], + required_if=[['state', 'present', ['name']]], + ) + + name = module.params['name'] + group_id = module.params['group_id'] + description = module.params['description'] + vpc_id = module.params['vpc_id'] + rules = flatten_nested_targets(module, deepcopy(module.params['rules'])) + rules_egress = flatten_nested_targets(module, deepcopy(module.params['rules_egress'])) + rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules))) + rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules_egress))) + state = module.params.get('state') + purge_rules = module.params['purge_rules'] + purge_rules_egress = module.params['purge_rules_egress'] + tags = module.params['tags'] + purge_tags = module.params['purge_tags'] + + if state == 'present' and not description: + module.fail_json(msg='Must provide description when state is present.') + + changed = False + client = module.client('ec2', AWSRetry.jittered_backoff()) + + verify_rules_with_descriptions_permitted(client, module, rules, rules_egress) + group, groups = group_exists(client, module, vpc_id, group_id, name) + group_created_new = not bool(group) + + global current_account_id + current_account_id = get_aws_account_id(module) + + before = {} + after = {} + + # Ensure requested group is absent + if state == 'absent': + if group: + # found a match, delete it + before = camel_dict_to_snake_dict(group, ignore_list=['Tags']) + before['tags'] = boto3_tag_list_to_ansible_dict(before.get('tags', [])) + try: + if not module.check_mode: + client.delete_security_group(aws_retry=True, GroupId=group['GroupId']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Unable to delete security group '%s'" % group) + else: + group = None + changed = True + else: + # no match found, no changes required + pass + + # Ensure requested group is present + elif state == 'present': + if group: + # existing group + before = camel_dict_to_snake_dict(group, ignore_list=['Tags']) + before['tags'] = boto3_tag_list_to_ansible_dict(before.get('tags', [])) + if group['Description'] != description: + module.warn("Group description does not match existing group. Descriptions cannot be changed without deleting " + "and re-creating the security group. Try using state=absent to delete, then rerunning this task.") + else: + # no match found, create it + group = create_security_group(client, module, name, description, vpc_id) + changed = True + + if tags is not None and group is not None: + current_tags = boto3_tag_list_to_ansible_dict(group.get('Tags', [])) + changed |= update_tags(client, module, group['GroupId'], current_tags, tags, purge_tags) + + if group: + named_tuple_ingress_list = [] + named_tuple_egress_list = [] + current_ingress = sum([list(rule_from_group_permission(p)) for p in group['IpPermissions']], []) + current_egress = sum([list(rule_from_group_permission(p)) for p in group['IpPermissionsEgress']], []) + + for new_rules, rule_type, named_tuple_rule_list in [(rules, 'in', named_tuple_ingress_list), + (rules_egress, 'out', named_tuple_egress_list)]: + if new_rules is None: + continue + for rule in new_rules: + target_type, target, target_group_created = get_target_from_rule( + module, client, rule, name, group, groups, vpc_id) + changed |= target_group_created + + if rule.get('proto', 'tcp') in ('all', '-1', -1): + rule['proto'] = '-1' + rule['from_port'] = None + rule['to_port'] = None + try: + int(rule.get('proto', 'tcp')) + rule['proto'] = to_text(rule.get('proto', 'tcp')) + rule['from_port'] = None + rule['to_port'] = None + except ValueError: + # rule does not use numeric protocol spec + pass + + named_tuple_rule_list.append( + Rule( + port_range=(rule['from_port'], rule['to_port']), + protocol=to_text(rule.get('proto', 'tcp')), + target=target, target_type=target_type, + description=rule.get('rule_desc'), + ) + ) + + # List comprehensions for rules to add, rules to modify, and rule ids to determine purging + new_ingress_permissions = [to_permission(r) for r in (set(named_tuple_ingress_list) - set(current_ingress))] + new_egress_permissions = [to_permission(r) for r in (set(named_tuple_egress_list) - set(current_egress))] + + if module.params.get('rules_egress') is None and 'VpcId' in group: + # when no egress rules are specified and we're in a VPC, + # we add in a default allow all out rule, which was the + # default behavior before egress rules were added + rule = Rule((None, None), '-1', '0.0.0.0/0', 'ipv4', None) + if rule in current_egress: + named_tuple_egress_list.append(rule) + if rule not in current_egress: + current_egress.append(rule) + + # List comprehensions for rules to add, rules to modify, and rule ids to determine purging + present_ingress = list(set(named_tuple_ingress_list).union(set(current_ingress))) + present_egress = list(set(named_tuple_egress_list).union(set(current_egress))) + + if purge_rules: + revoke_ingress = [] + for p in present_ingress: + if not any([rule_cmp(p, b) for b in named_tuple_ingress_list]): + revoke_ingress.append(to_permission(p)) + else: + revoke_ingress = [] + if purge_rules_egress and module.params.get('rules_egress') is not None: + if module.params.get('rules_egress') is []: + revoke_egress = [ + to_permission(r) for r in set(present_egress) - set(named_tuple_egress_list) + if r != Rule((None, None), '-1', '0.0.0.0/0', 'ipv4', None) + ] + else: + revoke_egress = [] + for p in present_egress: + if not any([rule_cmp(p, b) for b in named_tuple_egress_list]): + revoke_egress.append(to_permission(p)) + else: + revoke_egress = [] + + # named_tuple_ingress_list and named_tuple_egress_list get updated by + # method update_rule_descriptions, deep copy these two lists to new + # variables for the record of the 'desired' ingress and egress sg permissions + desired_ingress = deepcopy(named_tuple_ingress_list) + desired_egress = deepcopy(named_tuple_egress_list) + + changed |= update_rule_descriptions(module, client, group['GroupId'], present_ingress, + named_tuple_ingress_list, present_egress, named_tuple_egress_list) + + # Revoke old rules + changed |= remove_old_permissions(client, module, revoke_ingress, revoke_egress, group['GroupId']) + rule_msg = 'Revoking {0}, and egress {1}'.format(revoke_ingress, revoke_egress) + + new_ingress_permissions = [to_permission(r) for r in (set(named_tuple_ingress_list) - set(current_ingress))] + new_ingress_permissions = rules_to_permissions(set(named_tuple_ingress_list) - set(current_ingress)) + new_egress_permissions = rules_to_permissions(set(named_tuple_egress_list) - set(current_egress)) + # Authorize new rules + changed |= add_new_permissions(client, module, new_ingress_permissions, new_egress_permissions, group['GroupId']) + + if group_created_new and module.params.get('rules') is None and module.params.get('rules_egress') is None: + # A new group with no rules provided is already being awaited. + # When it is created we wait for the default egress rule to be added by AWS + security_group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0] + elif changed and not module.check_mode: + # keep pulling until current security group rules match the desired ingress and egress rules + security_group = wait_for_rule_propagation(module, client, group, desired_ingress, desired_egress, purge_rules, purge_rules_egress) + else: + security_group = get_security_groups_with_backoff(client, GroupIds=[group['GroupId']])['SecurityGroups'][0] + security_group = camel_dict_to_snake_dict(security_group, ignore_list=['Tags']) + security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', [])) + + else: + security_group = {'group_id': None} + + if module._diff: + if module.params['state'] == 'present': + after = get_diff_final_resource(client, module, security_group) + if before.get('ip_permissions'): + before['ip_permissions'].sort(key=get_ip_permissions_sort_key) + + security_group['diff'] = [{'before': before, 'after': after}] + + module.exit_json(changed=changed, **security_group) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group_facts.py new file mode 100644 index 00000000..228b82d9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group_facts.py @@ -0,0 +1,148 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_group_info +version_added: 1.0.0 +short_description: Gather information about ec2 security groups in AWS. +description: + - Gather information about ec2 security groups in AWS. + - This module was called C(amazon.aws.ec2_group_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: +- Henrique Rodrigues (@Sodki) +options: + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See + U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) for + possible filters. Filter names and values are case sensitive. You can also use underscores (_) + instead of dashes (-) in the filter keys, which will take precedence in case of conflict. + required: false + default: {} + type: dict +notes: + - By default, the module will return all security groups. To limit results use the appropriate filters. + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all security groups +- amazon.aws.ec2_group_info: + +# Gather information about all security groups in a specific VPC +- amazon.aws.ec2_group_info: + filters: + vpc-id: vpc-12345678 + +# Gather information about all security groups in a specific VPC +- amazon.aws.ec2_group_info: + filters: + vpc-id: vpc-12345678 + +# Gather information about a security group +- amazon.aws.ec2_group_info: + filters: + group-name: example-1 + +# Gather information about a security group by id +- amazon.aws.ec2_group_info: + filters: + group-id: sg-12345678 + +# Gather information about a security group with multiple filters, also mixing the use of underscores as filter keys +- amazon.aws.ec2_group_info: + filters: + group_id: sg-12345678 + vpc-id: vpc-12345678 + +# Gather information about various security groups +- amazon.aws.ec2_group_info: + filters: + group-name: + - example-1 + - example-2 + - example-3 + +# Gather information about any security group with a tag key Name and value Example. +# The quotes around 'tag:name' are important because of the colon in the value +- amazon.aws.ec2_group_info: + filters: + "tag:Name": Example +''' + +RETURN = ''' +security_groups: + description: Security groups that match the provided filters. Each element consists of a dict with all the information related to that security group. + type: list + returned: always + sample: +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def main(): + argument_spec = dict( + filters=dict(default={}, type='dict') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_group_facts': + module.deprecate("The 'ec2_group_facts' module has been renamed to 'ec2_group_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', AWSRetry.jittered_backoff()) + + # Replace filter key underscores with dashes, for compatibility, except if we're dealing with tags + filters = module.params.get("filters") + sanitized_filters = dict() + + for key in filters: + if key.startswith("tag:"): + sanitized_filters[key] = filters[key] + else: + sanitized_filters[key.replace("_", "-")] = filters[key] + + try: + security_groups = connection.describe_security_groups( + aws_retry=True, + Filters=ansible_dict_to_boto3_filter_list(sanitized_filters) + ) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to describe security groups') + + snaked_security_groups = [] + for security_group in security_groups['SecurityGroups']: + # Modify boto3 tags list to be ansible friendly dict + # but don't camel case tags + security_group = camel_dict_to_snake_dict(security_group) + security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', {}), tag_name_key_name='key', tag_value_key_name='value') + snaked_security_groups.append(security_group) + + module.exit_json(security_groups=snaked_security_groups) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group_info.py new file mode 100644 index 00000000..228b82d9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_group_info.py @@ -0,0 +1,148 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_group_info +version_added: 1.0.0 +short_description: Gather information about ec2 security groups in AWS. +description: + - Gather information about ec2 security groups in AWS. + - This module was called C(amazon.aws.ec2_group_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: +- Henrique Rodrigues (@Sodki) +options: + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See + U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) for + possible filters. Filter names and values are case sensitive. You can also use underscores (_) + instead of dashes (-) in the filter keys, which will take precedence in case of conflict. + required: false + default: {} + type: dict +notes: + - By default, the module will return all security groups. To limit results use the appropriate filters. + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all security groups +- amazon.aws.ec2_group_info: + +# Gather information about all security groups in a specific VPC +- amazon.aws.ec2_group_info: + filters: + vpc-id: vpc-12345678 + +# Gather information about all security groups in a specific VPC +- amazon.aws.ec2_group_info: + filters: + vpc-id: vpc-12345678 + +# Gather information about a security group +- amazon.aws.ec2_group_info: + filters: + group-name: example-1 + +# Gather information about a security group by id +- amazon.aws.ec2_group_info: + filters: + group-id: sg-12345678 + +# Gather information about a security group with multiple filters, also mixing the use of underscores as filter keys +- amazon.aws.ec2_group_info: + filters: + group_id: sg-12345678 + vpc-id: vpc-12345678 + +# Gather information about various security groups +- amazon.aws.ec2_group_info: + filters: + group-name: + - example-1 + - example-2 + - example-3 + +# Gather information about any security group with a tag key Name and value Example. +# The quotes around 'tag:name' are important because of the colon in the value +- amazon.aws.ec2_group_info: + filters: + "tag:Name": Example +''' + +RETURN = ''' +security_groups: + description: Security groups that match the provided filters. Each element consists of a dict with all the information related to that security group. + type: list + returned: always + sample: +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def main(): + argument_spec = dict( + filters=dict(default={}, type='dict') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_group_facts': + module.deprecate("The 'ec2_group_facts' module has been renamed to 'ec2_group_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', AWSRetry.jittered_backoff()) + + # Replace filter key underscores with dashes, for compatibility, except if we're dealing with tags + filters = module.params.get("filters") + sanitized_filters = dict() + + for key in filters: + if key.startswith("tag:"): + sanitized_filters[key] = filters[key] + else: + sanitized_filters[key.replace("_", "-")] = filters[key] + + try: + security_groups = connection.describe_security_groups( + aws_retry=True, + Filters=ansible_dict_to_boto3_filter_list(sanitized_filters) + ) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to describe security groups') + + snaked_security_groups = [] + for security_group in security_groups['SecurityGroups']: + # Modify boto3 tags list to be ansible friendly dict + # but don't camel case tags + security_group = camel_dict_to_snake_dict(security_group) + security_group['tags'] = boto3_tag_list_to_ansible_dict(security_group.get('tags', {}), tag_name_key_name='key', tag_value_key_name='value') + snaked_security_groups.append(security_group) + + module.exit_json(security_groups=snaked_security_groups) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_key.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_key.py new file mode 100644 index 00000000..815130f9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_key.py @@ -0,0 +1,267 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_key +version_added: 1.0.0 +short_description: create or delete an ec2 key pair +description: + - create or delete an ec2 key pair. +options: + name: + description: + - Name of the key pair. + required: true + type: str + key_material: + description: + - Public key material. + required: false + type: str + force: + description: + - Force overwrite of already existing key pair if key has changed. + required: false + default: true + type: bool + state: + description: + - create or delete keypair + required: false + choices: [ present, absent ] + default: 'present' + type: str + wait: + description: + - This option has no effect since version 2.5 and will be removed after 2022-06-01. + type: bool + wait_timeout: + description: + - This option has no effect since version 2.5 and will be removed after 2022-06-01. + type: int + required: false + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +requirements: [ boto3 ] +author: + - "Vincent Viallet (@zbal)" + - "Prasad Katti (@prasadkatti)" +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: create a new ec2 key pair, returns generated private key + amazon.aws.ec2_key: + name: my_keypair + +- name: create key pair using provided key_material + amazon.aws.ec2_key: + name: my_keypair + key_material: 'ssh-rsa AAAAxyz...== me@example.com' + +- name: create key pair using key_material obtained using 'file' lookup plugin + amazon.aws.ec2_key: + name: my_keypair + key_material: "{{ lookup('file', '/path/to/public_key/id_rsa.pub') }}" + +# try creating a key pair with the name of an already existing keypair +# but don't overwrite it even if the key is different (force=false) +- name: try creating a key pair with name of an already existing keypair + amazon.aws.ec2_key: + name: my_existing_keypair + key_material: 'ssh-rsa AAAAxyz...== me@example.com' + force: false + +- name: remove key pair by name + amazon.aws.ec2_key: + name: my_keypair + state: absent +''' + +RETURN = ''' +changed: + description: whether a keypair was created/deleted + returned: always + type: bool + sample: true +msg: + description: short message describing the action taken + returned: always + type: str + sample: key pair created +key: + description: details of the keypair (this is set to null when state is absent) + returned: always + type: complex + contains: + fingerprint: + description: fingerprint of the key + returned: when state is present + type: str + sample: 'b0:22:49:61:d9:44:9d:0c:7e:ac:8a:32:93:21:6c:e8:fb:59:62:43' + name: + description: name of the keypair + returned: when state is present + type: str + sample: my_keypair + private_key: + description: private key of a newly created keypair + returned: when a new keypair is created by AWS (key_material is not provided) + type: str + sample: '-----BEGIN RSA PRIVATE KEY----- + MIIEowIBAAKC... + -----END RSA PRIVATE KEY-----' +''' + +import uuid + +try: + import botocore +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible.module_utils._text import to_bytes + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry + + +def extract_key_data(key): + + data = { + 'name': key['KeyName'], + 'fingerprint': key['KeyFingerprint'] + } + if 'KeyMaterial' in key: + data['private_key'] = key['KeyMaterial'] + return data + + +def get_key_fingerprint(module, ec2_client, key_material): + ''' + EC2's fingerprints are non-trivial to generate, so push this key + to a temporary name and make ec2 calculate the fingerprint for us. + http://blog.jbrowne.com/?p=23 + https://forums.aws.amazon.com/thread.jspa?messageID=352828 + ''' + + # find an unused name + name_in_use = True + while name_in_use: + random_name = "ansible-" + str(uuid.uuid4()) + name_in_use = find_key_pair(module, ec2_client, random_name) + + temp_key = import_key_pair(module, ec2_client, random_name, key_material) + delete_key_pair(module, ec2_client, random_name, finish_task=False) + return temp_key['KeyFingerprint'] + + +def find_key_pair(module, ec2_client, name): + + try: + key = ec2_client.describe_key_pairs(aws_retry=True, KeyNames=[name])['KeyPairs'][0] + except is_boto3_error_code('InvalidKeyPair.NotFound'): + return None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as err: + module.fail_json_aws(err, msg="error finding keypair") + except IndexError: + key = None + return key + + +def create_key_pair(module, ec2_client, name, key_material, force): + + key = find_key_pair(module, ec2_client, name) + if key: + if key_material and force: + if not module.check_mode: + new_fingerprint = get_key_fingerprint(module, ec2_client, key_material) + if key['KeyFingerprint'] != new_fingerprint: + delete_key_pair(module, ec2_client, name, finish_task=False) + key = import_key_pair(module, ec2_client, name, key_material) + key_data = extract_key_data(key) + module.exit_json(changed=True, key=key_data, msg="key pair updated") + else: + # Assume a change will be made in check mode since a comparison can't be done + module.exit_json(changed=True, key=extract_key_data(key), msg="key pair updated") + key_data = extract_key_data(key) + module.exit_json(changed=False, key=key_data, msg="key pair already exists") + else: + # key doesn't exist, create it now + key_data = None + if not module.check_mode: + if key_material: + key = import_key_pair(module, ec2_client, name, key_material) + else: + try: + key = ec2_client.create_key_pair(aws_retry=True, KeyName=name) + except botocore.exceptions.ClientError as err: + module.fail_json_aws(err, msg="error creating key") + key_data = extract_key_data(key) + module.exit_json(changed=True, key=key_data, msg="key pair created") + + +def import_key_pair(module, ec2_client, name, key_material): + + try: + key = ec2_client.import_key_pair(aws_retry=True, KeyName=name, PublicKeyMaterial=to_bytes(key_material)) + except botocore.exceptions.ClientError as err: + module.fail_json_aws(err, msg="error importing key") + return key + + +def delete_key_pair(module, ec2_client, name, finish_task=True): + + key = find_key_pair(module, ec2_client, name) + if key: + if not module.check_mode: + try: + ec2_client.delete_key_pair(aws_retry=True, KeyName=name) + except botocore.exceptions.ClientError as err: + module.fail_json_aws(err, msg="error deleting key") + if not finish_task: + return + module.exit_json(changed=True, key=None, msg="key deleted") + module.exit_json(key=None, msg="key did not exist") + + +def main(): + + argument_spec = dict( + name=dict(required=True), + key_material=dict(), + force=dict(type='bool', default=True), + state=dict(default='present', choices=['present', 'absent']), + wait=dict(type='bool', removed_at_date='2022-06-01', removed_from_collection='amazon.aws'), + wait_timeout=dict(type='int', removed_at_date='2022-06-01', removed_from_collection='amazon.aws') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + + ec2_client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + name = module.params['name'] + state = module.params.get('state') + key_material = module.params.get('key_material') + force = module.params.get('force') + + if state == 'absent': + delete_key_pair(module, ec2_client, name) + elif state == 'present': + create_key_pair(module, ec2_client, name, key_material, force) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_metadata_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_metadata_facts.py new file mode 100644 index 00000000..e871f2d9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_metadata_facts.py @@ -0,0 +1,563 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_metadata_facts +version_added: 1.0.0 +short_description: Gathers facts (instance metadata) about remote hosts within ec2 +author: + - Silviu Dicu (@silviud) + - Vinay Dandekar (@roadmapper) +description: + - This module fetches data from the instance metadata endpoint in ec2 as per + U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html). + - The module must be called from within the EC2 instance itself. +notes: + - Parameters to filter on ec2_metadata_facts may be added later. +''' + +EXAMPLES = ''' +# Gather EC2 metadata facts +- amazon.aws.ec2_metadata_facts: + +- debug: + msg: "This instance is a t1.micro" + when: ansible_ec2_instance_type == "t1.micro" +''' + +RETURN = ''' +ansible_facts: + description: Dictionary of new facts representing discovered properties of the EC2 instance. + returned: changed + type: complex + contains: + ansible_ec2_ami_id: + description: The AMI ID used to launch the instance. + type: str + sample: "ami-XXXXXXXX" + ansible_ec2_ami_launch_index: + description: + - If you started more than one instance at the same time, this value indicates the order in which the instance was launched. + - The value of the first instance launched is 0. + type: str + sample: "0" + ansible_ec2_ami_manifest_path: + description: + - The path to the AMI manifest file in Amazon S3. + - If you used an Amazon EBS-backed AMI to launch the instance, the returned result is unknown. + type: str + sample: "(unknown)" + ansible_ec2_ancestor_ami_ids: + description: + - The AMI IDs of any instances that were rebundled to create this AMI. + - This value will only exist if the AMI manifest file contained an ancestor-amis key. + type: str + sample: "(unknown)" + ansible_ec2_block_device_mapping_ami: + description: The virtual device that contains the root/boot file system. + type: str + sample: "/dev/sda1" + ansible_ec2_block_device_mapping_ebsN: + description: + - The virtual devices associated with Amazon EBS volumes, if any are present. + - Amazon EBS volumes are only available in metadata if they were present at launch time or when the instance was last started. + - The N indicates the index of the Amazon EBS volume (such as ebs1 or ebs2). + type: str + sample: "/dev/xvdb" + ansible_ec2_block_device_mapping_ephemeralN: + description: The virtual devices associated with ephemeral devices, if any are present. The N indicates the index of the ephemeral volume. + type: str + sample: "/dev/xvdc" + ansible_ec2_block_device_mapping_root: + description: + - The virtual devices or partitions associated with the root devices, or partitions on the virtual device, + where the root (/ or C) file system is associated with the given instance. + type: str + sample: "/dev/sda1" + ansible_ec2_block_device_mapping_swap: + description: The virtual devices associated with swap. Not always present. + type: str + sample: "/dev/sda2" + ansible_ec2_fws_instance_monitoring: + description: "Value showing whether the customer has enabled detailed one-minute monitoring in CloudWatch." + type: str + sample: "enabled" + ansible_ec2_hostname: + description: + - The private IPv4 DNS hostname of the instance. + - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + type: str + sample: "ip-10-0-0-1.ec2.internal" + ansible_ec2_iam_info: + description: + - If there is an IAM role associated with the instance, contains information about the last time the instance profile was updated, + including the instance's LastUpdated date, InstanceProfileArn, and InstanceProfileId. Otherwise, not present. + type: complex + sample: "" + contains: + LastUpdated: + description: The last time which InstanceProfile is associated with the Instance changed. + type: str + InstanceProfileArn: + description: The ARN of the InstanceProfile associated with the Instance. + type: str + InstanceProfileId: + description: The Id of the InstanceProfile associated with the Instance. + type: str + ansible_ec2_iam_info_instanceprofilearn: + description: The IAM instance profile ARN. + type: str + sample: "arn:aws:iam::<account id>:instance-profile/<role name>" + ansible_ec2_iam_info_instanceprofileid: + description: IAM instance profile ID. + type: str + sample: "" + ansible_ec2_iam_info_lastupdated: + description: IAM info last updated time. + type: str + sample: "2017-05-12T02:42:27Z" + ansible_ec2_iam_instance_profile_role: + description: IAM instance role. + type: str + sample: "role_name" + ansible_ec2_iam_security_credentials_<role name>: + description: + - If there is an IAM role associated with the instance, role-name is the name of the role, + and role-name contains the temporary security credentials associated with the role. Otherwise, not present. + type: str + sample: "" + ansible_ec2_iam_security_credentials_<role name>_accesskeyid: + description: IAM role access key ID. + type: str + sample: "" + ansible_ec2_iam_security_credentials_<role name>_code: + description: IAM code. + type: str + sample: "Success" + ansible_ec2_iam_security_credentials_<role name>_expiration: + description: IAM role credentials expiration time. + type: str + sample: "2017-05-12T09:11:41Z" + ansible_ec2_iam_security_credentials_<role name>_lastupdated: + description: IAM role last updated time. + type: str + sample: "2017-05-12T02:40:44Z" + ansible_ec2_iam_security_credentials_<role name>_secretaccesskey: + description: IAM role secret access key. + type: str + sample: "" + ansible_ec2_iam_security_credentials_<role name>_token: + description: IAM role token. + type: str + sample: "" + ansible_ec2_iam_security_credentials_<role name>_type: + description: IAM role type. + type: str + sample: "AWS-HMAC" + ansible_ec2_instance_action: + description: Notifies the instance that it should reboot in preparation for bundling. + type: str + sample: "none" + ansible_ec2_instance_id: + description: The ID of this instance. + type: str + sample: "i-XXXXXXXXXXXXXXXXX" + ansible_ec2_instance_identity_document: + description: JSON containing instance attributes, such as instance-id, private IP address, etc. + type: str + sample: "" + ansible_ec2_instance_identity_document_accountid: + description: "" + type: str + sample: "012345678901" + ansible_ec2_instance_identity_document_architecture: + description: Instance system architecture. + type: str + sample: "x86_64" + ansible_ec2_instance_identity_document_availabilityzone: + description: The Availability Zone in which the instance launched. + type: str + sample: "us-east-1a" + ansible_ec2_instance_identity_document_billingproducts: + description: Billing products for this instance. + type: str + sample: "" + ansible_ec2_instance_identity_document_devpayproductcodes: + description: Product codes for the launched AMI. + type: str + sample: "" + ansible_ec2_instance_identity_document_imageid: + description: The AMI ID used to launch the instance. + type: str + sample: "ami-01234567" + ansible_ec2_instance_identity_document_instanceid: + description: The ID of this instance. + type: str + sample: "i-0123456789abcdef0" + ansible_ec2_instance_identity_document_instancetype: + description: The type of instance. + type: str + sample: "m4.large" + ansible_ec2_instance_identity_document_kernelid: + description: The ID of the kernel launched with this instance, if applicable. + type: str + sample: "" + ansible_ec2_instance_identity_document_pendingtime: + description: The instance pending time. + type: str + sample: "2017-05-11T20:51:20Z" + ansible_ec2_instance_identity_document_privateip: + description: + - The private IPv4 address of the instance. + - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + type: str + sample: "10.0.0.1" + ansible_ec2_instance_identity_document_ramdiskid: + description: The ID of the RAM disk specified at launch time, if applicable. + type: str + sample: "" + ansible_ec2_instance_identity_document_region: + description: The Region in which the instance launched. + type: str + sample: "us-east-1" + ansible_ec2_instance_identity_document_version: + description: Identity document version. + type: str + sample: "2010-08-31" + ansible_ec2_instance_identity_pkcs7: + description: Used to verify the document's authenticity and content against the signature. + type: str + sample: "" + ansible_ec2_instance_identity_rsa2048: + description: Used to verify the document's authenticity and content against the signature. + type: str + sample: "" + ansible_ec2_instance_identity_signature: + description: Data that can be used by other parties to verify its origin and authenticity. + type: str + sample: "" + ansible_ec2_instance_life_cycle: + description: The purchasing option of the instance. + type: str + sample: "on-demand" + ansible_ec2_instance_type: + description: The type of the instance. + type: str + sample: "m4.large" + ansible_ec2_local_hostname: + description: + - The private IPv4 DNS hostname of the instance. + - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + type: str + sample: "ip-10-0-0-1.ec2.internal" + ansible_ec2_local_ipv4: + description: + - The private IPv4 address of the instance. + - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + type: str + sample: "10.0.0.1" + ansible_ec2_mac: + description: + - The instance's media access control (MAC) address. + - In cases where multiple network interfaces are present, this refers to the eth0 device (the device for which the device number is 0). + type: str + sample: "00:11:22:33:44:55" + ansible_ec2_metrics_vhostmd: + description: Metrics. + type: str + sample: "" + ansible_ec2_network_interfaces_macs_<mac address>_device_number: + description: + - The unique device number associated with that interface. The device number corresponds to the device name; + for example, a device-number of 2 is for the eth2 device. + - This category corresponds to the DeviceIndex and device-index fields that are used by the Amazon EC2 API and the EC2 commands for the AWS CLI. + type: str + sample: "0" + ansible_ec2_network_interfaces_macs_<mac address>_interface_id: + description: The elastic network interface ID. + type: str + sample: "eni-12345678" + ansible_ec2_network_interfaces_macs_<mac address>_ipv4_associations_<ip address>: + description: The private IPv4 addresses that are associated with each public-ip address and assigned to that interface. + type: str + sample: "" + ansible_ec2_network_interfaces_macs_<mac address>_ipv6s: + description: The IPv6 addresses associated with the interface. Returned only for instances launched into a VPC. + type: str + sample: "" + ansible_ec2_network_interfaces_macs_<mac address>_local_hostname: + description: The interface's local hostname. + type: str + sample: "" + ansible_ec2_network_interfaces_macs_<mac address>_local_ipv4s: + description: The private IPv4 addresses associated with the interface. + type: str + sample: "" + ansible_ec2_network_interfaces_macs_<mac address>_mac: + description: The instance's MAC address. + type: str + sample: "00:11:22:33:44:55" + ansible_ec2_network_interfaces_macs_<mac address>_owner_id: + description: + - The ID of the owner of the network interface. + - In multiple-interface environments, an interface can be attached by a third party, such as Elastic Load Balancing. + - Traffic on an interface is always billed to the interface owner. + type: str + sample: "01234567890" + ansible_ec2_network_interfaces_macs_<mac address>_public_hostname: + description: + - The interface's public DNS (IPv4). If the instance is in a VPC, + this category is only returned if the enableDnsHostnames attribute is set to true. + type: str + sample: "ec2-1-2-3-4.compute-1.amazonaws.com" + ansible_ec2_network_interfaces_macs_<mac address>_public_ipv4s: + description: The Elastic IP addresses associated with the interface. There may be multiple IPv4 addresses on an instance. + type: str + sample: "1.2.3.4" + ansible_ec2_network_interfaces_macs_<mac address>_security_group_ids: + description: The IDs of the security groups to which the network interface belongs. Returned only for instances launched into a VPC. + type: str + sample: "sg-01234567,sg-01234568" + ansible_ec2_network_interfaces_macs_<mac address>_security_groups: + description: Security groups to which the network interface belongs. Returned only for instances launched into a VPC. + type: str + sample: "secgroup1,secgroup2" + ansible_ec2_network_interfaces_macs_<mac address>_subnet_id: + description: The ID of the subnet in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "subnet-01234567" + ansible_ec2_network_interfaces_macs_<mac address>_subnet_ipv4_cidr_block: + description: The IPv4 CIDR block of the subnet in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "10.0.1.0/24" + ansible_ec2_network_interfaces_macs_<mac address>_subnet_ipv6_cidr_blocks: + description: The IPv6 CIDR block of the subnet in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "" + ansible_ec2_network_interfaces_macs_<mac address>_vpc_id: + description: The ID of the VPC in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "vpc-0123456" + ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv4_cidr_block: + description: The IPv4 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "10.0.0.0/16" + ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv4_cidr_blocks: + description: The IPv4 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "10.0.0.0/16" + ansible_ec2_network_interfaces_macs_<mac address>_vpc_ipv6_cidr_blocks: + description: The IPv6 CIDR block of the VPC in which the interface resides. Returned only for instances launched into a VPC. + type: str + sample: "" + ansible_ec2_placement_availability_zone: + description: The Availability Zone in which the instance launched. + type: str + sample: "us-east-1a" + ansible_ec2_placement_region: + description: The Region in which the instance launched. + type: str + sample: "us-east-1" + ansible_ec2_product_codes: + description: Product codes associated with the instance, if any. + type: str + sample: "aw0evgkw8e5c1q413zgy5pjce" + ansible_ec2_profile: + description: EC2 instance hardware profile. + type: str + sample: "default-hvm" + ansible_ec2_public_hostname: + description: + - The instance's public DNS. If the instance is in a VPC, this category is only returned if the enableDnsHostnames attribute is set to true. + type: str + sample: "ec2-1-2-3-4.compute-1.amazonaws.com" + ansible_ec2_public_ipv4: + description: The public IPv4 address. If an Elastic IP address is associated with the instance, the value returned is the Elastic IP address. + type: str + sample: "1.2.3.4" + ansible_ec2_public_key: + description: Public key. Only available if supplied at instance launch time. + type: str + sample: "" + ansible_ec2_ramdisk_id: + description: The ID of the RAM disk specified at launch time, if applicable. + type: str + sample: "" + ansible_ec2_reservation_id: + description: The ID of the reservation. + type: str + sample: "r-0123456789abcdef0" + ansible_ec2_security_groups: + description: + - The names of the security groups applied to the instance. After launch, you can only change the security groups of instances running in a VPC. + - Such changes are reflected here and in network/interfaces/macs/mac/security-groups. + type: str + sample: "securitygroup1,securitygroup2" + ansible_ec2_services_domain: + description: The domain for AWS resources for the region; for example, amazonaws.com for us-east-1. + type: str + sample: "amazonaws.com" + ansible_ec2_services_partition: + description: + - The partition that the resource is in. For standard AWS regions, the partition is aws. + - If you have resources in other partitions, the partition is aws-partitionname. + - For example, the partition for resources in the China (Beijing) region is aws-cn. + type: str + sample: "aws" + ansible_ec2_spot_termination_time: + description: + - The approximate time, in UTC, that the operating system for your Spot instance will receive the shutdown signal. + - This item is present and contains a time value only if the Spot instance has been marked for termination by Amazon EC2. + - The termination-time item is not set to a time if you terminated the Spot instance yourself. + type: str + sample: "2015-01-05T18:02:00Z" + ansible_ec2_user_data: + description: The instance user data. + type: str + sample: "#!/bin/bash" +''' + +import json +import re +import socket +import time + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_text +from ansible.module_utils.urls import fetch_url +from ansible.module_utils.six.moves.urllib.parse import quote + +socket.setdefaulttimeout(5) + + +class Ec2Metadata(object): + ec2_metadata_uri = 'http://169.254.169.254/latest/meta-data/' + ec2_sshdata_uri = 'http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key' + ec2_userdata_uri = 'http://169.254.169.254/latest/user-data/' + ec2_dynamicdata_uri = 'http://169.254.169.254/latest/dynamic/' + + def __init__(self, module, ec2_metadata_uri=None, ec2_sshdata_uri=None, ec2_userdata_uri=None, ec2_dynamicdata_uri=None): + self.module = module + self.uri_meta = ec2_metadata_uri or self.ec2_metadata_uri + self.uri_user = ec2_userdata_uri or self.ec2_userdata_uri + self.uri_ssh = ec2_sshdata_uri or self.ec2_sshdata_uri + self.uri_dynamic = ec2_dynamicdata_uri or self.ec2_dynamicdata_uri + self._data = {} + self._prefix = 'ansible_ec2_%s' + + def _fetch(self, url): + encoded_url = quote(url, safe='%/:=&?~#+!$,;\'@()*[]') + response, info = fetch_url(self.module, encoded_url, force=True) + + if info.get('status') not in (200, 404): + time.sleep(3) + # request went bad, retry once then raise + self.module.warn('Retrying query to metadata service. First attempt failed: {0}'.format(info['msg'])) + response, info = fetch_url(self.module, encoded_url, force=True) + if info.get('status') not in (200, 404): + # fail out now + self.module.fail_json(msg='Failed to retrieve metadata from AWS: {0}'.format(info['msg']), response=info) + if response: + data = response.read() + else: + data = None + return to_text(data) + + def _mangle_fields(self, fields, uri, filter_patterns=None): + filter_patterns = ['public-keys-0'] if filter_patterns is None else filter_patterns + + new_fields = {} + for key, value in fields.items(): + split_fields = key[len(uri):].split('/') + # Parse out the IAM role name (which is _not_ the same as the instance profile name) + if len(split_fields) == 3 and split_fields[0:2] == ['iam', 'security-credentials'] and ':' not in split_fields[2]: + new_fields[self._prefix % "iam-instance-profile-role"] = split_fields[2] + if len(split_fields) > 1 and split_fields[1]: + new_key = "-".join(split_fields) + new_fields[self._prefix % new_key] = value + else: + new_key = "".join(split_fields) + new_fields[self._prefix % new_key] = value + for pattern in filter_patterns: + for key in dict(new_fields): + match = re.search(pattern, key) + if match: + new_fields.pop(key) + return new_fields + + def fetch(self, uri, recurse=True): + raw_subfields = self._fetch(uri) + if not raw_subfields: + return + subfields = raw_subfields.split('\n') + for field in subfields: + if field.endswith('/') and recurse: + self.fetch(uri + field) + if uri.endswith('/'): + new_uri = uri + field + else: + new_uri = uri + '/' + field + if new_uri not in self._data and not new_uri.endswith('/'): + content = self._fetch(new_uri) + if field == 'security-groups' or field == 'security-group-ids': + sg_fields = ",".join(content.split('\n')) + self._data['%s' % (new_uri)] = sg_fields + else: + try: + dict = json.loads(content) + self._data['%s' % (new_uri)] = content + for (key, value) in dict.items(): + self._data['%s:%s' % (new_uri, key.lower())] = value + except Exception: + self._data['%s' % (new_uri)] = content # not a stringified JSON string + + def fix_invalid_varnames(self, data): + """Change ':'' and '-' to '_' to ensure valid template variable names""" + new_data = data.copy() + for key, value in data.items(): + if ':' in key or '-' in key: + newkey = re.sub(':|-', '_', key) + new_data[newkey] = value + del new_data[key] + + return new_data + + def run(self): + self.fetch(self.uri_meta) # populate _data with metadata + data = self._mangle_fields(self._data, self.uri_meta) + data[self._prefix % 'user-data'] = self._fetch(self.uri_user) + data[self._prefix % 'public-key'] = self._fetch(self.uri_ssh) + + self._data = {} # clear out metadata in _data + self.fetch(self.uri_dynamic) # populate _data with dynamic data + dyndata = self._mangle_fields(self._data, self.uri_dynamic) + data.update(dyndata) + data = self.fix_invalid_varnames(data) + + # Maintain old key for backwards compatibility + if 'ansible_ec2_instance_identity_document_region' in data: + data['ansible_ec2_placement_region'] = data['ansible_ec2_instance_identity_document_region'] + return data + + +def main(): + module = AnsibleModule( + argument_spec={}, + supports_check_mode=True, + ) + + ec2_metadata_facts = Ec2Metadata(module).run() + ec2_metadata_facts_result = dict(changed=False, ansible_facts=ec2_metadata_facts) + + module.exit_json(**ec2_metadata_facts_result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot.py new file mode 100644 index 00000000..cf4762dd --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot.py @@ -0,0 +1,322 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_snapshot +version_added: 1.0.0 +short_description: Creates a snapshot from an existing volume +description: + - Creates an EC2 snapshot from an existing EBS volume. +options: + volume_id: + description: + - Volume from which to take the snapshot. + required: false + type: str + description: + description: + - Description to be applied to the snapshot. + required: false + type: str + instance_id: + description: + - Instance that has the required volume to snapshot mounted. + required: false + type: str + device_name: + description: + - Device name of a mounted volume to be snapshotted. + required: false + type: str + snapshot_tags: + description: + - A dictionary of tags to add to the snapshot. + type: dict + required: false + wait: + description: + - Wait for the snapshot to be ready. + type: bool + required: false + default: yes + wait_timeout: + description: + - How long before wait gives up, in seconds. + - Specify 0 to wait forever. + required: false + default: 0 + type: int + state: + description: + - Whether to add or create a snapshot. + required: false + default: present + choices: ['absent', 'present'] + type: str + snapshot_id: + description: + - Snapshot id to remove. + required: false + type: str + last_snapshot_min_age: + description: + - If the volume's most recent snapshot has started less than `last_snapshot_min_age' minutes ago, a new snapshot will not be created. + required: false + default: 0 + type: int + +author: "Will Thames (@willthames)" +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Simple snapshot of volume using volume_id +- amazon.aws.ec2_snapshot: + volume_id: vol-abcdef12 + description: snapshot of /data from DB123 taken 2013/11/28 12:18:32 + +# Snapshot of volume mounted on device_name attached to instance_id +- amazon.aws.ec2_snapshot: + instance_id: i-12345678 + device_name: /dev/sdb1 + description: snapshot of /data from DB123 taken 2013/11/28 12:18:32 + +# Snapshot of volume with tagging +- amazon.aws.ec2_snapshot: + instance_id: i-12345678 + device_name: /dev/sdb1 + snapshot_tags: + frequency: hourly + source: /data + +# Remove a snapshot +- amazon.aws.ec2_snapshot: + snapshot_id: snap-abcd1234 + state: absent + +# Create a snapshot only if the most recent one is older than 1 hour +- amazon.aws.ec2_snapshot: + volume_id: vol-abcdef12 + last_snapshot_min_age: 60 +''' + +RETURN = ''' +snapshot_id: + description: The ID of the snapshot. Each snapshot receives a unique identifier when it is created. + type: str + returned: always + sample: snap-01234567 +tags: + description: Any tags assigned to the snapshot. + type: dict + returned: always + sample: "{ 'Name': 'instance-name' }" +volume_id: + description: The ID of the volume that was used to create the snapshot. + type: str + returned: always + sample: vol-01234567 +volume_size: + description: The size of the volume, in GiB. + type: int + returned: always + sample: 8 +''' + +import time +import datetime + +try: + import boto.exception +except ImportError: + pass # Taken care of by ec2.HAS_BOTO + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import HAS_BOTO +from ..module_utils.ec2 import ec2_connect + + +# Find the most recent snapshot +def _get_snapshot_starttime(snap): + return datetime.datetime.strptime(snap.start_time, '%Y-%m-%dT%H:%M:%S.%fZ') + + +def _get_most_recent_snapshot(snapshots, max_snapshot_age_secs=None, now=None): + """ + Gets the most recently created snapshot and optionally filters the result + if the snapshot is too old + :param snapshots: list of snapshots to search + :param max_snapshot_age_secs: filter the result if its older than this + :param now: simulate time -- used for unit testing + :return: + """ + if len(snapshots) == 0: + return None + + if not now: + now = datetime.datetime.utcnow() + + youngest_snapshot = max(snapshots, key=_get_snapshot_starttime) + + # See if the snapshot is younger that the given max age + snapshot_start = datetime.datetime.strptime(youngest_snapshot.start_time, '%Y-%m-%dT%H:%M:%S.%fZ') + snapshot_age = now - snapshot_start + + if max_snapshot_age_secs is not None: + if snapshot_age.total_seconds() > max_snapshot_age_secs: + return None + + return youngest_snapshot + + +def _create_with_wait(snapshot, wait_timeout_secs, sleep_func=time.sleep): + """ + Wait for the snapshot to be created + :param snapshot: + :param wait_timeout_secs: fail this step after this many seconds + :param sleep_func: + :return: + """ + time_waited = 0 + snapshot.update() + while snapshot.status != 'completed': + sleep_func(3) + snapshot.update() + time_waited += 3 + if wait_timeout_secs and time_waited > wait_timeout_secs: + return False + return True + + +def create_snapshot(module, ec2, state=None, description=None, wait=None, + wait_timeout=None, volume_id=None, instance_id=None, + snapshot_id=None, device_name=None, snapshot_tags=None, + last_snapshot_min_age=None): + snapshot = None + changed = False + + required = [volume_id, snapshot_id, instance_id] + if required.count(None) != len(required) - 1: # only 1 must be set + module.fail_json(msg='One and only one of volume_id or instance_id or snapshot_id must be specified') + if instance_id and not device_name or device_name and not instance_id: + module.fail_json(msg='Instance ID and device name must both be specified') + + if instance_id: + try: + volumes = ec2.get_all_volumes(filters={'attachment.instance-id': instance_id, 'attachment.device': device_name}) + except boto.exception.BotoServerError as e: + module.fail_json_aws(e) + + if not volumes: + module.fail_json(msg="Could not find volume with name %s attached to instance %s" % (device_name, instance_id)) + + volume_id = volumes[0].id + + if state == 'absent': + if not snapshot_id: + module.fail_json(msg='snapshot_id must be set when state is absent') + try: + ec2.delete_snapshot(snapshot_id) + except boto.exception.BotoServerError as e: + # exception is raised if snapshot does not exist + if e.error_code == 'InvalidSnapshot.NotFound': + module.exit_json(changed=False) + else: + module.fail_json_aws(e) + + # successful delete + module.exit_json(changed=True) + + if last_snapshot_min_age > 0: + try: + current_snapshots = ec2.get_all_snapshots(filters={'volume_id': volume_id}) + except boto.exception.BotoServerError as e: + module.fail_json_aws(e) + + last_snapshot_min_age = last_snapshot_min_age * 60 # Convert to seconds + snapshot = _get_most_recent_snapshot(current_snapshots, + max_snapshot_age_secs=last_snapshot_min_age) + try: + # Create a new snapshot if we didn't find an existing one to use + if snapshot is None: + snapshot = ec2.create_snapshot(volume_id, description=description) + changed = True + if wait: + if not _create_with_wait(snapshot, wait_timeout): + module.fail_json(msg='Timed out while creating snapshot.') + if snapshot_tags: + for k, v in snapshot_tags.items(): + snapshot.add_tag(k, v) + except boto.exception.BotoServerError as e: + module.fail_json_aws(e) + + module.exit_json(changed=changed, + snapshot_id=snapshot.id, + volume_id=snapshot.volume_id, + volume_size=snapshot.volume_size, + tags=snapshot.tags.copy()) + + +def create_snapshot_ansible_module(): + argument_spec = dict( + volume_id=dict(), + description=dict(), + instance_id=dict(), + snapshot_id=dict(), + device_name=dict(), + wait=dict(type='bool', default=True), + wait_timeout=dict(type='int', default=0), + last_snapshot_min_age=dict(type='int', default=0), + snapshot_tags=dict(type='dict', default=dict()), + state=dict(choices=['absent', 'present'], default='present'), + ) + module = AnsibleAWSModule(argument_spec=argument_spec, check_boto3=False) + return module + + +def main(): + module = create_snapshot_ansible_module() + + if not HAS_BOTO: + module.fail_json(msg='boto required for this module') + + volume_id = module.params.get('volume_id') + snapshot_id = module.params.get('snapshot_id') + description = module.params.get('description') + instance_id = module.params.get('instance_id') + device_name = module.params.get('device_name') + wait = module.params.get('wait') + wait_timeout = module.params.get('wait_timeout') + last_snapshot_min_age = module.params.get('last_snapshot_min_age') + snapshot_tags = module.params.get('snapshot_tags') + state = module.params.get('state') + + ec2 = ec2_connect(module) + + create_snapshot( + module=module, + state=state, + description=description, + wait=wait, + wait_timeout=wait_timeout, + ec2=ec2, + volume_id=volume_id, + instance_id=instance_id, + snapshot_id=snapshot_id, + device_name=device_name, + snapshot_tags=snapshot_tags, + last_snapshot_min_age=last_snapshot_min_age + ) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot_facts.py new file mode 100644 index 00000000..d2b29f04 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot_facts.py @@ -0,0 +1,248 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_snapshot_info +version_added: 1.0.0 +short_description: Gather information about ec2 volume snapshots in AWS +description: + - Gather information about ec2 volume snapshots in AWS. + - This module was called C(ec2_snapshot_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: "Rob White (@wimnat)" +options: + snapshot_ids: + description: + - If you specify one or more snapshot IDs, only snapshots that have the specified IDs are returned. + required: false + default: [] + type: list + elements: str + owner_ids: + description: + - If you specify one or more snapshot owners, only snapshots from the specified owners and for which you have + access are returned. + required: false + default: [] + type: list + elements: str + restorable_by_user_ids: + description: + - If you specify a list of restorable users, only snapshots with create snapshot permissions for those users are + returned. + required: false + default: [] + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See + U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSnapshots.html) for possible filters. Filter + names and values are case sensitive. + required: false + type: dict + default: {} +notes: + - By default, the module will return all snapshots, including public ones. To limit results to snapshots owned by + the account use the filter 'owner-id'. + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all snapshots, including public ones +- amazon.aws.ec2_snapshot_info: + +# Gather information about all snapshots owned by the account 0123456789 +- amazon.aws.ec2_snapshot_info: + filters: + owner-id: 0123456789 + +# Or alternatively... +- amazon.aws.ec2_snapshot_info: + owner_ids: + - 0123456789 + +# Gather information about a particular snapshot using ID +- amazon.aws.ec2_snapshot_info: + filters: + snapshot-id: snap-00112233 + +# Or alternatively... +- amazon.aws.ec2_snapshot_info: + snapshot_ids: + - snap-00112233 + +# Gather information about any snapshot with a tag key Name and value Example +- amazon.aws.ec2_snapshot_info: + filters: + "tag:Name": Example + +# Gather information about any snapshot with an error status +- amazon.aws.ec2_snapshot_info: + filters: + status: error + +''' + +RETURN = ''' +snapshot_id: + description: The ID of the snapshot. Each snapshot receives a unique identifier when it is created. + type: str + returned: always + sample: snap-01234567 +volume_id: + description: The ID of the volume that was used to create the snapshot. + type: str + returned: always + sample: vol-01234567 +state: + description: The snapshot state (completed, pending or error). + type: str + returned: always + sample: completed +state_message: + description: Encrypted Amazon EBS snapshots are copied asynchronously. If a snapshot copy operation fails (for example, if the proper + AWS Key Management Service (AWS KMS) permissions are not obtained) this field displays error state details to help you diagnose why the + error occurred. + type: str + returned: always + sample: +start_time: + description: The time stamp when the snapshot was initiated. + type: str + returned: always + sample: "2015-02-12T02:14:02+00:00" +progress: + description: The progress of the snapshot, as a percentage. + type: str + returned: always + sample: "100%" +owner_id: + description: The AWS account ID of the EBS snapshot owner. + type: str + returned: always + sample: "099720109477" +description: + description: The description for the snapshot. + type: str + returned: always + sample: "My important backup" +volume_size: + description: The size of the volume, in GiB. + type: int + returned: always + sample: 8 +owner_alias: + description: The AWS account alias (for example, amazon, self) or AWS account ID that owns the snapshot. + type: str + returned: always + sample: "033440102211" +tags: + description: Any tags assigned to the snapshot. + type: dict + returned: always + sample: "{ 'my_tag_key': 'my_tag_value' }" +encrypted: + description: Indicates whether the snapshot is encrypted. + type: bool + returned: always + sample: "True" +kms_key_id: + description: The full ARN of the AWS Key Management Service (AWS KMS) customer master key (CMK) that was used to \ + protect the volume encryption key for the parent volume. + type: str + returned: always + sample: "74c9742a-a1b2-45cb-b3fe-abcdef123456" +data_encryption_key_id: + description: The data encryption key identifier for the snapshot. This value is a unique identifier that \ + corresponds to the data encryption key that was used to encrypt the original volume or snapshot copy. + type: str + returned: always + sample: "arn:aws:kms:ap-southeast-2:012345678900:key/74c9742a-a1b2-45cb-b3fe-abcdef123456" + +''' + +try: + from botocore.exceptions import ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def list_ec2_snapshots(connection, module): + + snapshot_ids = module.params.get("snapshot_ids") + owner_ids = [str(owner_id) for owner_id in module.params.get("owner_ids")] + restorable_by_user_ids = [str(user_id) for user_id in module.params.get("restorable_by_user_ids")] + filters = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + + try: + snapshots = connection.describe_snapshots( + aws_retry=True, + SnapshotIds=snapshot_ids, OwnerIds=owner_ids, + RestorableByUserIds=restorable_by_user_ids, Filters=filters) + except is_boto3_error_code('InvalidSnapshot.NotFound') as e: + if len(snapshot_ids) > 1: + module.warn("Some of your snapshots may exist, but %s" % str(e)) + snapshots = {'Snapshots': []} + except ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Failed to describe snapshots') + + # Turn the boto3 result in to ansible_friendly_snaked_names + snaked_snapshots = [] + for snapshot in snapshots['Snapshots']: + snaked_snapshots.append(camel_dict_to_snake_dict(snapshot)) + + # Turn the boto3 result in to ansible friendly tag dictionary + for snapshot in snaked_snapshots: + if 'tags' in snapshot: + snapshot['tags'] = boto3_tag_list_to_ansible_dict(snapshot['tags'], 'key', 'value') + + module.exit_json(snapshots=snaked_snapshots) + + +def main(): + + argument_spec = dict( + snapshot_ids=dict(default=[], type='list', elements='str'), + owner_ids=dict(default=[], type='list', elements='str'), + restorable_by_user_ids=dict(default=[], type='list', elements='str'), + filters=dict(default={}, type='dict') + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ['snapshot_ids', 'owner_ids', 'restorable_by_user_ids', 'filters'] + ], + supports_check_mode=True + ) + if module._name == 'ec2_snapshot_facts': + module.deprecate("The 'ec2_snapshot_facts' module has been renamed to 'ec2_snapshot_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + list_ec2_snapshots(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot_info.py new file mode 100644 index 00000000..d2b29f04 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_snapshot_info.py @@ -0,0 +1,248 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_snapshot_info +version_added: 1.0.0 +short_description: Gather information about ec2 volume snapshots in AWS +description: + - Gather information about ec2 volume snapshots in AWS. + - This module was called C(ec2_snapshot_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: "Rob White (@wimnat)" +options: + snapshot_ids: + description: + - If you specify one or more snapshot IDs, only snapshots that have the specified IDs are returned. + required: false + default: [] + type: list + elements: str + owner_ids: + description: + - If you specify one or more snapshot owners, only snapshots from the specified owners and for which you have + access are returned. + required: false + default: [] + type: list + elements: str + restorable_by_user_ids: + description: + - If you specify a list of restorable users, only snapshots with create snapshot permissions for those users are + returned. + required: false + default: [] + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See + U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSnapshots.html) for possible filters. Filter + names and values are case sensitive. + required: false + type: dict + default: {} +notes: + - By default, the module will return all snapshots, including public ones. To limit results to snapshots owned by + the account use the filter 'owner-id'. + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all snapshots, including public ones +- amazon.aws.ec2_snapshot_info: + +# Gather information about all snapshots owned by the account 0123456789 +- amazon.aws.ec2_snapshot_info: + filters: + owner-id: 0123456789 + +# Or alternatively... +- amazon.aws.ec2_snapshot_info: + owner_ids: + - 0123456789 + +# Gather information about a particular snapshot using ID +- amazon.aws.ec2_snapshot_info: + filters: + snapshot-id: snap-00112233 + +# Or alternatively... +- amazon.aws.ec2_snapshot_info: + snapshot_ids: + - snap-00112233 + +# Gather information about any snapshot with a tag key Name and value Example +- amazon.aws.ec2_snapshot_info: + filters: + "tag:Name": Example + +# Gather information about any snapshot with an error status +- amazon.aws.ec2_snapshot_info: + filters: + status: error + +''' + +RETURN = ''' +snapshot_id: + description: The ID of the snapshot. Each snapshot receives a unique identifier when it is created. + type: str + returned: always + sample: snap-01234567 +volume_id: + description: The ID of the volume that was used to create the snapshot. + type: str + returned: always + sample: vol-01234567 +state: + description: The snapshot state (completed, pending or error). + type: str + returned: always + sample: completed +state_message: + description: Encrypted Amazon EBS snapshots are copied asynchronously. If a snapshot copy operation fails (for example, if the proper + AWS Key Management Service (AWS KMS) permissions are not obtained) this field displays error state details to help you diagnose why the + error occurred. + type: str + returned: always + sample: +start_time: + description: The time stamp when the snapshot was initiated. + type: str + returned: always + sample: "2015-02-12T02:14:02+00:00" +progress: + description: The progress of the snapshot, as a percentage. + type: str + returned: always + sample: "100%" +owner_id: + description: The AWS account ID of the EBS snapshot owner. + type: str + returned: always + sample: "099720109477" +description: + description: The description for the snapshot. + type: str + returned: always + sample: "My important backup" +volume_size: + description: The size of the volume, in GiB. + type: int + returned: always + sample: 8 +owner_alias: + description: The AWS account alias (for example, amazon, self) or AWS account ID that owns the snapshot. + type: str + returned: always + sample: "033440102211" +tags: + description: Any tags assigned to the snapshot. + type: dict + returned: always + sample: "{ 'my_tag_key': 'my_tag_value' }" +encrypted: + description: Indicates whether the snapshot is encrypted. + type: bool + returned: always + sample: "True" +kms_key_id: + description: The full ARN of the AWS Key Management Service (AWS KMS) customer master key (CMK) that was used to \ + protect the volume encryption key for the parent volume. + type: str + returned: always + sample: "74c9742a-a1b2-45cb-b3fe-abcdef123456" +data_encryption_key_id: + description: The data encryption key identifier for the snapshot. This value is a unique identifier that \ + corresponds to the data encryption key that was used to encrypt the original volume or snapshot copy. + type: str + returned: always + sample: "arn:aws:kms:ap-southeast-2:012345678900:key/74c9742a-a1b2-45cb-b3fe-abcdef123456" + +''' + +try: + from botocore.exceptions import ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def list_ec2_snapshots(connection, module): + + snapshot_ids = module.params.get("snapshot_ids") + owner_ids = [str(owner_id) for owner_id in module.params.get("owner_ids")] + restorable_by_user_ids = [str(user_id) for user_id in module.params.get("restorable_by_user_ids")] + filters = ansible_dict_to_boto3_filter_list(module.params.get("filters")) + + try: + snapshots = connection.describe_snapshots( + aws_retry=True, + SnapshotIds=snapshot_ids, OwnerIds=owner_ids, + RestorableByUserIds=restorable_by_user_ids, Filters=filters) + except is_boto3_error_code('InvalidSnapshot.NotFound') as e: + if len(snapshot_ids) > 1: + module.warn("Some of your snapshots may exist, but %s" % str(e)) + snapshots = {'Snapshots': []} + except ClientError as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Failed to describe snapshots') + + # Turn the boto3 result in to ansible_friendly_snaked_names + snaked_snapshots = [] + for snapshot in snapshots['Snapshots']: + snaked_snapshots.append(camel_dict_to_snake_dict(snapshot)) + + # Turn the boto3 result in to ansible friendly tag dictionary + for snapshot in snaked_snapshots: + if 'tags' in snapshot: + snapshot['tags'] = boto3_tag_list_to_ansible_dict(snapshot['tags'], 'key', 'value') + + module.exit_json(snapshots=snaked_snapshots) + + +def main(): + + argument_spec = dict( + snapshot_ids=dict(default=[], type='list', elements='str'), + owner_ids=dict(default=[], type='list', elements='str'), + restorable_by_user_ids=dict(default=[], type='list', elements='str'), + filters=dict(default={}, type='dict') + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ['snapshot_ids', 'owner_ids', 'restorable_by_user_ids', 'filters'] + ], + supports_check_mode=True + ) + if module._name == 'ec2_snapshot_facts': + module.deprecate("The 'ec2_snapshot_facts' module has been renamed to 'ec2_snapshot_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + list_ec2_snapshots(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_tag.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_tag.py new file mode 100644 index 00000000..1d8a1e6f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_tag.py @@ -0,0 +1,200 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_tag +version_added: 1.0.0 +short_description: create and remove tags on ec2 resources +description: + - Creates, modifies and removes tags for any EC2 resource. + - Resources are referenced by their resource id (for example, an instance being i-XXXXXXX, a VPC being vpc-XXXXXXX). + - This module is designed to be used with complex args (tags), see the examples. +requirements: [ "boto3", "botocore" ] +options: + resource: + description: + - The EC2 resource id. + required: true + type: str + state: + description: + - Whether the tags should be present or absent on the resource. + - The use of I(state=list) to interrogate the tags of an instance has been + deprecated and will be removed after 2022-06-01. The 'list' + functionality has been moved to a dedicated module M(amazon.aws.ec2_tag_info). + default: present + choices: ['present', 'absent', 'list'] + type: str + tags: + description: + - A dictionary of tags to add or remove from the resource. + - If the value provided for a key is not set and I(state=absent), the tag will be removed regardless of its current value. + - Required when I(state=present) or I(state=absent). + type: dict + purge_tags: + description: + - Whether unspecified tags should be removed from the resource. + - Note that when combined with I(state=absent), specified tags with non-matching values are not purged. + type: bool + default: false + +author: + - Lester Wade (@lwade) + - Paul Arthur (@flowerysong) +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +- name: Ensure tags are present on a resource + amazon.aws.ec2_tag: + region: eu-west-1 + resource: vol-XXXXXX + state: present + tags: + Name: ubervol + env: prod + +- name: Ensure all volumes are tagged + amazon.aws.ec2_tag: + region: eu-west-1 + resource: '{{ item.id }}' + state: present + tags: + Name: dbserver + Env: production + loop: '{{ ec2_vol.volumes }}' + +- name: Remove the Env tag + amazon.aws.ec2_tag: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + tags: + Env: + state: absent + +- name: Remove the Env tag if it's currently 'development' + amazon.aws.ec2_tag: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + tags: + Env: development + state: absent + +- name: Remove all tags except for Name from an instance + amazon.aws.ec2_tag: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + tags: + Name: '' + state: absent + purge_tags: true +''' + +RETURN = ''' +tags: + description: A dict containing the tags on the resource + returned: always + type: dict +added_tags: + description: A dict of tags that were added to the resource + returned: If tags were added + type: dict +removed_tags: + description: A dict of tags that were removed from the resource + returned: If tags were removed + type: dict +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_aws_tags + + +def get_tags(ec2, module, resource): + filters = [{'Name': 'resource-id', 'Values': [resource]}] + try: + result = AWSRetry.jittered_backoff()(ec2.describe_tags)(Filters=filters) + return boto3_tag_list_to_ansible_dict(result['Tags']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to fetch tags for resource {0}'.format(resource)) + + +def main(): + argument_spec = dict( + resource=dict(required=True), + tags=dict(type='dict'), + purge_tags=dict(type='bool', default=False), + state=dict(default='present', choices=['present', 'absent', 'list']), + ) + required_if = [('state', 'present', ['tags']), ('state', 'absent', ['tags'])] + + module = AnsibleAWSModule(argument_spec=argument_spec, required_if=required_if, supports_check_mode=True) + + resource = module.params['resource'] + tags = module.params['tags'] + state = module.params['state'] + purge_tags = module.params['purge_tags'] + + result = {'changed': False} + + ec2 = module.client('ec2') + + current_tags = get_tags(ec2, module, resource) + + if state == 'list': + module.deprecate( + 'Using the "list" state has been deprecated. Please use the ec2_tag_info module instead', date='2022-06-01', collection_name='amazon.aws') + module.exit_json(changed=False, tags=current_tags) + + add_tags, remove = compare_aws_tags(current_tags, tags, purge_tags=purge_tags) + + remove_tags = {} + if state == 'absent': + for key in tags: + if key in current_tags and (tags[key] is None or current_tags[key] == tags[key]): + remove_tags[key] = current_tags[key] + + for key in remove: + remove_tags[key] = current_tags[key] + + if remove_tags: + result['changed'] = True + result['removed_tags'] = remove_tags + if not module.check_mode: + try: + AWSRetry.jittered_backoff()(ec2.delete_tags)(Resources=[resource], Tags=ansible_dict_to_boto3_tag_list(remove_tags)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to remove tags {0} from resource {1}'.format(remove_tags, resource)) + + if state == 'present' and add_tags: + result['changed'] = True + result['added_tags'] = add_tags + current_tags.update(add_tags) + if not module.check_mode: + try: + AWSRetry.jittered_backoff()(ec2.create_tags)(Resources=[resource], Tags=ansible_dict_to_boto3_tag_list(add_tags)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to set tags {0} on resource {1}'.format(add_tags, resource)) + + result['tags'] = get_tags(ec2, module, resource) + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_tag_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_tag_info.py new file mode 100644 index 00000000..947ce363 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_tag_info.py @@ -0,0 +1,88 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_tag_info +version_added: 1.0.0 +short_description: list tags on ec2 resources +description: + - Lists tags for any EC2 resource. + - Resources are referenced by their resource id (e.g. an instance being i-XXXXXXX, a vpc being vpc-XXXXXX). + - Resource tags can be managed using the M(amazon.aws.ec2_tag) module. +requirements: [ "boto3", "botocore" ] +options: + resource: + description: + - The EC2 resource id (for example i-XXXXXX or vpc-XXXXXX). + required: true + type: str + +author: + - Mark Chappell (@tremble) +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +- name: Retrieve all tags on an instance + amazon.aws.ec2_tag_info: + region: eu-west-1 + resource: i-xxxxxxxxxxxxxxxxx + register: instance_tags + +- name: Retrieve all tags on a VPC + amazon.aws.ec2_tag_info: + region: eu-west-1 + resource: vpc-xxxxxxxxxxxxxxxxx + register: vpc_tags +''' + +RETURN = ''' +tags: + description: A dict containing the tags on the resource + returned: always + type: dict +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except Exception: + pass # Handled by AnsibleAWSModule + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict, AWSRetry + + +@AWSRetry.jittered_backoff() +def get_tags(ec2, module, resource): + filters = [{'Name': 'resource-id', 'Values': [resource]}] + return boto3_tag_list_to_ansible_dict(ec2.describe_tags(Filters=filters)['Tags']) + + +def main(): + argument_spec = dict( + resource=dict(required=True), + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + resource = module.params['resource'] + ec2 = module.client('ec2') + + try: + current_tags = get_tags(ec2, module, resource) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to fetch tags for resource {0}'.format(resource)) + + module.exit_json(changed=False, tags=current_tags) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol.py new file mode 100644 index 00000000..fb85a85d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol.py @@ -0,0 +1,809 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vol +version_added: 1.0.0 +short_description: Create and attach a volume, return volume id and device map +description: + - Creates an EBS volume and optionally attaches it to an instance. + - If both I(instance) and I(name) are given and the instance has a device at the device name, then no volume is created and no attachment is made. + - This module has a dependency on python-boto. +options: + instance: + description: + - Instance ID if you wish to attach the volume. Since 1.9 you can set to None to detach. + type: str + name: + description: + - Volume Name tag if you wish to attach an existing volume (requires instance) + type: str + id: + description: + - Volume id if you wish to attach an existing volume (requires instance) or remove an existing volume + type: str + volume_size: + description: + - Size of volume (in GiB) to create. + type: int + volume_type: + description: + - Type of EBS volume; standard (magnetic), gp2 (SSD), gp3 (SSD), io1 (Provisioned IOPS), io2 (Provisioned IOPS), + st1 (Throughput Optimized HDD), sc1 (Cold HDD). + "Standard" is the old EBS default and continues to remain the Ansible default for backwards compatibility. + default: standard + choices: ['standard', 'gp2', 'io1', 'st1', 'sc1', 'gp3', 'io2'] + type: str + iops: + description: + - The provisioned IOPs you want to associate with this volume (integer). + - By default AWS will set this to 100. + type: int + encrypted: + description: + - Enable encryption at rest for this volume. + default: false + type: bool + kms_key_id: + description: + - Specify the id of the KMS key to use. + type: str + device_name: + description: + - Device id to override device mapping. Assumes /dev/sdf for Linux/UNIX and /dev/xvdf for Windows. + type: str + delete_on_termination: + description: + - When set to C(true), the volume will be deleted upon instance termination. + type: bool + default: false + zone: + description: + - Zone in which to create the volume, if unset uses the zone the instance is in (if set). + aliases: ['availability_zone', 'aws_zone', 'ec2_zone'] + type: str + snapshot: + description: + - Snapshot ID on which to base the volume. + type: str + validate_certs: + description: + - When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0. + type: bool + default: true + state: + description: + - Whether to ensure the volume is present or absent. + - The use of I(state=list) to interrogate the volume has been deprecated + and will be removed after 2022-06-01. The 'list' functionality + has been moved to a dedicated module M(amazon.aws.ec2_vol_info). + default: present + choices: ['absent', 'present', 'list'] + type: str + tags: + description: + - tag:value pairs to add to the volume after creation. + default: {} + type: dict + modify_volume: + description: + - The volume won't be modify unless this key is C(true). + type: bool + default: false + version_added: 1.4.0 + throughput: + description: + - Volume throughput in MB/s. + - This parameter is only valid for gp3 volumes. + - Valid range is from 125 to 1000. + type: int + version_added: 1.4.0 +author: "Lester Wade (@lwade)" +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +requirements: [ boto3>=1.16.33 ] +''' + +EXAMPLES = ''' +# Simple attachment action +- amazon.aws.ec2_vol: + instance: XXXXXX + volume_size: 5 + device_name: sdd + region: us-west-2 + +# Example using custom iops params +- amazon.aws.ec2_vol: + instance: XXXXXX + volume_size: 5 + iops: 100 + device_name: sdd + region: us-west-2 + +# Example using snapshot id +- amazon.aws.ec2_vol: + instance: XXXXXX + snapshot: "{{ snapshot }}" + +# Playbook example combined with instance launch +- amazon.aws.ec2: + keypair: "{{ keypair }}" + image: "{{ image }}" + wait: yes + count: 3 + register: ec2 +- amazon.aws.ec2_vol: + instance: "{{ item.id }}" + volume_size: 5 + loop: "{{ ec2.instances }}" + register: ec2_vol + +# Example: Launch an instance and then add a volume if not already attached +# * Volume will be created with the given name if not already created. +# * Nothing will happen if the volume is already attached. +# * Requires Ansible 2.0 + +- amazon.aws.ec2: + keypair: "{{ keypair }}" + image: "{{ image }}" + zone: YYYYYY + id: my_instance + wait: yes + count: 1 + register: ec2 + +- amazon.aws.ec2_vol: + instance: "{{ item.id }}" + name: my_existing_volume_Name_tag + device_name: /dev/xvdf + loop: "{{ ec2.instances }}" + register: ec2_vol + +# Remove a volume +- amazon.aws.ec2_vol: + id: vol-XXXXXXXX + state: absent + +# Detach a volume (since 1.9) +- amazon.aws.ec2_vol: + id: vol-XXXXXXXX + instance: None + region: us-west-2 + +# List volumes for an instance +- amazon.aws.ec2_vol: + instance: i-XXXXXX + state: list + region: us-west-2 + +# Create new volume using SSD storage +- amazon.aws.ec2_vol: + instance: XXXXXX + volume_size: 50 + volume_type: gp2 + device_name: /dev/xvdf + +# Attach an existing volume to instance. The volume will be deleted upon instance termination. +- amazon.aws.ec2_vol: + instance: XXXXXX + id: XXXXXX + device_name: /dev/sdf + delete_on_termination: yes +''' + +RETURN = ''' +device: + description: device name of attached volume + returned: when success + type: str + sample: "/def/sdf" +volume_id: + description: the id of volume + returned: when success + type: str + sample: "vol-35b333d9" +volume_type: + description: the volume type + returned: when success + type: str + sample: "standard" +volume: + description: a dictionary containing detailed attributes of the volume + returned: when success + type: str + sample: { + "attachment_set": { + "attach_time": "2015-10-23T00:22:29.000Z", + "deleteOnTermination": "false", + "device": "/dev/sdf", + "instance_id": "i-8356263c", + "status": "attached" + }, + "create_time": "2015-10-21T14:36:08.870Z", + "encrypted": false, + "id": "vol-35b333d9", + "iops": null, + "size": 1, + "snapshot_id": "", + "status": "in-use", + "tags": { + "env": "dev" + }, + "type": "standard", + "zone": "us-east-1b" + } +''' + +import time + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import camel_dict_to_snake_dict +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import compare_aws_tags +from ..module_utils.ec2 import AWSRetry +from ..module_utils.core import is_boto3_error_code + +try: + import botocore +except ImportError: + pass # Taken care of by AnsibleAWSModule + + +def get_instance(module, ec2_conn, instance_id=None): + instance = None + if not instance_id: + return instance + + try: + reservation_response = ec2_conn.describe_instances(aws_retry=True, InstanceIds=[instance_id]) + instance = camel_dict_to_snake_dict(reservation_response['Reservations'][0]['Instances'][0]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Error while getting instance_id with id {0}'.format(instance)) + + return instance + + +def get_volume(module, ec2_conn, vol_id=None, fail_on_not_found=True): + name = module.params.get('name') + param_id = module.params.get('id') + zone = module.params.get('zone') + + if not vol_id: + vol_id = param_id + + # If no name or id supplied, just try volume creation based on module parameters + if vol_id is None and name is None: + return None + + find_params = dict() + vols = [] + + if vol_id: + find_params['VolumeIds'] = [vol_id] + elif name: + find_params['Filters'] = ansible_dict_to_boto3_filter_list({'tag:Name': name}) + elif zone: + find_params['Filters'] = ansible_dict_to_boto3_filter_list({'availability-zone': zone}) + + try: + paginator = ec2_conn.get_paginator('describe_volumes') + vols_response = paginator.paginate(**find_params) + vols = list(vols_response)[0].get('Volumes') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + if is_boto3_error_code('InvalidVolume.NotFound'): + module.exit_json(msg="Volume {0} does not exist".format(vol_id), changed=False) + module.fail_json_aws(e, msg='Error while getting EBS volumes with the parameters {0}'.format(find_params)) + + if not vols: + if fail_on_not_found and vol_id: + msg = "Could not find volume with id: {0}".format(vol_id) + if name: + msg += (" and name: {0}".format(name)) + module.fail_json(msg=msg) + else: + return None + + if len(vols) > 1: + module.fail_json( + msg="Found more than one volume in zone (if specified) with name: {0}".format(name), + found=[v['VolumeId'] for v in vols] + ) + vol = camel_dict_to_snake_dict(vols[0]) + return vol + + +def get_volumes(module, ec2_conn): + instance = module.params.get('instance') + + find_params = dict() + if instance: + find_params['Filters'] = ansible_dict_to_boto3_filter_list({'attachment.instance-id': instance}) + + vols = [] + try: + vols_response = ec2_conn.describe_volumes(aws_retry=True, **find_params) + vols = [camel_dict_to_snake_dict(vol) for vol in vols_response.get('Volumes', [])] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Error while getting EBS volumes') + return vols + + +def delete_volume(module, ec2_conn, volume_id=None): + changed = False + if volume_id: + try: + ec2_conn.delete_volume(aws_retry=True, VolumeId=volume_id) + changed = True + except is_boto3_error_code('InvalidVolume.NotFound'): + module.exit_json(changed=False) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Error while deleting volume') + return changed + + +def update_volume(module, ec2_conn, volume): + changed = False + req_obj = {'VolumeId': volume['volume_id']} + + if module.params.get('modify_volume'): + iops_changed = False + if volume['volume_type'] != 'standard': + target_iops = module.params.get('iops') + if target_iops: + original_iops = volume['iops'] + if target_iops != original_iops: + iops_changed = True + req_obj['iops'] = target_iops + + target_size = module.params.get('volume_size') + size_changed = False + if target_size: + original_size = volume['size'] + if target_size != original_size: + size_changed = True + req_obj['size'] = target_size + + target_type = module.params.get('volume_type') + original_type = None + type_changed = False + if target_type: + original_type = volume['volume_type'] + if target_type != original_type: + type_changed = True + req_obj['VolumeType'] = target_type + + target_throughput = module.params.get('throughput') + throughput_changed = False + if 'gp3' in [target_type, original_type]: + if target_throughput: + original_throughput = volume.get('throughput') + if target_throughput != original_throughput: + throughput_changed = True + req_obj['Throughput'] = target_throughput + + changed = iops_changed or size_changed or type_changed or throughput_changed + + if changed: + response = ec2_conn.modify_volume(**req_obj) + + volume['size'] = response.get('VolumeModification').get('TargetSize') + volume['volume_type'] = response.get('VolumeModification').get('TargetVolumeType') + volume['iops'] = response.get('VolumeModification').get('TargetIops') + volume['throughput'] = response.get('VolumeModification').get('TargetThroughput') + + return volume, changed + + +def create_volume(module, ec2_conn, zone): + changed = False + iops = module.params.get('iops') + encrypted = module.params.get('encrypted') + kms_key_id = module.params.get('kms_key_id') + volume_size = module.params.get('volume_size') + volume_type = module.params.get('volume_type') + snapshot = module.params.get('snapshot') + throughput = module.params.get('throughput') + # If custom iops is defined we use volume_type "io1" rather than the default of "standard" + if iops: + volume_type = 'io1' + + volume = get_volume(module, ec2_conn) + + if volume is None: + + try: + changed = True + additional_params = dict() + + if volume_size: + additional_params['Size'] = int(volume_size) + + if kms_key_id: + additional_params['KmsKeyId'] = kms_key_id + + if snapshot: + additional_params['SnapshotId'] = snapshot + + if iops: + additional_params['Iops'] = int(iops) + + if throughput: + additional_params['Throughput'] = int(throughput) + + create_vol_response = ec2_conn.create_volume( + aws_retry=True, + AvailabilityZone=zone, + Encrypted=encrypted, + VolumeType=volume_type, + **additional_params + ) + + waiter = ec2_conn.get_waiter('volume_available') + waiter.wait( + VolumeIds=[create_vol_response['VolumeId']], + ) + volume = get_volume(module, ec2_conn, vol_id=create_vol_response['VolumeId']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Error while creating EBS volume') + + return volume, changed + + +def attach_volume(module, ec2_conn, volume_dict, instance_dict, device_name): + changed = False + + # If device_name isn't set, make a choice based on best practices here: + # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html + + # In future this needs to be more dynamic but combining block device mapping best practices + # (bounds for devices, as above) with instance.block_device_mapping data would be tricky. For me ;) + + attachment_data = get_attachment_data(volume_dict, wanted_state='attached') + if attachment_data: + if attachment_data.get('instance_id', None) != instance_dict['instance_id']: + module.fail_json(msg="Volume {0} is already attached to another instance: {1}".format(volume_dict['volume_id'], + attachment_data.get('instance_id', None))) + else: + return volume_dict, changed + + try: + attach_response = ec2_conn.attach_volume(aws_retry=True, Device=device_name, + InstanceId=instance_dict['instance_id'], + VolumeId=volume_dict['volume_id']) + + waiter = ec2_conn.get_waiter('volume_in_use') + waiter.wait(VolumeIds=[attach_response['VolumeId']]) + changed = True + + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Error while attaching EBS volume') + + modify_dot_attribute(module, ec2_conn, instance_dict, device_name) + + volume = get_volume(module, ec2_conn, vol_id=volume_dict['volume_id']) + return volume, changed + + +def modify_dot_attribute(module, ec2_conn, instance_dict, device_name): + """ Modify delete_on_termination attribute """ + + delete_on_termination = module.params.get('delete_on_termination') + changed = False + + # volume_in_use can return *shortly* before it appears on the instance + # description + mapped_block_device = None + _attempt = 0 + while mapped_block_device is None: + _attempt += 1 + instance_dict = get_instance(module, ec2_conn=ec2_conn, instance_id=instance_dict['instance_id']) + mapped_block_device = get_mapped_block_device(instance_dict=instance_dict, device_name=device_name) + if mapped_block_device is None: + if _attempt > 2: + module.fail_json(msg='Unable to find device on instance', + device=device_name, instance=instance_dict) + time.sleep(1) + + if delete_on_termination != mapped_block_device['ebs'].get('delete_on_termination'): + try: + ec2_conn.modify_instance_attribute( + aws_retry=True, + InstanceId=instance_dict['instance_id'], + BlockDeviceMappings=[{ + "DeviceName": device_name, + "Ebs": { + "DeleteOnTermination": delete_on_termination + } + }] + ) + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, + msg='Error while modifying Block Device Mapping of instance {0}'.format(instance_dict['instance_id'])) + + return changed + + +def get_attachment_data(volume_dict, wanted_state=None): + changed = False + + attachment_data = {} + if not volume_dict: + return attachment_data + for data in volume_dict.get('attachments', []): + if wanted_state and wanted_state == data['state']: + attachment_data = data + break + else: + # No filter, return first + attachment_data = data + break + + return attachment_data + + +def detach_volume(module, ec2_conn, volume_dict): + changed = False + + attachment_data = get_attachment_data(volume_dict, wanted_state='attached') + if attachment_data: + ec2_conn.detach_volume(aws_retry=True, VolumeId=volume_dict['volume_id']) + waiter = ec2_conn.get_waiter('volume_available') + waiter.wait( + VolumeIds=[volume_dict['volume_id']], + ) + changed = True + + volume_dict = get_volume(module, ec2_conn, vol_id=volume_dict['volume_id']) + return volume_dict, changed + + +def get_volume_info(volume, tags=None): + if not tags: + tags = boto3_tag_list_to_ansible_dict(volume.get('tags')) + attachment_data = get_attachment_data(volume) + volume_info = { + 'create_time': volume.get('create_time'), + 'encrypted': volume.get('encrypted'), + 'id': volume.get('volume_id'), + 'iops': volume.get('iops'), + 'size': volume.get('size'), + 'snapshot_id': volume.get('snapshot_id'), + 'status': volume.get('state'), + 'type': volume.get('volume_type'), + 'zone': volume.get('availability_zone'), + 'throughput': volume.get('throughput'), + 'attachment_set': { + 'attach_time': attachment_data.get('attach_time', None), + 'device': attachment_data.get('device', None), + 'instance_id': attachment_data.get('instance_id', None), + 'status': attachment_data.get('state', None), + 'deleteOnTermination': attachment_data.get('delete_on_termination', None) + }, + 'tags': tags + } + + return volume_info + + +def get_mapped_block_device(instance_dict=None, device_name=None): + mapped_block_device = None + if not instance_dict: + return mapped_block_device + if not device_name: + return mapped_block_device + + for device in instance_dict.get('block_device_mappings', []): + if device['device_name'] == device_name: + mapped_block_device = device + break + + return mapped_block_device + + +def ensure_tags(module, connection, res_id, res_type, tags, add_only): + changed = False + + filters = ansible_dict_to_boto3_filter_list({'resource-id': res_id, 'resource-type': res_type}) + cur_tags = None + try: + cur_tags = connection.describe_tags(aws_retry=True, Filters=filters) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't describe tags") + + purge_tags = bool(not add_only) + to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags) + final_tags = boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')) + + if to_update: + try: + if module.check_mode: + # update tags + final_tags.update(to_update) + else: + connection.create_tags( + aws_retry=True, + Resources=[res_id], + Tags=ansible_dict_to_boto3_tag_list(to_update) + ) + + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't create tags") + + if to_delete: + try: + if module.check_mode: + # update tags + for key in to_delete: + del final_tags[key] + else: + tags_list = [] + for key in to_delete: + tags_list.append({'Key': key}) + + connection.delete_tags(aws_retry=True, Resources=[res_id], Tags=tags_list) + + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete tags") + + if not module.check_mode and (to_update or to_delete): + try: + response = connection.describe_tags(aws_retry=True, Filters=filters) + final_tags = boto3_tag_list_to_ansible_dict(response.get('Tags')) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't describe tags") + + return final_tags, changed + + +def main(): + argument_spec = dict( + instance=dict(), + id=dict(), + name=dict(), + volume_size=dict(type='int'), + volume_type=dict(default='standard', choices=['standard', 'gp2', 'io1', 'st1', 'sc1', 'gp3', 'io2']), + iops=dict(type='int'), + encrypted=dict(default=False, type='bool'), + kms_key_id=dict(), + device_name=dict(), + delete_on_termination=dict(default=False, type='bool'), + zone=dict(aliases=['availability_zone', 'aws_zone', 'ec2_zone']), + snapshot=dict(), + state=dict(default='present', choices=['absent', 'present', 'list']), + tags=dict(default={}, type='dict'), + modify_volume=dict(default=False, type='bool'), + throughput=dict(type='int') + ) + + module = AnsibleAWSModule(argument_spec=argument_spec) + + param_id = module.params.get('id') + name = module.params.get('name') + instance = module.params.get('instance') + volume_size = module.params.get('volume_size') + device_name = module.params.get('device_name') + zone = module.params.get('zone') + snapshot = module.params.get('snapshot') + state = module.params.get('state') + tags = module.params.get('tags') + + if state == 'list': + module.deprecate( + 'Using the "list" state has been deprecated. Please use the ec2_vol_info module instead', date='2022-06-01', collection_name='amazon.aws') + + # Ensure we have the zone or can get the zone + if instance is None and zone is None and state == 'present': + module.fail_json(msg="You must specify either instance or zone") + + # Set volume detach flag + if instance == 'None' or instance == '': + instance = None + detach_vol_flag = True + else: + detach_vol_flag = False + + # Set changed flag + changed = False + + ec2_conn = module.client('ec2', AWSRetry.jittered_backoff()) + + if state == 'list': + returned_volumes = [] + vols = get_volumes(module, ec2_conn) + + for v in vols: + returned_volumes.append(get_volume_info(v)) + + module.exit_json(changed=False, volumes=returned_volumes) + + # Here we need to get the zone info for the instance. This covers situation where + # instance is specified but zone isn't. + # Useful for playbooks chaining instance launch with volume create + attach and where the + # zone doesn't matter to the user. + inst = None + + # Delaying the checks until after the instance check allows us to get volume ids for existing volumes + # without needing to pass an unused volume_size + if not volume_size and not (param_id or name or snapshot): + module.fail_json(msg="You must specify volume_size or identify an existing volume by id, name, or snapshot") + + # Try getting volume + volume = get_volume(module, ec2_conn, fail_on_not_found=False) + if state == 'present': + if instance: + inst = get_instance(module, ec2_conn, instance_id=instance) + zone = inst['placement']['availability_zone'] + + # Use password data attribute to tell whether the instance is Windows or Linux + if device_name is None: + if inst['platform'] == 'Windows': + device_name = '/dev/xvdf' + else: + device_name = '/dev/sdf' + + # Check if there is a volume already mounted there. + mapped_device = get_mapped_block_device(instance_dict=inst, device_name=device_name) + if mapped_device: + other_volume_mapped = False + + if volume: + if volume['volume_id'] != mapped_device['ebs']['volume_id']: + other_volume_mapped = True + else: + # No volume found so this is another volume + other_volume_mapped = True + + if other_volume_mapped: + module.exit_json( + msg="Volume mapping for {0} already exists on instance {1}".format(device_name, instance), + volume_id=mapped_device['ebs']['volume_id'], + found_volume=volume, + device=device_name, + changed=False + ) + + attach_state_changed = False + + if volume: + volume, changed = update_volume(module, ec2_conn, volume) + else: + volume, changed = create_volume(module, ec2_conn, zone=zone) + + tags['Name'] = name + final_tags, tags_changed = ensure_tags(module, ec2_conn, volume['volume_id'], 'volume', tags, False) + + if detach_vol_flag: + volume, changed = detach_volume(module, ec2_conn, volume_dict=volume) + elif inst is not None: + volume, changed = attach_volume(module, ec2_conn, volume_dict=volume, instance_dict=inst, device_name=device_name) + + # Add device, volume_id and volume_type parameters separately to maintain backward compatibility + volume_info = get_volume_info(volume, tags=final_tags) + + module.exit_json(changed=changed, volume=volume_info, device=volume_info['attachment_set']['device'], + volume_id=volume_info['id'], volume_type=volume_info['type']) + elif state == 'absent': + if not name and not param_id: + module.fail_json('A volume name or id is required for deletion') + if volume: + detach_volume(module, ec2_conn, volume_dict=volume) + changed = delete_volume(module, ec2_conn, volume_id=volume['volume_id']) + module.exit_json(changed=changed) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol_facts.py new file mode 100644 index 00000000..fb6a6587 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol_facts.py @@ -0,0 +1,195 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vol_info +version_added: 1.0.0 +short_description: Gather information about ec2 volumes in AWS +description: + - Gather information about ec2 volumes in AWS. + - This module was called C(ec2_vol_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: "Rob White (@wimnat)" +options: + filters: + type: dict + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVolumes.html) for possible filters. +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all volumes +- amazon.aws.ec2_vol_info: + +# Gather information about a particular volume using volume ID +- amazon.aws.ec2_vol_info: + filters: + volume-id: vol-00112233 + +# Gather information about any volume with a tag key Name and value Example +- amazon.aws.ec2_vol_info: + filters: + "tag:Name": Example + +# Gather information about any volume that is attached +- amazon.aws.ec2_vol_info: + filters: + attachment.status: attached + +''' + +RETURN = ''' +volumes: + description: Volumes that match the provided filters. Each element consists of a dict with all the information related to that volume. + type: list + elements: dict + returned: always + contains: + attachment_set: + description: Information about the volume attachments. + type: dict + sample: { + "attach_time": "2015-10-23T00:22:29.000Z", + "deleteOnTermination": "false", + "device": "/dev/sdf", + "instance_id": "i-8356263c", + "status": "attached" + } + create_time: + description: The time stamp when volume creation was initiated. + type: str + sample: "2015-10-21T14:36:08.870Z" + encrypted: + description: Indicates whether the volume is encrypted. + type: bool + sample: False + id: + description: The ID of the volume. + type: str + sample: "vol-35b333d9" + iops: + description: The number of I/O operations per second (IOPS) that the volume supports. + type: int + sample: null + size: + description: The size of the volume, in GiBs. + type: int + sample: 1 + snapshot_id: + description: The snapshot from which the volume was created, if applicable. + type: str + sample: "" + status: + description: The volume state. + type: str + sample: "in-use" + tags: + description: Any tags assigned to the volume. + type: dict + sample: { + env: "dev" + } + type: + description: The volume type. This can be gp2, io1, st1, sc1, or standard. + type: str + sample: "standard" + zone: + description: The Availability Zone of the volume. + type: str + sample: "us-east-1b" +''' + +try: + from botocore.exceptions import ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def get_volume_info(volume, region): + + attachment = volume["attachments"] + + volume_info = { + 'create_time': volume["create_time"], + 'id': volume["volume_id"], + 'encrypted': volume["encrypted"], + 'iops': volume["iops"] if "iops" in volume else None, + 'size': volume["size"], + 'snapshot_id': volume["snapshot_id"], + 'status': volume["state"], + 'type': volume["volume_type"], + 'zone': volume["availability_zone"], + 'region': region, + 'attachment_set': { + 'attach_time': attachment[0]["attach_time"] if len(attachment) > 0 else None, + 'device': attachment[0]["device"] if len(attachment) > 0 else None, + 'instance_id': attachment[0]["instance_id"] if len(attachment) > 0 else None, + 'status': attachment[0]["state"] if len(attachment) > 0 else None, + 'delete_on_termination': attachment[0]["delete_on_termination"] if len(attachment) > 0 else None + }, + 'tags': boto3_tag_list_to_ansible_dict(volume['tags']) if "tags" in volume else None + } + + return volume_info + + +@AWSRetry.jittered_backoff() +def describe_volumes_with_backoff(connection, filters): + paginator = connection.get_paginator('describe_volumes') + return paginator.paginate(Filters=filters).build_full_result() + + +def list_ec2_volumes(connection, module): + + # Replace filter key underscores with dashes, for compatibility, except if we're dealing with tags + sanitized_filters = module.params.get("filters") + for key in list(sanitized_filters): + if not key.startswith("tag:"): + sanitized_filters[key.replace("_", "-")] = sanitized_filters.pop(key) + volume_dict_array = [] + + try: + all_volumes = describe_volumes_with_backoff(connection, ansible_dict_to_boto3_filter_list(sanitized_filters)) + except ClientError as e: + module.fail_json_aws(e, msg="Failed to describe volumes.") + + for volume in all_volumes["Volumes"]: + volume = camel_dict_to_snake_dict(volume, ignore_list=['Tags']) + volume_dict_array.append(get_volume_info(volume, module.region)) + module.exit_json(volumes=volume_dict_array) + + +def main(): + argument_spec = dict(filters=dict(default={}, type='dict')) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_vol_facts': + module.deprecate("The 'ec2_vol_facts' module has been renamed to 'ec2_vol_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2') + + list_ec2_volumes(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol_info.py new file mode 100644 index 00000000..fb6a6587 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vol_info.py @@ -0,0 +1,195 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vol_info +version_added: 1.0.0 +short_description: Gather information about ec2 volumes in AWS +description: + - Gather information about ec2 volumes in AWS. + - This module was called C(ec2_vol_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: "Rob White (@wimnat)" +options: + filters: + type: dict + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + - See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVolumes.html) for possible filters. +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all volumes +- amazon.aws.ec2_vol_info: + +# Gather information about a particular volume using volume ID +- amazon.aws.ec2_vol_info: + filters: + volume-id: vol-00112233 + +# Gather information about any volume with a tag key Name and value Example +- amazon.aws.ec2_vol_info: + filters: + "tag:Name": Example + +# Gather information about any volume that is attached +- amazon.aws.ec2_vol_info: + filters: + attachment.status: attached + +''' + +RETURN = ''' +volumes: + description: Volumes that match the provided filters. Each element consists of a dict with all the information related to that volume. + type: list + elements: dict + returned: always + contains: + attachment_set: + description: Information about the volume attachments. + type: dict + sample: { + "attach_time": "2015-10-23T00:22:29.000Z", + "deleteOnTermination": "false", + "device": "/dev/sdf", + "instance_id": "i-8356263c", + "status": "attached" + } + create_time: + description: The time stamp when volume creation was initiated. + type: str + sample: "2015-10-21T14:36:08.870Z" + encrypted: + description: Indicates whether the volume is encrypted. + type: bool + sample: False + id: + description: The ID of the volume. + type: str + sample: "vol-35b333d9" + iops: + description: The number of I/O operations per second (IOPS) that the volume supports. + type: int + sample: null + size: + description: The size of the volume, in GiBs. + type: int + sample: 1 + snapshot_id: + description: The snapshot from which the volume was created, if applicable. + type: str + sample: "" + status: + description: The volume state. + type: str + sample: "in-use" + tags: + description: Any tags assigned to the volume. + type: dict + sample: { + env: "dev" + } + type: + description: The volume type. This can be gp2, io1, st1, sc1, or standard. + type: str + sample: "standard" + zone: + description: The Availability Zone of the volume. + type: str + sample: "us-east-1b" +''' + +try: + from botocore.exceptions import ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def get_volume_info(volume, region): + + attachment = volume["attachments"] + + volume_info = { + 'create_time': volume["create_time"], + 'id': volume["volume_id"], + 'encrypted': volume["encrypted"], + 'iops': volume["iops"] if "iops" in volume else None, + 'size': volume["size"], + 'snapshot_id': volume["snapshot_id"], + 'status': volume["state"], + 'type': volume["volume_type"], + 'zone': volume["availability_zone"], + 'region': region, + 'attachment_set': { + 'attach_time': attachment[0]["attach_time"] if len(attachment) > 0 else None, + 'device': attachment[0]["device"] if len(attachment) > 0 else None, + 'instance_id': attachment[0]["instance_id"] if len(attachment) > 0 else None, + 'status': attachment[0]["state"] if len(attachment) > 0 else None, + 'delete_on_termination': attachment[0]["delete_on_termination"] if len(attachment) > 0 else None + }, + 'tags': boto3_tag_list_to_ansible_dict(volume['tags']) if "tags" in volume else None + } + + return volume_info + + +@AWSRetry.jittered_backoff() +def describe_volumes_with_backoff(connection, filters): + paginator = connection.get_paginator('describe_volumes') + return paginator.paginate(Filters=filters).build_full_result() + + +def list_ec2_volumes(connection, module): + + # Replace filter key underscores with dashes, for compatibility, except if we're dealing with tags + sanitized_filters = module.params.get("filters") + for key in list(sanitized_filters): + if not key.startswith("tag:"): + sanitized_filters[key.replace("_", "-")] = sanitized_filters.pop(key) + volume_dict_array = [] + + try: + all_volumes = describe_volumes_with_backoff(connection, ansible_dict_to_boto3_filter_list(sanitized_filters)) + except ClientError as e: + module.fail_json_aws(e, msg="Failed to describe volumes.") + + for volume in all_volumes["Volumes"]: + volume = camel_dict_to_snake_dict(volume, ignore_list=['Tags']) + volume_dict_array.append(get_volume_info(volume, module.region)) + module.exit_json(volumes=volume_dict_array) + + +def main(): + argument_spec = dict(filters=dict(default={}, type='dict')) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_vol_facts': + module.deprecate("The 'ec2_vol_facts' module has been renamed to 'ec2_vol_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2') + + list_ec2_volumes(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option.py new file mode 100644 index 00000000..5cbb8e6b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option.py @@ -0,0 +1,413 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_dhcp_option +version_added: 1.0.0 +short_description: Manages DHCP Options, and can ensure the DHCP options for the given VPC match what's + requested +description: + - This module removes, or creates DHCP option sets, and can associate them to a VPC. + Optionally, a new DHCP Options set can be created that converges a VPC's existing + DHCP option set with values provided. + When dhcp_options_id is provided, the module will + 1. remove (with state='absent') + 2. ensure tags are applied (if state='present' and tags are provided + 3. attach it to a VPC (if state='present' and a vpc_id is provided. + If any of the optional values are missing, they will either be treated + as a no-op (i.e., inherit what already exists for the VPC) + To remove existing options while inheriting, supply an empty value + (e.g. set ntp_servers to [] if you want to remove them from the VPC's options) + Most of the options should be self-explanatory. +author: "Joel Thompson (@joelthompson)" +options: + domain_name: + description: + - The domain name to set in the DHCP option sets + type: str + dns_servers: + description: + - A list of hosts to set the DNS servers for the VPC to. (Should be a + list of IP addresses rather than host names.) + type: list + elements: str + ntp_servers: + description: + - List of hosts to advertise as NTP servers for the VPC. + type: list + elements: str + netbios_name_servers: + description: + - List of hosts to advertise as NetBIOS servers. + type: list + elements: str + netbios_node_type: + description: + - NetBIOS node type to advertise in the DHCP options. + The AWS recommendation is to use 2 (when using netbios name services) + U(https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html) + type: int + vpc_id: + description: + - VPC ID to associate with the requested DHCP option set. + If no vpc id is provided, and no matching option set is found then a new + DHCP option set is created. + type: str + delete_old: + description: + - Whether to delete the old VPC DHCP option set when associating a new one. + This is primarily useful for debugging/development purposes when you + want to quickly roll back to the old option set. Note that this setting + will be ignored, and the old DHCP option set will be preserved, if it + is in use by any other VPC. (Otherwise, AWS will return an error.) + type: bool + default: 'yes' + inherit_existing: + description: + - For any DHCP options not specified in these parameters, whether to + inherit them from the options set already applied to vpc_id, or to + reset them to be empty. + type: bool + default: 'no' + tags: + description: + - Tags to be applied to a VPC options set if a new one is created, or + if the resource_id is provided. (options must match) + aliases: [ 'resource_tags'] + type: dict + dhcp_options_id: + description: + - The resource_id of an existing DHCP options set. + If this is specified, then it will override other settings, except tags + (which will be updated to match) + type: str + state: + description: + - create/assign or remove the DHCP options. + If state is set to absent, then a DHCP options set matched either + by id, or tags and options will be removed if possible. + default: present + choices: [ 'absent', 'present' ] + type: str +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +requirements: + - boto +''' + +RETURN = """ +new_options: + description: The DHCP options created, associated or found + returned: when appropriate + type: dict + sample: + domain-name-servers: + - 10.0.0.1 + - 10.0.1.1 + netbois-name-servers: + - 10.0.0.1 + - 10.0.1.1 + netbios-node-type: 2 + domain-name: "my.example.com" +dhcp_options_id: + description: The aws resource id of the primary DCHP options set created, found or removed + type: str + returned: when available +changed: + description: Whether the dhcp options were changed + type: bool + returned: always +""" + +EXAMPLES = """ +# Completely overrides the VPC DHCP options associated with VPC vpc-123456 and deletes any existing +# DHCP option set that may have been attached to that VPC. +- amazon.aws.ec2_vpc_dhcp_option: + domain_name: "foo.example.com" + region: us-east-1 + dns_servers: + - 10.0.0.1 + - 10.0.1.1 + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + netbios_node_type: 2 + vpc_id: vpc-123456 + delete_old: True + inherit_existing: False + + +# Ensure the DHCP option set for the VPC has 10.0.0.4 and 10.0.1.4 as the specified DNS servers, but +# keep any other existing settings. Also, keep the old DHCP option set around. +- amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dns_servers: + - "{{groups['dns-primary']}}" + - "{{groups['dns-secondary']}}" + vpc_id: vpc-123456 + inherit_existing: True + delete_old: False + + +## Create a DHCP option set with 4.4.4.4 and 8.8.8.8 as the specified DNS servers, with tags +## but do not assign to a VPC +- amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dns_servers: + - 4.4.4.4 + - 8.8.8.8 + tags: + Name: google servers + Environment: Test + +## Delete a DHCP options set that matches the tags and options specified +- amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dns_servers: + - 4.4.4.4 + - 8.8.8.8 + tags: + Name: google servers + Environment: Test + state: absent + +## Associate a DHCP options set with a VPC by ID +- amazon.aws.ec2_vpc_dhcp_option: + region: us-east-1 + dhcp_options_id: dopt-12345678 + vpc_id: vpc-123456 + +""" + +import collections +from time import sleep, time + +try: + import boto.vpc + import boto.ec2 + from boto.exception import EC2ResponseError +except ImportError: + pass # Taken care of by ec2.HAS_BOTO + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import HAS_BOTO +from ..module_utils.ec2 import connect_to_aws +from ..module_utils.ec2 import get_aws_connection_info + + +def get_resource_tags(vpc_conn, resource_id): + return dict((t.name, t.value) for t in vpc_conn.get_all_tags(filters={'resource-id': resource_id})) + + +def retry_not_found(to_call, *args, **kwargs): + start_time = time() + while time() < start_time + 300: + try: + return to_call(*args, **kwargs) + except EC2ResponseError as e: + if e.error_code in ['InvalidDhcpOptionID.NotFound', 'InvalidDhcpOptionsID.NotFound']: + sleep(3) + continue + raise e + + +def ensure_tags(module, vpc_conn, resource_id, tags, add_only, check_mode): + try: + cur_tags = get_resource_tags(vpc_conn, resource_id) + if tags == cur_tags: + return {'changed': False, 'tags': cur_tags} + + to_delete = dict((k, cur_tags[k]) for k in cur_tags if k not in tags) + if to_delete and not add_only: + retry_not_found(vpc_conn.delete_tags, resource_id, to_delete, dry_run=check_mode) + + to_add = dict((k, tags[k]) for k in tags if k not in cur_tags) + if to_add: + retry_not_found(vpc_conn.create_tags, resource_id, to_add, dry_run=check_mode) + + latest_tags = get_resource_tags(vpc_conn, resource_id) + return {'changed': True, 'tags': latest_tags} + except EC2ResponseError as e: + module.fail_json_aws(e, msg='Failed to modify tags') + + +def fetch_dhcp_options_for_vpc(vpc_conn, vpc_id): + """ + Returns the DHCP options object currently associated with the requested VPC ID using the VPC + connection variable. + """ + vpcs = vpc_conn.get_all_vpcs(vpc_ids=[vpc_id]) + if len(vpcs) != 1 or vpcs[0].dhcp_options_id == "default": + return None + dhcp_options = vpc_conn.get_all_dhcp_options(dhcp_options_ids=[vpcs[0].dhcp_options_id]) + if len(dhcp_options) != 1: + return None + return dhcp_options[0] + + +def match_dhcp_options(vpc_conn, tags=None, options=None): + """ + Finds a DHCP Options object that optionally matches the tags and options provided + """ + dhcp_options = vpc_conn.get_all_dhcp_options() + for dopts in dhcp_options: + if (not tags) or get_resource_tags(vpc_conn, dopts.id) == tags: + if (not options) or dopts.options == options: + return(True, dopts) + return(False, None) + + +def remove_dhcp_options_by_id(vpc_conn, dhcp_options_id): + associations = vpc_conn.get_all_vpcs(filters={'dhcpOptionsId': dhcp_options_id}) + if len(associations) > 0: + return False + else: + vpc_conn.delete_dhcp_options(dhcp_options_id) + return True + + +def main(): + argument_spec = dict( + dhcp_options_id=dict(type='str', default=None), + domain_name=dict(type='str', default=None), + dns_servers=dict(type='list', elements='str', default=None), + ntp_servers=dict(type='list', elements='str', default=None), + netbios_name_servers=dict(type='list', elements='str', default=None), + netbios_node_type=dict(type='int', default=None), + vpc_id=dict(type='str', default=None), + delete_old=dict(type='bool', default=True), + inherit_existing=dict(type='bool', default=False), + tags=dict(type='dict', default=None, aliases=['resource_tags']), + state=dict(type='str', default='present', choices=['present', 'absent']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + check_boto3=False, + supports_check_mode=True + ) + + params = module.params + found = False + changed = False + new_options = collections.defaultdict(lambda: None) + + if not HAS_BOTO: + module.fail_json(msg='boto is required for this module') + + region, ec2_url, boto_params = get_aws_connection_info(module) + connection = connect_to_aws(boto.vpc, region, **boto_params) + + existing_options = None + + # First check if we were given a dhcp_options_id + if not params['dhcp_options_id']: + # No, so create new_options from the parameters + if params['dns_servers'] is not None: + new_options['domain-name-servers'] = params['dns_servers'] + if params['netbios_name_servers'] is not None: + new_options['netbios-name-servers'] = params['netbios_name_servers'] + if params['ntp_servers'] is not None: + new_options['ntp-servers'] = params['ntp_servers'] + if params['domain_name'] is not None: + # needs to be a list for comparison with boto objects later + new_options['domain-name'] = [params['domain_name']] + if params['netbios_node_type'] is not None: + # needs to be a list for comparison with boto objects later + new_options['netbios-node-type'] = [str(params['netbios_node_type'])] + # If we were given a vpc_id then we need to look at the options on that + if params['vpc_id']: + existing_options = fetch_dhcp_options_for_vpc(connection, params['vpc_id']) + # if we've been asked to inherit existing options, do that now + if params['inherit_existing']: + if existing_options: + for option in ['domain-name-servers', 'netbios-name-servers', 'ntp-servers', 'domain-name', 'netbios-node-type']: + if existing_options.options.get(option) and new_options[option] != [] and (not new_options[option] or [''] == new_options[option]): + new_options[option] = existing_options.options.get(option) + + # Do the vpc's dhcp options already match what we're asked for? if so we are done + if existing_options and new_options == existing_options.options: + module.exit_json(changed=changed, new_options=new_options, dhcp_options_id=existing_options.id) + + # If no vpc_id was given, or the options don't match then look for an existing set using tags + found, dhcp_option = match_dhcp_options(connection, params['tags'], new_options) + + # Now let's cover the case where there are existing options that we were told about by id + # If a dhcp_options_id was supplied we don't look at options inside, just set tags (if given) + else: + supplied_options = connection.get_all_dhcp_options(filters={'dhcp-options-id': params['dhcp_options_id']}) + if len(supplied_options) != 1: + if params['state'] != 'absent': + module.fail_json(msg=" a dhcp_options_id was supplied, but does not exist") + else: + found = True + dhcp_option = supplied_options[0] + if params['state'] != 'absent' and params['tags']: + ensure_tags(module, connection, dhcp_option.id, params['tags'], False, module.check_mode) + + # Now we have the dhcp options set, let's do the necessary + + # if we found options we were asked to remove then try to do so + if params['state'] == 'absent': + if not module.check_mode: + if found: + changed = remove_dhcp_options_by_id(connection, dhcp_option.id) + module.exit_json(changed=changed, new_options={}) + + # otherwise if we haven't found the required options we have something to do + elif not module.check_mode and not found: + + # create some dhcp options if we weren't able to use existing ones + if not found: + # Convert netbios-node-type and domain-name back to strings + if new_options['netbios-node-type']: + new_options['netbios-node-type'] = new_options['netbios-node-type'][0] + if new_options['domain-name']: + new_options['domain-name'] = new_options['domain-name'][0] + + # create the new dhcp options set requested + dhcp_option = connection.create_dhcp_options( + new_options['domain-name'], + new_options['domain-name-servers'], + new_options['ntp-servers'], + new_options['netbios-name-servers'], + new_options['netbios-node-type']) + + # wait for dhcp option to be accessible + found_dhcp_opt = False + start_time = time() + try: + found_dhcp_opt = retry_not_found(connection.get_all_dhcp_options, dhcp_options_ids=[dhcp_option.id]) + except EC2ResponseError as e: + module.fail_json_aws(e, msg="Failed to describe DHCP options") + if not found_dhcp_opt: + module.fail_json(msg="Failed to wait for {0} to be available.".format(dhcp_option.id)) + + changed = True + if params['tags']: + ensure_tags(module, connection, dhcp_option.id, params['tags'], False, module.check_mode) + + # If we were given a vpc_id, then attach the options we now have to that before we finish + if params['vpc_id'] and not module.check_mode: + changed = True + connection.associate_dhcp_options(dhcp_option.id, params['vpc_id']) + # and remove old ones if that was requested + if params['delete_old'] and existing_options: + remove_dhcp_options_by_id(connection, existing_options.id) + + module.exit_json(changed=changed, new_options=new_options, dhcp_options_id=dhcp_option.id) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option_facts.py new file mode 100644 index 00000000..f82f8b3f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option_facts.py @@ -0,0 +1,144 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_dhcp_option_info +version_added: 1.0.0 +short_description: Gather information about dhcp options sets in AWS +description: + - Gather information about dhcp options sets in AWS. + - This module was called C(ec2_vpc_dhcp_option_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: "Nick Aslanidis (@naslanidis)" +options: + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeDhcpOptions.html) for possible filters. + type: dict + dhcp_options_ids: + description: + - Get details of specific DHCP Option IDs. + aliases: ['DhcpOptionIds'] + type: list + elements: str + dry_run: + description: + - Checks whether you have the required permissions to view the DHCP + Options. + aliases: ['DryRun'] + type: bool + default: false +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# # Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Gather information about all DHCP Option sets for an account or profile + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + register: dhcp_info + +- name: Gather information about a filtered list of DHCP Option sets + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + filters: + "tag:Name": "abc-123" + register: dhcp_info + +- name: Gather information about a specific DHCP Option set by DhcpOptionId + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + DhcpOptionsIds: dopt-123fece2 + register: dhcp_info + +''' + +RETURN = ''' +dhcp_options: + description: The dhcp option sets for the account + returned: always + type: list + +changed: + description: True if listing the dhcp options succeeds + type: bool + returned: always +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def get_dhcp_options_info(dhcp_option): + dhcp_option_info = {'DhcpOptionsId': dhcp_option['DhcpOptionsId'], + 'DhcpConfigurations': dhcp_option['DhcpConfigurations'], + 'Tags': boto3_tag_list_to_ansible_dict(dhcp_option.get('Tags', [{'Value': '', 'Key': 'Name'}]))} + return dhcp_option_info + + +def list_dhcp_options(client, module): + params = dict(Filters=ansible_dict_to_boto3_filter_list(module.params.get('filters'))) + + if module.params.get("dry_run"): + params['DryRun'] = True + + if module.params.get("dhcp_options_ids"): + params['DhcpOptionsIds'] = module.params.get("dhcp_options_ids") + + try: + all_dhcp_options = client.describe_dhcp_options(aws_retry=True, **params) + except botocore.exceptions.ClientError as e: + module.fail_json_aws(e) + + return [camel_dict_to_snake_dict(get_dhcp_options_info(option)) + for option in all_dhcp_options['DhcpOptions']] + + +def main(): + argument_spec = dict( + filters=dict(type='dict', default={}), + dry_run=dict(type='bool', default=False, aliases=['DryRun']), + dhcp_options_ids=dict(type='list', elements='str', aliases=['DhcpOptionIds']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + if module._name == 'ec2_vpc_dhcp_option_facts': + module.deprecate("The 'ec2_vpc_dhcp_option_facts' module has been renamed to 'ec2_vpc_dhcp_option_info'", + date='2021-12-01', collection_name='amazon.aws') + + client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + # call your function here + results = list_dhcp_options(client, module) + + module.exit_json(dhcp_options=results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option_info.py new file mode 100644 index 00000000..f82f8b3f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_dhcp_option_info.py @@ -0,0 +1,144 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_dhcp_option_info +version_added: 1.0.0 +short_description: Gather information about dhcp options sets in AWS +description: + - Gather information about dhcp options sets in AWS. + - This module was called C(ec2_vpc_dhcp_option_facts) before Ansible 2.9. The usage did not change. +requirements: [ boto3 ] +author: "Nick Aslanidis (@naslanidis)" +options: + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeDhcpOptions.html) for possible filters. + type: dict + dhcp_options_ids: + description: + - Get details of specific DHCP Option IDs. + aliases: ['DhcpOptionIds'] + type: list + elements: str + dry_run: + description: + - Checks whether you have the required permissions to view the DHCP + Options. + aliases: ['DryRun'] + type: bool + default: false +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# # Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Gather information about all DHCP Option sets for an account or profile + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + register: dhcp_info + +- name: Gather information about a filtered list of DHCP Option sets + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + filters: + "tag:Name": "abc-123" + register: dhcp_info + +- name: Gather information about a specific DHCP Option set by DhcpOptionId + amazon.aws.ec2_vpc_dhcp_option_info: + region: ap-southeast-2 + profile: production + DhcpOptionsIds: dopt-123fece2 + register: dhcp_info + +''' + +RETURN = ''' +dhcp_options: + description: The dhcp option sets for the account + returned: always + type: list + +changed: + description: True if listing the dhcp options succeeds + type: bool + returned: always +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def get_dhcp_options_info(dhcp_option): + dhcp_option_info = {'DhcpOptionsId': dhcp_option['DhcpOptionsId'], + 'DhcpConfigurations': dhcp_option['DhcpConfigurations'], + 'Tags': boto3_tag_list_to_ansible_dict(dhcp_option.get('Tags', [{'Value': '', 'Key': 'Name'}]))} + return dhcp_option_info + + +def list_dhcp_options(client, module): + params = dict(Filters=ansible_dict_to_boto3_filter_list(module.params.get('filters'))) + + if module.params.get("dry_run"): + params['DryRun'] = True + + if module.params.get("dhcp_options_ids"): + params['DhcpOptionsIds'] = module.params.get("dhcp_options_ids") + + try: + all_dhcp_options = client.describe_dhcp_options(aws_retry=True, **params) + except botocore.exceptions.ClientError as e: + module.fail_json_aws(e) + + return [camel_dict_to_snake_dict(get_dhcp_options_info(option)) + for option in all_dhcp_options['DhcpOptions']] + + +def main(): + argument_spec = dict( + filters=dict(type='dict', default={}), + dry_run=dict(type='bool', default=False, aliases=['DryRun']), + dhcp_options_ids=dict(type='list', elements='str', aliases=['DhcpOptionIds']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + if module._name == 'ec2_vpc_dhcp_option_facts': + module.deprecate("The 'ec2_vpc_dhcp_option_facts' module has been renamed to 'ec2_vpc_dhcp_option_info'", + date='2021-12-01', collection_name='amazon.aws') + + client = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff()) + + # call your function here + results = list_dhcp_options(client, module) + + module.exit_json(dhcp_options=results) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net.py new file mode 100644 index 00000000..0d912031 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net.py @@ -0,0 +1,535 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_net +version_added: 1.0.0 +short_description: Configure AWS virtual private clouds +description: + - Create, modify, and terminate AWS virtual private clouds. +author: + - Jonathan Davila (@defionscode) + - Sloane Hertel (@s-hertel) +options: + name: + description: + - The name to give your VPC. This is used in combination with C(cidr_block) to determine if a VPC already exists. + required: yes + type: str + cidr_block: + description: + - The primary CIDR of the VPC. After 2.5 a list of CIDRs can be provided. The first in the list will be used as the primary CIDR + and is used in conjunction with the C(name) to ensure idempotence. + required: yes + type: list + elements: str + ipv6_cidr: + description: + - Request an Amazon-provided IPv6 CIDR block with /56 prefix length. You cannot specify the range of IPv6 addresses, + or the size of the CIDR block. + default: False + type: bool + purge_cidrs: + description: + - Remove CIDRs that are associated with the VPC and are not specified in C(cidr_block). + default: no + type: bool + tenancy: + description: + - Whether to be default or dedicated tenancy. This cannot be changed after the VPC has been created. + default: default + choices: [ 'default', 'dedicated' ] + type: str + dns_support: + description: + - Whether to enable AWS DNS support. + default: yes + type: bool + dns_hostnames: + description: + - Whether to enable AWS hostname support. + default: yes + type: bool + dhcp_opts_id: + description: + - The id of the DHCP options to use for this VPC. + type: str + tags: + description: + - The tags you want attached to the VPC. This is independent of the name value, note if you pass a 'Name' key it would override the Name of + the VPC if it's different. + aliases: [ 'resource_tags' ] + type: dict + state: + description: + - The state of the VPC. Either absent or present. + default: present + choices: [ 'present', 'absent' ] + type: str + multi_ok: + description: + - By default the module will not create another VPC if there is another VPC with the same name and CIDR block. Specify this as true if you want + duplicate VPCs created. + type: bool + default: false +requirements: + - boto3 + - botocore +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: create a VPC with dedicated tenancy and a couple of tags + amazon.aws.ec2_vpc_net: + name: Module_dev2 + cidr_block: 10.10.0.0/16 + region: us-east-1 + tags: + module: ec2_vpc_net + this: works + tenancy: dedicated + +- name: create a VPC with dedicated tenancy and request an IPv6 CIDR + amazon.aws.ec2_vpc_net: + name: Module_dev2 + cidr_block: 10.10.0.0/16 + ipv6_cidr: True + region: us-east-1 + tenancy: dedicated +''' + +RETURN = ''' +vpc: + description: info about the VPC that was created or deleted + returned: always + type: complex + contains: + cidr_block: + description: The CIDR of the VPC + returned: always + type: str + sample: 10.0.0.0/16 + cidr_block_association_set: + description: IPv4 CIDR blocks associated with the VPC + returned: success + type: list + sample: + "cidr_block_association_set": [ + { + "association_id": "vpc-cidr-assoc-97aeeefd", + "cidr_block": "10.0.0.0/24", + "cidr_block_state": { + "state": "associated" + } + } + ] + classic_link_enabled: + description: indicates whether ClassicLink is enabled + returned: always + type: bool + sample: false + dhcp_options_id: + description: the id of the DHCP options associated with this VPC + returned: always + type: str + sample: dopt-12345678 + id: + description: VPC resource id + returned: always + type: str + sample: vpc-12345678 + instance_tenancy: + description: indicates whether VPC uses default or dedicated tenancy + returned: always + type: str + sample: default + ipv6_cidr_block_association_set: + description: IPv6 CIDR blocks associated with the VPC + returned: success + type: list + sample: + "ipv6_cidr_block_association_set": [ + { + "association_id": "vpc-cidr-assoc-97aeeefd", + "ipv6_cidr_block": "2001:db8::/56", + "ipv6_cidr_block_state": { + "state": "associated" + } + } + ] + is_default: + description: indicates whether this is the default VPC + returned: always + type: bool + sample: false + state: + description: state of the VPC + returned: always + type: str + sample: available + tags: + description: tags attached to the VPC, includes name + returned: always + type: complex + contains: + Name: + description: name tag for the VPC + returned: always + type: str + sample: pk_vpc4 + owner_id: + description: The AWS account which owns the VPC. + returned: always + type: str + sample: 123456789012 +''' + +from time import sleep +from time import time + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils._text import to_native +from ansible.module_utils.common.network import to_subnet +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_message +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_aws_tags + + +def vpc_exists(module, vpc, name, cidr_block, multi): + """Returns None or a vpc object depending on the existence of a VPC. When supplied + with a CIDR, it will check for matching tags to determine if it is a match + otherwise it will assume the VPC does not exist and thus return None. + """ + try: + vpc_filters = ansible_dict_to_boto3_filter_list({'tag:Name': name, 'cidr-block': cidr_block}) + matching_vpcs = vpc.describe_vpcs(aws_retry=True, Filters=vpc_filters)['Vpcs'] + # If an exact matching using a list of CIDRs isn't found, check for a match with the first CIDR as is documented for C(cidr_block) + if not matching_vpcs: + vpc_filters = ansible_dict_to_boto3_filter_list({'tag:Name': name, 'cidr-block': [cidr_block[0]]}) + matching_vpcs = vpc.describe_vpcs(aws_retry=True, Filters=vpc_filters)['Vpcs'] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to describe VPCs") + + if multi: + return None + elif len(matching_vpcs) == 1: + return matching_vpcs[0]['VpcId'] + elif len(matching_vpcs) > 1: + module.fail_json(msg='Currently there are %d VPCs that have the same name and ' + 'CIDR block you specified. If you would like to create ' + 'the VPC anyway please pass True to the multi_ok param.' % len(matching_vpcs)) + return None + + +@AWSRetry.backoff(delay=3, tries=8, catch_extra_error_codes=['InvalidVpcID.NotFound']) +def get_classic_link_with_backoff(connection, vpc_id): + try: + return connection.describe_vpc_classic_link(VpcIds=[vpc_id])['Vpcs'][0].get('ClassicLinkEnabled') + except is_boto3_error_message('The functionality you requested is not available in this region.'): + return False + + +def get_vpc(module, connection, vpc_id): + # wait for vpc to be available + try: + connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id]) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to wait for VPC {0} to be available.".format(vpc_id)) + + try: + vpc_obj = connection.describe_vpcs(VpcIds=[vpc_id], aws_retry=True)['Vpcs'][0] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to describe VPCs") + try: + vpc_obj['ClassicLinkEnabled'] = get_classic_link_with_backoff(connection, vpc_id) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to describe VPCs") + + return vpc_obj + + +def update_vpc_tags(connection, module, vpc_id, tags, name): + if tags is None: + tags = dict() + + tags.update({'Name': name}) + tags = dict((k, to_native(v)) for k, v in tags.items()) + try: + filters = ansible_dict_to_boto3_filter_list({'resource-id': vpc_id}) + current_tags = dict((t['Key'], t['Value']) for t in connection.describe_tags(Filters=filters, aws_retry=True)['Tags']) + tags_to_update, dummy = compare_aws_tags(current_tags, tags, False) + if tags_to_update: + if not module.check_mode: + tags = ansible_dict_to_boto3_tag_list(tags_to_update) + vpc_obj = connection.create_tags(Resources=[vpc_id], Tags=tags, aws_retry=True) + + # Wait for tags to be updated + expected_tags = boto3_tag_list_to_ansible_dict(tags) + filters = [{'Name': 'tag:{0}'.format(key), 'Values': [value]} for key, value in expected_tags.items()] + connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id], Filters=filters) + + return True + else: + return False + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to update tags") + + +def update_dhcp_opts(connection, module, vpc_obj, dhcp_id): + if vpc_obj['DhcpOptionsId'] != dhcp_id: + if not module.check_mode: + try: + connection.associate_dhcp_options(DhcpOptionsId=dhcp_id, VpcId=vpc_obj['VpcId'], aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to associate DhcpOptionsId {0}".format(dhcp_id)) + + try: + # Wait for DhcpOptionsId to be updated + filters = [{'Name': 'dhcp-options-id', 'Values': [dhcp_id]}] + connection.get_waiter('vpc_available').wait(VpcIds=[vpc_obj['VpcId']], Filters=filters) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to wait for DhcpOptionsId to be updated") + + return True + else: + return False + + +def create_vpc(connection, module, cidr_block, tenancy): + try: + if not module.check_mode: + vpc_obj = connection.create_vpc(CidrBlock=cidr_block, InstanceTenancy=tenancy, aws_retry=True) + else: + module.exit_json(changed=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to create the VPC") + + # wait up to 30 seconds for vpc to exist + try: + connection.get_waiter('vpc_exists').wait( + VpcIds=[vpc_obj['Vpc']['VpcId']], + WaiterConfig=dict(MaxAttempts=30) + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to wait for VPC {0} to be created.".format(vpc_obj['Vpc']['VpcId'])) + + return vpc_obj['Vpc']['VpcId'] + + +def wait_for_vpc_attribute(connection, module, vpc_id, attribute, expected_value): + start_time = time() + updated = False + while time() < start_time + 300: + current_value = connection.describe_vpc_attribute( + Attribute=attribute, + VpcId=vpc_id, + aws_retry=True + )['{0}{1}'.format(attribute[0].upper(), attribute[1:])]['Value'] + if current_value != expected_value: + sleep(3) + else: + updated = True + break + if not updated: + module.fail_json(msg="Failed to wait for {0} to be updated".format(attribute)) + + +def get_cidr_network_bits(module, cidr_block): + fixed_cidrs = [] + for cidr in cidr_block: + split_addr = cidr.split('/') + if len(split_addr) == 2: + # this_ip is a IPv4 CIDR that may or may not have host bits set + # Get the network bits. + valid_cidr = to_subnet(split_addr[0], split_addr[1]) + if cidr != valid_cidr: + module.warn("One of your CIDR addresses ({0}) has host bits set. To get rid of this warning, " + "check the network mask and make sure that only network bits are set: {1}.".format(cidr, valid_cidr)) + fixed_cidrs.append(valid_cidr) + else: + # let AWS handle invalid CIDRs + fixed_cidrs.append(cidr) + return fixed_cidrs + + +def main(): + argument_spec = dict( + name=dict(required=True), + cidr_block=dict(type='list', required=True, elements='str'), + ipv6_cidr=dict(type='bool', default=False), + tenancy=dict(choices=['default', 'dedicated'], default='default'), + dns_support=dict(type='bool', default=True), + dns_hostnames=dict(type='bool', default=True), + dhcp_opts_id=dict(), + tags=dict(type='dict', aliases=['resource_tags']), + state=dict(choices=['present', 'absent'], default='present'), + multi_ok=dict(type='bool', default=False), + purge_cidrs=dict(type='bool', default=False), + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + name = module.params.get('name') + cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block')) + ipv6_cidr = module.params.get('ipv6_cidr') + purge_cidrs = module.params.get('purge_cidrs') + tenancy = module.params.get('tenancy') + dns_support = module.params.get('dns_support') + dns_hostnames = module.params.get('dns_hostnames') + dhcp_id = module.params.get('dhcp_opts_id') + tags = module.params.get('tags') + state = module.params.get('state') + multi = module.params.get('multi_ok') + + changed = False + + connection = module.client( + 'ec2', + retry_decorator=AWSRetry.jittered_backoff( + retries=8, delay=3, catch_extra_error_codes=['InvalidVpcID.NotFound'] + ) + ) + + if dns_hostnames and not dns_support: + module.fail_json(msg='In order to enable DNS Hostnames you must also enable DNS support') + + if state == 'present': + + # Check if VPC exists + vpc_id = vpc_exists(module, connection, name, cidr_block, multi) + + if vpc_id is None: + vpc_id = create_vpc(connection, module, cidr_block[0], tenancy) + changed = True + + vpc_obj = get_vpc(module, connection, vpc_id) + + associated_cidrs = dict((cidr['CidrBlock'], cidr['AssociationId']) for cidr in vpc_obj.get('CidrBlockAssociationSet', []) + if cidr['CidrBlockState']['State'] != 'disassociated') + to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs] + to_remove = [associated_cidrs[cidr] for cidr in associated_cidrs if cidr not in cidr_block] + expected_cidrs = [cidr for cidr in associated_cidrs if associated_cidrs[cidr] not in to_remove] + to_add + + if len(cidr_block) > 1: + for cidr in to_add: + changed = True + try: + connection.associate_vpc_cidr_block(CidrBlock=cidr, VpcId=vpc_id, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to associate CIDR {0}.".format(ipv6_cidr)) + if ipv6_cidr: + if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys(): + module.warn("Only one IPv6 CIDR is permitted per VPC, {0} already has CIDR {1}".format( + vpc_id, + vpc_obj['Ipv6CidrBlockAssociationSet'][0]['Ipv6CidrBlock'])) + else: + try: + connection.associate_vpc_cidr_block(AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id, aws_retry=True) + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to associate CIDR {0}.".format(ipv6_cidr)) + + if purge_cidrs: + for association_id in to_remove: + changed = True + try: + connection.disassociate_vpc_cidr_block(AssociationId=association_id, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Unable to disassociate {0}. You must detach or delete all gateways and resources that " + "are associated with the CIDR block before you can disassociate it.".format(association_id)) + + if dhcp_id is not None: + try: + if update_dhcp_opts(connection, module, vpc_obj, dhcp_id): + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to update DHCP options") + + if tags is not None or name is not None: + try: + if update_vpc_tags(connection, module, vpc_id, tags, name): + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to update tags") + + current_dns_enabled = connection.describe_vpc_attribute(Attribute='enableDnsSupport', VpcId=vpc_id, aws_retry=True)['EnableDnsSupport']['Value'] + current_dns_hostnames = connection.describe_vpc_attribute(Attribute='enableDnsHostnames', VpcId=vpc_id, aws_retry=True)['EnableDnsHostnames']['Value'] + if current_dns_enabled != dns_support: + changed = True + if not module.check_mode: + try: + connection.modify_vpc_attribute(VpcId=vpc_id, EnableDnsSupport={'Value': dns_support}, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to update enabled dns support attribute") + if current_dns_hostnames != dns_hostnames: + changed = True + if not module.check_mode: + try: + connection.modify_vpc_attribute(VpcId=vpc_id, EnableDnsHostnames={'Value': dns_hostnames}, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to update enabled dns hostnames attribute") + + # wait for associated cidrs to match + if to_add or to_remove: + try: + connection.get_waiter('vpc_available').wait( + VpcIds=[vpc_id], + Filters=[{'Name': 'cidr-block-association.cidr-block', 'Values': expected_cidrs}] + ) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Failed to wait for CIDRs to update", vpc_id=vpc_id) + + # try to wait for enableDnsSupport and enableDnsHostnames to match + wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport', dns_support) + wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsHostnames', dns_hostnames) + + final_state = camel_dict_to_snake_dict(get_vpc(module, connection, vpc_id)) + final_state['tags'] = boto3_tag_list_to_ansible_dict(final_state.get('tags', [])) + final_state['id'] = final_state.pop('vpc_id') + debugging = dict(to_add=to_add, to_remove=to_remove, expected_cidrs=expected_cidrs) + + module.exit_json(changed=changed, vpc=final_state, debugging=debugging) + + elif state == 'absent': + + # Check if VPC exists + vpc_id = vpc_exists(module, connection, name, cidr_block, multi) + + if vpc_id is not None: + try: + if not module.check_mode: + connection.delete_vpc(VpcId=vpc_id, aws_retry=True) + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to delete VPC {0} You may want to use the ec2_vpc_subnet, ec2_vpc_igw, " + "and/or ec2_vpc_route_table modules to ensure the other components are absent.".format(vpc_id)) + + module.exit_json(changed=changed, vpc={}) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net_facts.py new file mode 100644 index 00000000..62a9b1ee --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net_facts.py @@ -0,0 +1,268 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_net_info +version_added: 1.0.0 +short_description: Gather information about ec2 VPCs in AWS +description: + - Gather information about ec2 VPCs in AWS + - This module was called C(ec2_vpc_net_facts) before Ansible 2.9. The usage did not change. +author: "Rob White (@wimnat)" +requirements: + - boto3 + - botocore +options: + vpc_ids: + description: + - A list of VPC IDs that exist in your account. + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html) for possible filters. + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all VPCs +- amazon.aws.ec2_vpc_net_info: + +# Gather information about a particular VPC using VPC ID +- amazon.aws.ec2_vpc_net_info: + vpc_ids: vpc-00112233 + +# Gather information about any VPC with a tag key Name and value Example +- amazon.aws.ec2_vpc_net_info: + filters: + "tag:Name": Example + +''' + +RETURN = ''' +vpcs: + description: Returns an array of complex objects as described below. + returned: success + type: complex + contains: + id: + description: The ID of the VPC (for backwards compatibility). + returned: always + type: str + vpc_id: + description: The ID of the VPC . + returned: always + type: str + state: + description: The state of the VPC. + returned: always + type: str + tags: + description: A dict of tags associated with the VPC. + returned: always + type: dict + instance_tenancy: + description: The instance tenancy setting for the VPC. + returned: always + type: str + is_default: + description: True if this is the default VPC for account. + returned: always + type: bool + cidr_block: + description: The IPv4 CIDR block assigned to the VPC. + returned: always + type: str + classic_link_dns_supported: + description: True/False depending on attribute setting for classic link DNS support. + returned: always + type: bool + classic_link_enabled: + description: True/False depending on if classic link support is enabled. + returned: always + type: bool + enable_dns_hostnames: + description: True/False depending on attribute setting for DNS hostnames support. + returned: always + type: bool + enable_dns_support: + description: True/False depending on attribute setting for DNS support. + returned: always + type: bool + cidr_block_association_set: + description: An array of IPv4 cidr block association set information. + returned: always + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + cidr_block: + description: The IPv4 CIDR block that is associated with the VPC. + returned: always + type: str + cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str + ipv6_cidr_block_association_set: + description: An array of IPv6 cidr block association set information. + returned: always + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + ipv6_cidr_block: + description: The IPv6 CIDR block that is associated with the VPC. + returned: always + type: str + ipv6_cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str + owner_id: + description: The AWS account which owns the VPC. + returned: always + type: str + sample: 123456789012 + dhcp_options_id: + description: The ID of the DHCP options associated with this VPC. + returned: always + type: str + sample: dopt-12345678 +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def describe_vpcs(connection, module): + """ + Describe VPCs. + + connection : boto3 client connection object + module : AnsibleAWSModule object + """ + # collect parameters + filters = ansible_dict_to_boto3_filter_list(module.params.get('filters')) + vpc_ids = module.params.get('vpc_ids') + + # init empty list for return vars + vpc_info = list() + vpc_list = list() + + # Get the basic VPC info + try: + response = connection.describe_vpcs(VpcIds=vpc_ids, Filters=filters, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to describe VPCs {0}".format(vpc_ids)) + + # Loop through results and create a list of VPC IDs + for vpc in response['Vpcs']: + vpc_list.append(vpc['VpcId']) + + # We can get these results in bulk but still needs two separate calls to the API + try: + cl_enabled = connection.describe_vpc_classic_link(VpcIds=vpc_list, aws_retry=True) + except is_boto3_error_code('UnsupportedOperation'): + cl_enabled = {'Vpcs': [{'VpcId': vpc_id, 'ClassicLinkEnabled': False} for vpc_id in vpc_list]} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Unable to describe if ClassicLink is enabled') + + try: + cl_dns_support = connection.describe_vpc_classic_link_dns_support(VpcIds=vpc_list, aws_retry=True) + except is_boto3_error_code('UnsupportedOperation'): + cl_dns_support = {'Vpcs': [{'VpcId': vpc_id, 'ClassicLinkDnsSupported': False} for vpc_id in vpc_list]} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Unable to describe if ClassicLinkDns is supported') + + # Loop through the results and add the other VPC attributes we gathered + for vpc in response['Vpcs']: + error_message = "Unable to describe VPC attribute {0}" + # We have to make two separate calls per VPC to get these attributes. + try: + dns_support = connection.describe_vpc_attribute(VpcId=vpc['VpcId'], + Attribute='enableDnsSupport', aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg=error_message.format('enableDnsSupport')) + try: + dns_hostnames = connection.describe_vpc_attribute(VpcId=vpc['VpcId'], + Attribute='enableDnsHostnames', aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg=error_message.format('enableDnsHostnames')) + + # loop through the ClassicLink Enabled results and add the value for the correct VPC + for item in cl_enabled['Vpcs']: + if vpc['VpcId'] == item['VpcId']: + vpc['ClassicLinkEnabled'] = item['ClassicLinkEnabled'] + + # loop through the ClassicLink DNS support results and add the value for the correct VPC + for item in cl_dns_support['Vpcs']: + if vpc['VpcId'] == item['VpcId']: + vpc['ClassicLinkDnsSupported'] = item['ClassicLinkDnsSupported'] + + # add the two DNS attributes + vpc['EnableDnsSupport'] = dns_support['EnableDnsSupport'].get('Value') + vpc['EnableDnsHostnames'] = dns_hostnames['EnableDnsHostnames'].get('Value') + # for backwards compatibility + vpc['id'] = vpc['VpcId'] + vpc_info.append(camel_dict_to_snake_dict(vpc)) + # convert tag list to ansible dict + vpc_info[-1]['tags'] = boto3_tag_list_to_ansible_dict(vpc.get('Tags', [])) + + module.exit_json(vpcs=vpc_info) + + +def main(): + argument_spec = dict( + vpc_ids=dict(type='list', elements='str', default=[]), + filters=dict(type='dict', default={}) + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_vpc_net_facts': + module.deprecate("The 'ec2_vpc_net_facts' module has been renamed to 'ec2_vpc_net_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff(retries=10)) + + describe_vpcs(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net_info.py new file mode 100644 index 00000000..62a9b1ee --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_net_info.py @@ -0,0 +1,268 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_net_info +version_added: 1.0.0 +short_description: Gather information about ec2 VPCs in AWS +description: + - Gather information about ec2 VPCs in AWS + - This module was called C(ec2_vpc_net_facts) before Ansible 2.9. The usage did not change. +author: "Rob White (@wimnat)" +requirements: + - boto3 + - botocore +options: + vpc_ids: + description: + - A list of VPC IDs that exist in your account. + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html) for possible filters. + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all VPCs +- amazon.aws.ec2_vpc_net_info: + +# Gather information about a particular VPC using VPC ID +- amazon.aws.ec2_vpc_net_info: + vpc_ids: vpc-00112233 + +# Gather information about any VPC with a tag key Name and value Example +- amazon.aws.ec2_vpc_net_info: + filters: + "tag:Name": Example + +''' + +RETURN = ''' +vpcs: + description: Returns an array of complex objects as described below. + returned: success + type: complex + contains: + id: + description: The ID of the VPC (for backwards compatibility). + returned: always + type: str + vpc_id: + description: The ID of the VPC . + returned: always + type: str + state: + description: The state of the VPC. + returned: always + type: str + tags: + description: A dict of tags associated with the VPC. + returned: always + type: dict + instance_tenancy: + description: The instance tenancy setting for the VPC. + returned: always + type: str + is_default: + description: True if this is the default VPC for account. + returned: always + type: bool + cidr_block: + description: The IPv4 CIDR block assigned to the VPC. + returned: always + type: str + classic_link_dns_supported: + description: True/False depending on attribute setting for classic link DNS support. + returned: always + type: bool + classic_link_enabled: + description: True/False depending on if classic link support is enabled. + returned: always + type: bool + enable_dns_hostnames: + description: True/False depending on attribute setting for DNS hostnames support. + returned: always + type: bool + enable_dns_support: + description: True/False depending on attribute setting for DNS support. + returned: always + type: bool + cidr_block_association_set: + description: An array of IPv4 cidr block association set information. + returned: always + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + cidr_block: + description: The IPv4 CIDR block that is associated with the VPC. + returned: always + type: str + cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str + ipv6_cidr_block_association_set: + description: An array of IPv6 cidr block association set information. + returned: always + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + ipv6_cidr_block: + description: The IPv6 CIDR block that is associated with the VPC. + returned: always + type: str + ipv6_cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str + owner_id: + description: The AWS account which owns the VPC. + returned: always + type: str + sample: 123456789012 + dhcp_options_id: + description: The ID of the DHCP options associated with this VPC. + returned: always + type: str + sample: dopt-12345678 +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +def describe_vpcs(connection, module): + """ + Describe VPCs. + + connection : boto3 client connection object + module : AnsibleAWSModule object + """ + # collect parameters + filters = ansible_dict_to_boto3_filter_list(module.params.get('filters')) + vpc_ids = module.params.get('vpc_ids') + + # init empty list for return vars + vpc_info = list() + vpc_list = list() + + # Get the basic VPC info + try: + response = connection.describe_vpcs(VpcIds=vpc_ids, Filters=filters, aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Unable to describe VPCs {0}".format(vpc_ids)) + + # Loop through results and create a list of VPC IDs + for vpc in response['Vpcs']: + vpc_list.append(vpc['VpcId']) + + # We can get these results in bulk but still needs two separate calls to the API + try: + cl_enabled = connection.describe_vpc_classic_link(VpcIds=vpc_list, aws_retry=True) + except is_boto3_error_code('UnsupportedOperation'): + cl_enabled = {'Vpcs': [{'VpcId': vpc_id, 'ClassicLinkEnabled': False} for vpc_id in vpc_list]} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Unable to describe if ClassicLink is enabled') + + try: + cl_dns_support = connection.describe_vpc_classic_link_dns_support(VpcIds=vpc_list, aws_retry=True) + except is_boto3_error_code('UnsupportedOperation'): + cl_dns_support = {'Vpcs': [{'VpcId': vpc_id, 'ClassicLinkDnsSupported': False} for vpc_id in vpc_list]} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Unable to describe if ClassicLinkDns is supported') + + # Loop through the results and add the other VPC attributes we gathered + for vpc in response['Vpcs']: + error_message = "Unable to describe VPC attribute {0}" + # We have to make two separate calls per VPC to get these attributes. + try: + dns_support = connection.describe_vpc_attribute(VpcId=vpc['VpcId'], + Attribute='enableDnsSupport', aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg=error_message.format('enableDnsSupport')) + try: + dns_hostnames = connection.describe_vpc_attribute(VpcId=vpc['VpcId'], + Attribute='enableDnsHostnames', aws_retry=True) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg=error_message.format('enableDnsHostnames')) + + # loop through the ClassicLink Enabled results and add the value for the correct VPC + for item in cl_enabled['Vpcs']: + if vpc['VpcId'] == item['VpcId']: + vpc['ClassicLinkEnabled'] = item['ClassicLinkEnabled'] + + # loop through the ClassicLink DNS support results and add the value for the correct VPC + for item in cl_dns_support['Vpcs']: + if vpc['VpcId'] == item['VpcId']: + vpc['ClassicLinkDnsSupported'] = item['ClassicLinkDnsSupported'] + + # add the two DNS attributes + vpc['EnableDnsSupport'] = dns_support['EnableDnsSupport'].get('Value') + vpc['EnableDnsHostnames'] = dns_hostnames['EnableDnsHostnames'].get('Value') + # for backwards compatibility + vpc['id'] = vpc['VpcId'] + vpc_info.append(camel_dict_to_snake_dict(vpc)) + # convert tag list to ansible dict + vpc_info[-1]['tags'] = boto3_tag_list_to_ansible_dict(vpc.get('Tags', [])) + + module.exit_json(vpcs=vpc_info) + + +def main(): + argument_spec = dict( + vpc_ids=dict(type='list', elements='str', default=[]), + filters=dict(type='dict', default={}) + ) + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + if module._name == 'ec2_vpc_net_facts': + module.deprecate("The 'ec2_vpc_net_facts' module has been renamed to 'ec2_vpc_net_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff(retries=10)) + + describe_vpcs(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet.py new file mode 100644 index 00000000..d9b34a1b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet.py @@ -0,0 +1,599 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_subnet +version_added: 1.0.0 +short_description: Manage subnets in AWS virtual private clouds +description: + - Manage subnets in AWS virtual private clouds. +author: +- Robert Estelle (@erydo) +- Brad Davidson (@brandond) +requirements: [ boto3 ] +options: + az: + description: + - "The availability zone for the subnet." + type: str + cidr: + description: + - "The CIDR block for the subnet. E.g. 192.0.2.0/24." + type: str + required: true + ipv6_cidr: + description: + - "The IPv6 CIDR block for the subnet. The VPC must have a /56 block assigned and this value must be a valid IPv6 /64 that falls in the VPC range." + - "Required if I(assign_instances_ipv6=true)" + type: str + tags: + description: + - "A dict of tags to apply to the subnet. Any tags currently applied to the subnet and not present here will be removed." + aliases: [ 'resource_tags' ] + type: dict + state: + description: + - "Create or remove the subnet." + default: present + choices: [ 'present', 'absent' ] + type: str + vpc_id: + description: + - "VPC ID of the VPC in which to create or delete the subnet." + required: true + type: str + map_public: + description: + - "Specify C(yes) to indicate that instances launched into the subnet should be assigned public IP address by default." + type: bool + default: 'no' + assign_instances_ipv6: + description: + - "Specify C(yes) to indicate that instances launched into the subnet should be automatically assigned an IPv6 address." + type: bool + default: false + wait: + description: + - "When I(wait=true) and I(state=present), module will wait for subnet to be in available state before continuing." + type: bool + default: true + wait_timeout: + description: + - "Number of seconds to wait for subnet to become available I(wait=True)." + default: 300 + type: int + purge_tags: + description: + - Whether or not to remove tags that do not appear in the I(tags) list. + type: bool + default: true +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Create subnet for database servers + amazon.aws.ec2_vpc_subnet: + state: present + vpc_id: vpc-123456 + cidr: 10.0.1.16/28 + tags: + Name: Database Subnet + register: database_subnet + +- name: Remove subnet for database servers + amazon.aws.ec2_vpc_subnet: + state: absent + vpc_id: vpc-123456 + cidr: 10.0.1.16/28 + +- name: Create subnet with IPv6 block assigned + amazon.aws.ec2_vpc_subnet: + state: present + vpc_id: vpc-123456 + cidr: 10.1.100.0/24 + ipv6_cidr: 2001:db8:0:102::/64 + +- name: Remove IPv6 block assigned to subnet + amazon.aws.ec2_vpc_subnet: + state: present + vpc_id: vpc-123456 + cidr: 10.1.100.0/24 + ipv6_cidr: '' +''' + +RETURN = ''' +subnet: + description: Dictionary of subnet values + returned: I(state=present) + type: complex + contains: + id: + description: Subnet resource id + returned: I(state=present) + type: str + sample: subnet-b883b2c4 + cidr_block: + description: The IPv4 CIDR of the Subnet + returned: I(state=present) + type: str + sample: "10.0.0.0/16" + ipv6_cidr_block: + description: The IPv6 CIDR block actively associated with the Subnet + returned: I(state=present) + type: str + sample: "2001:db8:0:102::/64" + availability_zone: + description: Availability zone of the Subnet + returned: I(state=present) + type: str + sample: us-east-1a + state: + description: state of the Subnet + returned: I(state=present) + type: str + sample: available + tags: + description: tags attached to the Subnet, includes name + returned: I(state=present) + type: dict + sample: {"Name": "My Subnet", "env": "staging"} + map_public_ip_on_launch: + description: whether public IP is auto-assigned to new instances + returned: I(state=present) + type: bool + sample: false + assign_ipv6_address_on_creation: + description: whether IPv6 address is auto-assigned to new instances + returned: I(state=present) + type: bool + sample: false + vpc_id: + description: the id of the VPC where this Subnet exists + returned: I(state=present) + type: str + sample: vpc-67236184 + available_ip_address_count: + description: number of available IPv4 addresses + returned: I(state=present) + type: str + sample: 251 + default_for_az: + description: indicates whether this is the default Subnet for this Availability Zone + returned: I(state=present) + type: bool + sample: false + ipv6_association_id: + description: The IPv6 association ID for the currently associated CIDR + returned: I(state=present) + type: str + sample: subnet-cidr-assoc-b85c74d2 + ipv6_cidr_block_association_set: + description: An array of IPv6 cidr block association set information. + returned: I(state=present) + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + ipv6_cidr_block: + description: The IPv6 CIDR block that is associated with the subnet. + returned: always + type: str + ipv6_cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str +''' + + +import time + +try: + import botocore +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible.module_utils._text import to_text +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_aws_tags +from ..module_utils.waiters import get_waiter + + +def get_subnet_info(subnet): + if 'Subnets' in subnet: + return [get_subnet_info(s) for s in subnet['Subnets']] + elif 'Subnet' in subnet: + subnet = camel_dict_to_snake_dict(subnet['Subnet']) + else: + subnet = camel_dict_to_snake_dict(subnet) + + if 'tags' in subnet: + subnet['tags'] = boto3_tag_list_to_ansible_dict(subnet['tags']) + else: + subnet['tags'] = dict() + + if 'subnet_id' in subnet: + subnet['id'] = subnet['subnet_id'] + del subnet['subnet_id'] + + subnet['ipv6_cidr_block'] = '' + subnet['ipv6_association_id'] = '' + ipv6set = subnet.get('ipv6_cidr_block_association_set') + if ipv6set: + for item in ipv6set: + if item.get('ipv6_cidr_block_state', {}).get('state') in ('associated', 'associating'): + subnet['ipv6_cidr_block'] = item['ipv6_cidr_block'] + subnet['ipv6_association_id'] = item['association_id'] + + return subnet + + +@AWSRetry.exponential_backoff() +def describe_subnets_with_backoff(client, **params): + return client.describe_subnets(**params) + + +def waiter_params(module, params, start_time): + if not module.botocore_at_least("1.7.0"): + remaining_wait_timeout = int(module.params['wait_timeout'] + start_time - time.time()) + params['WaiterConfig'] = {'Delay': 5, 'MaxAttempts': remaining_wait_timeout // 5} + return params + + +def handle_waiter(conn, module, waiter_name, params, start_time): + try: + get_waiter(conn, waiter_name).wait( + **waiter_params(module, params, start_time) + ) + except botocore.exceptions.WaiterError as e: + module.fail_json_aws(e, "Failed to wait for updates to complete") + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "An exception happened while trying to wait for updates") + + +def create_subnet(conn, module, vpc_id, cidr, ipv6_cidr=None, az=None, start_time=None): + wait = module.params['wait'] + wait_timeout = module.params['wait_timeout'] + + params = dict(VpcId=vpc_id, + CidrBlock=cidr) + + if ipv6_cidr: + params['Ipv6CidrBlock'] = ipv6_cidr + + if az: + params['AvailabilityZone'] = az + + try: + subnet = get_subnet_info(conn.create_subnet(**params)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't create subnet") + + # Sometimes AWS takes its time to create a subnet and so using + # new subnets's id to do things like create tags results in + # exception. + if wait and subnet.get('state') != 'available': + handle_waiter(conn, module, 'subnet_exists', {'SubnetIds': [subnet['id']]}, start_time) + try: + conn.get_waiter('subnet_available').wait( + **waiter_params(module, {'SubnetIds': [subnet['id']]}, start_time) + ) + subnet['state'] = 'available' + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Create subnet action timed out waiting for subnet to become available") + + return subnet + + +def ensure_tags(conn, module, subnet, tags, purge_tags, start_time): + changed = False + + filters = ansible_dict_to_boto3_filter_list({'resource-id': subnet['id'], 'resource-type': 'subnet'}) + try: + cur_tags = conn.describe_tags(Filters=filters) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't describe tags") + + to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags) + + if to_update: + try: + if not module.check_mode: + AWSRetry.exponential_backoff( + catch_extra_error_codes=['InvalidSubnetID.NotFound'] + )(conn.create_tags)( + Resources=[subnet['id']], + Tags=ansible_dict_to_boto3_tag_list(to_update) + ) + + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't create tags") + + if to_delete: + try: + if not module.check_mode: + tags_list = [] + for key in to_delete: + tags_list.append({'Key': key}) + + AWSRetry.exponential_backoff( + catch_extra_error_codes=['InvalidSubnetID.NotFound'] + )(conn.delete_tags)(Resources=[subnet['id']], Tags=tags_list) + + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete tags") + + if module.params['wait'] and not module.check_mode: + # Wait for tags to be updated + filters = [{'Name': 'tag:{0}'.format(k), 'Values': [v]} for k, v in tags.items()] + handle_waiter(conn, module, 'subnet_exists', + {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time) + + return changed + + +def ensure_map_public(conn, module, subnet, map_public, check_mode, start_time): + if check_mode: + return + try: + conn.modify_subnet_attribute(SubnetId=subnet['id'], MapPublicIpOnLaunch={'Value': map_public}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't modify subnet attribute") + + +def ensure_assign_ipv6_on_create(conn, module, subnet, assign_instances_ipv6, check_mode, start_time): + if check_mode: + return + try: + conn.modify_subnet_attribute(SubnetId=subnet['id'], AssignIpv6AddressOnCreation={'Value': assign_instances_ipv6}) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't modify subnet attribute") + + +def disassociate_ipv6_cidr(conn, module, subnet, start_time): + if subnet.get('assign_ipv6_address_on_creation'): + ensure_assign_ipv6_on_create(conn, module, subnet, False, False, start_time) + + try: + conn.disassociate_subnet_cidr_block(AssociationId=subnet['ipv6_association_id']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't disassociate ipv6 cidr block id {0} from subnet {1}" + .format(subnet['ipv6_association_id'], subnet['id'])) + + # Wait for cidr block to be disassociated + if module.params['wait']: + filters = ansible_dict_to_boto3_filter_list( + {'ipv6-cidr-block-association.state': ['disassociated'], + 'vpc-id': subnet['vpc_id']} + ) + handle_waiter(conn, module, 'subnet_exists', + {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time) + + +def ensure_ipv6_cidr_block(conn, module, subnet, ipv6_cidr, check_mode, start_time): + wait = module.params['wait'] + changed = False + + if subnet['ipv6_association_id'] and not ipv6_cidr: + if not check_mode: + disassociate_ipv6_cidr(conn, module, subnet, start_time) + changed = True + + if ipv6_cidr: + filters = ansible_dict_to_boto3_filter_list({'ipv6-cidr-block-association.ipv6-cidr-block': ipv6_cidr, + 'vpc-id': subnet['vpc_id']}) + + try: + check_subnets = get_subnet_info(describe_subnets_with_backoff(conn, Filters=filters)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't get subnet info") + + if check_subnets and check_subnets[0]['ipv6_cidr_block']: + module.fail_json(msg="The IPv6 CIDR '{0}' conflicts with another subnet".format(ipv6_cidr)) + + if subnet['ipv6_association_id']: + if not check_mode: + disassociate_ipv6_cidr(conn, module, subnet, start_time) + changed = True + + try: + if not check_mode: + associate_resp = conn.associate_subnet_cidr_block(SubnetId=subnet['id'], Ipv6CidrBlock=ipv6_cidr) + changed = True + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't associate ipv6 cidr {0} to {1}".format(ipv6_cidr, subnet['id'])) + else: + if not check_mode and wait: + filters = ansible_dict_to_boto3_filter_list( + {'ipv6-cidr-block-association.state': ['associated'], + 'vpc-id': subnet['vpc_id']} + ) + handle_waiter(conn, module, 'subnet_exists', + {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time) + + if associate_resp.get('Ipv6CidrBlockAssociation', {}).get('AssociationId'): + subnet['ipv6_association_id'] = associate_resp['Ipv6CidrBlockAssociation']['AssociationId'] + subnet['ipv6_cidr_block'] = associate_resp['Ipv6CidrBlockAssociation']['Ipv6CidrBlock'] + if subnet['ipv6_cidr_block_association_set']: + subnet['ipv6_cidr_block_association_set'][0] = camel_dict_to_snake_dict(associate_resp['Ipv6CidrBlockAssociation']) + else: + subnet['ipv6_cidr_block_association_set'].append(camel_dict_to_snake_dict(associate_resp['Ipv6CidrBlockAssociation'])) + + return changed + + +def get_matching_subnet(conn, module, vpc_id, cidr): + filters = ansible_dict_to_boto3_filter_list({'vpc-id': vpc_id, 'cidr-block': cidr}) + try: + subnets = get_subnet_info(describe_subnets_with_backoff(conn, Filters=filters)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't get matching subnet") + + if subnets: + return subnets[0] + + return None + + +def ensure_subnet_present(conn, module): + subnet = get_matching_subnet(conn, module, module.params['vpc_id'], module.params['cidr']) + changed = False + + # Initialize start so max time does not exceed the specified wait_timeout for multiple operations + start_time = time.time() + + if subnet is None: + if not module.check_mode: + subnet = create_subnet(conn, module, module.params['vpc_id'], module.params['cidr'], + ipv6_cidr=module.params['ipv6_cidr'], az=module.params['az'], start_time=start_time) + changed = True + # Subnet will be None when check_mode is true + if subnet is None: + return { + 'changed': changed, + 'subnet': {} + } + if module.params['wait']: + handle_waiter(conn, module, 'subnet_exists', {'SubnetIds': [subnet['id']]}, start_time) + + if module.params['ipv6_cidr'] != subnet.get('ipv6_cidr_block'): + if ensure_ipv6_cidr_block(conn, module, subnet, module.params['ipv6_cidr'], module.check_mode, start_time): + changed = True + + if module.params['map_public'] != subnet['map_public_ip_on_launch']: + ensure_map_public(conn, module, subnet, module.params['map_public'], module.check_mode, start_time) + changed = True + + if module.params['assign_instances_ipv6'] != subnet.get('assign_ipv6_address_on_creation'): + ensure_assign_ipv6_on_create(conn, module, subnet, module.params['assign_instances_ipv6'], module.check_mode, start_time) + changed = True + + if module.params['tags'] != subnet['tags']: + stringified_tags_dict = dict((to_text(k), to_text(v)) for k, v in module.params['tags'].items()) + if ensure_tags(conn, module, subnet, stringified_tags_dict, module.params['purge_tags'], start_time): + changed = True + + subnet = get_matching_subnet(conn, module, module.params['vpc_id'], module.params['cidr']) + if not module.check_mode and module.params['wait']: + # GET calls are not monotonic for map_public_ip_on_launch and assign_ipv6_address_on_creation + # so we only wait for those if necessary just before returning the subnet + subnet = ensure_final_subnet(conn, module, subnet, start_time) + + return { + 'changed': changed, + 'subnet': subnet + } + + +def ensure_final_subnet(conn, module, subnet, start_time): + for rewait in range(0, 30): + map_public_correct = False + assign_ipv6_correct = False + + if module.params['map_public'] == subnet['map_public_ip_on_launch']: + map_public_correct = True + else: + if module.params['map_public']: + handle_waiter(conn, module, 'subnet_has_map_public', {'SubnetIds': [subnet['id']]}, start_time) + else: + handle_waiter(conn, module, 'subnet_no_map_public', {'SubnetIds': [subnet['id']]}, start_time) + + if module.params['assign_instances_ipv6'] == subnet.get('assign_ipv6_address_on_creation'): + assign_ipv6_correct = True + else: + if module.params['assign_instances_ipv6']: + handle_waiter(conn, module, 'subnet_has_assign_ipv6', {'SubnetIds': [subnet['id']]}, start_time) + else: + handle_waiter(conn, module, 'subnet_no_assign_ipv6', {'SubnetIds': [subnet['id']]}, start_time) + + if map_public_correct and assign_ipv6_correct: + break + + time.sleep(5) + subnet = get_matching_subnet(conn, module, module.params['vpc_id'], module.params['cidr']) + + return subnet + + +def ensure_subnet_absent(conn, module): + subnet = get_matching_subnet(conn, module, module.params['vpc_id'], module.params['cidr']) + if subnet is None: + return {'changed': False} + + try: + if not module.check_mode: + conn.delete_subnet(SubnetId=subnet['id']) + if module.params['wait']: + handle_waiter(conn, module, 'subnet_deleted', {'SubnetIds': [subnet['id']]}, time.time()) + return {'changed': True} + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg="Couldn't delete subnet") + + +def main(): + argument_spec = dict( + az=dict(default=None, required=False), + cidr=dict(required=True), + ipv6_cidr=dict(default='', required=False), + state=dict(default='present', choices=['present', 'absent']), + tags=dict(default={}, required=False, type='dict', aliases=['resource_tags']), + vpc_id=dict(required=True), + map_public=dict(default=False, required=False, type='bool'), + assign_instances_ipv6=dict(default=False, required=False, type='bool'), + wait=dict(type='bool', default=True), + wait_timeout=dict(type='int', default=300, required=False), + purge_tags=dict(default=True, type='bool') + ) + + required_if = [('assign_instances_ipv6', True, ['ipv6_cidr'])] + + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=required_if) + + if module.params.get('assign_instances_ipv6') and not module.params.get('ipv6_cidr'): + module.fail_json(msg="assign_instances_ipv6 is True but ipv6_cidr is None or an empty string") + + if not module.botocore_at_least("1.7.0"): + module.warn("botocore >= 1.7.0 is required to use wait_timeout for custom wait times") + + connection = module.client('ec2') + + state = module.params.get('state') + + try: + if state == 'present': + result = ensure_subnet_present(connection, module) + elif state == 'absent': + result = ensure_subnet_absent(connection, module) + except botocore.exceptions.ClientError as e: + module.fail_json_aws(e) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet_facts.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet_facts.py new file mode 100644 index 00000000..316d532e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet_facts.py @@ -0,0 +1,229 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_subnet_info +version_added: 1.0.0 +short_description: Gather information about ec2 VPC subnets in AWS +description: + - Gather information about ec2 VPC subnets in AWS + - This module was called C(ec2_vpc_subnet_facts) before Ansible 2.9. The usage did not change. +author: "Rob White (@wimnat)" +requirements: + - boto3 + - botocore +options: + subnet_ids: + description: + - A list of subnet IDs to gather information for. + aliases: ['subnet_id'] + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html) for possible filters. + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all VPC subnets +- amazon.aws.ec2_vpc_subnet_info: + +# Gather information about a particular VPC subnet using ID +- amazon.aws.ec2_vpc_subnet_info: + subnet_ids: subnet-00112233 + +# Gather information about any VPC subnet with a tag key Name and value Example +- amazon.aws.ec2_vpc_subnet_info: + filters: + "tag:Name": Example + +# Gather information about any VPC subnet within VPC with ID vpc-abcdef00 +- amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: vpc-abcdef00 + +# Gather information about a set of VPC subnets, publicA, publicB and publicC within a +# VPC with ID vpc-abcdef00 and then use the jinja map function to return the +# subnet_ids as a list. + +- amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: vpc-abcdef00 + "tag:Name": "{{ item }}" + loop: + - publicA + - publicB + - publicC + register: subnet_info + +- set_fact: + subnet_ids: "{{ subnet_info.subnets|map(attribute='id')|list }}" +''' + +RETURN = ''' +subnets: + description: Returns an array of complex objects as described below. + returned: success + type: complex + contains: + subnet_id: + description: The ID of the Subnet. + returned: always + type: str + id: + description: The ID of the Subnet (for backwards compatibility). + returned: always + type: str + vpc_id: + description: The ID of the VPC . + returned: always + type: str + state: + description: The state of the subnet. + returned: always + type: str + tags: + description: A dict of tags associated with the Subnet. + returned: always + type: dict + map_public_ip_on_launch: + description: True/False depending on attribute setting for public IP mapping. + returned: always + type: bool + default_for_az: + description: True if this is the default subnet for AZ. + returned: always + type: bool + cidr_block: + description: The IPv4 CIDR block assigned to the subnet. + returned: always + type: str + available_ip_address_count: + description: Count of available IPs in subnet. + returned: always + type: str + availability_zone: + description: The availability zone where the subnet exists. + returned: always + type: str + assign_ipv6_address_on_creation: + description: True/False depending on attribute setting for IPv6 address assignment. + returned: always + type: bool + ipv6_cidr_block_association_set: + description: An array of IPv6 cidr block association set information. + returned: always + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + ipv6_cidr_block: + description: The IPv6 CIDR block that is associated with the subnet. + returned: always + type: str + ipv6_cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +@AWSRetry.exponential_backoff() +def describe_subnets_with_backoff(connection, subnet_ids, filters): + """ + Describe Subnets with AWSRetry backoff throttling support. + + connection : boto3 client connection object + subnet_ids : list of subnet ids for which to gather information + filters : additional filters to apply to request + """ + return connection.describe_subnets(SubnetIds=subnet_ids, Filters=filters) + + +def describe_subnets(connection, module): + """ + Describe Subnets. + + module : AnsibleAWSModule object + connection : boto3 client connection object + """ + # collect parameters + filters = ansible_dict_to_boto3_filter_list(module.params.get('filters')) + subnet_ids = module.params.get('subnet_ids') + + if subnet_ids is None: + # Set subnet_ids to empty list if it is None + subnet_ids = [] + + # init empty list for return vars + subnet_info = list() + + # Get the basic VPC info + try: + response = describe_subnets_with_backoff(connection, subnet_ids, filters) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to describe subnets') + + for subnet in response['Subnets']: + # for backwards compatibility + subnet['id'] = subnet['SubnetId'] + subnet_info.append(camel_dict_to_snake_dict(subnet)) + # convert tag list to ansible dict + subnet_info[-1]['tags'] = boto3_tag_list_to_ansible_dict(subnet.get('Tags', [])) + + module.exit_json(subnets=subnet_info) + + +def main(): + argument_spec = dict( + subnet_ids=dict(type='list', elements='str', default=[], aliases=['subnet_id']), + filters=dict(type='dict', default={}) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + if module._name == 'ec2_vpc_subnet_facts': + module.deprecate("The 'ec2_vpc_subnet_facts' module has been renamed to 'ec2_vpc_subnet_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2') + + describe_subnets(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet_info.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet_info.py new file mode 100644 index 00000000..316d532e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/ec2_vpc_subnet_info.py @@ -0,0 +1,229 @@ +#!/usr/bin/python +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ec2_vpc_subnet_info +version_added: 1.0.0 +short_description: Gather information about ec2 VPC subnets in AWS +description: + - Gather information about ec2 VPC subnets in AWS + - This module was called C(ec2_vpc_subnet_facts) before Ansible 2.9. The usage did not change. +author: "Rob White (@wimnat)" +requirements: + - boto3 + - botocore +options: + subnet_ids: + description: + - A list of subnet IDs to gather information for. + aliases: ['subnet_id'] + type: list + elements: str + filters: + description: + - A dict of filters to apply. Each dict item consists of a filter key and a filter value. + See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSubnets.html) for possible filters. + type: dict +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Gather information about all VPC subnets +- amazon.aws.ec2_vpc_subnet_info: + +# Gather information about a particular VPC subnet using ID +- amazon.aws.ec2_vpc_subnet_info: + subnet_ids: subnet-00112233 + +# Gather information about any VPC subnet with a tag key Name and value Example +- amazon.aws.ec2_vpc_subnet_info: + filters: + "tag:Name": Example + +# Gather information about any VPC subnet within VPC with ID vpc-abcdef00 +- amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: vpc-abcdef00 + +# Gather information about a set of VPC subnets, publicA, publicB and publicC within a +# VPC with ID vpc-abcdef00 and then use the jinja map function to return the +# subnet_ids as a list. + +- amazon.aws.ec2_vpc_subnet_info: + filters: + vpc-id: vpc-abcdef00 + "tag:Name": "{{ item }}" + loop: + - publicA + - publicB + - publicC + register: subnet_info + +- set_fact: + subnet_ids: "{{ subnet_info.subnets|map(attribute='id')|list }}" +''' + +RETURN = ''' +subnets: + description: Returns an array of complex objects as described below. + returned: success + type: complex + contains: + subnet_id: + description: The ID of the Subnet. + returned: always + type: str + id: + description: The ID of the Subnet (for backwards compatibility). + returned: always + type: str + vpc_id: + description: The ID of the VPC . + returned: always + type: str + state: + description: The state of the subnet. + returned: always + type: str + tags: + description: A dict of tags associated with the Subnet. + returned: always + type: dict + map_public_ip_on_launch: + description: True/False depending on attribute setting for public IP mapping. + returned: always + type: bool + default_for_az: + description: True if this is the default subnet for AZ. + returned: always + type: bool + cidr_block: + description: The IPv4 CIDR block assigned to the subnet. + returned: always + type: str + available_ip_address_count: + description: Count of available IPs in subnet. + returned: always + type: str + availability_zone: + description: The availability zone where the subnet exists. + returned: always + type: str + assign_ipv6_address_on_creation: + description: True/False depending on attribute setting for IPv6 address assignment. + returned: always + type: bool + ipv6_cidr_block_association_set: + description: An array of IPv6 cidr block association set information. + returned: always + type: complex + contains: + association_id: + description: The association ID + returned: always + type: str + ipv6_cidr_block: + description: The IPv6 CIDR block that is associated with the subnet. + returned: always + type: str + ipv6_cidr_block_state: + description: A hash/dict that contains a single item. The state of the cidr block association. + returned: always + type: dict + contains: + state: + description: The CIDR block association state. + returned: always + type: str +''' + +try: + import botocore +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict + + +@AWSRetry.exponential_backoff() +def describe_subnets_with_backoff(connection, subnet_ids, filters): + """ + Describe Subnets with AWSRetry backoff throttling support. + + connection : boto3 client connection object + subnet_ids : list of subnet ids for which to gather information + filters : additional filters to apply to request + """ + return connection.describe_subnets(SubnetIds=subnet_ids, Filters=filters) + + +def describe_subnets(connection, module): + """ + Describe Subnets. + + module : AnsibleAWSModule object + connection : boto3 client connection object + """ + # collect parameters + filters = ansible_dict_to_boto3_filter_list(module.params.get('filters')) + subnet_ids = module.params.get('subnet_ids') + + if subnet_ids is None: + # Set subnet_ids to empty list if it is None + subnet_ids = [] + + # init empty list for return vars + subnet_info = list() + + # Get the basic VPC info + try: + response = describe_subnets_with_backoff(connection, subnet_ids, filters) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to describe subnets') + + for subnet in response['Subnets']: + # for backwards compatibility + subnet['id'] = subnet['SubnetId'] + subnet_info.append(camel_dict_to_snake_dict(subnet)) + # convert tag list to ansible dict + subnet_info[-1]['tags'] = boto3_tag_list_to_ansible_dict(subnet.get('Tags', [])) + + module.exit_json(subnets=subnet_info) + + +def main(): + argument_spec = dict( + subnet_ids=dict(type='list', elements='str', default=[], aliases=['subnet_id']), + filters=dict(type='dict', default={}) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + if module._name == 'ec2_vpc_subnet_facts': + module.deprecate("The 'ec2_vpc_subnet_facts' module has been renamed to 'ec2_vpc_subnet_info'", date='2021-12-01', collection_name='amazon.aws') + + connection = module.client('ec2') + + describe_subnets(connection, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/s3_bucket.py b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/s3_bucket.py new file mode 100644 index 00000000..3c4f6422 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/plugins/modules/s3_bucket.py @@ -0,0 +1,876 @@ +#!/usr/bin/python +# +# This is a free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This Ansible library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: s3_bucket +version_added: 1.0.0 +short_description: Manage S3 buckets in AWS, DigitalOcean, Ceph, Walrus, FakeS3 and StorageGRID +description: + - Manage S3 buckets in AWS, DigitalOcean, Ceph, Walrus, FakeS3 and StorageGRID. +requirements: [ boto3 ] +author: "Rob White (@wimnat)" +options: + force: + description: + - When trying to delete a bucket, delete all keys (including versions and delete markers) + in the bucket first (an S3 bucket must be empty for a successful deletion). + type: bool + default: 'no' + name: + description: + - Name of the S3 bucket. + required: true + type: str + policy: + description: + - The JSON policy as a string. + type: json + s3_url: + description: + - S3 URL endpoint for usage with DigitalOcean, Ceph, Eucalyptus and FakeS3 etc. + - Assumes AWS if not specified. + - For Walrus, use FQDN of the endpoint without scheme nor path. + aliases: [ S3_URL ] + type: str + ceph: + description: + - Enable API compatibility with Ceph. It takes into account the S3 API subset working + with Ceph in order to provide the same module behaviour where possible. + type: bool + default: false + requester_pays: + description: + - With Requester Pays buckets, the requester instead of the bucket owner pays the cost + of the request and the data download from the bucket. + type: bool + state: + description: + - Create or remove the S3 bucket. + required: false + default: present + choices: [ 'present', 'absent' ] + type: str + tags: + description: + - Tags dict to apply to bucket. + type: dict + purge_tags: + description: + - Whether to remove tags that aren't present in the I(tags) parameter. + type: bool + default: True + versioning: + description: + - Whether versioning is enabled or disabled (note that once versioning is enabled, it can only be suspended). + type: bool + encryption: + description: + - Describes the default server-side encryption to apply to new objects in the bucket. + In order to remove the server-side encryption, the encryption needs to be set to 'none' explicitly. + choices: [ 'none', 'AES256', 'aws:kms' ] + type: str + encryption_key_id: + description: KMS master key ID to use for the default encryption. This parameter is allowed if I(encryption) is C(aws:kms). If + not specified then it will default to the AWS provided KMS key. + type: str + public_access: + description: + - Configure public access block for S3 bucket. + - This option cannot be used together with I(delete_public_access). + suboptions: + block_public_acls: + description: Sets BlockPublicAcls value. + type: bool + default: False + block_public_policy: + description: Sets BlockPublicPolicy value. + type: bool + default: False + ignore_public_acls: + description: Sets IgnorePublicAcls value. + type: bool + default: False + restrict_public_buckets: + description: Sets RestrictPublicAcls value. + type: bool + default: False + type: dict + version_added: 1.3.0 + delete_public_access: + description: + - Delete public access block configuration from bucket. + - This option cannot be used together with a I(public_access) definition. + default: false + type: bool + version_added: 1.3.0 + +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +notes: + - If C(requestPayment), C(policy), C(tagging) or C(versioning) + operations/API aren't implemented by the endpoint, module doesn't fail + if each parameter satisfies the following condition. + I(requester_pays) is C(False), I(policy), I(tags), and I(versioning) are C(None). +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +# Create a simple S3 bucket +- amazon.aws.s3_bucket: + name: mys3bucket + state: present + +# Create a simple S3 bucket on Ceph Rados Gateway +- amazon.aws.s3_bucket: + name: mys3bucket + s3_url: http://your-ceph-rados-gateway-server.xxx + ceph: true + +# Remove an S3 bucket and any keys it contains +- amazon.aws.s3_bucket: + name: mys3bucket + state: absent + force: yes + +# Create a bucket, add a policy from a file, enable requester pays, enable versioning and tag +- amazon.aws.s3_bucket: + name: mys3bucket + policy: "{{ lookup('file','policy.json') }}" + requester_pays: yes + versioning: yes + tags: + example: tag1 + another: tag2 + +# Create a simple DigitalOcean Spaces bucket using their provided regional endpoint +- amazon.aws.s3_bucket: + name: mydobucket + s3_url: 'https://nyc3.digitaloceanspaces.com' + +# Create a bucket with AES256 encryption +- amazon.aws.s3_bucket: + name: mys3bucket + state: present + encryption: "AES256" + +# Create a bucket with aws:kms encryption, KMS key +- amazon.aws.s3_bucket: + name: mys3bucket + state: present + encryption: "aws:kms" + encryption_key_id: "arn:aws:kms:us-east-1:1234/5678example" + +# Create a bucket with aws:kms encryption, default key +- amazon.aws.s3_bucket: + name: mys3bucket + state: present + encryption: "aws:kms" + +# Create a bucket with public policy block configuration +- amazon.aws.s3_bucket: + name: mys3bucket + state: present + public_access: + BlockPublicAcls: true + IgnorePublicAcls: true + ## keys == 'false' can be ommited, undefined keys defaults to 'false' + # BlockPublicPolicy: false + # RestrictPublicBuckets: false + +# Delete public policy block from bucket +- amazon.aws.s3_bucket: + name: mys3bucket + state: present + delete_public_access: true +''' + +import json +import os +import time + +try: + from botocore.exceptions import BotoCoreError, ClientError, EndpointConnectionError, WaiterError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible.module_utils.basic import to_text +from ansible.module_utils.six import string_types +from ansible.module_utils.six.moves.urllib.parse import urlparse + +from ..module_utils.core import AnsibleAWSModule +from ..module_utils.core import is_boto3_error_code +from ..module_utils.ec2 import AWSRetry +from ..module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ..module_utils.ec2 import boto3_conn +from ..module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ..module_utils.ec2 import compare_policies +from ..module_utils.ec2 import get_aws_connection_info +from ..module_utils.ec2 import snake_dict_to_camel_dict + + +def create_or_update_bucket(s3_client, module, location): + + policy = module.params.get("policy") + name = module.params.get("name") + requester_pays = module.params.get("requester_pays") + tags = module.params.get("tags") + purge_tags = module.params.get("purge_tags") + versioning = module.params.get("versioning") + encryption = module.params.get("encryption") + encryption_key_id = module.params.get("encryption_key_id") + public_access = module.params.get("public_access") + delete_public_access = module.params.get("delete_public_access") + changed = False + result = {} + + try: + bucket_is_present = bucket_exists(s3_client, name) + except EndpointConnectionError as e: + module.fail_json_aws(e, msg="Invalid endpoint provided: %s" % to_text(e)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to check bucket presence") + + if not bucket_is_present: + try: + bucket_changed = create_bucket(s3_client, name, location) + s3_client.get_waiter('bucket_exists').wait(Bucket=name) + changed = changed or bucket_changed + except WaiterError as e: + module.fail_json_aws(e, msg='An error occurred waiting for the bucket to become available') + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed while creating bucket") + + # Versioning + try: + versioning_status = get_bucket_versioning(s3_client, name) + except is_boto3_error_code(['NotImplemented', 'XNotImplemented']) as exp: + if versioning is not None: + module.fail_json_aws(exp, msg="Failed to get bucket versioning") + except (BotoCoreError, ClientError) as exp: + module.fail_json_aws(exp, msg="Failed to get bucket versioning") + else: + if versioning is not None: + required_versioning = None + if versioning and versioning_status.get('Status') != "Enabled": + required_versioning = 'Enabled' + elif not versioning and versioning_status.get('Status') == "Enabled": + required_versioning = 'Suspended' + + if required_versioning: + try: + put_bucket_versioning(s3_client, name, required_versioning) + changed = True + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to update bucket versioning") + + versioning_status = wait_versioning_is_applied(module, s3_client, name, required_versioning) + + # This output format is there to ensure compatibility with previous versions of the module + result['versioning'] = { + 'Versioning': versioning_status.get('Status', 'Disabled'), + 'MfaDelete': versioning_status.get('MFADelete', 'Disabled'), + } + + # Requester pays + try: + requester_pays_status = get_bucket_request_payment(s3_client, name) + except is_boto3_error_code(['NotImplemented', 'XNotImplemented']): + if requester_pays is not None: + module.fail_json_aws(exp, msg="Failed to get bucket request payment") + except (BotoCoreError, ClientError) as exp: + module.fail_json_aws(exp, msg="Failed to get bucket request payment") + else: + if requester_pays is not None: + payer = 'Requester' if requester_pays else 'BucketOwner' + if requester_pays_status != payer: + put_bucket_request_payment(s3_client, name, payer) + requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=False) + if requester_pays_status is None: + # We have seen that it happens quite a lot of times that the put request was not taken into + # account, so we retry one more time + put_bucket_request_payment(s3_client, name, payer) + requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=True) + changed = True + + result['requester_pays'] = requester_pays + + # Policy + try: + current_policy = get_bucket_policy(s3_client, name) + except is_boto3_error_code(['NotImplemented', 'XNotImplemented']): + if policy is not None: + module.fail_json_aws(exp, msg="Failed to get bucket policy") + except (BotoCoreError, ClientError) as exp: + module.fail_json_aws(exp, msg="Failed to get bucket policy") + else: + if policy is not None: + if isinstance(policy, string_types): + policy = json.loads(policy) + + if not policy and current_policy: + try: + delete_bucket_policy(s3_client, name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to delete bucket policy") + current_policy = wait_policy_is_applied(module, s3_client, name, policy) + changed = True + elif compare_policies(current_policy, policy): + try: + put_bucket_policy(s3_client, name, policy) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to update bucket policy") + current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=False) + if current_policy is None: + # As for request payement, it happens quite a lot of times that the put request was not taken into + # account, so we retry one more time + put_bucket_policy(s3_client, name, policy) + current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=True) + changed = True + + result['policy'] = current_policy + + # Tags + try: + current_tags_dict = get_current_bucket_tags_dict(s3_client, name) + except is_boto3_error_code(['NotImplemented', 'XNotImplemented']): + if tags is not None: + module.fail_json_aws(exp, msg="Failed to get bucket tags") + except (ClientError, BotoCoreError) as exp: + module.fail_json_aws(exp, msg="Failed to get bucket tags") + else: + if tags is not None: + # Tags are always returned as text + tags = dict((to_text(k), to_text(v)) for k, v in tags.items()) + if not purge_tags: + # Ensure existing tags that aren't updated by desired tags remain + current_copy = current_tags_dict.copy() + current_copy.update(tags) + tags = current_copy + if current_tags_dict != tags: + if tags: + try: + put_bucket_tagging(s3_client, name, tags) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to update bucket tags") + else: + if purge_tags: + try: + delete_bucket_tagging(s3_client, name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to delete bucket tags") + current_tags_dict = wait_tags_are_applied(module, s3_client, name, tags) + changed = True + + result['tags'] = current_tags_dict + + # Encryption + try: + current_encryption = get_bucket_encryption(s3_client, name) + except (ClientError, BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to get bucket encryption") + + if encryption is not None: + current_encryption_algorithm = current_encryption.get('SSEAlgorithm') if current_encryption else None + current_encryption_key = current_encryption.get('KMSMasterKeyID') if current_encryption else None + if encryption == 'none' and current_encryption_algorithm is not None: + try: + delete_bucket_encryption(s3_client, name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to delete bucket encryption") + current_encryption = wait_encryption_is_applied(module, s3_client, name, None) + changed = True + elif encryption != 'none' and (encryption != current_encryption_algorithm) or (encryption == 'aws:kms' and current_encryption_key != encryption_key_id): + expected_encryption = {'SSEAlgorithm': encryption} + if encryption == 'aws:kms' and encryption_key_id is not None: + expected_encryption.update({'KMSMasterKeyID': encryption_key_id}) + current_encryption = put_bucket_encryption_with_retry(module, s3_client, name, expected_encryption) + changed = True + + result['encryption'] = current_encryption + + # Public access clock configuration + current_public_access = {} + + # -- Create / Update public access block + if public_access is not None: + try: + current_public_access = get_bucket_public_access(s3_client, name) + except (ClientError, BotoCoreError) as err_public_access: + module.fail_json_aws(err_public_access, msg="Failed to get bucket public access configuration") + camel_public_block = snake_dict_to_camel_dict(public_access, capitalize_first=True) + + if current_public_access == camel_public_block: + result['public_access_block'] = current_public_access + else: + put_bucket_public_access(s3_client, name, camel_public_block) + changed = True + result['public_access_block'] = camel_public_block + + # -- Delete public access block + if delete_public_access: + try: + current_public_access = get_bucket_public_access(s3_client, name) + except (ClientError, BotoCoreError) as err_public_access: + module.fail_json_aws(err_public_access, msg="Failed to get bucket public access configuration") + + if current_public_access == {}: + result['public_access_block'] = current_public_access + else: + delete_bucket_public_access(s3_client, name) + changed = True + result['public_access_block'] = {} + + # Module exit + module.exit_json(changed=changed, name=name, **result) + + +def bucket_exists(s3_client, bucket_name): + # head_bucket appeared to be really inconsistent, so we use list_buckets instead, + # and loop over all the buckets, even if we know it's less performant :( + all_buckets = s3_client.list_buckets(Bucket=bucket_name)['Buckets'] + return any(bucket['Name'] == bucket_name for bucket in all_buckets) + + +@AWSRetry.exponential_backoff(max_delay=120) +def create_bucket(s3_client, bucket_name, location): + try: + configuration = {} + if location not in ('us-east-1', None): + configuration['LocationConstraint'] = location + if len(configuration) > 0: + s3_client.create_bucket(Bucket=bucket_name, CreateBucketConfiguration=configuration) + else: + s3_client.create_bucket(Bucket=bucket_name) + return True + except is_boto3_error_code('BucketAlreadyOwnedByYou'): + # We should never get here since we check the bucket presence before calling the create_or_update_bucket + # method. However, the AWS Api sometimes fails to report bucket presence, so we catch this exception + return False + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def put_bucket_tagging(s3_client, bucket_name, tags): + s3_client.put_bucket_tagging(Bucket=bucket_name, Tagging={'TagSet': ansible_dict_to_boto3_tag_list(tags)}) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def put_bucket_policy(s3_client, bucket_name, policy): + s3_client.put_bucket_policy(Bucket=bucket_name, Policy=json.dumps(policy)) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def delete_bucket_policy(s3_client, bucket_name): + s3_client.delete_bucket_policy(Bucket=bucket_name) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def get_bucket_policy(s3_client, bucket_name): + try: + current_policy = json.loads(s3_client.get_bucket_policy(Bucket=bucket_name).get('Policy')) + except is_boto3_error_code('NoSuchBucketPolicy'): + return None + + return current_policy + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def put_bucket_request_payment(s3_client, bucket_name, payer): + s3_client.put_bucket_request_payment(Bucket=bucket_name, RequestPaymentConfiguration={'Payer': payer}) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def get_bucket_request_payment(s3_client, bucket_name): + return s3_client.get_bucket_request_payment(Bucket=bucket_name).get('Payer') + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def get_bucket_versioning(s3_client, bucket_name): + return s3_client.get_bucket_versioning(Bucket=bucket_name) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def put_bucket_versioning(s3_client, bucket_name, required_versioning): + s3_client.put_bucket_versioning(Bucket=bucket_name, VersioningConfiguration={'Status': required_versioning}) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def get_bucket_encryption(s3_client, bucket_name): + if not hasattr(s3_client, "get_bucket_encryption"): + return None + + try: + result = s3_client.get_bucket_encryption(Bucket=bucket_name) + return result.get('ServerSideEncryptionConfiguration', {}).get('Rules', [])[0].get('ApplyServerSideEncryptionByDefault') + except is_boto3_error_code('ServerSideEncryptionConfigurationNotFoundError'): + return None + except (IndexError, KeyError): + return None + + +def put_bucket_encryption_with_retry(module, s3_client, name, expected_encryption): + max_retries = 3 + for retries in range(1, max_retries + 1): + try: + put_bucket_encryption(s3_client, name, expected_encryption) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to set bucket encryption") + current_encryption = wait_encryption_is_applied(module, s3_client, name, expected_encryption, + should_fail=(retries == max_retries), retries=5) + if current_encryption == expected_encryption: + return current_encryption + + # We shouldn't get here, the only time this should happen is if + # current_encryption != expected_encryption and retries == max_retries + # Which should use module.fail_json and fail out first. + module.fail_json(msg='Failed to apply bucket encryption', + current=current_encryption, expected=expected_encryption, retries=retries) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def put_bucket_encryption(s3_client, bucket_name, encryption): + server_side_encryption_configuration = {'Rules': [{'ApplyServerSideEncryptionByDefault': encryption}]} + s3_client.put_bucket_encryption(Bucket=bucket_name, ServerSideEncryptionConfiguration=server_side_encryption_configuration) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def delete_bucket_tagging(s3_client, bucket_name): + s3_client.delete_bucket_tagging(Bucket=bucket_name) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def delete_bucket_encryption(s3_client, bucket_name): + s3_client.delete_bucket_encryption(Bucket=bucket_name) + + +@AWSRetry.exponential_backoff(max_delay=240, catch_extra_error_codes=['OperationAborted']) +def delete_bucket(s3_client, bucket_name): + try: + s3_client.delete_bucket(Bucket=bucket_name) + except is_boto3_error_code('NoSuchBucket'): + # This means bucket should have been in a deleting state when we checked it existence + # We just ignore the error + pass + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def put_bucket_public_access(s3_client, bucket_name, public_acces): + ''' + Put new public access block to S3 bucket + ''' + s3_client.put_public_access_block(Bucket=bucket_name, PublicAccessBlockConfiguration=public_acces) + + +@AWSRetry.exponential_backoff(max_delay=120, catch_extra_error_codes=['NoSuchBucket', 'OperationAborted']) +def delete_bucket_public_access(s3_client, bucket_name): + ''' + Delete public access block from S3 bucket + ''' + s3_client.delete_public_access_block(Bucket=bucket_name) + + +def wait_policy_is_applied(module, s3_client, bucket_name, expected_policy, should_fail=True): + for dummy in range(0, 12): + try: + current_policy = get_bucket_policy(s3_client, bucket_name) + except (ClientError, BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to get bucket policy") + + if compare_policies(current_policy, expected_policy): + time.sleep(5) + else: + return current_policy + if should_fail: + module.fail_json(msg="Bucket policy failed to apply in the expected time", + requested_policy=expected_policy, live_policy=current_policy) + else: + return None + + +def wait_payer_is_applied(module, s3_client, bucket_name, expected_payer, should_fail=True): + for dummy in range(0, 12): + try: + requester_pays_status = get_bucket_request_payment(s3_client, bucket_name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to get bucket request payment") + if requester_pays_status != expected_payer: + time.sleep(5) + else: + return requester_pays_status + if should_fail: + module.fail_json(msg="Bucket request payment failed to apply in the expected time", + requested_status=expected_payer, live_status=requester_pays_status) + else: + return None + + +def wait_encryption_is_applied(module, s3_client, bucket_name, expected_encryption, should_fail=True, retries=12): + for dummy in range(0, retries): + try: + encryption = get_bucket_encryption(s3_client, bucket_name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to get updated encryption for bucket") + if encryption != expected_encryption: + time.sleep(5) + else: + return encryption + + if should_fail: + module.fail_json(msg="Bucket encryption failed to apply in the expected time", + requested_encryption=expected_encryption, live_encryption=encryption) + + return encryption + + +def wait_versioning_is_applied(module, s3_client, bucket_name, required_versioning): + for dummy in range(0, 24): + try: + versioning_status = get_bucket_versioning(s3_client, bucket_name) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to get updated versioning for bucket") + if versioning_status.get('Status') != required_versioning: + time.sleep(8) + else: + return versioning_status + module.fail_json(msg="Bucket versioning failed to apply in the expected time", + requested_versioning=required_versioning, live_versioning=versioning_status) + + +def wait_tags_are_applied(module, s3_client, bucket_name, expected_tags_dict): + for dummy in range(0, 12): + try: + current_tags_dict = get_current_bucket_tags_dict(s3_client, bucket_name) + except (ClientError, BotoCoreError) as e: + module.fail_json_aws(e, msg="Failed to get bucket policy") + if current_tags_dict != expected_tags_dict: + time.sleep(5) + else: + return current_tags_dict + module.fail_json(msg="Bucket tags failed to apply in the expected time", + requested_tags=expected_tags_dict, live_tags=current_tags_dict) + + +def get_current_bucket_tags_dict(s3_client, bucket_name): + try: + current_tags = s3_client.get_bucket_tagging(Bucket=bucket_name).get('TagSet') + except is_boto3_error_code('NoSuchTagSet'): + return {} + # The Ceph S3 API returns a different error code to AWS + except is_boto3_error_code('NoSuchTagSetError'): # pylint: disable=duplicate-except + return {} + + return boto3_tag_list_to_ansible_dict(current_tags) + + +def get_bucket_public_access(s3_client, bucket_name): + ''' + Get current bucket public access block + ''' + try: + bucket_public_access_block = s3_client.get_public_access_block(Bucket=bucket_name) + return bucket_public_access_block['PublicAccessBlockConfiguration'] + except is_boto3_error_code('NoSuchPublicAccessBlockConfiguration'): + return {} + + +def paginated_list(s3_client, **pagination_params): + pg = s3_client.get_paginator('list_objects_v2') + for page in pg.paginate(**pagination_params): + yield [data['Key'] for data in page.get('Contents', [])] + + +def paginated_versions_list(s3_client, **pagination_params): + try: + pg = s3_client.get_paginator('list_object_versions') + for page in pg.paginate(**pagination_params): + # We have to merge the Versions and DeleteMarker lists here, as DeleteMarkers can still prevent a bucket deletion + yield [(data['Key'], data['VersionId']) for data in (page.get('Versions', []) + page.get('DeleteMarkers', []))] + except is_boto3_error_code('NoSuchBucket'): + yield [] + + +def destroy_bucket(s3_client, module): + + force = module.params.get("force") + name = module.params.get("name") + try: + bucket_is_present = bucket_exists(s3_client, name) + except EndpointConnectionError as e: + module.fail_json_aws(e, msg="Invalid endpoint provided: %s" % to_text(e)) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to check bucket presence") + + if not bucket_is_present: + module.exit_json(changed=False) + + if force: + # if there are contents then we need to delete them (including versions) before we can delete the bucket + try: + for key_version_pairs in paginated_versions_list(s3_client, Bucket=name): + formatted_keys = [{'Key': key, 'VersionId': version} for key, version in key_version_pairs] + for fk in formatted_keys: + # remove VersionId from cases where they are `None` so that + # unversioned objects are deleted using `DeleteObject` + # rather than `DeleteObjectVersion`, improving backwards + # compatibility with older IAM policies. + if not fk.get('VersionId'): + fk.pop('VersionId') + + if formatted_keys: + resp = s3_client.delete_objects(Bucket=name, Delete={'Objects': formatted_keys}) + if resp.get('Errors'): + module.fail_json( + msg='Could not empty bucket before deleting. Could not delete objects: {0}'.format( + ', '.join([k['Key'] for k in resp['Errors']]) + ), + errors=resp['Errors'], response=resp + ) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed while deleting bucket") + + try: + delete_bucket(s3_client, name) + s3_client.get_waiter('bucket_not_exists').wait(Bucket=name, WaiterConfig=dict(Delay=5, MaxAttempts=60)) + except WaiterError as e: + module.fail_json_aws(e, msg='An error occurred waiting for the bucket to be deleted.') + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Failed to delete bucket") + + module.exit_json(changed=True) + + +def is_fakes3(s3_url): + """ Return True if s3_url has scheme fakes3:// """ + if s3_url is not None: + return urlparse(s3_url).scheme in ('fakes3', 'fakes3s') + else: + return False + + +def get_s3_client(module, aws_connect_kwargs, location, ceph, s3_url): + if s3_url and ceph: # TODO - test this + ceph = urlparse(s3_url) + params = dict(module=module, conn_type='client', resource='s3', use_ssl=ceph.scheme == 'https', region=location, endpoint=s3_url, **aws_connect_kwargs) + elif is_fakes3(s3_url): + fakes3 = urlparse(s3_url) + port = fakes3.port + if fakes3.scheme == 'fakes3s': + protocol = "https" + if port is None: + port = 443 + else: + protocol = "http" + if port is None: + port = 80 + params = dict(module=module, conn_type='client', resource='s3', region=location, + endpoint="%s://%s:%s" % (protocol, fakes3.hostname, to_text(port)), + use_ssl=fakes3.scheme == 'fakes3s', **aws_connect_kwargs) + else: + params = dict(module=module, conn_type='client', resource='s3', region=location, endpoint=s3_url, **aws_connect_kwargs) + return boto3_conn(**params) + + +def main(): + + argument_spec = dict( + force=dict(default=False, type='bool'), + policy=dict(type='json'), + name=dict(required=True), + requester_pays=dict(type='bool'), + s3_url=dict(aliases=['S3_URL']), + state=dict(default='present', choices=['present', 'absent']), + tags=dict(type='dict'), + purge_tags=dict(type='bool', default=True), + versioning=dict(type='bool'), + ceph=dict(default=False, type='bool'), + encryption=dict(choices=['none', 'AES256', 'aws:kms']), + encryption_key_id=dict(), + public_access=dict(type='dict', options=dict( + block_public_acls=dict(type='bool', default=False), + ignore_public_acls=dict(type='bool', default=False), + block_public_policy=dict(type='bool', default=False), + restrict_public_buckets=dict(type='bool', default=False))), + delete_public_access=dict(type='bool', default=False) + ) + + required_by = dict( + encryption_key_id=('encryption',), + ) + + mutually_exclusive = [ + ['public_access', 'delete_public_access'] + ] + + module = AnsibleAWSModule( + argument_spec=argument_spec, required_by=required_by, mutually_exclusive=mutually_exclusive + ) + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + + if region in ('us-east-1', '', None): + # default to US Standard region + location = 'us-east-1' + else: + # Boto uses symbolic names for locations but region strings will + # actually work fine for everything except us-east-1 (US Standard) + location = region + + s3_url = module.params.get('s3_url') + ceph = module.params.get('ceph') + + # allow eucarc environment variables to be used if ansible vars aren't set + if not s3_url and 'S3_URL' in os.environ: + s3_url = os.environ['S3_URL'] + + if ceph and not s3_url: + module.fail_json(msg='ceph flavour requires s3_url') + + # Look at s3_url and tweak connection settings + # if connecting to Ceph RGW, Walrus or fakes3 + if s3_url: + for key in ['validate_certs', 'security_token', 'profile_name']: + aws_connect_kwargs.pop(key, None) + s3_client = get_s3_client(module, aws_connect_kwargs, location, ceph, s3_url) + + if s3_client is None: # this should never happen + module.fail_json(msg='Unknown error, failed to create s3 connection, no information from boto.') + + state = module.params.get("state") + encryption = module.params.get("encryption") + encryption_key_id = module.params.get("encryption_key_id") + + if not hasattr(s3_client, "get_bucket_encryption"): + if encryption is not None: + module.fail_json(msg="Using bucket encryption requires botocore version >= 1.7.41") + + # Parameter validation + if encryption_key_id is not None and encryption != 'aws:kms': + module.fail_json(msg="Only 'aws:kms' is a valid option for encryption parameter when you specify encryption_key_id.") + + if state == 'present': + create_or_update_bucket(s3_client, module, location) + elif state == 'absent': + destroy_bucket(s3_client, module) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/requirements.txt b/collections-debian-merged/ansible_collections/amazon/aws/requirements.txt new file mode 100644 index 00000000..5c4c76b8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/requirements.txt @@ -0,0 +1,3 @@ +boto>=2.49.0 +botocore>=1.12.249 +boto3>=1.9.249 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/shippable.yml b/collections-debian-merged/ansible_collections/amazon/aws/shippable.yml new file mode 100644 index 00000000..9dc19538 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/shippable.yml @@ -0,0 +1,73 @@ +language: python + +env: + matrix: + - T=none + +matrix: + exclude: + - env: T=none + include: + - env: T=sanity/1 A_REV=devel + - env: T=sanity/1 A_REV=stable-2.9 + - env: T=sanity/1 A_REV=stable-2.10 + + - env: T=units/2.7/1 A_REV=devel + - env: T=units/3.6/1 A_REV=devel + - env: T=units/3.7/1 A_REV=devel + - env: T=units/3.8/1 A_REV=devel + - env: T=units/3.9/1 A_REV=devel + - env: T=units/2.7/1 A_REV=stable-2.9 + - env: T=units/3.6/1 A_REV=stable-2.9 + - env: T=units/3.7/1 A_REV=stable-2.9 + - env: T=units/3.8/1 A_REV=stable-2.9 + - env: T=units/2.7/1 A_REV=stable-2.10 + - env: T=units/3.6/1 A_REV=stable-2.10 + - env: T=units/3.7/1 A_REV=stable-2.10 + - env: T=units/3.8/1 A_REV=stable-2.10 + - env: T=units/3.9/1 A_REV=stable-2.10 + + - env: T=aws/2.7/1 A_REV=devel + - env: T=aws/3.7/1 A_REV=devel + - env: T=aws/2.7/1 A_REV=stable-2.9 + - env: T=aws/3.7/1 A_REV=stable-2.9 + - env: T=aws/2.7/1 A_REV=stable-2.10 + - env: T=aws/3.7/1 A_REV=stable-2.10 + + - env: T=aws/2.7/2 A_REV=devel + - env: T=aws/3.7/2 A_REV=devel + - env: T=aws/2.7/2 A_REV=stable-2.9 + - env: T=aws/3.7/2 A_REV=stable-2.9 + - env: T=aws/2.7/2 A_REV=stable-2.10 + - env: T=aws/3.7/2 A_REV=stable-2.10 + + - env: T=aws/2.7/3 A_REV=devel + - env: T=aws/3.7/3 A_REV=devel + - env: T=aws/2.7/3 A_REV=stable-2.9 + - env: T=aws/3.7/3 A_REV=stable-2.9 + - env: T=aws/2.7/3 A_REV=stable-2.10 + - env: T=aws/3.7/3 A_REV=stable-2.10 + + - env: T=aws/2.7/4 A_REV=devel + - env: T=aws/3.7/4 A_REV=devel + - env: T=aws/2.7/4 A_REV=stable-2.9 + - env: T=aws/3.7/4 A_REV=stable-2.9 + - env: T=aws/2.7/4 A_REV=stable-2.10 + - env: T=aws/3.7/4 A_REV=stable-2.10 +branches: + except: + - "*-patch-*" + - "revert-*-*" + +build: + ci: + - tests/utils/shippable/timing.sh tests/utils/shippable/shippable.sh $T + +integrations: + notifications: + - integrationName: email + type: email + on_success: never + on_failure: never + on_start: never + on_pull_request: never diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/.gitignore b/collections-debian-merged/ansible_collections/amazon/aws/tests/.gitignore new file mode 100644 index 00000000..e17f1f38 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/.gitignore @@ -0,0 +1,3 @@ +output/ +integration/cloud-config-aws.ini +integration/cloud-config-aws.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/requirements.txt b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/requirements.txt new file mode 100644 index 00000000..4f1c4feb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/requirements.txt @@ -0,0 +1,3 @@ +# netaddr is needed for ansible.netcommon.ipv6 +netaddr +virtualenv diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/aliases new file mode 100644 index 00000000..157ce0c9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group3 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/main.yml new file mode 100644 index 00000000..2fe745f0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/main.yml @@ -0,0 +1,5 @@ +- hosts: localhost + connection: local + environment: "{{ ansible_test.environment }}" + tasks: + - include_tasks: 'tasks/main.yml' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/meta/main.yml new file mode 100644 index 00000000..1810d4be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/tasks/main.yml new file mode 100644 index 00000000..ba92416e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/tasks/main.yml @@ -0,0 +1,26 @@ +- set_fact: + virtualenv: "{{ remote_tmp_dir }}/virtualenv" + virtualenv_command: "{{ ansible_python_interpreter }} -m virtualenv" + +- set_fact: + virtualenv_interpreter: "{{ virtualenv }}/bin/python" + +- pip: + name: virtualenv + +- pip: + name: + - 'botocore>=1.13.0' + - boto3 + - coverage<5 + virtualenv: "{{ virtualenv }}" + virtualenv_command: "{{ virtualenv_command }}" + virtualenv_site_packages: no + +- include_tasks: tests.yml + vars: + ansible_python_interpreter: "{{ virtualenv_interpreter }}" + +- file: + path: "{{ virtualenv }}" + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/tasks/tests.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/tasks/tests.yml new file mode 100644 index 00000000..8e8935f3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_az_info/tasks/tests.yml @@ -0,0 +1,183 @@ +--- +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + - name: 'List available AZs in current Region' + aws_az_info: + register: region_azs + + - name: check task return attributes + vars: + first_az: '{{ region_azs.availability_zones[0] }}' + assert: + that: + - region_azs is successful + - '"availability_zones" in region_azs' + - '"group_name" in first_az' + - '"messages" in first_az' + - '"network_border_group" in first_az' + - '"opt_in_status" in first_az' + - '"region_name" in first_az' + - '"state" in first_az' + - '"zone_id" in first_az' + - '"zone_name" in first_az' + - '"zone_type" in first_az' + + - name: 'List available AZs in current Region - check_mode' + aws_az_info: + check_mode: yes + register: check_azs + + - name: check task return attributes + vars: + first_az: '{{ check_azs.availability_zones[0] }}' + assert: + that: + - check_azs is successful + - '"availability_zones" in check_azs' + - '"group_name" in first_az' + - '"messages" in first_az' + - '"network_border_group" in first_az' + - '"opt_in_status" in first_az' + - '"region_name" in first_az' + - '"state" in first_az' + - '"zone_id" in first_az' + - '"zone_name" in first_az' + - '"zone_type" in first_az' + + + # Be specific - aws_region isn't guaranteed to be any specific value + - name: 'List Available AZs in us-east-1' + aws_az_info: + region: 'us-east-1' + register: us_east_1 + + - name: 'Check that an AZ from us-east-1 has valid looking attributes' + vars: + first_az: '{{ us_east_1.availability_zones[0] }}' + assert: + that: + - us_east_1 is successful + - '"availability_zones" in us_east_1' + - '"group_name" in first_az' + - '"messages" in first_az' + - '"network_border_group" in first_az' + - '"opt_in_status" in first_az' + - '"region_name" in first_az' + - '"state" in first_az' + - '"zone_id" in first_az' + - '"zone_name" in first_az' + - '"zone_type" in first_az' + - first_az.group_name.startswith('us-east-1') + - first_az.network_border_group.startswith('us-east-1') + - first_az.region_name == 'us-east-1' + - first_az.zone_id.startswith('use1-az') + - not first_az.zone_id == "use1-az" + - first_az.zone_name.startswith('us-east-1') + - not first_az.zone_name == 'us-east-1' + - first_az.zone_type == 'availability-zone' + + - name: 'Filter Available AZs in us-west-2 using - ("zone-name")' + aws_az_info: + region: 'us-west-2' + filters: + zone-name: 'us-west-2c' + register: us_west_2 + + - name: 'Check that an AZ from us-west-2 has attributes we expect' + vars: + first_az: '{{ us_west_2.availability_zones[0] }}' + assert: + that: + - us_west_2 is successful + - '"availability_zones" in us_west_2' + - us_west_2.availability_zones | length == 1 + - '"group_name" in first_az' + - '"messages" in first_az' + - '"network_border_group" in first_az' + - '"opt_in_status" in first_az' + - '"region_name" in first_az' + - '"state" in first_az' + - '"zone_id" in first_az' + - '"zone_name" in first_az' + - '"zone_type" in first_az' + - first_az.group_name == 'us-west-2' + - first_az.network_border_group == 'us-west-2' + - first_az.region_name == 'us-west-2' + # AZs are mapped to the 'real' AZs on a per-account basis + - first_az.zone_id.startswith('usw2-az') + - not first_az.zone_id == 'usw2-az' + - first_az.zone_name == 'us-west-2c' + - first_az.zone_type == 'availability-zone' + + - name: 'Filter Available AZs in eu-central-1 using _ ("zone_name")' + aws_az_info: + region: 'eu-central-1' + filters: + zone_name: 'eu-central-1b' + register: eu_central_1 + + - name: 'Check that eu-central-1b has the attributes we expect' + vars: + first_az: '{{ eu_central_1.availability_zones[0] }}' + assert: + that: + - eu_central_1 is successful + - '"availability_zones" in eu_central_1' + - eu_central_1.availability_zones | length == 1 + - '"group_name" in first_az' + - '"messages" in first_az' + - '"network_border_group" in first_az' + - '"opt_in_status" in first_az' + - '"region_name" in first_az' + - '"state" in first_az' + - '"zone_id" in first_az' + - '"zone_name" in first_az' + - '"zone_type" in first_az' + - first_az.group_name == 'eu-central-1' + - first_az.network_border_group == 'eu-central-1' + - first_az.region_name == 'eu-central-1' + # AZs are mapped to the 'real' AZs on a per-account basis + - first_az.zone_id.startswith('euc1-az') + - not first_az.zone_id == "euc1-az" + - first_az.zone_name == 'eu-central-1b' + - first_az.zone_type == 'availability-zone' + + - name: 'Filter Available AZs in eu-west-2 using _ and - ("zone_name" and "zone-name") : _ wins ' + aws_az_info: + region: 'eu-west-2' + filters: + zone-name: 'eu-west-2a' + zone_name: 'eu-west-2c' + register: eu_west_2 + + - name: 'Check that we get the AZ specified by zone_name rather than zone-name' + vars: + first_az: '{{ eu_west_2.availability_zones[0] }}' + assert: + that: + - eu_west_2 is successful + - '"availability_zones" in eu_west_2' + - eu_west_2.availability_zones | length == 1 + - '"group_name" in first_az' + - '"messages" in first_az' + - '"network_border_group" in first_az' + - '"opt_in_status" in first_az' + - '"region_name" in first_az' + - '"state" in first_az' + - '"zone_id" in first_az' + - '"zone_name" in first_az' + - '"zone_type" in first_az' + - first_az.group_name == 'eu-west-2' + - first_az.network_border_group == 'eu-west-2' + - first_az.region_name == 'eu-west-2' + # AZs are mapped to the 'real' AZs on a per-account basis + - first_az.zone_id.startswith('euw2-az') + - not first_az.zone_id == "euw2-az" + - first_az.zone_name == 'eu-west-2c' + - first_az.zone_type == 'availability-zone' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_caller_info/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_caller_info/aliases new file mode 100644 index 00000000..6e3860be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_caller_info/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_caller_info/tasks/main.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_caller_info/tasks/main.yaml new file mode 100644 index 00000000..c40d0f11 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_caller_info/tasks/main.yaml @@ -0,0 +1,18 @@ +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + block: + - name: retrieve caller facts + aws_caller_info: + register: result + + - name: assert correct keys are returned + assert: + that: + - result.account is not none + - result.arn is not none + - result.user_id is not none + - result.account_alias is not none diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/aliases new file mode 100644 index 00000000..72a9fb4f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group4 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/defaults/main.yml new file mode 100644 index 00000000..eb7dd2d3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for s3 +bucket_name: '{{resource_prefix}}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/files/hello.txt b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/files/hello.txt new file mode 100644 index 00000000..8ab686ea --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/files/hello.txt @@ -0,0 +1 @@ +Hello, World! diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/files/test.png b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/files/test.png Binary files differnew file mode 100644 index 00000000..1dc64bab --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/files/test.png diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/meta/main.yml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/meta/main.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/tasks/main.yml new file mode 100644 index 00000000..5d811ce1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/tasks/main.yml @@ -0,0 +1,663 @@ +--- +# Integration tests for aws_s3 +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + block: + - name: Create temporary directory + tempfile: + state: directory + register: tmpdir + + - name: Create content + set_fact: + content: "{{ lookup('password', '/dev/null chars=ascii_letters,digits,hexdigits,punctuation') }}" + + - name: test create bucket without permissions + module_defaults: { group/aws: {} } + aws_s3: + bucket: "{{ bucket_name }}" + mode: create + register: result + ignore_errors: yes + + - assert: + that: + - result is failed + - "result.msg != 'MODULE FAILURE'" + + - name: test create bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: create + register: result + + - assert: + that: + - result is changed + + - name: trying to create a bucket name that already exists + aws_s3: + bucket: "{{ bucket_name }}" + mode: create + register: result + + - assert: + that: + - result is not changed + + - name: Create local upload.txt + copy: + content: "{{ content }}" + dest: "{{ tmpdir.path }}/upload.txt" + + - name: stat the file + stat: + path: "{{ tmpdir.path }}/upload.txt" + get_checksum: yes + register: upload_file + + - name: test putting an object in the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: delete.txt + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + - result.msg == "PUT operation complete" + + - name: test using aws_s3 with async + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: delete.txt + register: test_async + async: 30 + poll: 0 + + - name: ensure it completed + async_status: + jid: "{{ test_async.ansible_job_id }}" + register: status + until: status is finished + retries: 10 + + - name: test put with overwrite=different and unmodified object + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: delete.txt + overwrite: different + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is not changed + + - name: check that roles file lookups work as expected + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: hello.txt + object: delete.txt + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + - result.msg == "PUT operation complete" + + - name: test put with overwrite=never + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: delete.txt + overwrite: never + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is not changed + + - name: test put with overwrite=different and modified object + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: delete.txt + overwrite: different + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + + - name: test put with overwrite=always + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + object: delete.txt + overwrite: always + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + + - name: test get object + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: delete.txt + retries: 3 + delay: 3 + register: result + until: "result.msg == 'GET operation complete'" + + - name: stat the file so we can compare the checksums + stat: + path: "{{ tmpdir.path }}/download.txt" + get_checksum: yes + register: download_file + + - assert: + that: + - upload_file.stat.checksum == download_file.stat.checksum + + - name: test get with overwrite=different and identical files + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: delete.txt + overwrite: different + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is not changed + + - name: modify destination + copy: + dest: "{{ tmpdir.path }}/download.txt" + src: hello.txt + + - name: test get with overwrite=never + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: delete.txt + overwrite: never + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is not changed + + - name: test get with overwrite=different and modified file + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: delete.txt + overwrite: different + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + + - name: test get with overwrite=always + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: delete.txt + overwrite: always + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + + - name: test geturl of the object + aws_s3: + bucket: "{{ bucket_name }}" + mode: geturl + object: delete.txt + retries: 3 + delay: 3 + register: result + until: result is changed + + - assert: + that: + - "'Download url:' in result.msg" + - result is changed + + - name: test getstr of the object + aws_s3: + bucket: "{{ bucket_name }}" + mode: getstr + object: delete.txt + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result.msg == "GET operation complete" + - result.contents == content + + - name: test list to get all objects in the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: list + retries: 3 + delay: 3 + register: result + + - assert: + that: + - "'delete.txt' in result.s3_keys" + - result.msg == "LIST operation complete" + + - name: test delobj to just delete an object in the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: delobj + object: delete.txt + retries: 3 + delay: 3 + register: result + + - assert: + that: + - "'Object deleted from bucket' in result.msg" + - result is changed + + - name: test putting an encrypted object in the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + encrypt: yes + object: delete_encrypt.txt + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + - result.msg == "PUT operation complete" + + - name: test get encrypted object + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download_encrypted.txt" + object: delete_encrypt.txt + retries: 3 + delay: 3 + register: result + until: "result.msg == 'GET operation complete'" + + - name: stat the file so we can compare the checksums + stat: + path: "{{ tmpdir.path }}/download_encrypted.txt" + get_checksum: yes + register: download_file + + - assert: + that: + - upload_file.stat.checksum == download_file.stat.checksum + + - name: delete encrypted file + aws_s3: + bucket: "{{ bucket_name }}" + mode: delobj + object: delete_encrypt.txt + retries: 3 + delay: 3 + + - name: test putting an aws:kms encrypted object in the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/upload.txt" + encrypt: yes + encryption_mode: aws:kms + object: delete_encrypt_kms.txt + retries: 3 + delay: 3 + register: result + + - assert: + that: + - result is changed + - result.msg == "PUT operation complete" + + - name: test get KMS encrypted object + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download_kms.txt" + object: delete_encrypt_kms.txt + retries: 3 + delay: 3 + register: result + until: "result.msg == 'GET operation complete'" + + - name: get the stat of the file so we can compare the checksums + stat: + path: "{{ tmpdir.path }}/download_kms.txt" + get_checksum: yes + register: download_file + + - assert: + that: + - upload_file.stat.checksum == download_file.stat.checksum + + # FIXME - could use a test that checks uploaded file is *actually* aws:kms encrypted + + - name: delete KMS encrypted file + aws_s3: + bucket: "{{ bucket_name }}" + mode: delobj + object: delete_encrypt_kms.txt + retries: 3 + delay: 3 + + # FIXME: could use a test that checks non standard KMS key + # but that would require ability to create and remove such keys. + # PRs exist for that, but propose deferring until after merge. + + - name: test creation of empty path + aws_s3: + bucket: "{{ bucket_name }}" + mode: create + object: foo/bar/baz/ + retries: 3 + delay: 3 + register: result + + - assert: + that: + - "'Virtual directory foo/bar/baz/ created' in result.msg" + - result is changed + + - name: test deletion of empty path + aws_s3: + bucket: "{{ bucket_name }}" + mode: delobj + object: foo/bar/baz/ + retries: 3 + delay: 3 + + - name: test delete bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: delete + register: result + retries: 3 + delay: 3 + until: result is changed + + - assert: + that: + - result is changed + + - name: test create a bucket with a dot in the name + aws_s3: + bucket: "{{ bucket_name + '.bucket' }}" + mode: create + register: result + + - assert: + that: + - result is changed + + - name: test delete a bucket with a dot in the name + aws_s3: + bucket: "{{ bucket_name + '.bucket' }}" + mode: delete + register: result + + - assert: + that: + - result is changed + + - name: test delete a nonexistent bucket + aws_s3: + bucket: "{{ bucket_name + '.bucket' }}" + mode: delete + register: result + + - assert: + that: + - result is not changed + + - name: make tempfile 4 GB for OSX + command: + _raw_params: "dd if=/dev/zero of={{ tmpdir.path }}/largefile bs=1m count=4096" + when: ansible_distribution == 'MacOSX' + + - name: make tempfile 4 GB for linux + command: + _raw_params: "dd if=/dev/zero of={{ tmpdir.path }}/largefile bs=1M count=4096" + when: ansible_system == 'Linux' + + - name: test multipart download - platform specific + block: + - name: make a bucket to upload the file + aws_s3: + bucket: "{{ bucket_name }}" + mode: create + + - name: upload the file to the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: put + src: "{{ tmpdir.path }}/largefile" + object: multipart.txt + + - name: download file once + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: multipart.txt + overwrite: different + retries: 3 + delay: 3 + until: "result.msg == 'GET operation complete'" + register: result + + - assert: + that: + - result is changed + + - name: download file again + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download.txt" + object: multipart.txt + overwrite: different + register: result + + - assert: + that: + - result is not changed + when: ansible_system == 'Linux' or ansible_distribution == 'MacOSX' + + - name: create an object from static content + aws_s3: + bucket: "{{ bucket_name }}" + object: put-content.txt + mode: put + content: >- + test content + register: result + + - assert: + that: + - result is changed + + - name: ensure idempotency on static content + aws_s3: + bucket: "{{ bucket_name }}" + object: put-content.txt + mode: put + overwrite: different + content: >- + test content + register: result + + - assert: + that: + - result is not changed + + - name: fetch test content + aws_s3: + bucket: "{{ bucket_name }}" + mode: getstr + object: put-content.txt + register: result + + - assert: + that: + - result.contents == "test content" + + - set_fact: + put_template_text: test template + + - name: create an object from a template + aws_s3: + bucket: "{{ bucket_name }}" + object: put-template.txt + mode: put + content: "{{ lookup('template', 'templates/put-template.txt.j2') }}" + register: result + + - assert: + that: + - result is changed + + - name: fetch template content + aws_s3: + bucket: "{{ bucket_name }}" + mode: getstr + object: put-template.txt + register: result + + - assert: + that: + - result.contents == "{{ lookup('template', 'templates/put-template.txt.j2') }}" + + # at present, there is no lookup that can process binary data, so we use slurp instead + - slurp: + src: "{{ role_path }}/files/test.png" + register: put_binary + + - name: create an object from binary data + aws_s3: + bucket: "{{ bucket_name }}" + object: put-binary.bin + mode: put + content_base64: "{{ put_binary.content }}" + register: result + + - assert: + that: + - result is changed + + - name: fetch binary content + aws_s3: + bucket: "{{ bucket_name }}" + mode: get + dest: "{{ tmpdir.path }}/download_binary.bin" + object: put-binary.bin + register: result + + - name: stat the files so we can compare the checksums + stat: + path: "{{ item }}" + get_checksum: yes + loop: + - "{{ role_path }}/files/test.png" + - "{{ tmpdir.path }}/download_binary.bin" + register: binary_files + + - assert: + that: + - binary_files.results[0].stat.checksum == binary_files.results[1].stat.checksum + + always: + - name: remove uploaded files + aws_s3: + bucket: "{{ bucket_name }}" + mode: delobj + object: "{{ item }}" + loop: + - hello.txt + - delete.txt + - delete_encrypt.txt + - delete_encrypt_kms.txt + - put-content.txt + - put-template.txt + - put-binary.txt + ignore_errors: yes + + - name: delete temporary files + file: + state: absent + path: "{{ tmpdir.path }}" + ignore_errors: yes + + - name: delete the bucket + aws_s3: + bucket: "{{ bucket_name }}" + mode: delete + ignore_errors: yes + + - name: delete the dot bucket + aws_s3: + bucket: "{{ bucket_name + '.bucket' }}" + mode: delete + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/templates/put-template.txt.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/templates/put-template.txt.j2 new file mode 100644 index 00000000..2a75e9f2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/aws_s3/templates/put-template.txt.j2 @@ -0,0 +1,2 @@ +template: +{{ put_template_text }} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/aliases new file mode 100644 index 00000000..55555be7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group2 +cloudformation_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/defaults/main.yml new file mode 100644 index 00000000..aaf0ca7e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/defaults/main.yml @@ -0,0 +1,8 @@ +stack_name: "{{ resource_prefix }}" + +vpc_name: '{{ resource_prefix }}-vpc' +vpc_seed: '{{ resource_prefix }}' +vpc_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.0.0/16' +subnet_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' + +ec2_ami_name: 'amzn2-ami-hvm-2.*-x86_64-gp2' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/files/cf_template.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/files/cf_template.json new file mode 100644 index 00000000..ff4c5693 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/files/cf_template.json @@ -0,0 +1,37 @@ +{ + "AWSTemplateFormatVersion" : "2010-09-09", + + "Description" : "Create an Amazon EC2 instance.", + + "Parameters" : { + "InstanceType" : { + "Description" : "EC2 instance type", + "Type" : "String", + "Default" : "t3.nano", + "AllowedValues" : [ "t3.micro", "t3.nano"] + }, + "ImageId" : { + "Type" : "String" + }, + "SubnetId" : { + "Type" : "String" + } + }, + + "Resources" : { + "EC2Instance" : { + "Type" : "AWS::EC2::Instance", + "Properties" : { + "InstanceType" : { "Ref" : "InstanceType" }, + "ImageId" : { "Ref" : "ImageId" }, + "SubnetId": { "Ref" : "SubnetId" } + } + } + }, + + "Outputs" : { + "InstanceId" : { + "Value" : { "Ref" : "EC2Instance" } + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/tasks/main.yml new file mode 100644 index 00000000..9b89722b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/cloudformation/tasks/main.yml @@ -0,0 +1,463 @@ +--- + +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + block: + + # ==== Env setup ========================================================== + - name: list available AZs + aws_az_info: + register: region_azs + + - name: pick an AZ for testing + set_fact: + availability_zone: "{{ region_azs.availability_zones[0].zone_name }}" + + - name: Create a test VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + tags: + Name: Cloudformation testing + register: testing_vpc + + - name: Create a test subnet + ec2_vpc_subnet: + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: "{{ subnet_cidr }}" + az: "{{ availability_zone }}" + register: testing_subnet + + - name: Find AMI to use + ec2_ami_info: + owners: 'amazon' + filters: + name: '{{ ec2_ami_name }}' + register: ec2_amis + + - name: Set fact with latest AMI + vars: + latest_ami: '{{ ec2_amis.images | sort(attribute="creation_date") | last }}' + set_fact: + ec2_ami_image: '{{ latest_ami.image_id }}' + + # ==== Cloudformation tests =============================================== + + # 1. Basic stack creation (check mode, actual run and idempotency) + # 2. Tags + # 3. cloudformation_info tests (basic + all_facts) + # 4. termination_protection + # 5. create_changeset + changeset_name + + # There is still scope to add tests for - + # 1. capabilities + # 2. stack_policy + # 3. on_create_failure (covered in unit tests) + # 4. Passing in a role + # 5. nested stacks? + + + - name: create a cloudformation stack (check mode) + cloudformation: + stack_name: "{{ stack_name }}" + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: cf_stack + check_mode: yes + + - name: check task return attributes + assert: + that: + - cf_stack.changed + - "'msg' in cf_stack and 'New stack would be created' in cf_stack.msg" + + - name: create a cloudformation stack + cloudformation: + stack_name: "{{ stack_name }}" + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: cf_stack + + - name: check task return attributes + assert: + that: + - cf_stack.changed + - "'events' in cf_stack" + - "'output' in cf_stack and 'Stack CREATE complete' in cf_stack.output" + - "'stack_outputs' in cf_stack and 'InstanceId' in cf_stack.stack_outputs" + - "'stack_resources' in cf_stack" + + - name: create a cloudformation stack (check mode) (idempotent) + cloudformation: + stack_name: "{{ stack_name }}" + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: cf_stack + check_mode: yes + + - name: check task return attributes + assert: + that: + - not cf_stack.changed + + - name: create a cloudformation stack (idempotent) + cloudformation: + stack_name: "{{ stack_name }}" + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: cf_stack + + - name: check task return attributes + assert: + that: + - not cf_stack.changed + - "'output' in cf_stack and 'Stack is already up-to-date.' in cf_stack.output" + - "'stack_outputs' in cf_stack and 'InstanceId' in cf_stack.stack_outputs" + - "'stack_resources' in cf_stack" + + - name: get stack details + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + + - name: assert stack info + assert: + that: + - "'cloudformation' in stack_info" + - "stack_info.cloudformation | length == 1" + - "stack_name in stack_info.cloudformation" + - "'stack_description' in stack_info.cloudformation[stack_name]" + - "'stack_outputs' in stack_info.cloudformation[stack_name]" + - "'stack_parameters' in stack_info.cloudformation[stack_name]" + - "'stack_tags' in stack_info.cloudformation[stack_name]" + - "stack_info.cloudformation[stack_name].stack_tags.Stack == stack_name" + + - name: get stack details (checkmode) + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + check_mode: yes + + - name: assert stack info + assert: + that: + - "'cloudformation' in stack_info" + - "stack_info.cloudformation | length == 1" + - "stack_name in stack_info.cloudformation" + - "'stack_description' in stack_info.cloudformation[stack_name]" + - "'stack_outputs' in stack_info.cloudformation[stack_name]" + - "'stack_parameters' in stack_info.cloudformation[stack_name]" + - "'stack_tags' in stack_info.cloudformation[stack_name]" + - "stack_info.cloudformation[stack_name].stack_tags.Stack == stack_name" + + - name: get stack details (all_facts) + cloudformation_info: + stack_name: "{{ stack_name }}" + all_facts: yes + register: stack_info + + - name: assert stack info + assert: + that: + - "'stack_events' in stack_info.cloudformation[stack_name]" + - "'stack_policy' in stack_info.cloudformation[stack_name]" + - "'stack_resource_list' in stack_info.cloudformation[stack_name]" + - "'stack_resources' in stack_info.cloudformation[stack_name]" + - "'stack_template' in stack_info.cloudformation[stack_name]" + + - name: get stack details (all_facts) (checkmode) + cloudformation_info: + stack_name: "{{ stack_name }}" + all_facts: yes + register: stack_info + check_mode: yes + + - name: assert stack info + assert: + that: + - "'stack_events' in stack_info.cloudformation[stack_name]" + - "'stack_policy' in stack_info.cloudformation[stack_name]" + - "'stack_resource_list' in stack_info.cloudformation[stack_name]" + - "'stack_resources' in stack_info.cloudformation[stack_name]" + - "'stack_template' in stack_info.cloudformation[stack_name]" + + # ==== Cloudformation tests (create changeset) ============================ + + # try to create a changeset by changing instance type + - name: create a changeset + cloudformation: + stack_name: "{{ stack_name }}" + create_changeset: yes + changeset_name: "test-changeset" + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.micro" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: create_changeset_result + + - name: assert changeset created + assert: + that: + - "create_changeset_result.changed" + - "'change_set_id' in create_changeset_result" + - "'Stack CREATE_CHANGESET complete' in create_changeset_result.output" + + - name: get stack details with changesets + cloudformation_info: + stack_name: "{{ stack_name }}" + stack_change_sets: True + register: stack_info + + - name: assert changesets in info + assert: + that: + - "'stack_change_sets' in stack_info.cloudformation[stack_name]" + + - name: get stack details with changesets (checkmode) + cloudformation_info: + stack_name: "{{ stack_name }}" + stack_change_sets: True + register: stack_info + check_mode: yes + + - name: assert changesets in info + assert: + that: + - "'stack_change_sets' in stack_info.cloudformation[stack_name]" + + # try to create an empty changeset by passing in unchanged template + - name: create a changeset + cloudformation: + stack_name: "{{ stack_name }}" + create_changeset: yes + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: create_changeset_result + + - name: assert changeset created + assert: + that: + - "not create_changeset_result.changed" + - "'The created Change Set did not contain any changes to this stack and was deleted.' in create_changeset_result.output" + + # ==== Cloudformation tests (termination_protection) ====================== + + - name: set termination protection to true + cloudformation: + stack_name: "{{ stack_name }}" + termination_protection: yes + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: cf_stack + +# This fails - #65592 +# - name: check task return attributes +# assert: +# that: +# - cf_stack.changed + + - name: get stack details + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + + - name: assert stack info + assert: + that: + - "stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" + + - name: get stack details (checkmode) + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + check_mode: yes + + - name: assert stack info + assert: + that: + - "stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" + + - name: set termination protection to false + cloudformation: + stack_name: "{{ stack_name }}" + termination_protection: no + template_body: "{{ lookup('file','cf_template.json') }}" + template_parameters: + InstanceType: "t3.nano" + ImageId: "{{ ec2_ami_image }}" + SubnetId: "{{ testing_subnet.subnet.id }}" + tags: + Stack: "{{ stack_name }}" + test: "{{ resource_prefix }}" + register: cf_stack + +# This fails - #65592 +# - name: check task return attributes +# assert: +# that: +# - cf_stack.changed + + - name: get stack details + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + + - name: assert stack info + assert: + that: + - "not stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" + + - name: get stack details (checkmode) + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + check_mode: yes + + - name: assert stack info + assert: + that: + - "not stack_info.cloudformation[stack_name].stack_description.enable_termination_protection" + + # ==== Cloudformation tests (delete stack tests) ========================== + + - name: delete cloudformation stack (check mode) + cloudformation: + stack_name: "{{ stack_name }}" + state: absent + check_mode: yes + register: cf_stack + + - name: check task return attributes + assert: + that: + - cf_stack.changed + - "'msg' in cf_stack and 'Stack would be deleted' in cf_stack.msg" + + - name: delete cloudformation stack + cloudformation: + stack_name: "{{ stack_name }}" + state: absent + register: cf_stack + + - name: check task return attributes + assert: + that: + - cf_stack.changed + - "'output' in cf_stack and 'Stack Deleted' in cf_stack.output" + + - name: delete cloudformation stack (check mode) (idempotent) + cloudformation: + stack_name: "{{ stack_name }}" + state: absent + check_mode: yes + register: cf_stack + + - name: check task return attributes + assert: + that: + - not cf_stack.changed + - "'msg' in cf_stack" + - >- + "Stack doesn't exist" in cf_stack.msg + + - name: delete cloudformation stack (idempotent) + cloudformation: + stack_name: "{{ stack_name }}" + state: absent + register: cf_stack + + - name: check task return attributes + assert: + that: + - not cf_stack.changed + - "'output' in cf_stack and 'Stack not found.' in cf_stack.output" + + - name: get stack details + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + + - name: assert stack info + assert: + that: + - "not stack_info.cloudformation" + + - name: get stack details (checkmode) + cloudformation_info: + stack_name: "{{ stack_name }}" + register: stack_info + check_mode: yes + + - name: assert stack info + assert: + that: + - "not stack_info.cloudformation" + + # ==== Cleanup ============================================================ + + always: + + - name: delete stack + cloudformation: + stack_name: "{{ stack_name }}" + state: absent + ignore_errors: yes + + - name: Delete test subnet + ec2_vpc_subnet: + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: "{{ subnet_cidr }}" + state: absent + ignore_errors: yes + + - name: Delete test VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/aliases new file mode 100644 index 00000000..72a9fb4f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group4 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/defaults/main.yml new file mode 100644 index 00000000..cc65a725 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/defaults/main.yml @@ -0,0 +1,4 @@ +--- +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +ec2_ami_name: 'amzn2-ami-hvm-2.*-x86_64-gp2' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/tasks/main.yml new file mode 100644 index 00000000..624ddbb7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2/tasks/main.yml @@ -0,0 +1,208 @@ +--- +- module_defaults: + group/aws: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + collections: + - community.aws + + block: + + # SETUP: vpc, ec2 key pair, subnet, security group, ec2 instance + - name: list available AZs + aws_az_info: + register: region_azs + + - name: pick an AZ for testing + set_fact: + availability_zone: "{{ region_azs.availability_zones[0].zone_name }}" + + - name: create a VPC to work in + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: present + name: '{{ resource_prefix }}_setup' + resource_tags: + Name: '{{ resource_prefix }}_setup' + register: setup_vpc + + - name: create a key pair to use for creating an ec2 instance + ec2_key: + name: '{{ resource_prefix }}_setup' + state: present + register: setup_key + + - name: create a subnet to use for creating an ec2 instance + ec2_vpc_subnet: + az: '{{ availability_zone }}' + tags: '{{ resource_prefix }}_setup' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr }}' + state: present + resource_tags: + Name: '{{ resource_prefix }}_setup' + register: setup_subnet + + - name: create a security group to use for creating an ec2 instance + ec2_group: + name: '{{ resource_prefix }}_setup' + description: 'created by Ansible integration tests' + state: present + vpc_id: '{{ setup_vpc.vpc.id }}' + register: setup_sg + + - name: Find AMI to use + ec2_ami_info: + owners: 'amazon' + filters: + name: '{{ ec2_ami_name }}' + register: ec2_amis + + - name: Set fact with latest AMI + vars: + latest_ami: '{{ ec2_amis.images | sort(attribute="creation_date") | last }}' + set_fact: + ec2_ami_image: '{{ latest_ami.image_id }}' + + # ============================================================ + + - name: test first instance is started + ec2: + instance_type: t2.micro + key_name: '{{ setup_key.key.name }}' + state: present + image: '{{ ec2_ami_image }}' + wait: yes + instance_tags: + ResourcePrefix: '{{ resource_prefix }}-integration_tests' + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + register: test_instance_1 + + - name: test second instance is started + ec2: + instance_type: t2.micro + key_name: '{{ setup_key.key.name }}' + state: present + image: '{{ ec2_ami_image }}' + wait: yes + instance_tags: + ResourcePrefix: '{{ resource_prefix }}-another_tag' + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + register: test_instance_2 + + - name: assert instances started + assert: + that: + - "test_instance_1.instances[0].state == 'running'" + - "test_instance_2.instances[0].state == 'running'" + + - name: test first instance is terminated + ec2: + instance_ids: "{{ test_instance_1.instance_ids }}" + state: absent + wait: yes + register: result + + - name: assert instance terminated + assert: + that: + - "result.instances[0].state == 'terminated'" + + - name: test terminated instance is ignored when stopping + ec2: + instance_tags: + ResourcePrefix: '{{ resource_prefix }}-integration_tests' + state: stopped + wait: yes + register: result + + - name: assert resource not changed + assert: + that: + - "result.changed == False" + + - name: test second instance not terminated + ec2_instance_info: + instance_ids: "{{ test_instance_2.instance_ids }}" + register: result + + - name: assert second instance still running + assert: + that: + - (result.instances|length) == 1 + - "result.instances[0].state.name == 'running'" + + - name: test second instance is stopped + ec2: + instance_ids: "{{ test_instance_2.instance_ids }}" + state: stopped + wait: yes + register: result + + - name: assert second instance is stopped + assert: + that: + - "result.instances[0].state == 'stopped'" + + # ======================================================== + + always: + + # ============================================================ + + + # TEAR DOWN: ec2 instance, ec2 key pair, security group, vpc + - name: Announce teardown start + debug: + msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****" + + - name: get list of test instances + ec2_instance_info: + filters: + "tag:ResourcePrefix": "{{ resource_prefix }}-*" + register: test_instances + + - name: delete test instances + ec2: + instance_ids: "{{ test_instances.instances|map(attribute='instance_id') }}" + state: absent + wait: yes + ignore_errors: yes + + - name: remove setup keypair + ec2_key: + name: '{{resource_prefix}}_setup' + state: absent + ignore_errors: yes + + - name: remove setup security group + ec2_group: + name: '{{ resource_prefix }}_setup' + description: 'created by Ansible integration tests' + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + ignore_errors: yes + + - name: remove setup subnet + ec2_vpc_subnet: + az: '{{ ec2_region }}a' + tags: '{{resource_prefix}}_setup' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr }}' + state: absent + resource_tags: + Name: '{{ resource_prefix }}_setup' + ignore_errors: yes + + - name: remove setup VPC + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: absent + name: '{{ resource_prefix }}_setup' + resource_tags: + Name: '{{ resource_prefix }}_setup' + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/aliases new file mode 100644 index 00000000..d74e5f69 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group3 +ec2_ami_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/defaults/main.yml new file mode 100644 index 00000000..65bcbbc0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# defaults file for test_ec2_ami +ec2_ami_name: '{{resource_prefix}}' +ec2_ami_description: 'Created by ansible integration tests' +# image for Amazon Linux AMI 2017.03.1 (HVM), SSD Volume Type +ec2_ami_image: + us-east-1: ami-4fffc834 + us-east-2: ami-ea87a78f + +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml new file mode 100644 index 00000000..38084ce2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml @@ -0,0 +1,490 @@ +--- +# Test suite for ec2_ami +- module_defaults: + group/aws: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + block: + + # ============================================================ + + # SETUP: vpc, ec2 key pair, subnet, security group, ec2 instance, snapshot + - name: create a VPC to work in + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: present + name: '{{ ec2_ami_name }}_setup' + resource_tags: + Name: '{{ ec2_ami_name }}_setup' + register: setup_vpc + + - name: create a key pair to use for creating an ec2 instance + ec2_key: + name: '{{ ec2_ami_name }}_setup' + state: present + register: setup_key + + - name: create a subnet to use for creating an ec2 instance + ec2_vpc_subnet: + az: '{{ ec2_region }}a' + tags: '{{ ec2_ami_name }}_setup' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr }}' + state: present + resource_tags: + Name: '{{ ec2_ami_name }}_setup' + register: setup_subnet + + - name: create a security group to use for creating an ec2 instance + ec2_group: + name: '{{ ec2_ami_name }}_setup' + description: 'created by Ansible integration tests' + state: present + vpc_id: '{{ setup_vpc.vpc.id }}' + register: setup_sg + + - name: provision ec2 instance to create an image + ec2: + key_name: '{{ setup_key.key.name }}' + instance_type: t2.micro + state: present + image: '{{ ec2_region_images[ec2_region] }}' + wait: yes + instance_tags: + '{{ec2_ami_name}}_instance_setup': 'integration_tests' + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + register: setup_instance + + - name: take a snapshot of the instance to create an image + ec2_snapshot: + instance_id: '{{ setup_instance.instance_ids[0] }}' + device_name: /dev/xvda + state: present + register: setup_snapshot + + # ============================================================ + + - name: test clean failure if not providing image_id or name with state=present + ec2_ami: + instance_id: '{{ setup_instance.instance_ids[0] }}' + state: present + description: '{{ ec2_ami_description }}' + tags: + Name: '{{ ec2_ami_name }}_ami' + wait: yes + root_device_name: /dev/xvda + register: result + ignore_errors: yes + + - name: assert error message is helpful + assert: + that: + - result.failed + - "result.msg == 'one of the following is required: name, image_id'" + + # ============================================================ + + - name: create an image from the instance + ec2_ami: + instance_id: '{{ setup_instance.instance_ids[0] }}' + state: present + name: '{{ ec2_ami_name }}_ami' + description: '{{ ec2_ami_description }}' + tags: + Name: '{{ ec2_ami_name }}_ami' + wait: yes + root_device_name: /dev/xvda + register: result + + - name: set image id fact for deletion later + set_fact: + ec2_ami_image_id: "{{ result.image_id }}" + + - name: assert that image has been created + assert: + that: + - "result.changed" + - "result.image_id.startswith('ami-')" + - "'Name' in result.tags and result.tags.Name == ec2_ami_name + '_ami'" + + # ============================================================ + + - name: gather facts about the image created + ec2_ami_info: + image_ids: '{{ ec2_ami_image_id }}' + register: ami_facts_result + ignore_errors: true + + - name: assert that the right image was found + assert: + that: + - "ami_facts_result.images[0].image_id == ec2_ami_image_id" + + # some ec2_ami_info tests to test if the filtering is working fine. + # ============================================================ + + - name: gather info about the image + ec2_ami_info: + image_ids: '{{ ec2_region_images[ec2_region] }}' + register: ami_info_result + ignore_errors: true + + - name: assert that the right image was found + assert: + that: + - "ami_info_result.images[0].image_id == '{{ ec2_region_images[ec2_region] }}'" + + # ============================================================ + + - name: gather info about the image using boolean filter + ec2_ami_info: + image_ids: '{{ ec2_region_images[ec2_region] }}' + filters: + is-public: true + register: ami_info_result + ignore_errors: true + + - name: assert that the right image was found + assert: + that: + - "ami_info_result.images[0].image_id == '{{ ec2_region_images[ec2_region] }}'" + + # ============================================================ + + - name: gather info about the image using integer filter + ec2_ami_info: + image_ids: '{{ ec2_region_images[ec2_region] }}' + filters: + owner-id: 137112412989 + register: ami_info_result + ignore_errors: true + + - name: assert that the right image was found + assert: + that: + - "ami_info_result.images[0].image_id == '{{ ec2_region_images[ec2_region] }}'" + + # ============================================================ + + - name: gather info about the image using string filter + ec2_ami_info: + image_ids: '{{ ec2_region_images[ec2_region] }}' + filters: + name: 'amzn-ami-hvm-2017.09.0.20170930-x86_64-gp2' + register: ami_info_result + ignore_errors: true + + - name: assert that the right image was found + assert: + that: + - "ami_info_result.images[0].image_id == '{{ ec2_region_images[ec2_region] }}'" + + # e2_ami_info filtering tests ends + # ============================================================ + + - name: delete the image + ec2_ami: + instance_id: '{{ setup_instance.instance_ids[0] }}' + state: absent + delete_snapshot: yes + name: '{{ ec2_ami_name }}_ami' + description: '{{ ec2_ami_description }}' + image_id: '{{ result.image_id }}' + tags: + Name: '{{ ec2_ami_name }}_ami' + wait: yes + ignore_errors: true + register: result + + - name: assert that the image has been deleted + assert: + that: + - "result.changed" + - "'image_id' not in result" + - "result.snapshots_deleted" + + # ============================================================== + + - name: test removing an ami if no image ID is provided (expected failed=true) + ec2_ami: + state: absent + register: result + ignore_errors: yes + + - name: assert that an image ID is required + assert: + that: + - "result.failed" + - "result.msg == 'state is absent but all of the following are missing: image_id'" + + # ============================================================ + + - name: create an image from the snapshot + ec2_ami: + name: '{{ ec2_ami_name }}_ami' + description: '{{ ec2_ami_description }}' + state: present + launch_permissions: + user_ids: [] + tags: + Name: '{{ ec2_ami_name }}_ami' + root_device_name: /dev/xvda + device_mapping: + - device_name: /dev/xvda + volume_type: gp2 + size: 8 + delete_on_termination: true + snapshot_id: '{{ setup_snapshot.snapshot_id }}' + register: result + ignore_errors: true + + - name: set image id fact for deletion later + set_fact: + ec2_ami_image_id: "{{ result.image_id }}" + ec2_ami_snapshot: "{{ result.block_device_mapping['/dev/xvda'].snapshot_id }}" + + - name: assert a new ami has been created + assert: + that: + - "result.changed" + - "result.image_id.startswith('ami-')" + + # ============================================================ + + - name: test default launch permissions idempotence + ec2_ami: + description: '{{ ec2_ami_description }}' + state: present + name: '{{ ec2_ami_name }}_ami' + tags: + Name: '{{ ec2_ami_name }}_ami' + root_device_name: /dev/xvda + image_id: '{{ result.image_id }}' + launch_permissions: + user_ids: [] + device_mapping: + - device_name: /dev/xvda + volume_type: gp2 + size: 8 + delete_on_termination: true + snapshot_id: '{{ setup_snapshot.snapshot_id }}' + register: result + + - name: assert a new ami has not been created + assert: + that: + - "not result.changed" + - "result.image_id.startswith('ami-')" + + # ============================================================ + + - name: add a tag to the AMI + ec2_ami: + state: present + description: '{{ ec2_ami_description }}' + image_id: '{{ result.image_id }}' + name: '{{ ec2_ami_name }}_ami' + tags: + New: Tag + register: result + + - name: assert a tag was added + assert: + that: + - "'Name' in result.tags and result.tags.Name == ec2_ami_name + '_ami'" + - "'New' in result.tags and result.tags.New == 'Tag'" + + - name: use purge_tags to remove a tag from the AMI + ec2_ami: + state: present + description: '{{ ec2_ami_description }}' + image_id: '{{ result.image_id }}' + name: '{{ ec2_ami_name }}_ami' + tags: + New: Tag + purge_tags: yes + register: result + + - name: assert a tag was removed + assert: + that: + - "'Name' not in result.tags" + - "'New' in result.tags and result.tags.New == 'Tag'" + + # ============================================================ + + - name: update AMI launch permissions + ec2_ami: + state: present + image_id: '{{ result.image_id }}' + description: '{{ ec2_ami_description }}' + tags: + Name: '{{ ec2_ami_name }}_ami' + launch_permissions: + group_names: ['all'] + register: result + + - name: assert launch permissions were updated + assert: + that: + - "result.changed" + + # ============================================================ + + - name: modify the AMI description + ec2_ami: + state: present + image_id: '{{ result.image_id }}' + name: '{{ ec2_ami_name }}_ami' + description: '{{ ec2_ami_description }}CHANGED' + tags: + Name: '{{ ec2_ami_name }}_ami' + launch_permissions: + group_names: ['all'] + register: result + + - name: assert the description changed + assert: + that: + - "result.changed" + + # ============================================================ + + - name: remove public launch permissions + ec2_ami: + state: present + image_id: '{{ result.image_id }}' + name: '{{ ec2_ami_name }}_ami' + tags: + Name: '{{ ec2_ami_name }}_ami' + launch_permissions: + group_names: [] + register: result + + - name: assert launch permissions were updated + assert: + that: + - "result.changed" + + # ============================================================ + + - name: delete ami without deleting the snapshot (default is not to delete) + ec2_ami: + instance_id: '{{ setup_instance.instance_ids[0] }}' + state: absent + name: '{{ ec2_ami_name }}_ami' + image_id: '{{ ec2_ami_image_id }}' + tags: + Name: '{{ ec2_ami_name }}_ami' + wait: yes + ignore_errors: true + register: result + + - name: assert that the image has been deleted + assert: + that: + - "result.changed" + - "'image_id' not in result" + + - name: ensure the snapshot still exists + ec2_snapshot_info: + snapshot_ids: + - '{{ ec2_ami_snapshot }}' + register: snapshot_result + + - name: assert the snapshot wasn't deleted + assert: + that: + - "snapshot_result.snapshots[0].snapshot_id == ec2_ami_snapshot" + + - name: delete ami for a second time + ec2_ami: + instance_id: '{{ setup_instance.instance_ids[0] }}' + state: absent + name: '{{ ec2_ami_name }}_ami' + image_id: '{{ ec2_ami_image_id }}' + tags: + Name: '{{ ec2_ami_name }}_ami' + wait: yes + register: result + + - name: assert that image does not exist + assert: + that: + - not result.changed + - not result.failed + + + # ============================================================ + + always: + + # ============================================================ + + # TEAR DOWN: snapshot, ec2 instance, ec2 key pair, security group, vpc + - name: Announce teardown start + debug: + msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****" + + - name: delete ami + ec2_ami: + state: absent + image_id: "{{ ec2_ami_image_id }}" + name: '{{ ec2_ami_name }}_ami' + wait: yes + ignore_errors: yes + + - name: remove setup snapshot of ec2 instance + ec2_snapshot: + state: absent + snapshot_id: '{{ setup_snapshot.snapshot_id }}' + ignore_errors: yes + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + '{{ec2_ami_name}}_instance_setup': 'integration_tests' + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + ignore_errors: yes + + - name: remove setup keypair + ec2_key: + name: '{{ec2_ami_name}}_setup' + state: absent + ignore_errors: yes + + - name: remove setup security group + ec2_group: + name: '{{ ec2_ami_name }}_setup' + description: 'created by Ansible integration tests' + state: absent + vpc_id: '{{ setup_vpc.vpc.id }}' + ignore_errors: yes + + - name: remove setup subnet + ec2_vpc_subnet: + az: '{{ ec2_region }}a' + tags: '{{ec2_ami_name}}_setup' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr }}' + state: absent + resource_tags: + Name: '{{ ec2_ami_name }}_setup' + ignore_errors: yes + + - name: remove setup VPC + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: absent + name: '{{ ec2_ami_name }}_setup' + resource_tags: + Name: '{{ ec2_ami_name }}_setup' + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/vars/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/vars/main.yml new file mode 100644 index 00000000..dac1fda2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/vars/main.yml @@ -0,0 +1,20 @@ +--- +# vars file for test_ec2_ami + +# based on Amazon Linux AMI 2017.09.0 (HVM), SSD Volume Type +ec2_region_images: + us-east-1: ami-8c1be5f6 + us-east-2: ami-c5062ba0 + us-west-1: ami-02eada62 + us-west-2: ami-e689729e + ca-central-1: ami-fd55ec99 + eu-west-1: ami-acd005d5 + eu-central-1: ami-c7ee5ca8 + eu-west-2: ami-1a7f6d7e + ap-southeast-1: ami-0797ea64 + ap-southeast-2: ami-8536d6e7 + ap-northeast-2: ami-9bec36f5 + ap-northeast-1: ami-2a69be4c + ap-south-1: ami-4fc58420 + sa-east-1: ami-f1344b9d + cn-north-1: ami-fba67596 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/aliases new file mode 100644 index 00000000..6e3860be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/defaults/main.yml new file mode 100644 index 00000000..76164523 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for test_ec2_eip +tag_prefix: '{{resource_prefix}}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/tasks/main.yml new file mode 100644 index 00000000..4d62df7e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/tasks/main.yml @@ -0,0 +1,334 @@ +--- +# __Test Info__ +# Create a self signed cert and upload it to AWS +# http://www.akadia.com/services/ssh_test_certificate.html +# http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/ssl-server-cert.html + +# __Test Outline__ +# +# __ec2_elb_lb__ +# create test elb with listeners and certificate +# change AZ's +# change listeners +# remove listeners +# remove elb + +# __ec2-common__ +# test environment variable EC2_REGION +# test with no parameters +# test with only instance_id +# test invalid region parameter +# test valid region parameter +# test invalid ec2_url parameter +# test valid ec2_url parameter +# test credentials from environment +# test credential parameters + +- module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + block: + + # ============================================================ + # create test elb with listeners, certificate, and health check + + - name: Create ELB + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + - protocol: http + load_balancer_port: 8080 + instance_port: 8080 + health_check: + ping_protocol: http + ping_port: 80 + ping_path: "/index.html" + response_timeout: 5 + interval: 30 + unhealthy_threshold: 2 + healthy_threshold: 10 + register: info + + - assert: + that: + - 'info.changed' + - 'info.elb.status == "created"' + - '"{{ aws_region }}a" in info.elb.zones' + - '"{{ aws_region }}b" in info.elb.zones' + - 'info.elb.health_check.healthy_threshold == 10' + - 'info.elb.health_check.interval == 30' + - 'info.elb.health_check.target == "HTTP:80/index.html"' + - 'info.elb.health_check.timeout == 5' + - 'info.elb.health_check.unhealthy_threshold == 2' + - '[80, 80, "HTTP", "HTTP"] in info.elb.listeners' + - '[8080, 8080, "HTTP", "HTTP"] in info.elb.listeners' + + # ============================================================ + + # check ports, would be cool, but we are at the mercy of AWS + # to start things in a timely manner + + #- name: check to make sure 80 is listening + # wait_for: host={{ info.elb.dns_name }} port=80 timeout=600 + # register: result + + #- name: assert can connect to port# + # assert: 'result.state == "started"' + + #- name: check to make sure 443 is listening + # wait_for: host={{ info.elb.dns_name }} port=443 timeout=600 + # register: result + + #- name: assert can connect to port# + # assert: 'result.state == "started"' + + # ============================================================ + + # Change AZ's + + - name: Change AZ's + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + zones: + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_zones: yes + health_check: + ping_protocol: http + ping_port: 80 + ping_path: "/index.html" + response_timeout: 5 + interval: 30 + unhealthy_threshold: 2 + healthy_threshold: 10 + register: info + + + + - assert: + that: + - 'info.elb.status == "ok"' + - 'info.changed' + - 'info.elb.zones[0] == "{{ aws_region }}c"' + + # ============================================================ + + # Update AZ's + + - name: Update AZ's + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + purge_zones: yes + register: info + + - assert: + that: + - 'info.changed' + - 'info.elb.status == "ok"' + - '"{{ aws_region }}a" in info.elb.zones' + - '"{{ aws_region }}b" in info.elb.zones' + - '"{{ aws_region }}c" in info.elb.zones' + + + # ============================================================ + + # Purge Listeners + + - name: Purge Listeners + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 81 + purge_listeners: yes + register: info + + - assert: + that: + - 'info.elb.status == "ok"' + - 'info.changed' + - '[80, 81, "HTTP", "HTTP"] in info.elb.listeners' + - 'info.elb.listeners|length == 1' + + + + # ============================================================ + + # add Listeners + + - name: Add Listeners + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 8081 + instance_port: 8081 + purge_listeners: no + register: info + + - assert: + that: + - 'info.elb.status == "ok"' + - 'info.changed' + - '[80, 81, "HTTP", "HTTP"] in info.elb.listeners' + - '[8081, 8081, "HTTP", "HTTP"] in info.elb.listeners' + - 'info.elb.listeners|length == 2' + + + # ============================================================ + + - name: test with no name + ec2_elb_lb: + state: present + register: result + ignore_errors: true + + - name: assert failure when called with no parameters + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: name"' + + + # ============================================================ + - name: test with only name (state missing) + ec2_elb_lb: + name: "{{ tag_prefix }}" + register: result + ignore_errors: true + + - name: assert failure when called with only name + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: state"' + + + # ============================================================ + - name: test invalid region parameter + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + region: 'asdf querty 1234' + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + register: result + ignore_errors: true + + - name: assert invalid region parameter + assert: + that: + - 'result.failed' + - '"Region asdf querty 1234 does not seem to be available" in result.msg' + + + # ============================================================ + - name: test no authentication parameters + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + aws_access_key: '{{ omit }}' + aws_secret_key: '{{ omit }}' + security_token: '{{ omit }}' + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + register: result + ignore_errors: true + + - name: assert valid region parameter + assert: + that: + - 'result.failed' + - '"No handler was ready to authenticate" in result.msg' + + + # ============================================================ + - name: test credentials from environment + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: present + aws_access_key: "{{ omit }}" + aws_secret_key: "{{ omit }}" + security_token: "{{ omit }}" + zones: + - "{{ aws_region }}a" + - "{{ aws_region }}b" + - "{{ aws_region }}c" + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 81 + environment: + EC2_ACCESS_KEY: bogus_access_key + EC2_SECRET_KEY: bogus_secret_key + register: result + ignore_errors: true + + - name: assert credentials from environment + assert: + that: + - 'result.failed' + - '"InvalidClientTokenId" in result.exception' + + + always: + + # ============================================================ + - name: remove the test load balancer completely + ec2_elb_lb: + name: "{{ tag_prefix }}" + state: absent + register: result + + - name: assert the load balancer was removed + assert: + that: + - 'result.changed' + - 'result.elb.name == "{{tag_prefix}}"' + - 'result.elb.status == "deleted"' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/vars/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/vars/main.yml new file mode 100644 index 00000000..79194af1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_elb_lb/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for test_ec2_elb_lb diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/aliases new file mode 100644 index 00000000..96a59fec --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group1 +ec2_eni_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/defaults/main.yml new file mode 100644 index 00000000..cb3895af --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/defaults/main.yml @@ -0,0 +1,14 @@ +--- +vpc_seed_a: '{{ resource_prefix }}' +vpc_seed_b: '{{ resource_prefix }}-ec2_eni' +vpc_prefix: '10.{{ 256 | random(seed=vpc_seed_a) }}.{{ 256 | random(seed=vpc_seed_b ) }}' +vpc_cidr: '{{ vpc_prefix}}.128/26' +ip_1: "{{ vpc_prefix }}.132" +ip_2: "{{ vpc_prefix }}.133" +ip_3: "{{ vpc_prefix }}.134" +ip_4: "{{ vpc_prefix }}.135" +ip_5: "{{ vpc_prefix }}.136" + +ec2_ips: +- "{{ vpc_prefix }}.137" +- "{{ vpc_prefix }}.138" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/main.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/main.yaml new file mode 100644 index 00000000..3a099661 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/main.yaml @@ -0,0 +1,166 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + collections: + - community.aws + + block: + - name: Get available AZs + aws_az_info: + filters: + region-name: "{{ aws_region }}" + register: az_info + + - name: Pick an AZ + set_fact: + availability_zone: "{{ az_info['availability_zones'][0]['zone_name'] }}" + + # ============================================================ + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: "{{ vpc_cidr }}" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc_result + + - name: create a subnet + ec2_vpc_subnet: + cidr: "{{ vpc_cidr }}" + az: "{{ availability_zone }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + state: present + register: vpc_subnet_result + + - name: create a security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: "Created by {{ resource_prefix }}" + rules: [] + state: present + vpc_id: "{{ vpc_result.vpc.id }}" + register: vpc_sg_result + + - name: Get a list of images + ec2_ami_info: + filters: + owner-alias: amazon + name: "amzn2-ami-minimal-hvm-*" + description: "Amazon Linux 2 AMI *" + register: images_info + + - name: Set facts to simplify use of extra resources + set_fact: + vpc_id: "{{ vpc_result.vpc.id }}" + vpc_subnet_id: "{{ vpc_subnet_result.subnet.id }}" + vpc_sg_id: "{{ vpc_sg_result.group_id }}" + image_id: "{{ images_info.images | sort(attribute='creation_date') | reverse | first | json_query('image_id') }}" + + # ============================================================ + + - name: Create 2 instances to test attaching and detaching network interfaces + ec2_instance: + name: "{{ resource_prefix }}-eni-instance-{{ item }}" + image_id: "{{ image_id }}" + vpc_subnet_id: "{{ vpc_subnet_id }}" + instance_type: t2.micro + wait: false + security_group: "{{ vpc_sg_id }}" + network: + private_ip_address: '{{ ec2_ips[item] }}' + register: ec2_instances + loop: + - 0 + - 1 + + # We only need these instances to be running + - name: set variables for the instance IDs + set_fact: + instance_id_1: "{{ ec2_instances.results[0].instance_ids[0] }}" + instance_id_2: "{{ ec2_instances.results[1].instance_ids[0] }}" + + # ============================================================ + - name: test attaching and detaching network interfaces + include_tasks: ./test_eni_basic_creation.yaml + + - name: test attaching and detaching network interfaces + include_tasks: ./test_ipaddress_assign.yaml + + - name: test attaching and detaching network interfaces + include_tasks: ./test_attachment.yaml + + - name: test modifying source_dest_check + include_tasks: ./test_modifying_source_dest_check.yaml + + - name: test modifying tags + include_tasks: ./test_modifying_tags.yaml + + # Note: will delete *both* EC2 instances + - name: test modifying delete_on_termination + include_tasks: ./test_modifying_delete_on_termination.yaml + + - name: test deleting ENIs + include_tasks: ./test_deletion.yaml + + always: + + # ============================================================ + - name: remove the network interfaces + ec2_eni: + eni_id: "{{ item }}" + force_detach: True + state: absent + ignore_errors: true + retries: 5 + loop: + - "{{ eni_id_1 | default(omit) }}" + - "{{ eni_id_2 | default(omit) }}" + + - name: terminate the instances + ec2_instance: + state: absent + instance_ids: + - "{{ instance_id_1 }}" + - "{{ instance_id_2 }}" + wait: True + ignore_errors: true + retries: 5 + when: instance_id_1 is defined and instance_id_2 is defined + + - name: remove the security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: "{{ resource_prefix }}" + rules: [] + state: absent + vpc_id: "{{ vpc_result.vpc.id }}" + ignore_errors: true + retries: 5 + + - name: remove the subnet + ec2_vpc_subnet: + cidr: "{{ vpc_cidr }}" + az: "{{ availability_zone }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + ignore_errors: true + retries: 5 + when: vpc_subnet_result is defined + + - name: remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: "{{ vpc_cidr }}" + state: absent + ignore_errors: true + retries: 5 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_attachment.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_attachment.yaml new file mode 100644 index 00000000..bb0e1336 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_attachment.yaml @@ -0,0 +1,204 @@ + # ============================================================ +- name: attach the network interface to instance 1 + ec2_eni: + instance_id: "{{ instance_id_1 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.attachment is defined + - result.interface.attachment is mapping + - result.interface.attachment.instance_id == instance_id_1 + - _interface_0.attachment is defined + - _interface_0.attachment is mapping + - '"attach_time" in _interface_0.attachment' + - _interface_0.attachment.attach_time is string + - '"attachment_id" in _interface_0.attachment' + - _interface_0.attachment.attachment_id.startswith("eni-attach-") + - '"delete_on_termination" in _interface_0.attachment' + - _interface_0.attachment.delete_on_termination == False + - '"device_index" in _interface_0.attachment' + - _interface_0.attachment.device_index == 1 + - '"instance_id" in _interface_0.attachment' + - _interface_0.attachment.instance_id == instance_id_1 + - '"instance_owner_id" in _interface_0.attachment' + - _interface_0.attachment.instance_owner_id is string + - '"status" in _interface_0.attachment' + - _interface_0.attachment.status == "attached" + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: verify the eni is attached + ec2_eni: + instance_id: "{{ instance_id_1 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.attachment is defined + - result.interface.attachment.instance_id == instance_id_1 + - _interface_0.attachment is defined + - _interface_0.attachment is mapping + - '"attach_time" in _interface_0.attachment' + - _interface_0.attachment.attach_time is string + - '"attachment_id" in _interface_0.attachment' + - _interface_0.attachment.attachment_id.startswith("eni-attach-") + - '"delete_on_termination" in _interface_0.attachment' + - _interface_0.attachment.delete_on_termination == False + - '"device_index" in _interface_0.attachment' + - _interface_0.attachment.device_index == 1 + - '"instance_id" in _interface_0.attachment' + - _interface_0.attachment.instance_id == instance_id_1 + - '"instance_owner_id" in _interface_0.attachment' + - _interface_0.attachment.instance_owner_id is string + - '"status" in _interface_0.attachment' + - _interface_0.attachment.status == "attached" + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: test attaching the network interface to a different instance + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.attachment is defined + - result.interface.attachment.instance_id == instance_id_2 + - _interface_0.attachment is defined + - '"instance_id" in _interface_0.attachment' + - _interface_0.attachment.instance_id == instance_id_2 + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: detach the network interface + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: False + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.attachment is undefined + - _interface_0.attachment is undefined + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: verify the network interface was detached + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: False + register: result + +- assert: + that: + - not result.changed + - result.interface.attachment is undefined + + # ============================================================ +- name: reattach the network interface to test deleting it + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + register: result + +- assert: + that: + - result.changed + - result.interface.attachment is defined + - result.interface.attachment.instance_id == instance_id_2 + +- name: test that deleting the network interface while attached must be intentional + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: absent + register: result + ignore_errors: True + +- assert: + that: + - result.failed + - '"currently in use" in result.msg' + +# ============================================================ +- name: delete an attached network interface with force_detach + ec2_eni: + force_detach: True + eni_id: "{{ eni_id_1 }}" + state: absent + register: result + ignore_errors: True + +- assert: + that: + - result.changed + - result.interface.attachment is undefined + +- name: test removing a network interface that does not exist + ec2_eni: + force_detach: True + eni_id: "{{ eni_id_1 }}" + state: absent + register: result + +- assert: + that: + - not result.changed + - result.interface.attachment is undefined + +# ============================================================ +- name: recreate the network interface + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + register: result + +- set_fact: + eni_id_1: "{{ result.interface.id }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_deletion.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_deletion.yaml new file mode 100644 index 00000000..aecb625e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_deletion.yaml @@ -0,0 +1,92 @@ +--- +# ============================================================ +- name: test deleting the unattached network interface by using the ID + ec2_eni: + eni_id: "{{ eni_id_1 }}" + name: "{{ resource_prefix }}" + subnet_id: "{{ vpc_subnet_id }}" + state: absent + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface is undefined + - '"network_interfaces" in eni_info' + - eni_id_1 not in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + +- name: test removing the network interface by ID is idempotent + ec2_eni: + eni_id: "{{ eni_id_1 }}" + name: "{{ resource_prefix }}" + subnet_id: "{{ vpc_subnet_id }}" + state: absent + register: result + +- assert: + that: + - not result.changed + - result.interface is undefined + +# ============================================================ +- name: add a name tag to the other network interface before deleting it + ec2_eni: + eni_id: "{{ eni_id_2 }}" + name: "{{ resource_prefix }}" + state: present + +- name: test deleting the unattached network interface by using the name + ec2_eni: + name: "{{ resource_prefix }}" + subnet_id: "{{ vpc_subnet_id }}" + state: absent + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_2 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface is undefined + - '"network_interfaces" in eni_info' + - eni_id_2 not in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + +- name: test removing the network interface by name is idempotent + ec2_eni: + name: "{{ resource_prefix }}" + subnet_id: "{{ vpc_subnet_id }}" + state: absent + register: result + +- assert: + that: + - not result.changed + - result.interface is undefined + +- name: verify that the network interface ID does not exist (retry-delete by ID) + ec2_eni: + eni_id: "{{ eni_id_2 }}" + state: absent + register: result + +- assert: + that: + - not result.changed + - result.interface is undefined + +# ============================================================ + +- name: Fetch ENI info without filter + ec2_eni_info: + register: eni_info + +- name: Assert that ec2_eni_info doesn't contain the two interfaces we just deleted + assert: + that: + - '"network_interfaces" in eni_info' + - eni_id_1 not in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + - eni_id_2 not in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_eni_basic_creation.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_eni_basic_creation.yaml new file mode 100644 index 00000000..b18af2dc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_eni_basic_creation.yaml @@ -0,0 +1,219 @@ +--- +# ============================================================ +- name: create a network interface + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + register: result + +- assert: + that: + - result.changed + - result.interface.private_ip_addresses | length == 1 + +- set_fact: + eni_id_1: "{{ result.interface.id }}" + +- name: Fetch ENI info (by ID) + ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- name: Assert that ec2_eni_info returns all the values we expect + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + assert: + that: + - '"network_interfaces" in eni_info' + - eni_info.network_interfaces | length == 1 + - '"association" not in _interface_0' + - '"attachment" not in _interface_0' + - '"availability_zone" in _interface_0' + - _interface_0.availability_zone.startswith(aws_region) + - '"description" in _interface_0' + - _interface_0.description == "" + - '"groups" in _interface_0' + - _interface_0.groups is iterable + - _interface_0.groups | length == 1 + - '"id" in _interface_0' + - _interface_0.id.startswith("eni-") + - _interface_0.id == eni_id_1 + - '"interface_type" in _interface_0' + - _interface_0.owner_id is string + - '"ipv6_addresses" in _interface_0' + - _interface_0.ipv6_addresses is iterable + - _interface_0.ipv6_addresses | length == 0 + - '"mac_address" in _interface_0' + - _interface_0.owner_id is string + - _interface_0.mac_address | length == 17 + - '"network_interface_id" in _interface_0' + - _interface_0.network_interface_id.startswith("eni-") + - _interface_0.network_interface_id == eni_id_1 + - '"owner_id" in _interface_0' + - _interface_0.owner_id is string + - '"private_dns_name" in _interface_0' + - _interface_0.private_dns_name is string + - _interface_0.private_dns_name.endswith("ec2.internal") + - '"private_ip_address" in _interface_0' + - _interface_0.private_ip_address | ipaddr() + - _interface_0.private_ip_address == ip_1 + - '"private_ip_addresses" in _interface_0' + - _interface_0.private_ip_addresses | length == 1 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + - '"requester_id" in _interface_0' + - _interface_0.requester_id is string + - '"requester_managed" in _interface_0' + - _interface_0.requester_managed == False + - '"source_dest_check" in _interface_0' + - _interface_0.source_dest_check == True + - '"status" in _interface_0' + - _interface_0.status == "available" + - '"subnet_id" in _interface_0' + - _interface_0.subnet_id == vpc_subnet_id + - '"tag_set" in _interface_0' + - _interface_0.tag_set is mapping + - '"vpc_id" in _interface_0' + - _interface_0.vpc_id == vpc_id + +- name: test idempotence by using the same private_ip_address + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + register: result + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 1 + +# ============================================================ + +- name: create a second network interface to test IP reassignment + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_5 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + register: result + +- assert: + that: + - result.changed + - result.interface.id != eni_id_1 + +- name: save the second network interface ID for cleanup + set_fact: + eni_id_2: "{{ result.interface.id }}" + +- name: Fetch ENI info (using filter) + ec2_eni_info: + filters: + network-interface-id: '{{ eni_id_2 }}' + register: eni_info + +- name: Assert that ec2_eni_info returns all the values we expect + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + assert: + that: + - '"network_interfaces" in eni_info' + - eni_info.network_interfaces | length == 1 + - '"association" not in _interface_0' + - '"attachment" not in _interface_0' + - '"availability_zone" in _interface_0' + - _interface_0.availability_zone.startswith(aws_region) + - '"description" in _interface_0' + - _interface_0.description == "" + - '"groups" in _interface_0' + - _interface_0.groups is iterable + - _interface_0.groups | length == 1 + - '"id" in _interface_0' + - _interface_0.id.startswith("eni-") + - _interface_0.id == eni_id_2 + - '"interface_type" in _interface_0' + - _interface_0.owner_id is string + - '"ipv6_addresses" in _interface_0' + - _interface_0.ipv6_addresses is iterable + - _interface_0.ipv6_addresses | length == 0 + - '"mac_address" in _interface_0' + - _interface_0.owner_id is string + - _interface_0.mac_address | length == 17 + - '"network_interface_id" in _interface_0' + - _interface_0.network_interface_id.startswith("eni-") + - _interface_0.network_interface_id == eni_id_2 + - '"owner_id" in _interface_0' + - _interface_0.owner_id is string + - '"private_dns_name" in _interface_0' + - _interface_0.private_dns_name is string + - _interface_0.private_dns_name.endswith("ec2.internal") + - '"private_ip_address" in _interface_0' + - _interface_0.private_ip_address | ipaddr() + - _interface_0.private_ip_address == ip_5 + - '"private_ip_addresses" in _interface_0' + - _interface_0.private_ip_addresses | length == 1 + - ip_5 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + - '"requester_id" in _interface_0' + - _interface_0.requester_id is string + - '"requester_managed" in _interface_0' + - _interface_0.requester_managed == False + - '"source_dest_check" in _interface_0' + - _interface_0.source_dest_check == True + - '"status" in _interface_0' + - _interface_0.status == "available" + - '"subnet_id" in _interface_0' + - _interface_0.subnet_id == vpc_subnet_id + - '"tag_set" in _interface_0' + - _interface_0.tag_set is mapping + - '"vpc_id" in _interface_0' + - _interface_0.vpc_id == vpc_id + +- name: Fetch ENI info without filter + ec2_eni_info: + register: eni_info + +- name: Assert that ec2_eni_info contains at least the two interfaces we expect + assert: + that: + - '"network_interfaces" in eni_info' + - eni_info.network_interfaces | length >= 2 + - eni_id_1 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + - eni_id_2 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + +# ============================================================ +# Run some VPC filter based tests of ec2_eni_info + +- name: Fetch ENI info with VPC filters - Available + ec2_eni_info: + filters: + vpc-id: '{{ vpc_id }}' + status: 'available' + register: eni_info + +- name: Assert that ec2_eni_info contains at least the two interfaces we expect + assert: + that: + - '"network_interfaces" in eni_info' + - eni_info.network_interfaces | length == 2 + - eni_id_1 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + - eni_id_2 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + +- name: Fetch ENI info with VPC filters - VPC + ec2_eni_info: + filters: + vpc-id: '{{ vpc_id }}' + register: eni_info + +- name: Assert that ec2_eni_info contains at least the two interfaces we expect + assert: + that: + - '"network_interfaces" in eni_info' + - eni_info.network_interfaces | length == 4 + - eni_id_1 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + - eni_id_2 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + - ec2_ips[0] in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + - ec2_ips[1] in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_ipaddress_assign.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_ipaddress_assign.yaml new file mode 100644 index 00000000..a0a3696e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_ipaddress_assign.yaml @@ -0,0 +1,267 @@ +--- +# ============================================================ +- name: add two implicit secondary IPs + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_address_count: 2 + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 3 + - _interface_0.private_ip_addresses | length == 3 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: test idempotence with two implicit secondary IPs + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_address_count: 2 + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 3 + - _interface_0.private_ip_addresses | length == 3 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +# ============================================================ +- name: ensure secondary addresses are only removed if purge is set to true + ec2_eni: + purge_secondary_private_ip_addresses: false + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: [] + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 3 + - _interface_0.private_ip_addresses | length == 3 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +# ============================================================ + +# Using secondary_private_ip_address_count leads to unpredicable IP assignment +# For the following test, first find an IP that has not been used yet + +- name: save the list of private IPs in use + set_fact: + current_private_ips: "{{ result.interface | json_query('private_ip_addresses[*].private_ip_address') | list }}" + +- name: set new_secondary_ip to an IP that has not been used + set_fact: + new_secondary_ip: "{{ [ip_2, ip_3, ip_4] | difference(current_private_ips) | first }}" + +- name: add an explicit secondary address without purging the ones added implicitly + ec2_eni: + purge_secondary_private_ip_addresses: false + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: + - "{{ new_secondary_ip }}" + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 4 + - _interface_0.private_ip_addresses | length == 4 + # Only ip_1 and the explicitly requested IP are guaranteed to be present + - ip_1 in _private_ips + - new_secondary_ip in _private_ips + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + _private_ips: '{{ eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list }}' + +# ============================================================ +- name: remove secondary address + ec2_eni: + purge_secondary_private_ip_addresses: true + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: [] + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 1 + - _interface_0.private_ip_addresses | length == 1 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: test idempotent behavior purging secondary addresses + ec2_eni: + purge_secondary_private_ip_addresses: true + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: [] + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 1 + - result.interface.private_ip_addresses | length == 1 + - _interface_0.private_ip_addresses | length == 1 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +# ============================================================ + +- name: Assign secondary IP addess to second ENI + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_5 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: + - "{{ ip_4 }}" + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_2 }}' + register: eni_info + +- assert: + that: + - result.changed + - result.interface.id == eni_id_2 + - result.interface.private_ip_addresses | length == 2 + - _interface_0.private_ip_addresses | length == 2 + - ip_5 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + - ip_4 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: test that reassignment of an IP already in use fails when not explcitly allowed (default for allow_reassignment == False) + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: + - "{{ ip_2 }}" + - "{{ ip_3 }}" + - "{{ ip_4 }}" + register: result + ignore_errors: yes + +- assert: + that: + - result.failed + - '"move is not allowed" in result.msg' + +# ============================================================ +- name: allow reassignment to add the list of secondary addresses + ec2_eni: + allow_reassignment: true + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: + - "{{ ip_2 }}" + - "{{ ip_3 }}" + - "{{ ip_4 }}" + register: result + +- assert: + that: + - result.changed + - result.interface.id == eni_id_1 + - result.interface.private_ip_addresses | length == 4 + +- name: test reassigment is idempotent + ec2_eni: + allow_reassignment: true + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: + - "{{ ip_2 }}" + - "{{ ip_3 }}" + - "{{ ip_4 }}" + register: result + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + +# ============================================================ + +- name: purge all the secondary addresses + ec2_eni: + purge_secondary_private_ip_addresses: true + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + secondary_private_ip_addresses: [] + register: result +- ec2_eni_info: + eni_id: '{{ eni_id_1 }}' + register: eni_info + until: _interface_0.private_ip_addresses | length == 1 + retries: 5 + delay: 2 + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- assert: + that: + - result.changed + - _interface_0.private_ip_addresses | length == 1 + - ip_1 in ( eni_info | community.general.json_query("network_interfaces[].private_ip_addresses[].private_ip_address") | list ) + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_delete_on_termination.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_delete_on_termination.yaml new file mode 100644 index 00000000..8e8bd059 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_delete_on_termination.yaml @@ -0,0 +1,166 @@ +# ============================================================ + +- name: ensure delete_on_termination defaults to False + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result is successful + - result.interface.attachment.delete_on_termination == false + - _interface_0.attachment.delete_on_termination == False + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +# ============================================================ + +- name: enable delete_on_termination + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + delete_on_termination: True + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface.attachment.delete_on_termination == true + - _interface_0.attachment.delete_on_termination == True + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: test idempotent behavior enabling delete_on_termination + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + delete_on_termination: True + register: result + +- assert: + that: + - not result.changed + - result.interface.attachment.delete_on_termination == true + +# ============================================================ + +- name: disable delete_on_termination + ec2_eni: + instance_id: "{{ instance_id_2 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + state: present + attached: True + delete_on_termination: False + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface.attachment.delete_on_termination == false + - _interface_0.attachment.delete_on_termination == False + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +# ============================================================ + +- name: terminate the instance to make sure the attached ENI remains + ec2_instance: + state: absent + instance_ids: + - "{{ instance_id_2 }}" + wait: True + +- name: verify the eni still exists + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: present + register: result + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.attachment is undefined + +# ============================================================ + +- name: ensure the network interface is attached + ec2_eni: + instance_id: "{{ instance_id_1 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + attached: True + register: result + +- name: ensure delete_on_termination is true + ec2_eni: + instance_id: "{{ instance_id_1 }}" + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + attached: True + delete_on_termination: True + register: result + +- name: test terminating the instance after setting delete_on_termination to true + ec2_instance: + state: absent + instance_ids: + - "{{ instance_id_1 }}" + wait: True + +- name: verify the eni was also removed + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: absent + register: result +- ec2_eni_info: + register: eni_info + +- assert: + that: + - not result.changed + - '"network_interfaces" in eni_info' + - eni_info.network_interfaces | length >= 1 + - eni_id_1 not in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + - eni_id_2 in ( eni_info | community.general.json_query("network_interfaces[].id") | list ) + +# ============================================================ + +- name: recreate the network interface + ec2_eni: + device_index: 1 + private_ip_address: "{{ ip_1 }}" + subnet_id: "{{ vpc_subnet_id }}" + state: present + register: result + +- set_fact: + eni_id_1: "{{ result.interface.id }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_source_dest_check.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_source_dest_check.yaml new file mode 100644 index 00000000..3ba6c257 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_source_dest_check.yaml @@ -0,0 +1,74 @@ + # ============================================================ +- name: test source_dest_check defaults to true + ec2_eni: + eni_id: "{{ eni_id_1 }}" + source_dest_check: true + state: present + register: result + +- assert: + that: + - not result.changed + - result.interface.source_dest_check == true + + # ============================================================ +- name: disable source_dest_check + ec2_eni: + eni_id: "{{ eni_id_1 }}" + source_dest_check: false + state: present + register: result + +- name: Check source_dest_check state + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + until: _interface_0.source_dest_check == False + retries: 5 + delay: 2 + +- assert: + that: + - result.changed + - _interface_0.source_dest_check == False + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: test idempotence disabling source_dest_check + ec2_eni: + eni_id: "{{ eni_id_1 }}" + source_dest_check: false + state: present + register: result + +- assert: + that: + - not result.changed + - result.interface.source_dest_check == false + + # ============================================================ +- name: enable source_dest_check + ec2_eni: + eni_id: "{{ eni_id_1 }}" + source_dest_check: true + state: present + register: result + +- name: Check source_dest_check state + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + until: _interface_0.source_dest_check == True + retries: 5 + delay: 2 + +- assert: + that: + - result.changed + - _interface_0.source_dest_check == True + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_tags.yaml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_tags.yaml new file mode 100644 index 00000000..8164f869 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_eni/tasks/test_modifying_tags.yaml @@ -0,0 +1,213 @@ + # ============================================================ +- name: verify there are no tags associated with the network interface + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: present + tags: {} + register: result + +- assert: + that: + - not result.changed + - not result.interface.tags + - result.interface.name is undefined + + # ============================================================ +- name: add tags to the network interface + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: present + name: "{{ resource_prefix }}" + tags: + CreatedBy: "{{ resource_prefix }}" + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface.id == eni_id_1 + - result.interface.tags | length == 2 + - result.interface.tags.CreatedBy == resource_prefix + - result.interface.tags.Name == resource_prefix + - result.interface.name == resource_prefix + - _interface_0.tags | length == 2 + - _interface_0.tags.CreatedBy == resource_prefix + - _interface_0.tags.Name == resource_prefix + - _interface_0.tag_set | length == 2 + - _interface_0.tag_set.CreatedBy == resource_prefix + - _interface_0.tag_set.Name == resource_prefix + - _interface_0.name == resource_prefix + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: test idempotence by using the Name tag and the subnet + ec2_eni: + name: "{{ resource_prefix }}" + state: present + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + register: result + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + + # ============================================================ +- name: test tags are not purged if tags are null even if name is provided + ec2_eni: + name: "{{ resource_prefix }}" + state: present + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.tags | length == 2 + - result.interface.tags.CreatedBy == resource_prefix + - result.interface.tags.Name == resource_prefix + - result.interface.name == resource_prefix + - _interface_0.tag_set | length == 2 + - _interface_0.tag_set.CreatedBy == resource_prefix + - _interface_0.tag_set.Name == resource_prefix + - _interface_0.name == resource_prefix + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: test setting purge tags to false + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: present + purge_tags: false + tags: {} + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.tags | length == 2 + - result.interface.tags.CreatedBy == resource_prefix + - result.interface.tags.Name == resource_prefix + - result.interface.name == resource_prefix + - _interface_0.tag_set | length == 2 + - _interface_0.tag_set.CreatedBy == resource_prefix + - _interface_0.tag_set.Name == resource_prefix + - _interface_0.name == resource_prefix + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: test adding a new tag without removing any others + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: present + purge_tags: false + tags: + environment: test + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface.tags | length == 3 + - result.interface.tags.environment == 'test' + - result.interface.tags.CreatedBy == resource_prefix + - result.interface.tags.Name == resource_prefix + - result.interface.name == resource_prefix + - _interface_0.tag_set | length == 3 + - _interface_0.tag_set.environment == 'test' + - _interface_0.tag_set.CreatedBy == resource_prefix + - _interface_0.tag_set.Name == resource_prefix + - _interface_0.name == resource_prefix + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: test purging tags and adding a new one + ec2_eni: + name: "{{ resource_prefix }}" + state: present + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + tags: + Description: "{{ resource_prefix }}" + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - result.interface.id == eni_id_1 + - result.interface.tags | length == 2 + - result.interface.tags.Description == resource_prefix + - result.interface.tags.Name == resource_prefix + - result.interface.name == resource_prefix + - _interface_0.tag_set | length == 2 + - _interface_0.tag_set.Description == resource_prefix + - _interface_0.tag_set.Name == resource_prefix + - _interface_0.name == resource_prefix + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + +- name: test purging tags and adding a new one is idempotent + ec2_eni: + name: "{{ resource_prefix }}" + state: present + subnet_id: "{{ vpc_subnet_result.subnet.id }}" + tags: + Description: "{{ resource_prefix }}" + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - not result.changed + - result.interface.id == eni_id_1 + - result.interface.tags | length == 2 + - result.interface.tags.Description == resource_prefix + - result.interface.tags.Name == resource_prefix + - result.interface.name == resource_prefix + - _interface_0.tag_set | length == 2 + - _interface_0.tag_set.Description == resource_prefix + - _interface_0.tag_set.Name == resource_prefix + - _interface_0.name == resource_prefix + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' + + # ============================================================ +- name: test purging all tags + ec2_eni: + eni_id: "{{ eni_id_1 }}" + state: present + tags: {} + register: result +- ec2_eni_info: + eni_id: "{{ eni_id_1 }}" + register: eni_info + +- assert: + that: + - result.changed + - not result.interface.tags + - result.interface.name is undefined + - _interface_0.tag_set | length == 0 + vars: + _interface_0: '{{ eni_info.network_interfaces[0] }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/aliases new file mode 100644 index 00000000..ee717e99 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group2 +ec2_group_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/defaults/main.yml new file mode 100644 index 00000000..f17a67a5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# defaults file for test_ec2_group +ec2_group_name: '{{resource_prefix}}' +ec2_group_description: 'Created by ansible integration tests' + +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/credential_tests.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/credential_tests.yml new file mode 100644 index 00000000..1957eaae --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/credential_tests.yml @@ -0,0 +1,161 @@ +--- +# A Note about ec2 environment variable name preference: +# - EC2_URL -> AWS_URL +# - EC2_ACCESS_KEY -> AWS_ACCESS_KEY_ID -> AWS_ACCESS_KEY +# - EC2_SECRET_KEY -> AWS_SECRET_ACCESS_KEY -> AWX_SECRET_KEY +# - EC2_REGION -> AWS_REGION +# + +# - include: ../../setup_ec2/tasks/common.yml module_name: ec2_group + +- block: + # ============================================================ + - name: test failure with no parameters + ec2_group: + register: result + ignore_errors: true + + - name: assert failure with no parameters + assert: + that: + - 'result.failed' + - 'result.msg == "one of the following is required: name, group_id"' + + # ============================================================ + - name: test failure with only name + ec2_group: + name: '{{ec2_group_name}}' + register: result + ignore_errors: true + + - name: assert failure with only name + assert: + that: + - 'result.failed' + - 'result.msg == "Must provide description when state is present."' + + # ============================================================ + - name: test failure with only description + ec2_group: + description: '{{ec2_group_description}}' + register: result + ignore_errors: true + + - name: assert failure with only description + assert: + that: + - 'result.failed' + - 'result.msg == "one of the following is required: name, group_id"' + + # ============================================================ + - name: test failure with empty description (AWS API requires non-empty string desc) + ec2_group: + name: '{{ec2_group_name}}' + description: '' + region: '{{ec2_region}}' + register: result + ignore_errors: true + + - name: assert failure with empty description + assert: + that: + - 'result.failed' + - 'result.msg == "Must provide description when state is present."' + + # ============================================================ + - name: test valid region parameter + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + region: '{{ec2_region}}' + register: result + ignore_errors: true + + - name: assert valid region parameter + assert: + that: + - 'result.failed' + - '"Unable to locate credentials" in result.msg' + + # ============================================================ + - name: test environment variable EC2_REGION + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + environment: + EC2_REGION: '{{ec2_region}}' + register: result + ignore_errors: true + + - name: assert environment variable EC2_REGION + assert: + that: + - 'result.failed' + - '"Unable to locate credentials" in result.msg' + + # ============================================================ + - name: test invalid ec2_url parameter + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + environment: + EC2_URL: bogus.example.com + register: result + ignore_errors: true + + - name: assert invalid ec2_url parameter + assert: + that: + - 'result.failed' + - 'result.msg.startswith("The ec2_group module requires a region")' + + # ============================================================ + - name: test valid ec2_url parameter + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + environment: + EC2_URL: '{{ec2_url}}' + register: result + ignore_errors: true + + - name: assert valid ec2_url parameter + assert: + that: + - 'result.failed' + - 'result.msg.startswith("The ec2_group module requires a region")' + + # ============================================================ + - name: test credentials from environment + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + environment: + EC2_REGION: '{{ec2_region}}' + EC2_ACCESS_KEY: bogus_access_key + EC2_SECRET_KEY: bogus_secret_key + register: result + ignore_errors: true + + - name: assert ec2_group with valid ec2_url + assert: + that: + - 'result.failed' + - '"validate the provided access credentials" in result.msg' + + # ============================================================ + - name: test credential parameters + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + ec2_region: '{{ec2_region}}' + ec2_access_key: 'bogus_access_key' + ec2_secret_key: 'bogus_secret_key' + register: result + ignore_errors: true + + - name: assert credential parameters + assert: + that: + - 'result.failed' + - '"validate the provided access credentials" in result.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/data_validation.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/data_validation.yml new file mode 100644 index 00000000..c461287d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/data_validation.yml @@ -0,0 +1,33 @@ +--- +- block: + - name: Create a group with only the default rule + ec2_group: + name: '{{ec2_group_name}}-input-tests' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + + - name: Run through some common weird port specs + ec2_group: + name: '{{ec2_group_name}}-input-tests' + description: '{{ec2_group_description}}' + rules: + - "{{ item }}" + with_items: + - proto: tcp + from_port: "8182" + to_port: 8182 + cidr_ipv6: "fc00:ff9b::/96" + rule_desc: Mixed string and non-string ports + - proto: tcp + ports: + - "9000" + - 9001 + - 9002-9005 + cidr_ip: "10.2.3.0/24" + always: + - name: tidy up input testing group + ec2_group: + name: '{{ec2_group_name}}-input-tests' + vpc_id: '{{ vpc_result.vpc.id }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/diff_mode.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/diff_mode.yml new file mode 100644 index 00000000..e687bad2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/diff_mode.yml @@ -0,0 +1,167 @@ +--- + # ============================================================ + + - name: create a group with a rule (CHECK MODE + DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + rules_egress: + - proto: all + cidr_ip: 0.0.0.0/0 + register: check_mode_result + check_mode: true + diff: true + + - assert: + that: + - check_mode_result.changed + + - name: create a group with a rule (DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + rules_egress: + - proto: all + cidr_ip: 0.0.0.0/0 + register: result + diff: true + + - assert: + that: + - result.changed + - result.diff.0.after.ip_permissions == check_mode_result.diff.0.after.ip_permissions + - result.diff.0.after.ip_permissions_egress == check_mode_result.diff.0.after.ip_permissions_egress + + - name: add rules to make sorting occur (CHECK MODE + DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 172.16.0.0/12 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 10.0.0.0/8 + rules_egress: + - proto: all + cidr_ip: 0.0.0.0/0 + register: check_mode_result + check_mode: true + diff: true + + - assert: + that: + - check_mode_result.changed + + - name: add rules in a different order to test sorting consistency (DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 172.16.0.0/12 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 10.0.0.0/8 + rules_egress: + - proto: all + cidr_ip: 0.0.0.0/0 + register: result + diff: true + + - assert: + that: + - result.changed + - result.diff.0.after.ip_permissions == check_mode_result.diff.0.after.ip_permissions + - result.diff.0.after.ip_permissions_egress == check_mode_result.diff.0.after.ip_permissions_egress + + - name: purge rules (CHECK MODE + DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + rules_egress: [] + register: check_mode_result + check_mode: true + diff: true + + - assert: + that: + - check_mode_result.changed + + - name: purge rules (DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + rules_egress: [] + register: result + diff: true + + - assert: + that: + - result.changed + - result.diff.0.after.ip_permissions == check_mode_result.diff.0.after.ip_permissions + - result.diff.0.after.ip_permissions_egress == check_mode_result.diff.0.after.ip_permissions_egress + + - name: delete the security group (CHECK MODE + DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + state: absent + register: check_mode_result + diff: true + check_mode: true + + - assert: + that: + - check_mode_result.changed + + - name: delete the security group (DIFF) + ec2_group: + name: '{{ ec2_group_name }}' + state: absent + register: result + diff: true + + - assert: + that: + - result.changed + - not result.diff.0.after and not check_mode_result.diff.0.after diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/ec2_classic.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/ec2_classic.yml new file mode 100644 index 00000000..4ea8553e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/ec2_classic.yml @@ -0,0 +1,86 @@ +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - name: Get available AZs + aws_az_info: + filters: + region-name: "{{ aws_region }}" + register: az_facts + + - name: Create a classic ELB with classic networking + ec2_elb_lb: + name: "{{ resource_prefix }}-elb" + state: present + zones: + - "{{ az_facts['availability_zones'][0]['zone_name'] }}" + - "{{ az_facts['availability_zones'][1]['zone_name'] }}" + listeners: + - protocol: http # options are http, https, ssl, tcp + load_balancer_port: 80 + instance_port: 80 + proxy_protocol: True + register: classic_elb + + - name: Assert the elb was created + assert: + that: + - classic_elb.changed + + - name: Create a security group with a classic elb-sg rule + ec2_group: + name: "{{ resource_prefix }}-sg-a" + description: "EC2 classic test security group" + rules: + - proto: tcp + ports: 80 + group_id: amazon-elb/amazon-elb-sg + state: present + register: classic_sg + + - name: Assert the SG was created + assert: + that: + - classic_sg.changed + - "{{ classic_sg.ip_permissions | length }} == 1" + + - set_fact: + elb_sg_id: "{{ classic_sg.ip_permissions[0].user_id_group_pairs[0].user_id }}/{{ classic_sg.ip_permissions[0].user_id_group_pairs[0].group_id }}/{{ classic_sg.ip_permissions[0].user_id_group_pairs[0].group_name }}" + + - name: Update the security group + ec2_group: + name: "{{ resource_prefix }}-sg-a" + description: "EC2 classic test security group" + rules: + - proto: tcp + ports: 8080 + group_id: "{{ elb_sg_id }}" + - proto: tcp + ports: + - 80 + cidr_ip: 0.0.0.0/0 + state: present + register: updated_classic_sg + + + - name: Assert the SG was updated + assert: + that: + - updated_classic_sg.changed + - "{{ updated_classic_sg.ip_permissions | length }} == 2" + - "{{ classic_sg.ip_permissions[0]}} not in {{ updated_classic_sg.ip_permissions }}" + + # =========================================== + always: + - name: Terminate classic ELB + ec2_elb_lb: + name: "{{ resource_prefix }}-classic-elb" + state: absent + + - name: Delete security group + ec2_group: + name: "{{ resource_prefix }}-sg-a" + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/egress_tests.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/egress_tests.yml new file mode 100644 index 00000000..5635f443 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/egress_tests.yml @@ -0,0 +1,177 @@ +--- +- block: + - name: Create a group with only the default rule + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + state: present + register: result + + - name: assert default rule is in place (expected changed=true) + assert: + that: + - result is changed + - result.ip_permissions|length == 0 + - result.ip_permissions_egress|length == 1 + - result.ip_permissions_egress[0].ip_ranges[0].cidr_ip == '0.0.0.0/0' + + - name: Create a group with only the default rule + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + purge_rules_egress: false + state: present + register: result + + - name: assert default rule is not purged (expected changed=false) + assert: + that: + - result is not changed + - result.ip_permissions|length == 0 + - result.ip_permissions_egress|length == 1 + - result.ip_permissions_egress[0].ip_ranges[0].cidr_ip == '0.0.0.0/0' + + - name: Pass empty egress rules without purging, should leave default rule in place + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + rules_egress: [] + state: present + register: result + + - name: assert default rule is not purged (expected changed=false) + assert: + that: + - result is not changed + - result.ip_permissions|length == 0 + - result.ip_permissions_egress|length == 1 + - result.ip_permissions_egress[0].ip_ranges[0].cidr_ip == '0.0.0.0/0' + + - name: Purge rules, including the default + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: true + rules_egress: [] + state: present + register: result + + - name: assert default rule is not purged (expected changed=false) + assert: + that: + - result is changed + - result.ip_permissions|length == 0 + - result.ip_permissions_egress|length == 0 + + - name: Add a custom egress rule + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + rules_egress: + - proto: tcp + ports: + - 1212 + cidr_ip: 10.2.1.2/32 + state: present + register: result + + - name: assert first rule is here + assert: + that: + - result.ip_permissions_egress|length == 1 + + - name: Add a second custom egress rule + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + purge_rules_egress: false + vpc_id: '{{ vpc_result.vpc.id }}' + rules_egress: + - proto: tcp + ports: + - 2323 + cidr_ip: 10.3.2.3/32 + state: present + register: result + + - name: assert the first rule is not purged + assert: + that: + - result.ip_permissions_egress|length == 2 + + - name: Purge the second rule (CHECK MODE) (DIFF MODE) + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + rules_egress: + - proto: tcp + ports: + - 1212 + cidr_ip: 10.2.1.2/32 + state: present + register: result + check_mode: True + diff: True + + - name: assert first rule will be left + assert: + that: + - result.changed + - result.diff.0.after.ip_permissions_egress|length == 1 + - result.diff.0.after.ip_permissions_egress[0].ip_ranges[0].cidr_ip == '10.2.1.2/32' + + - name: Purge the second rule + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + rules_egress: + - proto: tcp + ports: + - 1212 + cidr_ip: 10.2.1.2/32 + state: present + register: result + + - name: assert first rule is here + assert: + that: + - result.ip_permissions_egress|length == 1 + - result.ip_permissions_egress[0].ip_ranges[0].cidr_ip == '10.2.1.2/32' + + - name: add a rule for all TCP ports + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + rules_egress: + - proto: tcp + ports: 0-65535 + cidr_ip: 0.0.0.0/0 + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + register: result + + - name: Re-add the default rule + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + description: '{{ec2_group_description}}' + rules_egress: + - proto: -1 + cidr_ip: 0.0.0.0/0 + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + register: result + always: + - name: tidy up egress rule test security group + ec2_group: + name: '{{ec2_group_name}}-egress-tests' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/group_info.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/group_info.yml new file mode 100644 index 00000000..86c8a546 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/group_info.yml @@ -0,0 +1,96 @@ +--- + +# file for testing the ec2_group_info module + +- block: + # ======================== Setup ===================================== + - name: Create a group for testing group info retrieval below + ec2_group: + name: '{{ ec2_group_name }}-info-1' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ ec2_group_description }}' + rules: + - proto: tcp + ports: + - 90 + cidr_ip: 10.2.2.2/32 + tags: + test: '{{ resource_prefix }}_ec2_group_info_module' + register: group_info_test_setup + + - name: Create another group for testing group info retrieval below + ec2_group: + name: '{{ ec2_group_name }}-info-2' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ ec2_group_description }}' + rules: + - proto: tcp + ports: + - 91 + cidr_ip: 10.2.2.2/32 + + # ========================= ec2_group_info tests ==================== + + - name: Retrieve security group info based on SG name + ec2_group_info: + filters: + group-name: '{{ ec2_group_name }}-info-2' + register: result_1 + + - name: Assert results found + assert: + that: + - result_1.security_groups is defined + - (result_1.security_groups|first).group_name == '{{ ec2_group_name }}-info-2' + + - name: Retrieve security group info based on SG VPC + ec2_group_info: + filters: + vpc-id: '{{ vpc_result.vpc.id }}' + register: result_2 + + - name: Assert results found + assert: + that: + - result_2.security_groups is defined + - (result_2.security_groups|first).vpc_id == vpc_result.vpc.id + - (result_2.security_groups|length) > 2 + + - name: Retrieve security group info based on SG tags + ec2_group_info: + filters: + "tag:test": "{{ resource_prefix }}_ec2_group_info_module" + register: result_3 + + - name: Assert results found + assert: + that: + - result_3.security_groups is defined + - (result_3.security_groups|first).group_id == group_info_test_setup.group_id + + - name: Retrieve security group info based on SG ID + ec2_group_info: + filters: + group-id: '{{ group_info_test_setup.group_id }}' + register: result_4 + + - name: Assert correct result found + assert: + that: + - result_4.security_groups is defined + - (result_4.security_groups|first).group_id == group_info_test_setup.group_id + - (result_4.security_groups|length) == 1 + + always: + # ========================= Cleanup ================================= + - name: tidy up test security group 1 + ec2_group: + name: '{{ ec2_group_name }}-info-1' + state: absent + ignore_errors: yes + + - name: tidy up test security group 2 + ec2_group: + name: '{{ ec2_group_name }}-info-2' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/ipv6_default_tests.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/ipv6_default_tests.yml new file mode 100644 index 00000000..2dea42a6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/ipv6_default_tests.yml @@ -0,0 +1,90 @@ +--- +# ============================================================ +- name: test state=present for ipv6 (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + check_mode: true + register: result + +- name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + +# ============================================================ +- name: test state=present for ipv6 (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + register: result + +- name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + +# ============================================================ +- name: test rules_egress state=present for ipv6 (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + rules_egress: + - proto: "tcp" + from_port: 8181 + to_port: 8181 + cidr_ipv6: "64:ff9b::/96" + check_mode: true + register: result + +- name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + +# ============================================================ +- name: test rules_egress state=present for ipv6 (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + rules_egress: + - proto: "tcp" + from_port: 8181 + to_port: 8181 + cidr_ipv6: "64:ff9b::/96" + register: result + +- name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' +- name: delete it + ec2_group: + name: '{{ec2_group_name}}' + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/main.yml new file mode 100644 index 00000000..cbd39cb3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/main.yml @@ -0,0 +1,1460 @@ +--- +# Runs a set of tets without the AWS connection credentials configured +- include: ./credential_tests.yml +- set_fact: + aws_security_token: '{{ security_token | default("") }}' + no_log: True + +# ============================================================ +# EC2 Classic tests can only be run on a pre-2013 AWS account with supported-platforms=EC2 +# Ansible CI does NOT have classic EC2 support; these tests are provided as-is for the +# community and can be run if you have access to a classic account. To check if your account +# has support for EC2 Classic you can use the `amazon.aws.aws_account_attribute` plugin. + +- name: determine if this is an EC2 Classic account + set_fact: + has_ec2_classic: "{{ lookup('amazon.aws.aws_account_attribute', + attribute='has-ec2-classic', + region=aws_region, + aws_access_key=aws_access_key, + aws_secret_key=aws_secret_key, + aws_security_token=aws_security_token, + wantlist=True) }}" + +# ============================================================ +- name: Run EC2 Classic accounts if account type is EC2 + include: ./ec2_classic.yml + when: has_ec2_classic + +# ============================================================ +# Other tests depend on attribute='default-vpc', ie no vpc_id is set. This is +# incompatible with EC2 classic accounts, so these tests can only be run in a +# VPC-type account. See "Q. I really want a default VPC for my existing EC2 +# account. Is that possible?" in https://aws.amazon.com/vpc/faqs/#Default_VPCs +- name: Run all other tests if account type is VPC + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit)}}" + region: "{{ aws_region }}" + block: + - name: determine if there is a default VPC + set_fact: + defaultvpc: "{{ lookup('amazon.aws.aws_account_attribute', + attribute='default-vpc', + region=aws_region, + aws_access_key=aws_access_key, + aws_secret_key=aws_secret_key, + aws_security_token=aws_security_token) }}" + register: default_vpc + + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: "{{ vpc_cidr }}" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc_result + #TODO(ryansb): Update CI for VPC peering permissions + #- include: ./multi_account.yml + - include: ./diff_mode.yml + - include: ./numeric_protos.yml + - include: ./rule_group_create.yml + - include: ./egress_tests.yml + - include: ./data_validation.yml + - include: ./multi_nested_target.yml + - include: ./group_info.yml + + # ============================================================ + - name: test state=absent (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: absent + check_mode: true + register: result + + - name: assert no changes would be made + assert: + that: + - not result.changed + + # =========================================================== + - name: test state=absent + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: absent + register: result + + # ============================================================ + - name: test state=present (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test state=present (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test state=present different description (expected changed=false) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}CHANGED' + state: present + check_mode: true + register: result + + - name: assert state=present (expected changed=false) + assert: + that: + - 'not result.changed' + + # ============================================================ + - name: test state=present different description (expected changed=false) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}CHANGED' + state: present + ignore_errors: true + register: result + + - name: assert state=present (expected changed=false) + assert: + that: + - 'not result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test state=present (expected changed=false) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + register: result + + - name: assert state=present (expected changed=false) + assert: + that: + - 'not result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: tests IPv6 with the default VPC + include: ./ipv6_default_tests.yml + when: default_vpc + + - name: test IPv6 with a specified VPC + block: + + # ============================================================ + - name: test state=present (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test state=present (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test state=present for ipv6 (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test state=present for ipv6 (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test state=present for ipv6 (expected changed=false) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + check_mode: true + register: result + + - name: assert nothing changed + assert: + that: + - 'not result.changed' + + # ============================================================ + - name: test state=present for ipv6 (expected changed=false) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + register: result + + - name: assert nothing changed + assert: + that: + - 'not result.changed' + + # ============================================================ + - name: test rules_egress state=present for ipv6 (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + rules_egress: + - proto: "tcp" + from_port: 8181 + to_port: 8181 + cidr_ipv6: "64:ff9b::/96" + check_mode: true + diff: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.diff.0.before.ip_permissions == result.diff.0.after.ip_permissions' + - 'result.diff.0.before.ip_permissions_egress != result.diff.0.after.ip_permissions_egress' + + # ============================================================ + - name: test rules_egress state=present for ipv6 (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: present + vpc_id: '{{ vpc_result.vpc.id }}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: "64:ff9b::/96" + rules_egress: + - proto: "tcp" + from_port: 8181 + to_port: 8181 + cidr_ipv6: "64:ff9b::/96" + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test state=absent (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + check_mode: true + diff: true + register: result + + - name: assert group was removed + assert: + that: + - 'result.changed' + - 'not result.diff.0.after' + + # ============================================================ + - name: test state=absent (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}-2' + description: '{{ ec2_group_description }}-2' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + register: result + + - name: assert group was removed + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test state=present for ipv4 (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test state=present for ipv4 (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + - 'result.ip_permissions|length == 1' + - 'result.ip_permissions_egress|length == 1' + + # ============================================================ + - name: add same rule to the existing group (expected changed=false) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + check_mode: true + diff: true + register: check_result + + - assert: + that: + - not check_result.changed + - check_result.diff.0.before.ip_permissions.0 == check_result.diff.0.after.ip_permissions.0 + + # ============================================================ + - name: add same rule to the existing group (expected changed=false) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + register: result + + - name: assert state=present (expected changed=false) + assert: + that: + - 'not result.changed' + - 'result.group_id.startswith("sg-")' + + - name: assert state=present (expected changed=false) + assert: + that: + - 'not check_result.changed' + + # ============================================================ + - name: add a rule that auto creates another security group (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + purge_rules: no + rules: + - proto: "tcp" + group_name: "{{ resource_prefix }} - Another security group" + group_desc: Another security group + ports: 7171 + check_mode: true + register: result + + - name: check that there are now two rules + assert: + that: + - result.changed + + # ============================================================ + - name: add a rule that auto creates another security group + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + purge_rules: no + rules: + - proto: "tcp" + group_name: "{{ resource_prefix }} - Another security group" + group_desc: Another security group + ports: 7171 + register: result + + - name: check that there are now two rules + assert: + that: + - result.changed + - result.warning is not defined + - result.ip_permissions|length == 2 + - result.ip_permissions[0].user_id_group_pairs or + result.ip_permissions[1].user_id_group_pairs + - 'result.ip_permissions_egress[0].ip_protocol == "-1"' + + # ============================================================ + - name: test ip rules convert port numbers from string to int (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: "8183" + to_port: "8183" + cidr_ip: "10.1.1.1/32" + rules_egress: + - proto: "tcp" + from_port: "8184" + to_port: "8184" + cidr_ip: "10.1.1.1/32" + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test ip rules convert port numbers from string to int (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: "8183" + to_port: "8183" + cidr_ip: "10.1.1.1/32" + rules_egress: + - proto: "tcp" + from_port: "8184" + to_port: "8184" + cidr_ip: "10.1.1.1/32" + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + - 'result.ip_permissions|length == 1' + - 'result.ip_permissions_egress[0].ip_protocol == "tcp"' + + + # ============================================================ + - name: test group rules convert port numbers from string to int (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: "8185" + to_port: "8185" + group_id: "{{result.group_id}}" + rules_egress: + - proto: "tcp" + from_port: "8186" + to_port: "8186" + group_id: "{{result.group_id}}" + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test group rules convert port numbers from string to int (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: "tcp" + from_port: "8185" + to_port: "8185" + group_id: "{{result.group_id}}" + rules_egress: + - proto: "tcp" + from_port: "8186" + to_port: "8186" + group_id: "{{result.group_id}}" + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + - result.warning is not defined + + # ============================================================ + - name: test adding a range of ports and ports given as strings (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8183-8190 + - '8192' + cidr_ip: 10.1.1.1/32 + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test adding a range of ports and ports given as strings (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8183-8190 + - '8192' + cidr_ip: 10.1.1.1/32 + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test adding a rule with a IPv4 CIDR with host bits set (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8195 + cidr_ip: 10.0.0.1/8 + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test adding a rule with a IPv4 CIDR with host bits set (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8195 + cidr_ip: 10.0.0.1/8 + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test adding the same rule with a IPv4 CIDR with host bits set (expected changed=false) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8195 + cidr_ip: 10.0.0.1/8 + check_mode: true + register: check_result + + # ============================================================ + - name: test adding the same rule with a IPv4 CIDR with host bits set (expected changed=false and a warning) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8195 + cidr_ip: 10.0.0.1/8 + register: result + + - name: assert state=present (expected changed=false and a warning) + assert: + that: + - 'not check_result.changed' + + - name: assert state=present (expected changed=false and a warning) + assert: + that: + # No way to assert for warnings? + - 'not result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test using the default VPC + block: + + - name: test adding a rule with a IPv6 CIDR with host bits set (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8196 + cidr_ipv6: '2001:db00::1/24' + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test adding a rule with a IPv6 CIDR with host bits set (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8196 + cidr_ipv6: '2001:db00::1/24' + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + + - name: test adding a rule again with a IPv6 CIDR with host bits set (expected changed=false and a warning) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + state: present + # set purge_rules to false so we don't get a false positive from previously added rules + purge_rules: false + rules: + - proto: "tcp" + ports: + - 8196 + cidr_ipv6: '2001:db00::1/24' + register: result + + - name: assert state=present (expected changed=false and a warning) + assert: + that: + # No way to assert for warnings? + - 'not result.changed' + - 'result.group_id.startswith("sg-")' + + when: default_vpc + + # ============================================================ + - name: test state=absent (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + state: absent + check_mode: true + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test state=absent (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + state: absent + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - 'result.changed' + - 'not result.group_id' + + # ============================================================ + - name: create security group in the VPC (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: create security group in the VPC + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.vpc_id == vpc_result.vpc.id' + - 'result.group_id.startswith("sg-")' + + # ============================================================ + - name: test adding tags (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: + tag1: test1 + tag2: test2 + check_mode: true + diff: true + register: result + + - name: assert that tags were added (expected changed=true) + assert: + that: + - 'result.changed' + - 'not result.diff.0.before.tags' + - 'result.diff.0.after.tags.tag1 == "test1"' + - 'result.diff.0.after.tags.tag2 == "test2"' + + # ============================================================ + - name: test adding tags (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: + tag1: test1 + tag2: test2 + register: result + + - name: assert that tags were added (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.tags == {"tag1": "test1", "tag2": "test2"}' + + # ============================================================ + - name: test that tags are present (expected changed=False) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + purge_rules_egress: false + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: + tag1: test1 + tag2: test2 + check_mode: true + register: result + + - name: assert that tags were not changed (expected changed=False) + assert: + that: + - 'not result.changed' + + # ============================================================ + - name: test that tags are present (expected changed=False) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + purge_rules_egress: false + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: + tag1: test1 + tag2: test2 + register: result + + - name: assert that tags were not changed (expected changed=False) + assert: + that: + - 'not result.changed' + - 'result.tags == {"tag1": "test1", "tag2": "test2"}' + + # ============================================================ + - name: test purging tags (expected changed=True) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: + tag1: test1 + check_mode: true + register: result + + - name: assert that tag2 was removed (expected changed=true) + assert: + that: + - 'result.changed' + + # ============================================================ + - name: test purging tags (expected changed=True) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: + tag1: test1 + register: result + + - name: assert that tag2 was removed (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.tags == {"tag1": "test1"}' + + # ============================================================ + + - name: assert that tags are left as-is if not specified (expected changed=False) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + register: result + + - name: assert that the tags stayed the same (expected changed=false) + assert: + that: + - 'not result.changed' + - 'result.tags == {"tag1": "test1"}' + + # ============================================================ + + - name: test purging all tags (expected changed=True) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ip: "10.1.1.1/32" + tags: {} + register: result + + - name: assert that tag1 was removed (expected changed=true) + assert: + that: + - 'result.changed' + - 'not result.tags' + + # ============================================================ + - name: test adding a rule and egress rule descriptions (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + # purge the other rules so assertions work for the subsequent tests for rule descriptions + purge_rules_egress: true + purge_rules: true + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: ipv6 rule desc 1 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 1 + check_mode: true + register: result + + - name: assert that rule descriptions are created (expected changed=true) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed changes should still have changed due to purged rules (expected changed=true) + assert: + that: + - 'result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined + + # ========================================================================================= + - name: add rules without descriptions ready for adding descriptions to existing rules + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + # purge the other rules so assertions work for the subsequent tests for rule descriptions + purge_rules_egress: true + purge_rules: true + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + register: result + + # ============================================================ + - name: test adding a rule and egress rule descriptions (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + # purge the other rules so assertions work for the subsequent tests for rule descriptions + purge_rules_egress: true + purge_rules: true + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: ipv6 rule desc 1 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 1 + register: result + + - name: assert that rule descriptions are created (expected changed=true) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.changed' + - 'result.ip_permissions[0].ipv6_ranges[0].description == "ipv6 rule desc 1"' + - 'result.ip_permissions_egress[0].ip_ranges[0].description == "egress rule desc 1"' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed changes should still have changed due to purged rules (expected changed=true) + assert: + that: + - 'result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined + + # ============================================================ + - name: test modifying rule and egress rule descriptions (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + purge_rules: false + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: ipv6 rule desc 2 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 2 + check_mode: true + register: result + + - name: assert that rule descriptions were modified (expected changed=true) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.ip_permissions | length > 0' + - 'result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed everything should stay the same (expected changed=false) + assert: + that: + - 'not result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined and result.ip_permissions_egress[1].ip_ranges[0].description is undefined + + # ============================================================ + - name: test modifying rule and egress rule descriptions (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + purge_rules: false + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: ipv6 rule desc 2 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 2 + register: result + + - name: assert that rule descriptions were modified (expected changed=true) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.changed' + - 'result.ip_permissions[0].ipv6_ranges[0].description == "ipv6 rule desc 2"' + - 'result.ip_permissions_egress[0].ip_ranges[0].description == "egress rule desc 2"' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed everything should stay the same (expected changed=false) + assert: + that: + - 'not result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined + + # ============================================================ + + - name: test creating rule in default vpc with egress rule (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}-default-vpc' + description: '{{ec2_group_description}} default VPC' + purge_rules_egress: true + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ip: 10.1.1.1/24 + rule_desc: ipv4 rule desc + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 2 + register: result + + - name: assert that rule descriptions were modified (expected changed=true) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.changed' + - 'result.ip_permissions_egress|length == 1' + + # ============================================================ + - name: test that keeping the same rule descriptions (expected changed=false) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + purge_rules: false + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: ipv6 rule desc 2 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 2 + check_mode: true + register: result + + - name: assert that rule descriptions stayed the same (expected changed=false) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'not result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed everything should stay the same (expected changed=false) + assert: + that: + - 'not result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined + + # ============================================================ + - name: test that keeping the same rule descriptions (expected changed=false) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + purge_rules: false + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: ipv6 rule desc 2 + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: egress rule desc 2 + register: result + + - name: assert that rule descriptions stayed the same (expected changed=false) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'not result.changed' + - 'result.ip_permissions[0].ipv6_ranges[0].description == "ipv6 rule desc 2"' + - 'result.ip_permissions_egress[0].ip_ranges[0].description == "egress rule desc 2"' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed everything should stay the same (expected changed=false) + assert: + that: + - 'not result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined + + # ============================================================ + - name: test removing rule descriptions (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + purge_rules: false + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: + check_mode: true + register: result + + - name: assert that rule descriptions were removed (expected changed=true) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is defined + + - name: if an older version of botocore is installed everything should stay the same (expected changed=false) + assert: + that: + - 'not result.changed' + when: result.ip_permissions_egress[0].ip_ranges[0].description is undefined + + # ============================================================ + - name: test removing rule descriptions (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + description: '{{ec2_group_description}}' + vpc_id: '{{ vpc_result.vpc.id }}' + purge_rules_egress: false + purge_rules: false + state: present + rules: + - proto: "tcp" + ports: + - 8281 + cidr_ipv6: 1001:d00::/24 + rule_desc: + rules_egress: + - proto: "tcp" + ports: + - 8282 + cidr_ip: 10.2.2.2/32 + rule_desc: + register: result + ignore_errors: true + + - name: assert that rule descriptions were removed (expected changed=true with newer botocore) + # Only assert this if rule description is defined as the botocore version may < 1.7.2. + # It's still helpful to have these tests run on older versions since it verifies backwards + # compatibility with this feature. + assert: + that: + - 'result.ip_permissions[0].ipv6_ranges[0].description is undefined' + - 'result.ip_permissions_egress[0].ip_ranges[0].description is undefined' + when: result is changed + + - name: if an older version of botocore is installed everything should stay the same (expected changed=false) + assert: + that: + - 'not result.changed' + when: result.failed + + # ============================================================ + + - name: test state=absent (expected changed=true) + ec2_group: + name: '{{ec2_group_name}}' + state: absent + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - 'result.changed' + - 'not result.group_id' + when: not has_ec2_classic + + always: + # ============================================================ + - name: tidy up security group + ec2_group: + name: '{{ec2_group_name}}' + state: absent + ignore_errors: yes + + - name: tidy up security group for IPv6 EC2-Classic tests + ec2_group: + name: '{{ ec2_group_name }}-2' + state: absent + ignore_errors: yes + + - name: tidy up default VPC security group + ec2_group: + name: '{{ec2_group_name}}-default-vpc' + state: absent + ignore_errors: yes + + - name: tidy up automatically created SG + ec2_group: + name: "{{ resource_prefix }} - Another security group" + state: absent + ignore_errors: yes + + - name: tidy up VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: absent + cidr_block: "{{ vpc_cidr }}" + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/multi_account.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/multi_account.yml new file mode 100644 index 00000000..675dfd93 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/multi_account.yml @@ -0,0 +1,124 @@ +- block: + - aws_caller_info: + register: caller_facts + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc-2" + state: present + cidr_block: "{{ vpc_cidr }}" + tags: + Description: "Created by ansible-test" + register: vpc_result_2 + - name: Peer the secondary-VPC to the main VPC + ec2_vpc_peer: + vpc_id: '{{ vpc_result_2.vpc.id }}' + peer_vpc_id: '{{ vpc_result.vpc.id }}' + peer_owner_id: '{{ caller_facts.account }}' + peer_region: '{{ aws_region }}' + register: peer_origin + - name: Accept the secondary-VPC peering connection in the main VPC + ec2_vpc_peer: + peer_vpc_id: '{{ vpc_result_2.vpc.id }}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: accept + peering_id: '{{ peer_origin.peering_id }}' + peer_owner_id: '{{ caller_facts.account }}' + peer_region: '{{ aws_region }}' + - name: Create group in second VPC + ec2_group: + name: '{{ ec2_group_name }}-external' + description: '{{ ec2_group_description }}' + vpc_id: '{{ vpc_result_2.vpc.id }}' + state: present + rules: + - proto: "tcp" + cidr_ip: 0.0.0.0/0 + ports: + - 80 + rule_desc: 'http whoo' + register: external + - name: Create group in internal VPC + ec2_group: + name: '{{ ec2_group_name }}-internal' + description: '{{ ec2_group_description }}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + group_id: '{{ caller_facts.account }}/{{ external.group_id }}/{{ ec2_group_name }}-external' + ports: + - 80 + - name: Re-make same rule, expecting changed=false in internal VPC + ec2_group: + name: '{{ ec2_group_name }}-internal' + description: '{{ ec2_group_description }}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + group_id: '{{ caller_facts.account }}/{{ external.group_id }}/{{ ec2_group_name }}-external' + ports: + - 80 + register: out + - assert: + that: + - out is not changed + - name: Try again with a bad group_id group in internal VPC + ec2_group: + name: '{{ ec2_group_name }}-internal' + description: '{{ ec2_group_description }}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: present + rules: + - proto: "tcp" + group_id: '{{ external.group_id }}/{{ caller_facts.account }}/{{ ec2_group_name }}-external' + ports: + - 80 + register: out + ignore_errors: true + - assert: + that: + - out is failed + always: + - pause: seconds=5 + - name: Delete secondary-VPC side of peer + ec2_vpc_peer: + vpc_id: '{{ vpc_result_2.vpc.id }}' + peer_vpc_id: '{{ vpc_result.vpc.id }}' + peering_id: '{{ peer_origin.peering_id }}' + state: absent + peer_owner_id: '{{ caller_facts.account }}' + peer_region: '{{ aws_region }}' + ignore_errors: yes + - name: Delete main-VPC side of peer + ec2_vpc_peer: + peer_vpc_id: '{{ vpc_result_2.vpc.id }}' + vpc_id: '{{ vpc_result.vpc.id }}' + state: absent + peering_id: '{{ peer_origin.peering_id }}' + peer_owner_id: '{{ caller_facts.account }}' + peer_region: '{{ aws_region }}' + ignore_errors: yes + - name: Clean up group in second VPC + ec2_group: + name: '{{ ec2_group_name }}-external' + description: '{{ ec2_group_description }}' + state: absent + vpc_id: '{{ vpc_result_2.vpc.id }}' + ignore_errors: yes + - name: Clean up group in second VPC + ec2_group: + name: '{{ ec2_group_name }}-internal' + description: '{{ ec2_group_description }}' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + ignore_errors: yes + - name: tidy up VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc-2" + state: absent + cidr_block: "{{ vpc_cidr }}" + ignore_errors: yes + register: removed + retries: 10 + until: removed is not failed diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/multi_nested_target.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/multi_nested_target.yml new file mode 100644 index 00000000..87f48468 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/multi_nested_target.yml @@ -0,0 +1,213 @@ +--- + # ============================================================ + + - name: test state=present for multiple ipv6 and ipv4 targets (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - "64:ff9b::/96" + - ["2620::/32"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24", "10.20.0.0/24"] + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + + - name: test state=present for multiple ipv6 and ipv4 targets (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - "64:ff9b::/96" + - ["2620::/32"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24", "10.20.0.0/24"] + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'result.changed' + - 'result.ip_permissions | length == 2' + - 'result.ip_permissions[0].ip_ranges | length == 4 or result.ip_permissions[1].ip_ranges | length == 4' + - 'result.ip_permissions[0].ipv6_ranges | length == 2 or result.ip_permissions[1].ipv6_ranges | length == 2' + + - name: test state=present for multiple ipv6 and ipv4 targets (expected changed=false) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - "64:ff9b::/96" + - ["2620::/32"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24", "10.20.0.0/24"] + check_mode: true + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'not result.changed' + + - name: test state=present for multiple ipv6 and ipv4 targets (expected changed=false) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - "64:ff9b::/96" + - ["2620::/32"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24", "10.20.0.0/24"] + register: result + + - name: assert state=present (expected changed=true) + assert: + that: + - 'not result.changed' + + - name: test state=present purging a nested ipv4 target (expected changed=true) (CHECK MODE) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - "64:ff9b::/96" + - ["2620::/32"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24"] + check_mode: true + register: result + + - assert: + that: + - result.changed + + - name: test state=present purging a nested ipv4 target (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - "64:ff9b::/96" + - ["2620::/32"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24"] + register: result + + - assert: + that: + - result.changed + - 'result.ip_permissions[0].ip_ranges | length == 3 or result.ip_permissions[1].ip_ranges | length == 3' + - 'result.ip_permissions[0].ipv6_ranges | length == 2 or result.ip_permissions[1].ipv6_ranges | length == 2' + + - name: test state=present with both associated ipv6 targets nested (expected changed=false) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - ["2620::/32", "64:ff9b::/96"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24"] + register: result + + - assert: + that: + - not result.changed + + - name: test state=present add another nested ipv6 target (expected changed=true) + ec2_group: + name: '{{ ec2_group_name }}' + description: '{{ ec2_group_description }}' + state: present + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + cidr_ipv6: + - ["2620::/32", "64:ff9b::/96"] + - ["2001:DB8:A0B:12F0::1/64"] + - proto: "tcp" + ports: 5665 + cidr_ip: + - 172.16.1.0/24 + - 172.16.17.0/24 + - ["10.0.0.0/24"] + register: result + + - assert: + that: + - result.changed + - result.warning is not defined + - 'result.ip_permissions[0].ip_ranges | length == 3 or result.ip_permissions[1].ip_ranges | length == 3' + - 'result.ip_permissions[0].ipv6_ranges | length == 3 or result.ip_permissions[1].ipv6_ranges | length == 3' + + - name: delete it + ec2_group: + name: '{{ ec2_group_name }}' + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/numeric_protos.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/numeric_protos.yml new file mode 100644 index 00000000..6cca9fc4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/numeric_protos.yml @@ -0,0 +1,60 @@ +--- +- block: + - name: set up temporary group name for tests + set_fact: + group_tmp_name: '{{ec2_group_name}}-numbered-protos' + + - name: Create a group with numbered protocol (GRE) + ec2_group: + name: '{{ group_tmp_name }}' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ ec2_group_description }}' + rules: + - proto: 47 + to_port: -1 + from_port: -1 + cidr_ip: 0.0.0.0/0 + state: present + register: result + + - name: Create a group with a quoted proto + ec2_group: + name: '{{ group_tmp_name }}' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ ec2_group_description }}' + rules: + - proto: '47' + to_port: -1 + from_port: -1 + cidr_ip: 0.0.0.0/0 + state: present + register: result + - assert: + that: + - result is not changed + - name: Add a tag with a numeric value + ec2_group: + name: '{{ group_tmp_name }}' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ ec2_group_description }}' + tags: + foo: 1 + - name: Read a tag with a numeric value + ec2_group: + name: '{{ group_tmp_name }}' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ ec2_group_description }}' + tags: + foo: 1 + register: result + - assert: + that: + - result is not changed + + always: + - name: tidy up egress rule test security group + ec2_group: + name: '{{group_tmp_name}}' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/rule_group_create.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/rule_group_create.yml new file mode 100644 index 00000000..ab14d32d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_group/tasks/rule_group_create.yml @@ -0,0 +1,126 @@ +--- +- block: + - name: Create a group with self-referring rule + ec2_group: + name: '{{ec2_group_name}}-auto-create-1' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + rules: + - proto: "tcp" + from_port: 8000 + to_port: 8100 + group_name: '{{ec2_group_name}}-auto-create-1' + state: present + register: result + + - name: Create a second group rule + ec2_group: + name: '{{ec2_group_name}}-auto-create-2' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + state: present + + - name: Create a series of rules with a recently created group as target + ec2_group: + name: '{{ec2_group_name}}-auto-create-1' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + purge_rules: false + rules: + - proto: "tcp" + from_port: "{{ item }}" + to_port: "{{ item }}" + group_name: '{{ec2_group_name}}-auto-create-2' + state: present + register: result + with_items: + - 20 + - 40 + - 60 + - 80 + + - assert: + that: + - result.warning is not defined + + - name: Create a group with only the default rule + ec2_group: + name: '{{ec2_group_name}}-auto-create-1' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + rules: + - proto: "tcp" + from_port: 8182 + to_port: 8182 + group_name: '{{ec2_group_name}}-auto-create-3' + state: present + register: result + ignore_errors: true + + - name: assert you can't create a new group from a rule target with no description + assert: + that: + - result is failed + + - name: Create a group with a target of a separate group + ec2_group: + name: '{{ec2_group_name}}-auto-create-1' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + rules: + - proto: tcp + ports: + - 22 + - 80 + group_name: '{{ec2_group_name}}-auto-create-3' + group_desc: '{{ec2_group_description}}' + state: present + register: result + + - assert: + that: + - result.warning is not defined + + - name: Create a 4th group + ec2_group: + name: '{{ec2_group_name}}-auto-create-4' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + state: present + rules: + - proto: tcp + ports: + - 22 + cidr_ip: 0.0.0.0/0 + + - name: use recently created group in a rule + ec2_group: + name: '{{ec2_group_name}}-auto-create-5' + vpc_id: '{{ vpc_result.vpc.id }}' + description: '{{ec2_group_description}}' + rules: + - proto: tcp + ports: + - 443 + group_name: '{{ec2_group_name}}-auto-create-4' + state: present + + - assert: + that: + - result.warning is not defined + + always: + - name: tidy up egress rule test security group + ec2_group: + name: '{{ec2_group_name}}-auto-create-{{ item }}' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + ignore_errors: yes + with_items: [5, 4, 3, 2, 1] + - name: tidy up egress rule test security group + ec2_group: + name: '{{ec2_group_name}}-auto-create-{{ item }}' + state: absent + vpc_id: '{{ vpc_result.vpc.id }}' + ignore_errors: yes + with_items: [1, 2, 3, 4, 5] diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/aliases new file mode 100644 index 00000000..6e3860be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/defaults/main.yml new file mode 100644 index 00000000..df0082d9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# defaults file for test_ec2_key +ec2_key_name: '{{resource_prefix}}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/meta/main.yml new file mode 100644 index 00000000..45f0cedf --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - prepare_tests + - setup_sshkey + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/tasks/main.yml new file mode 100644 index 00000000..69e7edcb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_key/tasks/main.yml @@ -0,0 +1,137 @@ +--- +# TODO - name: test 'validate_certs' parameter +# TODO - name: test creating key pair with another_key_material with force=yes +# ============================================================ + +- module_defaults: + group/aws: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + block: + + # ============================================================ + - name: test with no parameters + ec2_key: + register: result + ignore_errors: true + + - name: assert failure when called with no parameters + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: name"' + + # ============================================================ + - name: test removing a non-existent key pair + ec2_key: + name: '{{ ec2_key_name }}' + state: absent + register: result + + - name: assert removing a non-existent key pair + assert: + that: + - 'not result.changed' + + # ============================================================ + - name: test creating a new key pair + ec2_key: + name: '{{ ec2_key_name }}' + state: present + register: result + + - name: assert creating a new key pair + assert: + that: + - 'result.changed' + - '"key" in result' + - '"name" in result.key' + - '"fingerprint" in result.key' + - '"private_key" in result.key' + - 'result.key.name == "{{ec2_key_name}}"' + + # ============================================================ + - name: test removing an existent key + ec2_key: + name: '{{ ec2_key_name }}' + state: absent + register: result + + - name: assert removing an existent key + assert: + that: + - 'result.changed' + - '"key" in result' + - 'result.key == None' + + # ============================================================ + - name: test state=present with key_material + ec2_key: + name: '{{ ec2_key_name }}' + key_material: '{{ key_material }}' + state: present + register: result + + - name: assert state=present with key_material + assert: + that: + - 'result.changed == True' + - '"key" in result' + - '"name" in result.key' + - '"fingerprint" in result.key' + - '"private_key" not in result.key' + - 'result.key.name == "{{ec2_key_name}}"' + - 'result.key.fingerprint == "{{fingerprint}}"' + + # ============================================================ + + - name: test force=no with another_key_material (expect changed=false) + ec2_key: + name: '{{ ec2_key_name }}' + key_material: '{{ another_key_material }}' + force: no + register: result + + - name: assert force=no with another_key_material (expect changed=false) + assert: + that: + - 'not result.changed' + - 'result.key.fingerprint == "{{ fingerprint }}"' + + # ============================================================ + + - name: test updating a key pair using another_key_material (expect changed=True) + ec2_key: + name: '{{ ec2_key_name }}' + key_material: '{{ another_key_material }}' + register: result + + - name: assert updating a key pair using another_key_material (expect changed=True) + assert: + that: + - 'result.changed' + - 'result.key.fingerprint != "{{ fingerprint }}"' + + # ============================================================ + - name: test state=absent (expect changed=true) + ec2_key: + name: '{{ ec2_key_name }}' + state: absent + register: result + + - name: assert state=absent with key_material (expect changed=true) + assert: + that: + - 'result.changed' + - '"key" in result' + - 'result.key == None' + + always: + + # ============================================================ + - name: Always delete the key we might create + ec2_key: + name: '{{ ec2_key_name }}' + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/aliases new file mode 100644 index 00000000..412ce2c0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/aliases @@ -0,0 +1,3 @@ +non_local +cloud/aws +shippable/aws/group4 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/setup.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/setup.yml new file mode 100644 index 00000000..df73cf79 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/setup.yml @@ -0,0 +1,141 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + hosts: localhost + + collections: + - community.aws + + vars: + vpc_name: '{{ resource_prefix }}-vpc' + vpc_seed: '{{ resource_prefix }}' + vpc_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.0.0/16' + subnet_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' + ec2_ami_name: 'amzn2-ami-hvm-2.*-x86_64-gp2' + sshkey_file: '{{ resource_prefix }}_key' + + tasks: + + - name: Create an ssh key + shell: echo 'y' | ssh-keygen -P '' -f ../{{ sshkey_file }} + + - name: Get available AZs + aws_az_info: + filters: + region-name: "{{ aws_region }}" + register: az_info + + - name: Pick an AZ + set_fact: + availability_zone: "{{ az_info['availability_zones'][0]['zone_name'] }}" + + # ============================================================ + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: "{{ vpc_cidr }}" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc_result + + - set_fact: + vpc_id: "{{ vpc_result.vpc.id }}" + + - name: create an internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc_id }}" + state: present + tags: + "Name": "{{ resource_prefix }}" + register: igw_result + + - name: create a subnet + ec2_vpc_subnet: + cidr: "{{ vpc_cidr }}" + az: "{{ availability_zone }}" + vpc_id: "{{ vpc_id }}" + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + state: present + register: vpc_subnet_result + + - name: create a public route table + ec2_vpc_route_table: + vpc_id: "{{ vpc_id }}" + tags: + "Name": "{{ resource_prefix }}" + subnets: + - "{{ vpc_subnet_result.subnet.id }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw_result.gateway_id }}" + register: public_route_table + + - name: create a security group + ec2_group: + name: "{{ resource_prefix }}-sg" + description: "Created by {{ resource_prefix }}" + rules: + - proto: tcp + ports: 22 + cidr_ip: 0.0.0.0/0 + - proto: icmp + from_port: -1 + to_port: -1 + state: present + vpc_id: "{{ vpc_result.vpc.id }}" + register: vpc_sg_result + + - name: Create a key + ec2_key: + name: '{{ resource_prefix }}' + key_material: "{{ lookup('file', '../' ~ sshkey_file ~ '.pub') }}" + state: present + register: ec2_key_result + + - name: Get a list of images + ec2_ami_info: + filters: + owner-alias: amazon + name: "amzn2-ami-minimal-hvm-*" + description: "Amazon Linux 2 AMI *" + register: images_info + + - name: Set facts to simplify use of extra resources + set_fact: + vpc_subnet_id: "{{ vpc_subnet_result.subnet.id }}" + vpc_sg_id: "{{ vpc_sg_result.group_id }}" + vpc_igw_id: "{{ igw_result.gateway_id }}" + vpc_route_table_id: "{{ public_route_table.route_table.id }}" + image_id: "{{ images_info.images | sort(attribute='creation_date') | reverse | first | json_query('image_id') }}" + ec2_key_name: "{{ ec2_key_result.key.name }}" + + - name: Create an instance to test with + ec2_instance: + name: "{{ resource_prefix }}-ec2-metadata-facts" + image_id: "{{ image_id }}" + vpc_subnet_id: "{{ vpc_subnet_id }}" + security_group: "{{ vpc_sg_id }}" + instance_type: t2.micro + key_name: "{{ ec2_key_name }}" + network: + assign_public_ip: true + wait: true + wait_timeout: 300 + register: ec2_instance + + - set_fact: + ec2_instance_id: "{{ ec2_instance.instances[0].instance_id }}" + + - name: Create inventory file + template: + src: ../templates/inventory.j2 + dest: ../inventory diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/teardown.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/teardown.yml new file mode 100644 index 00000000..cda4043c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/teardown.yml @@ -0,0 +1,70 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + hosts: localhost + + collections: + - community.aws + + tasks: + # ============================================================ + + - name: terminate the instance + ec2_instance: + state: absent + instance_ids: + - "{{ ec2_instance_id }}" + wait: True + ignore_errors: true + retries: 5 + + - name: remove ssh key + ec2_key: + name: "{{ ec2_key_name }}" + state: absent + ignore_errors: true + + - name: remove the security group + ec2_group: + group_id: "{{ vpc_sg_id }}" + state: absent + ignore_errors: true + retries: 5 + + - name: remove the public route table + ec2_vpc_route_table: + vpc_id: "{{ vpc_id }}" + route_table_id: "{{ vpc_route_table_id }}" + lookup: id + state: absent + ignore_errors: true + retries: 5 + + - name: remove the subnet + ec2_vpc_subnet: + cidr: "{{ vpc_cidr }}" + az: "{{ availability_zone }}" + vpc_id: "{{ vpc_id }}" + state: absent + ignore_errors: true + retries: 5 + + - name: remove the internet gateway + ec2_vpc_igw: + vpc_id: "{{ vpc_id }}" + state: absent + ignore_errors: true + retries: 5 + + - name: remove the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: "{{ vpc_cidr }}" + state: absent + ignore_errors: true + retries: 5 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/test_metadata.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/test_metadata.yml new file mode 100644 index 00000000..fd49844b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/playbooks/test_metadata.yml @@ -0,0 +1,16 @@ +--- +- hosts: testhost + tasks: + + - name: Wait for EC2 to be available + wait_for_connection: + + - amazon.aws.ec2_metadata_facts: + + - name: Assert initial metadata for the instance + assert: + that: + - ansible_ec2_ami_id == image_id + - ansible_ec2_placement_availability_zone == "{{ availability_zone }}" + - ansible_ec2_security_groups == "{{ resource_prefix }}-sg" + - ansible_ec2_user_data == "None" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/runme.sh new file mode 100755 index 00000000..6f2bc466 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/runme.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -eux +export ANSIBLE_HOST_KEY_CHECKING=False +export ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null' + +CMD_ARGS=("$@") + +# Destroy Environment +cleanup() { + ansible-playbook playbooks/teardown.yml -i inventory -c local "${CMD_ARGS[@]}" +} +trap "cleanup" EXIT + +# create test resources and inventory +ansible-playbook playbooks/setup.yml -c local "$@" + +# test ec2_instance_metadata +ansible-playbook playbooks/test_metadata.yml -i inventory \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/templates/inventory.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/templates/inventory.j2 new file mode 100644 index 00000000..186edb8f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_metadata_facts/templates/inventory.j2 @@ -0,0 +1,20 @@ +[testhost] +"{{ ec2_instance.instances[0].public_ip_address }}" + +[testhost:vars] +ansible_user=ec2-user +ansible_ssh_private_key_file="{{ sshkey_file }}" +ansible_python_interpreter=/usr/bin/env python + +[all:vars] +# Template vars that will need to be used in used in tests and teardown +vpc_id="{{ vpc_id }}" +vpc_subnet_id="{{ vpc_subnet_id }}" +vpc_sg_id="{{ vpc_sg_id }}" +vpc_cidr="{{ vpc_cidr }}" +vpc_igw="{{ vpc_igw_id }}" +vpc_route_table_id="{{ vpc_route_table_id }}" +ec2_key_name="{{ ec2_key_name }}" +availability_zone="{{ availability_zone }}" +image_id="{{ image_id }}" +ec2_instance_id="{{ ec2_instance_id }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/aliases new file mode 100644 index 00000000..1dcb36b2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group4 +ec2_snapshot_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/defaults/main.yml new file mode 100644 index 00000000..dc1f0f70 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for ec2_snapshot diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/tasks/main.yml new file mode 100644 index 00000000..448d2f81 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_snapshot/tasks/main.yml @@ -0,0 +1,277 @@ +--- +# Tests for EC2 Snapshot +# +# Tests ec2_snapshot: +# - Snapshot creation +# - Create with last_snapshot_min_age +# - Snapshot deletion +# +# Tests ec2_snapshot_info: +# - Listing snapshots for filter: tag +# +# Possible Bugs: +# - check_mode not supported +# +- name: Integration testing for ec2_snapshot + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + collections: + - community.aws + + + block: + - ec2_ami_info: + owners: amazon + filters: + architecture: x86_64 + virtualization-type: hvm + root-device-type: ebs + name: "amzn-ami-hvm*" + register: amis + + - name: Setup an instance for testing + ec2_instance: + name: '{{ resource_prefix }}' + instance_type: t2.nano + image_id: "{{ (amis.images | sort(attribute='creation_date') | last).image_id }}" + wait: yes + volumes: + - device_name: /dev/xvda + ebs: + volume_size: 8 + delete_on_termination: true + register: instance + + - set_fact: + volume_id: '{{ instance.instances[0].block_device_mappings[0].ebs.volume_id }}' + instance_id: '{{ instance.instances[0].instance_id }}' + device_name: '{{ instance.instances[0].block_device_mappings[0].device_name }}' + +# JR: Check mode not supported +# - name: Take snapshot (check mode) +# ec2_snapshot: +# instance_id: '{{ instance_id }}' +# check_mode: true +# snapshot_tags: +# Test: '{{ resource_prefix }}' +# register: result +# - assert: +# that: +# - result is changed + + - name: Take snapshot of volume + ec2_snapshot: + volume_id: '{{ volume_id }}' + register: result + + # The Name tag is created automatically as the instance_name; ie the resource_prefix + - name: Get info about snapshots + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_result + + - assert: + that: + - result is changed + - info_result is not changed + - info_result.snapshots| length == 1 + - info_result.snapshots[0].snapshot_id == result.snapshot_id + - info_result.snapshots[0].volume_id == result.volume_id + - info_result.snapshots[0].volume_size == result.volume_size + - info_result.snapshots[0].tags == result.tags + + - name: Get info about snapshots (check_mode) + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_check + check_mode: yes + + - assert: + that: + - info_check is not changed + - info_check.snapshots| length == 1 + - info_check.snapshots[0].snapshot_id == result.snapshot_id + - info_check.snapshots[0].volume_id == result.volume_id + - info_check.snapshots[0].volume_size == result.volume_size + - info_check.snapshots[0].tags == result.tags + +# JR: Check mode not supported +# - name: Take snapshot if most recent >1hr (False) (check mode) +# ec2_snapshot: +# volume_id: '{{ volume_id }}' +# snapshot_tags: +# Name: '{{ resource_prefix }}' +# last_snapshot_min_age: 60 +# check_mode: true +# register: result +# - assert: +# that: +# - result is not changed + + - name: Take snapshot if most recent >1hr (False) + ec2_snapshot: + volume_id: '{{ volume_id }}' + last_snapshot_min_age: 60 + register: result + + - name: Get info about snapshots + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_result + + - assert: + that: + - result is not changed + - info_result.snapshots| length == 1 + + - name: Pause so we can do a last_snapshot_min_age test + pause: + minutes: 1 + +# JR: Check mode not supported +# - name: Take snapshot if most recent >1min (True) (check mode) +# ec2_snapshot: +# volume_id: '{{ volume_id }}' +# snapshot_tags: +# Name: '{{ resource_prefix }}' +# last_snapshot_min_age: 1 +# check_mode: true +# register: result +# - assert: +# that: +# - result is changed + + - name: Take snapshot if most recent >1min (True) + ec2_snapshot: + volume_id: '{{ volume_id }}' + last_snapshot_min_age: 1 + register: result + + - name: Get info about snapshots + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_result + + - assert: + that: + - result is changed + - info_result.snapshots| length == 2 + - '"{{ result.snapshot_id }}" in "{{ info_result| community.general.json_query("snapshots[].snapshot_id") }}"' + +# JR: Check mode not supported +# - name: Take snapshot with a tag (check mode) +# ec2_snapshot: +# volume_id: '{{ volume_id }}' +# snapshot_tags: +# MyTag: '{{ resource_prefix }}' +# register: result +# - assert: +# that: +# - result is changed + + # Wait at least 15 seconds between concurrent volume snapshots. + - name: Prevent SnapshotCreationPerVolumeRateExceeded errors + pause: + seconds: 15 + + - name: Take snapshot and tag it + ec2_snapshot: + volume_id: '{{ volume_id }}' + snapshot_tags: + MyTag: '{{ resource_prefix }}' + register: tagged_result + + - name: Get info about snapshots by tag + ec2_snapshot_info: + filters: + "tag:MyTag": '{{ resource_prefix }}' + register: tag_info_result + + - set_fact: + tagged_snapshot_id: '{{ tag_info_result.snapshots[0].snapshot_id }}' + + - assert: + that: + - tagged_result is changed + - tagged_result.tags| length == 2 + - tag_info_result.snapshots| length == 1 + - tagged_result.tags.MyTag == "{{ resource_prefix }}" + - '"{{ tagged_result.snapshot_id }}" == "{{ tagged_snapshot_id }}"' + + - name: Get info about all snapshots for this test + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_result + + - assert: + that: + - info_result.snapshots| length == 3 + + - name: Delete the tagged snapshot + ec2_snapshot: + state: absent + snapshot_id: '{{ tagged_snapshot_id }}' + + - name: Get info about all snapshots for this test + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_result + + - assert: + that: + - info_result.snapshots| length == 2 + - '"{{ tagged_snapshot_id }}" not in "{{ info_result| community.general.json_query("snapshots[].snapshot_id") }}"' + + - name: Delete snapshots + ec2_snapshot: + state: absent + snapshot_id: '{{ item.snapshot_id }}' + with_items: '{{ info_result.snapshots }}' + + - name: Get info about all snapshots for this test + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: info_result + + - assert: + that: + - info_result.snapshots| length == 0 + + always: + + - name: Snapshots to delete + ec2_snapshot_info: + filters: + "tag:Name": '{{ resource_prefix }}' + register: tagged_snapshots + + - name: Delete tagged snapshots + ec2_snapshot: + state: absent + snapshot_id: '{{ item.snapshot_id }}' + with_items: '{{ tagged_snapshots.snapshots }}' + ignore_errors: true + + - name: Delete instance + ec2_instance: + instance_ids: '{{ instance_id }}' + state: absent + ignore_errors: true + + - name: Delete volume + ec2_vol: + id: '{{ volume_id }}' + state: absent + ignore_errors: true diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/aliases new file mode 100644 index 00000000..be56eee8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group2 +ec2_tag_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/defaults/main.yml new file mode 100644 index 00000000..6aa39c73 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for test_ec2_tag diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/tasks/main.yml new file mode 100644 index 00000000..bf36afcb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/tasks/main.yml @@ -0,0 +1,143 @@ +--- +# tasks file for test_ec2_tag +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - name: Create an EC2 volume so we have something to tag + ec2_vol: + name: "{{ resource_prefix }} ec2_tag volume" + volume_size: 1 + state: present + zone: "{{ aws_region }}a" + register: volume + + - name: List the tags on the volume (ec2_tag) + ec2_tag: + resource: "{{ volume.volume_id }}" + state: list + register: result + - name: List the tags on the volume (ec2_tag_info) + ec2_tag_info: + resource: "{{ volume.volume_id }}" + register: result_info + + - assert: + that: + - result.tags | length == 1 + - result.tags.Name == '{{ resource_prefix }} ec2_tag volume' + - result_info.tags | length == 1 + - result_info.tags.Name == '{{ resource_prefix }} ec2_tag volume' + + - name: Set some new tags on the volume + ec2_tag: + resource: "{{ volume.volume_id }}" + state: present + tags: + foo: foo + bar: baz + baz: also baz + register: result + - name: List the new tags on the volume + ec2_tag_info: + resource: "{{ volume.volume_id }}" + register: result_info + + - assert: + that: + - result is changed + - result.tags | length == 4 + - result.added_tags | length == 3 + - result.tags.Name == '{{ resource_prefix }} ec2_tag volume' + - result.tags.foo == 'foo' + - result.tags.bar == 'baz' + - result.tags.baz == 'also baz' + - result_info.tags | length == 4 + - result_info.tags.Name == '{{ resource_prefix }} ec2_tag volume' + - result_info.tags.foo == 'foo' + - result_info.tags.bar == 'baz' + - result_info.tags.baz == 'also baz' + + - name: Remove a tag by name + ec2_tag: + resource: "{{ volume.volume_id }}" + state: absent + tags: + baz: + register: result + + - assert: + that: + - result is changed + - result.removed_tags | length == 1 + - "'baz' in result.removed_tags" + + - name: Don't remove a tag + ec2_tag: + resource: "{{ volume.volume_id }}" + state: absent + tags: + foo: baz + register: result + + - assert: + that: + - result is not changed + + - name: Remove a tag + ec2_tag: + resource: "{{ volume.volume_id }}" + state: absent + tags: + foo: foo + register: result + + - assert: + that: + - result is changed + - result.tags | length == 2 + - "'added_tags' not in result" + - result.removed_tags | length == 1 + - result.tags.Name == '{{ resource_prefix }} ec2_tag volume' + - result.tags.bar == 'baz' + + - name: Set an exclusive tag + ec2_tag: + resource: "{{ volume.volume_id }}" + purge_tags: true + tags: + baz: quux + register: result + + - assert: + that: + - result is changed + - result.tags | length == 1 + - result.added_tags | length == 1 + - result.removed_tags | length == 2 + - result.tags.baz == 'quux' + + - name: Remove all tags + ec2_tag: + resource: "{{ volume.volume_id }}" + purge_tags: true + tags: {} + register: result + + - assert: + that: + - result is changed + - result.tags | length == 0 + + always: + - name: Remove the volume + ec2_vol: + id: "{{ volume.volume_id }}" + state: absent + register: result + until: result is not failed + ignore_errors: yes + retries: 10 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/vars/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/vars/main.yml new file mode 100644 index 00000000..c2d0654a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_tag/vars/main.yml @@ -0,0 +1,2 @@ +--- +# vars file for test_ec2_tag diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/aliases new file mode 100644 index 00000000..b2f0c65b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group3 +ec2_vol_info diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/defaults/main.yml new file mode 100644 index 00000000..eb2594bc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/defaults/main.yml @@ -0,0 +1,5 @@ +vpc_name: '{{ resource_prefix }}-vpc' +vpc_seed: '{{ resource_prefix }}' +vpc_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.0.0/16' +subnet_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' +ec2_ami_name: 'amzn2-ami-hvm-2.*-x86_64-gp2'
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/main.yml new file mode 100644 index 00000000..70e711b3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/main.yml @@ -0,0 +1,5 @@ +- hosts: localhost + connection: local + environment: "{{ ansible_test.environment }}" + tasks: + - include_tasks: 'tasks/main.yml'
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/meta/main.yml new file mode 100644 index 00000000..bc4ebde8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/tasks/main.yml new file mode 100644 index 00000000..2cbb44e2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/tasks/main.yml @@ -0,0 +1,27 @@ +- set_fact: + virtualenv: "{{ remote_tmp_dir }}/virtualenv" + virtualenv_command: "{{ ansible_python_interpreter }} -m virtualenv" + +- set_fact: + virtualenv_interpreter: "{{ virtualenv }}/bin/python" + +- pip: + name: virtualenv + +- pip: + name: + - 'boto3>=1.16.33' + - 'botocore>=1.13.0' + - 'coverage<5' + - 'boto>=2.49.0' + virtualenv: "{{ virtualenv }}" + virtualenv_command: "{{ virtualenv_command }}" + virtualenv_site_packages: no + +- include_tasks: tests.yml + vars: + ansible_python_interpreter: "{{ virtualenv_interpreter }}" + +- file: + path: "{{ virtualenv }}" + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/tasks/tests.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/tasks/tests.yml new file mode 100644 index 00000000..42cd8de3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vol/tasks/tests.yml @@ -0,0 +1,546 @@ +--- + +- module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key | default(omit) }}' + aws_secret_key: '{{ aws_secret_key | default(omit) }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region | default(omit) }}' + + collections: + - amazon.aws + + block: + - name: list available AZs + aws_az_info: + register: region_azs + + - name: pick an AZ for testing + set_fact: + availability_zone: "{{ region_azs.availability_zones[0].zone_name }}" + + - name: Create a test VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + tags: + Name: ec2_vol testing + ResourcePrefix: "{{ resource_prefix }}" + register: testing_vpc + + - name: Create a test subnet + ec2_vpc_subnet: + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: "{{ subnet_cidr }}" + tags: + Name: ec2_vol testing + ResourcePrefix: "{{ resource_prefix }}" + az: '{{ availability_zone }}' + register: testing_subnet + + - name: Find AMI to use + ec2_ami_info: + owners: 'amazon' + filters: + name: '{{ ec2_ami_name }}' + register: ec2_amis + + - name: Set fact with latest AMI + vars: + latest_ami: '{{ ec2_amis.images | sort(attribute="creation_date") | last }}' + set_fact: + ec2_ami_image: '{{ latest_ami.image_id }}' + + # ==== ec2_vol tests =============================================== + + - name: create a volume (validate module defaults) + ec2_vol: + volume_size: 1 + zone: "{{ availability_zone }}" + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: volume1 + + - name: check task return attributes + assert: + that: + - volume1.changed + - "'volume' in volume1" + - "'volume_id' in volume1" + - "'volume_type' in volume1" + - "'device' in volume1" + - volume1.volume.status == 'available' + - volume1.volume_type == 'standard' + - "'attachment_set' in volume1.volume" + - "'instance_id' in volume1.volume.attachment_set" + - not volume1.volume.attachment_set.instance_id + - not volume1.volume.encrypted + - volume1.volume.tags.ResourcePrefix == "{{ resource_prefix }}" + + # no idempotency check needed here + + - name: create another volume (override module defaults) + ec2_vol: + encrypted: yes + volume_size: 4 + volume_type: io1 + iops: 101 + name: "{{ resource_prefix }}" + tags: + ResourcePrefix: "{{ resource_prefix }}" + zone: "{{ availability_zone }}" + register: volume2 + + - name: check task return attributes + assert: + that: + - volume2.changed + - "'volume' in volume2" + - "'volume_id' in volume2" + - "'volume_type' in volume2" + - "'device' in volume2" + - volume2.volume.status == 'available' + - volume2.volume_type == 'io1' + - volume2.volume.iops == 101 + - volume2.volume.size == 4 + - volume2.volume.encrypted + - volume2.volume.tags.ResourcePrefix == "{{ resource_prefix }}" + + - name: create another volume (override module defaults) (idempotent) + ec2_vol: + encrypted: yes + volume_size: 4 + volume_type: io1 + iops: 101 + name: "{{ resource_prefix }}" + tags: + ResourcePrefix: "{{ resource_prefix }}" + zone: "{{ availability_zone }}" + register: volume2_idem + + - name: check task return attributes + assert: + that: + - not volume2_idem.changed + + - name: create snapshot from volume + ec2_snapshot: + volume_id: "{{ volume1.volume_id }}" + description: "Resource Prefix - {{ resource_prefix }}" + snapshot_tags: + ResourcePrefix: "{{ resource_prefix }}" + register: vol1_snapshot + + - name: check task return attributes + assert: + that: + - vol1_snapshot.changed + + - name: create a volume from a snapshot + ec2_vol: + snapshot: "{{ vol1_snapshot.snapshot_id }}" + encrypted: yes + volume_type: gp2 + volume_size: 1 + zone: "{{ availability_zone }}" + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: volume3 + + - name: check task return attributes + assert: + that: + - volume3.changed + - "volume3.volume.snapshot_id == vol1_snapshot.snapshot_id" + + - name: create an ec2 instance + ec2_instance: + name: "{{ resource_prefix }}" + vpc_subnet_id: "{{ testing_subnet.subnet.id }}" + instance_type: t3.nano + image_id: "{{ ec2_ami_image }}" + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: test_instance + + - name: check task return attributes + assert: + that: + - test_instance.changed + + - name: attach existing volume to an instance + ec2_vol: + id: "{{ volume1.volume_id }}" + instance: "{{ test_instance.instance_ids[0] }}" + device_name: /dev/sdg + delete_on_termination: no + register: vol_attach_result + + - name: check task return attributes + assert: + that: + - vol_attach_result.changed + - "'device' in vol_attach_result and vol_attach_result.device == '/dev/sdg'" + - "'volume' in vol_attach_result" + - vol_attach_result.volume.attachment_set.status in ['attached', 'attaching'] + - vol_attach_result.volume.attachment_set.instance_id == test_instance.instance_ids[0] + - vol_attach_result.volume.attachment_set.device == '/dev/sdg' + +# Failing +# - "vol_attach_result.volume.attachment_set.deleteOnTermination" + + - name: attach existing volume to an instance (idempotent) + ec2_vol: + id: "{{ volume1.volume_id }}" + instance: "{{ test_instance.instance_ids[0] }}" + device_name: /dev/sdg + delete_on_termination: no + register: vol_attach_result + + - name: check task return attributes + assert: + that: + - "not vol_attach_result.changed" + - "vol_attach_result.volume.attachment_set.status == 'attached'" + + - name: attach a new volume to an instance + ec2_vol: + instance: "{{ test_instance.instance_ids[0] }}" + device_name: /dev/sdh + volume_size: 1 + volume_type: gp2 + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: new_vol_attach_result + + - name: check task return attributes + assert: + that: + - new_vol_attach_result.changed + - "'device' in new_vol_attach_result and new_vol_attach_result.device == '/dev/sdh'" + - "'volume' in new_vol_attach_result" + - new_vol_attach_result.volume.attachment_set.status in ['attached', 'attaching'] + - new_vol_attach_result.volume.attachment_set.instance_id == test_instance.instance_ids[0] + - new_vol_attach_result.volume.attachment_set.device == '/dev/sdh' + + - name: attach a new volume to an instance (idempotent) + ec2_vol: + instance: "{{ test_instance.instance_ids[0] }}" + device_name: /dev/sdh + volume_size: 1 + volume_type: gp2 + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: new_vol_attach_result_idem + ignore_errors: true + + - name: check task return attributes + assert: + that: + - "not new_vol_attach_result_idem.changed" + - "'Volume mapping for /dev/sdh already exists' in new_vol_attach_result_idem.msg" + + - name: create a volume from a snapshot and attach to the instance + ec2_vol: + instance: "{{ test_instance.instance_ids[0] }}" + device_name: /dev/sdi + snapshot: "{{ vol1_snapshot.snapshot_id }}" + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: attach_new_vol_from_snapshot_result + + - name: check task return attributes + assert: + that: + - attach_new_vol_from_snapshot_result.changed + - "'device' in attach_new_vol_from_snapshot_result and attach_new_vol_from_snapshot_result.device == '/dev/sdi'" + - "'volume' in attach_new_vol_from_snapshot_result" + - attach_new_vol_from_snapshot_result.volume.attachment_set.status in ['attached', 'attaching'] + - attach_new_vol_from_snapshot_result.volume.attachment_set.instance_id == test_instance.instance_ids[0] + + - name: list volumes attached to instance + ec2_vol: + instance: "{{ test_instance.instance_ids[0] }}" + state: list + register: inst_vols + + - name: check task return attributes + assert: + that: + - not inst_vols.changed + - "'volumes' in inst_vols" + - inst_vols.volumes | length == 4 + + - name: get info on ebs volumes + ec2_vol_info: + register: ec2_vol_info + + - name: check task return attributes + assert: + that: + - not ec2_vol_info.failed + + - name: get info on ebs volumes + ec2_vol_info: + filters: + attachment.instance-id: "{{ test_instance.instance_ids[0] }}" + register: ec2_vol_info + + - name: check task return attributes + assert: + that: + - ec2_vol_info.volumes | length == 4 + + - name: detach volume from the instance + ec2_vol: + id: "{{ new_vol_attach_result.volume_id }}" + instance: "" + register: new_vol_attach_result + + - name: check task return attributes + assert: + that: + - new_vol_attach_result.changed + - new_vol_attach_result.volume.status == 'available' + + - name: detach volume from the instance (idempotent) + ec2_vol: + id: "{{ new_vol_attach_result.volume_id }}" + instance: "" + register: new_vol_attach_result_idem + + - name: check task return attributes + assert: + that: + - not new_vol_attach_result_idem.changed + + - name: must not change because of missing parameter modify_volume + ec2_vol: + id: "{{ new_vol_attach_result.volume_id }}" + zone: "{{ availability_zone }}" + volume_type: gp3 + register: changed_gp3_volume + + - name: volume must not changed + assert: + that: + - not changed_gp3_volume.changed + + - name: change existing volume to gp3 + ec2_vol: + id: "{{ new_vol_attach_result.volume_id }}" + zone: "{{ availability_zone }}" + volume_type: gp3 + modify_volume: yes + register: changed_gp3_volume + + - name: check that volume_type has changed + assert: + that: + - changed_gp3_volume.volume_type == 'gp3' + - changed_gp3_volume.changed + + - name: volume must be from type gp3 (idempotent) + ec2_vol: + id: "{{ new_vol_attach_result.volume_id }}" + zone: "{{ availability_zone }}" + volume_type: gp3 + modify_volume: yes + register: changed_gp3_volume + retries: 3 + delay: 3 + until: not changed_gp3_volume.failed + # retry because ebs change is to slow + + - name: must not changed (idempotent) + assert: + that: + - changed_gp3_volume.volume_type == 'gp3' + - not changed_gp3_volume.changed + + - name: re-read volume information to validate new volume_type + ec2_vol_info: + filters: + volume-id: "{{ changed_gp3_volume.volume_id }}" + register: verify_gp3_change + + - name: volume type must be gp3 + assert: + that: + - v.type == 'gp3' + vars: + v: "{{ verify_gp3_change.volumes[0] }}" + + - name: delete volume + ec2_vol: + id: "{{ volume2.volume_id }}" + state: absent + register: delete_volume_result + + - name: check task return attributes + assert: + that: + - "delete_volume_result.changed" + + - name: delete volume (idempotent) + ec2_vol: + id: "{{ volume2.volume_id }}" + state: absent + register: delete_volume_result_idem + + - name: check task return attributes + assert: + that: + - not delete_volume_result_idem.changed + - '"Volume {{ volume2.volume_id }} does not exist" in delete_volume_result_idem.msg' + + # Originally from ec2_vol_info + + - name: Create test volume with Destroy on Terminate + ec2_vol: + instance: "{{ test_instance.instance_ids[0] }}" + volume_size: 4 + name: "{{ resource_prefix }}_delete_on_terminate" + device_name: /dev/sdj + iops: 100 + tags: + Tag Name with Space-and-dash: Tag Value with Space-and-dash + delete_on_termination: yes + register: dot_volume + + - name: Gather volume info without any filters + ec2_vol_info: + register: volume_info_wo_filters + check_mode: no + + - name: Check if info are returned without filters + assert: + that: + - "volume_info_wo_filters.volumes is defined" + + - name: Gather volume info + ec2_vol_info: + filters: + "tag:Name": "{{ resource_prefix }}_delete_on_terminate" + register: volume_info + check_mode: no + + - name: Format check + assert: + that: + - "volume_info.volumes|length == 1" + - "v.attachment_set.attach_time is defined" + - "v.attachment_set.device is defined and v.attachment_set.device == dot_volume.device" + - "v.attachment_set.instance_id is defined and v.attachment_set.instance_id == test_instance.instance_ids[0]" + - "v.attachment_set.status is defined and v.attachment_set.status == 'attached'" + - "v.create_time is defined" + - "v.encrypted is defined and v.encrypted == false" + - "v.id is defined and v.id == dot_volume.volume_id" + - "v.iops is defined and v.iops == 100" + - "v.region is defined and v.region == aws_region" + - "v.size is defined and v.size == 4" + - "v.snapshot_id is defined and v.snapshot_id == ''" + - "v.status is defined and v.status == 'in-use'" + - "v.tags.Name is defined and v.tags.Name == resource_prefix + '_delete_on_terminate'" + - "v.tags['Tag Name with Space-and-dash'] == 'Tag Value with Space-and-dash'" + - "v.type is defined and v.type == 'io1'" + - "v.zone is defined and v.zone == test_instance.instances[0].placement.availability_zone" + vars: + v: "{{ volume_info.volumes[0] }}" + + - name: New format check + assert: + that: + - "v.attachment_set.delete_on_termination is defined" + vars: + v: "{{ volume_info.volumes[0] }}" + when: ansible_version.full is version('2.7', '>=') + + - name: test create a new gp3 volume + ec2_vol: + volume_size: 1 + volume_type: gp3 + zone: "{{ availability_zone }}" + throughput: 130 + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: gp3_volume + + - name: check that volume_type is gp3 + assert: + that: + - gp3_volume.volume_type == 'gp3' + - gp3_volume.changed + - gp3_volume.volume.throughput == 130 + + - name: increase throughput + ec2_vol: + volume_size: 1 + volume_type: gp3 + zone: "{{ availability_zone }}" + throughput: 131 + tags: + ResourcePrefix: "{{ resource_prefix }}" + register: gp3_volume + + - name: check that throughput has changed + assert: + that: + - gp3_volume.volume_type == 'gp3' + - gp3_volume.changed + - gp3_volume.volume.throughput == 131 + + + # ==== Cleanup ============================================================ + + always: + - name: Describe the instance before we delete it + ec2_instance_info: + instance_ids: + - "{{ test_instance.instance_ids[0] }}" + ignore_errors: yes + register: pre_delete + + - debug: + var: pre_delete + + - name: delete test instance + ec2_instance: + instance_ids: + - "{{ test_instance.instance_ids[0] }}" + state: terminated + ignore_errors: yes + + - name: delete volumes + ec2_vol: + id: "{{ item.volume_id }}" + state: absent + ignore_errors: yes + with_items: + - "{{ volume1 }}" + - "{{ volume2 }}" + - "{{ volume3 }}" + - "{{ new_vol_attach_result }}" + - "{{ attach_new_vol_from_snapshot_result }}" + - "{{ dot_volume }}" + - "{{ gp3_volume }}" + + - name: delete snapshot + ec2_snapshot: + snapshot_id: "{{ vol1_snapshot.snapshot_id }}" + state: absent + ignore_errors: yes + + - name: delete test subnet + ec2_vpc_subnet: + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: "{{ subnet_cidr }}" + state: absent + ignore_errors: yes + + - name: delete test VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: "{{ vpc_cidr }}" + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/aliases new file mode 100644 index 00000000..a112c3d1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group1 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/defaults/main.yml new file mode 100644 index 00000000..26403c17 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for ec2_dhcp_option_info tests +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/24' +# default option sets get an AWS domain_name, which is different in us-east-1 +aws_domain_name: "{{ (aws_region == 'us-east-1') | ternary('ec2.internal', aws_region + '.compute.internal') }}"
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/tasks/main.yml new file mode 100644 index 00000000..4e4e6787 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_dhcp_option/tasks/main.yml @@ -0,0 +1,931 @@ +--- +# ============================================================ +# Known issues: +# +# `check_mode` throws a traceback when providing options +# there is no way to associate the `default` option set in the module +# ec2_vpc_dhcp_option_info needs to use camel_dict_to_snake_dict(..., ignore_list=['Tags']) +# Purging tags does nothing, but reports changed +# The module doesn't store/return tags in the new_options dictionary +# Adding tags is silently ignored and no change is made +# always reassociated (changed=True) when vpc_id is provided without options +# +# ============================================================ +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default('') }}" + region: "{{ aws_region }}" + + collections: + - community.general + + block: + + # DHCP option set can be attached to multiple VPCs, we don't want to use any that + # don't belong to this test run + - name: find all DHCP option sets that already exist before running tests + ec2_vpc_dhcp_option_info: + register: result + + - set_fact: + preexisting_option_sets: "{{ result | community.general.json_query('dhcp_options[*].dhcp_options_id') | list }}" + + - name: create a VPC with a default DHCP option set to test inheritance and delete_old + ec2_vpc_net: + name: "{{ resource_prefix }}" + cidr_block: "{{ vpc_cidr }}" + state: present + register: vpc + + - name: ensure a DHCP option set is attached to the VPC + assert: + that: + - vpc.vpc.dhcp_options_id is defined + + - set_fact: + vpc_id: "{{ vpc.vpc.id }}" + default_options_id: "{{ vpc.vpc.dhcp_options_id }}" + +## ============================================ + - name: Option Sets can be attached to multiple VPCs, create a new one if the test VPC is reusing a pre-existing one + when: vpc.vpc.dhcp_options_id in preexisting_option_sets + block: + - name: Create the new option set + ec2_vpc_dhcp_option: + state: present + domain_name: "{{ aws_domain_name }}" + dns_servers: + - AmazonProvidedDNS + delete_old: True + tags: + Name: "{{ resource_prefix }}" + register: new_dhcp_options + + - assert: + that: + - new_dhcp_options.dhcp_options_id not in preexisting_option_sets + + - name: Attach the new option set to the VPC + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + dhcp_options_id: "{{ new_dhcp_options.dhcp_options_id }}" + + - name: find the VPC's associated option set + ec2_vpc_net_info: + vpc_ids: "{{ vpc_id }}" + register: vpc_info + + - set_fact: + original_dhcp_options_id: "{{ vpc_info.vpcs[0].dhcp_options_id }}" + + - name: get information about the DHCP option + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ original_dhcp_options_id }}"] + register: original_dhcp_options_info + + - set_fact: + original_config: "{{ original_dhcp_options_info.dhcp_options[0].dhcp_configurations | items2dict(key_name='key', value_name='values') }}" + + - assert: + that: + - original_dhcp_options_info.dhcp_options | length == 1 + - original_config.keys() | list | sort == ['domain-name', 'domain-name-servers'] + - original_config['domain-name'][0]['value'] == "{{ aws_domain_name }}" + - original_config['domain-name-servers'][0]['value'] == 'AmazonProvidedDNS' + - original_dhcp_options_id not in preexisting_option_sets + +## ============================================ + + # FIXME: always reassociated to lowest alphanum dhcp_options_id when vpc_id is provided without options, + # This task will return an unpredictable dhcp_option_id so we can't assert anything about the option's values + - name: test a DHCP option exists (check mode) + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + register: found_dhcp_options + check_mode: true + + - assert: + that: + # FIXME: options have to be provided to match the option associated with the VPC + - not found_dhcp_options.changed + - not found_dhcp_options.new_options + + # FIXME: always reassociated when vpc_id is provided without options, so here we provide the default options + - name: test a DHCP option exists + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + domain_name: "{{ aws_domain_name }}" + dns_servers: + - AmazonProvidedDNS + tags: + Name: "{{ resource_prefix }}" + register: found_dhcp_options + + - assert: + that: + - found_dhcp_options is not changed + - found_dhcp_options.dhcp_options_id is defined + - not found_dhcp_options.changed or dhcp_options is defined and dhcp_options.dhcp_options_id == found_dhcp_options.dhcp_options_id + + # Create a DHCP option set that inherits from the default set and does not delete the old set + + # FIXME: check mode causes a traceback + #- name: create a DHCP option set that inherits from the default set (check mode) + # ec2_vpc_dhcp_option: + # state: present + # vpc_id: "{{ vpc_id }}" + # inherit_existing: True + # ntp_servers: + # - 10.0.0.2 + # - 10.0.1.2 + # netbios_name_servers: + # - 10.0.0.1 + # - 10.0.1.1 + # netbios_node_type: 2 + # delete_old: False + # register: dhcp_options + # check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + - name: create a DHCP option set that inherits from the default set + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + netbios_node_type: 2 + delete_old: False + register: dhcp_options + + - assert: + that: + - dhcp_options.changed + - dhcp_options.new_options + - dhcp_options.new_options.keys() | list | sort == ['domain-name', 'domain-name-servers', 'netbios-name-servers', 'netbios-node-type', 'ntp-servers'] + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + # FIXME: module/aws randomly returns as a string or list + - dhcp_options.new_options['netbios-node-type'] in ['2', ['2']] + # found I think is false, because the options are different and no id is provided? + - dhcp_options.new_options['domain-name'] in ["{{ aws_domain_name }}", ["{{ aws_domain_name }}"]] + - dhcp_options.new_options['domain-name-servers'] in ['AmazonProvidedDNS', ['AmazonProvidedDNS']] + - original_dhcp_options_id != dhcp_options.dhcp_options_id + + - set_fact: + new_dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + + - name: get information about the new DHCP option + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ new_dhcp_options_id }}"] + register: new_dhcp_options + + - set_fact: + new_config: "{{ new_dhcp_options.dhcp_options[0].dhcp_configurations | items2dict(key_name='key', value_name='values') }}" + + - assert: + that: + - new_config.keys() | list | sort == ['domain-name', 'domain-name-servers', 'netbios-name-servers', 'netbios-node-type', 'ntp-servers'] + - new_config['domain-name'][0]['value'] == "{{ aws_domain_name }}" + - new_config['domain-name-servers'][0]['value'] == 'AmazonProvidedDNS' + - new_config['ntp-servers'] | community.general.json_query('[*].value') | list | sort == ['10.0.0.2', '10.0.1.2'] + - new_config['netbios-name-servers'] | community.general.json_query('[*].value') | list | sort == ['10.0.0.1', '10.0.1.1'] + - new_config['netbios-node-type'][0]['value'] in ['2', ['2']] + + # FIXME: no way to associate `default` in the module + - name: Re-associate the default DHCP options set so that the new one can be deleted + ec2_vpc_dhcp_option: + vpc_id: '{{ vpc_id }}' + dhcp_options_id: '{{ default_options_id }}' + state: present + register: result + + - assert: + that: + - result.changed + - result is success + - result.dhcp_options_id == '{{ default_options_id }}' + + - name: delete it for the next test + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ new_dhcp_options_id }}" + state: absent + + # Create a DHCP option set that does not inherit from the old set and doesn't delete the old set + + # FIXME: check mode causes a traceback + #- name: create a DHCP option set that does not inherit from the default set (check mode) + # ec2_vpc_dhcp_option: + # state: present + # vpc_id: "{{ vpc_id }}" + # inherit_existing: False + # ntp_servers: + # - 10.0.0.2 + # - 10.0.1.2 + # netbios_name_servers: + # - 10.0.0.1 + # - 10.0.1.1 + # netbios_node_type: 2 + # delete_old: False + # register: dhcp_options + # check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + - name: create a DHCP option set that does not inherit from the default set + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + netbios_node_type: 2 + delete_old: False + register: dhcp_options + + - assert: + that: + - dhcp_options.changed + - dhcp_options.new_options + # FIXME extra keys are returned unpredictably + - dhcp_options.new_options.keys() | list | sort is superset(['netbios-name-servers', 'netbios-node-type', 'ntp-servers']) + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + # found should be false, so listified - found does [0] assignment +# - dhcp_options.new_options['netbios-node-type'] == ['2'] + - dhcp_options.new_options['netbios-node-type'] in ['2', ['2']] + - original_dhcp_options_id != dhcp_options.dhcp_options_id + + - set_fact: + new_dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + + - name: get information about the new DHCP option + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ new_dhcp_options_id }}"] + register: new_dhcp_options + + - set_fact: + new_config: "{{ new_dhcp_options.dhcp_options[0].dhcp_configurations | items2dict(key_name='key', value_name='values') }}" + + - assert: + that: + - new_config.keys() | list | sort == ['netbios-name-servers', 'netbios-node-type', 'ntp-servers'] + - new_config['ntp-servers'] | community.general.json_query('[*].value') | list | sort == ['10.0.0.2', '10.0.1.2'] + - new_config['netbios-name-servers'] | community.general.json_query('[*].value') | list | sort == ['10.0.0.1', '10.0.1.1'] + - new_config['netbios-node-type'][0]['value'] == '2' + + - name: disassociate the new DHCP option set so it can be deleted + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ original_dhcp_options_id }}" + vpc_id: "{{ vpc_id }}" + state: present + + - name: delete it for the next test + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ new_dhcp_options_id }}" + state: absent + + # Create a DHCP option set that inherits from the default set overwrites a default and deletes the old set + + # FIXME: check mode traceback + #- name: create a DHCP option set that inherits from the default set and deletes the original set (check mode) + # ec2_vpc_dhcp_option: + # state: present + # vpc_id: "{{ vpc_id }}" + # inherit_existing: True + # domain_name: us-west-2.compute.internal + # ntp_servers: + # - 10.0.0.2 + # - 10.0.1.2 + # netbios_name_servers: + # - 10.0.0.1 + # - 10.0.1.1 + # netbios_node_type: 2 + # delete_old: True + # register: dhcp_options + # check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + # FIXME: doesn't delete the original set + - name: create a DHCP option set that inherits from the default set and deletes the original set + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: True + domain_name: '{{ aws_domain_name }}' + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + netbios_node_type: 1 + delete_old: True + register: dhcp_options + + - assert: + that: + - dhcp_options.changed + - dhcp_options.new_options + - dhcp_options.new_options.keys() | list | sort is superset(['domain-name', 'netbios-name-servers', 'netbios-node-type', 'ntp-servers']) + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - dhcp_options.new_options['netbios-node-type'] in ['1', ['1']] + - dhcp_options.new_options['domain-name'] in ["{{ aws_domain_name }}", ["{{ aws_domain_name }}"]] + - original_dhcp_options_id != dhcp_options.dhcp_options_id + + - set_fact: + new_dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + + - name: get information about the new DHCP option + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ new_dhcp_options_id }}"] + register: new_dhcp_options + + - set_fact: + new_config: "{{ new_dhcp_options.dhcp_options[0].dhcp_configurations | items2dict(key_name='key', value_name='values') }}" + + - assert: + that: + - new_config.keys() | list | sort is superset(['domain-name', 'netbios-name-servers', 'netbios-node-type', 'ntp-servers']) + - new_config['domain-name'][0]['value'] in ["{{ aws_domain_name }}", ["{{ aws_domain_name }}"]] + - new_config['ntp-servers'] | community.general.json_query('[*].value') | list | sort == ['10.0.0.2', '10.0.1.2'] + - new_config['netbios-name-servers'] | community.general.json_query('[*].value') | list | sort == ['10.0.0.1', '10.0.1.1'] + - new_config['netbios-node-type'][0]['value'] == '1' + + - name: verify the original set was deleted + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ original_dhcp_options_id }}"] + register: dhcp_options + ignore_errors: yes + + - assert: + that: + - dhcp_options.failed + - '"does not exist" in dhcp_options.error.message' + ignore_errors: yes # FIXME - remove line and the following retry tasks + + - name: try to delete the original again + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ original_dhcp_options_id }}" + state: absent + + - name: verify the original set was deleted + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ original_dhcp_options_id }}"] + register: dhcp_options + ignore_errors: yes + + - assert: + that: + - dhcp_options.failed + - '"does not exist" in dhcp_options.error.message' + + - set_fact: + original_dhcp_options_id: "{{ new_dhcp_options_id }}" + + # Create a DHCP option set that does not inherit from the old set and deletes the old set + + # FIXME: check mode causes a traceback + #- name: create a DHCP option set that does not inherit from the default set and deletes the original set (check mode) + # ec2_vpc_dhcp_option: + # state: present + # vpc_id: "{{ vpc_id }}" + # inherit_existing: False + # domain_name: "{{ (aws_region == 'us-east-1') | ternary('ec2.internal', aws_region + '.compute.internal') }}" + # dns_servers: + # - AmazonProvidedDNS + # delete_old: True + # register: dhcp_options + # check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + - name: create a DHCP option set that does not inherit from the default set and deletes the original set + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + domain_name: "{{ aws_domain_name }}" + dns_servers: + - AmazonProvidedDNS + delete_old: True + register: dhcp_options + + - assert: + that: + - dhcp_options.new_options + - dhcp_options.new_options.keys() | list | sort is superset(['domain-name', 'domain-name-servers']) + - dhcp_options.new_options['domain-name'] in ["{{ aws_domain_name }}", ["{{ aws_domain_name }}"]] + - dhcp_options.new_options['domain-name-servers'] in ['AmazonProvidedDNS', ['AmazonProvidedDNS']] + - original_dhcp_options_id != dhcp_options.dhcp_options_id + + - set_fact: + new_dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + + - name: get information about the new DHCP option + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ new_dhcp_options_id }}"] + register: new_dhcp_options + + - set_fact: + new_config: "{{ new_dhcp_options.dhcp_options[0].dhcp_configurations | items2dict(key_name='key', value_name='values') }}" + + - assert: + that: + - new_config.keys() | list | sort == ['domain-name', 'domain-name-servers'] + - new_config['domain-name'][0]['value'] == "{{ aws_domain_name }}" + - new_config['domain-name-servers'][0]['value'] == 'AmazonProvidedDNS' + + - name: verify the original set was deleted + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ original_dhcp_options_id }}"] + register: dhcp_options + ignore_errors: yes + + - assert: + that: + - dhcp_options.failed + - '"does not exist" in dhcp_options.error.message' + + - set_fact: + original_dhcp_options_id: "{{ new_dhcp_options_id }}" + + # Create a DHCP option set with tags + + # FIXME: check mode causes a traceback + #- name: create a DHCP option set with tags (check mode) + # ec2_vpc_dhcp_option: + # state: present + # vpc_id: "{{ vpc_id }}" + # inherit_existing: False + # delete_old: True + # ntp_servers: + # - 10.0.0.2 + # - 10.0.1.2 + # netbios_name_servers: + # - 10.0.0.1 + # - 10.0.1.1 + # tags: + # CreatedBy: ansible-test + # Collection: amazon.aws + # register: dhcp_options + # check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + - name: create a DHCP option set with tags + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: + CreatedBy: ansible-test + Collection: amazon.aws + register: dhcp_options + + - assert: + that: + - dhcp_options.changed + - dhcp_options.new_options.keys() | list | sort is superset(['ntp-servers', 'netbios-name-servers']) + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - original_dhcp_options_id != dhcp_options.dhcp_options_id + # FIXME: tags are not returned by the module +# - dhcp_options.tags.keys() | length == 2 +# - dhcp_options.tags['CreatedBy'] is 'ansible-test' +# - dhcp_options.tags['Collection'] is 'amazon.aws' + + - set_fact: + new_dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + + # FIXME: ec2_vpc_dhcp_option_info needs to use camel_dict_to_snake_dict(..., ignore_list=['Tags']) + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ new_dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + - dhcp_options_info.dhcp_options[0].tags is defined + - dhcp_options_info.dhcp_options[0].tags | length == 2 + - dhcp_options_info.dhcp_options[0].tags['collection'] == 'amazon.aws' + - dhcp_options_info.dhcp_options[0].tags['created_by'] == 'ansible-test' + + - name: test no changes with the same tags (check mode) + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: + CreatedBy: ansible-test + Collection: amazon.aws + register: dhcp_options + check_mode: true + + - assert: + that: + - not dhcp_options.changed + - dhcp_options.new_options.keys() | list | sort == ['netbios-name-servers', 'ntp-servers'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + + - name: test no changes with the same tags + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: + CreatedBy: ansible-test + Collection: amazon.aws + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + - not dhcp_options.changed + - dhcp_options.new_options.keys() | list | sort == ['netbios-name-servers', 'ntp-servers'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - new_dhcp_options_id == dhcp_options.dhcp_options_id + - dhcp_options_info.dhcp_options[0].tags is defined + - dhcp_options_info.dhcp_options[0].tags.keys() | length == 2 + - dhcp_options_info.dhcp_options[0].tags['collection'] == 'amazon.aws' + - dhcp_options_info.dhcp_options[0].tags['created_by'] == 'ansible-test' + + - name: test no changes without specifying tags (check mode) + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + register: dhcp_options + check_mode: true + + - assert: + that: + - not dhcp_options.changed + - dhcp_options.new_options.keys() | list | sort == ['netbios-name-servers', 'ntp-servers'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + + - name: test no changes without specifying tags + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + - not dhcp_options.changed + - dhcp_options.new_options.keys() | list | sort == ['netbios-name-servers', 'ntp-servers'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - new_dhcp_options_id == dhcp_options.dhcp_options_id + - dhcp_options_info.dhcp_options[0].tags is defined + - dhcp_options_info.dhcp_options[0].tags.keys() | length == 2 + - dhcp_options_info.dhcp_options[0].tags['collection'] == 'amazon.aws' + - dhcp_options_info.dhcp_options[0].tags['created_by'] == 'ansible-test' + + # FIXME: the additional tag is silently ignored and no change is made + - name: add a tag without using dhcp_options_id + ec2_vpc_dhcp_option: + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: + CreatedBy: ansible-test + Collection: amazon.aws + another: tag + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + #- dhcp_options.changed + - dhcp_options.new_options.keys() | list | sort == ['netbios-name-servers', 'ntp-servers'] + - dhcp_options.new_options['netbios-name-servers'] | sort == ['10.0.0.1', '10.0.1.1'] + - dhcp_options.new_options['ntp-servers'] | sort == ['10.0.0.2', '10.0.1.2'] + - new_dhcp_options_id == dhcp_options.dhcp_options_id + - dhcp_options_info.dhcp_options[0].tags is defined + - dhcp_options_info.dhcp_options[0].tags.keys() | length == 2 + #- dhcp_options_info.dhcp_options[0].tags.keys() | length == 3 + - dhcp_options_info.dhcp_options[0].tags['collection'] == 'amazon.aws' + - dhcp_options_info.dhcp_options[0].tags['created_by'] == 'ansible-test' + + # FIXME: another check_mode traceback + #- name: add and removing tags (check mode) + # ec2_vpc_dhcp_option: + # dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + # state: present + # vpc_id: "{{ vpc_id }}" + # inherit_existing: False + # delete_old: True + # ntp_servers: + # - 10.0.0.2 + # - 10.0.1.2 + # netbios_name_servers: + # - 10.0.0.1 + # - 10.0.1.1 + # tags: + # AnsibleTest: integration + # Collection: amazon.aws + # register: dhcp_options + # check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + - name: add and remove tags + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: + AnsibleTest: integration + Collection: amazon.aws + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + - dhcp_options.changed + - not dhcp_options.new_options + - new_dhcp_options_id == dhcp_options.dhcp_options_id + - dhcp_options_info.dhcp_options[0].tags is defined + - dhcp_options_info.dhcp_options[0].tags.keys() | length == 2 + - dhcp_options_info.dhcp_options[0].tags['collection'] == 'amazon.aws' + - dhcp_options_info.dhcp_options[0].tags['ansible_test'] == 'integration' + + - name: add tags with different cases + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: + "lowercase spaced": 'hello cruel world' + "Title Case": 'Hello Cruel World' + CamelCase: 'SimpleCamelCase' + snake_case: 'simple_snake_case' + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + - dhcp_options.changed + - not dhcp_options.new_options + - new_dhcp_options_id == dhcp_options.dhcp_options_id + - dhcp_options_info.dhcp_options[0].tags is defined + - dhcp_options_info.dhcp_options[0].tags.keys() | length == 4 + - dhcp_options_info.dhcp_options[0].tags['lowercase spaced'] == 'hello cruel world' +# FIXME: these tags are returned incorrectly now +# - dhcp_options_info.dhcp_options[0].tags['Title Case'] == 'Hello Cruel World' +# - dhcp_options_info.dhcp_options[0].tags['CamelCase'] == 'SimpleCamelCase' + - dhcp_options_info.dhcp_options[0].tags['snake_case'] == 'simple_snake_case' + + # FIXME does nothing, but reports changed + - name: test purging all tags + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: {} + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + - dhcp_options.changed + - new_dhcp_options_id == dhcp_options.dhcp_options_id + #- not dhcp_options_info.dhcp_options[0].tags + + - name: test no changes removing all tags + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ dhcp_options.dhcp_options_id }}" + state: present + vpc_id: "{{ vpc_id }}" + inherit_existing: False + delete_old: True + ntp_servers: + - 10.0.0.2 + - 10.0.1.2 + netbios_name_servers: + - 10.0.0.1 + - 10.0.1.1 + tags: {} + register: dhcp_options + + - name: check if the expected tags are associated + ec2_vpc_dhcp_option_info: + dhcp_options_ids: ["{{ dhcp_options.dhcp_options_id }}"] + register: dhcp_options_info + + - assert: + that: + #- not dhcp_options.changed + - new_dhcp_options_id == dhcp_options.dhcp_options_id + #- not dhcp_options_info.dhcp_options[0].tags + + # FIXME: check mode returns changed as False + - name: remove the DHCP option set (check mode) + ec2_vpc_dhcp_option: + state: absent + vpc_id: "{{ vpc_id }}" + dhcp_options_id: "{{ new_dhcp_options_id }}" + register: dhcp_options + check_mode: true + + #- assert: + # that: + # - dhcp_options.changed + + # FIXME: does nothing - the module should associate "default" with the VPC provided + - name: removing the DHCP option set + ec2_vpc_dhcp_option: + state: absent + vpc_id: "{{ vpc_id }}" + dhcp_options_id: "{{ new_dhcp_options_id }}" + register: dhcp_options + + #- assert: + # that: + # - dhcp_options.changed + + - name: remove the DHCP option set again (check mode) + ec2_vpc_dhcp_option: + state: absent + vpc_id: "{{ vpc_id }}" + dhcp_options_id: "{{ new_dhcp_options_id }}" + register: dhcp_options + check_mode: true + + - assert: + that: + - not dhcp_options.changed + + - name: remove the DHCP option set again + ec2_vpc_dhcp_option: + state: absent + vpc_id: "{{ vpc_id }}" + dhcp_options_id: "{{ new_dhcp_options_id }}" + register: dhcp_options + + - assert: + that: + - not dhcp_options.changed + + always: + + - name: Re-associate the default DHCP options set so that the new one(s) can be deleted + ec2_vpc_dhcp_option: + vpc_id: '{{ vpc_id }}' + dhcp_options_id: '{{ default_options_id }}' + state: present + register: result + when: vpc_id is defined + ignore_errors: yes + + - name: Query all option sets created by the test + ec2_vpc_dhcp_option_info: + filters: + "tag:Name": "*'{{ resource_prefix }}*" + register: option_sets + + - name: clean up DHCP option sets + ec2_vpc_dhcp_option: + state: absent + dhcp_options_id: "{{ original_dhcp_options_id }}" + vpc_id: "{{ vpc_id }}" + when: original_dhcp_options_id is defined + ignore_errors: yes + + - name: clean up DHCP option sets + ec2_vpc_dhcp_option: + state: absent + dhcp_options_id: "{{ new_dhcp_options_id }}" + vpc_id: "{{ vpc_id }}" + when: new_dhcp_options_id is defined + ignore_errors: yes + + - name: Delete the VPC + ec2_vpc_net: + name: "{{ resource_prefix }}" + cidr_block: "{{ vpc_cidr }}" + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/aliases new file mode 100644 index 00000000..fb765ef7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/aliases @@ -0,0 +1,3 @@ +ec2_vpc_net_info +cloud/aws +shippable/aws/group1 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/defaults/main.yml new file mode 100644 index 00000000..3289b278 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for ec2_vpc_net +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/24' +vpc_cidr_a: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +vpc_cidr_b: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/tasks/main.yml new file mode 100644 index 00000000..728667ac --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_net/tasks/main.yml @@ -0,0 +1,1318 @@ +--- +- name: Setup AWS Environment + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + block: + + # ============================================================ + + - name: Get the current caller identity facts + aws_caller_info: + register: caller_facts + + - name: run the module without parameters + ec2_vpc_net: + ignore_errors: yes + register: result + + - name: assert failure + assert: + that: + - result is failed + - result.msg.startswith("missing required arguments") + + # ============================================================ + + - name: attempt to create a VPC without providing connnection information + module_defaults: { group/aws: {} } + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + region: us-east-1 + ignore_errors: yes + register: result + + - name: assert connection failure + assert: + that: + - result is failed + - '"Unable to locate credentials" in result.msg' + + # ============================================================ + + - name: Fetch existing VPC info + ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + - name: Check no-one is using the Prefix before we start + assert: + that: + - vpc_info.vpcs | length == 0 + + - name: test check mode creating a VPC + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: check for a change + assert: + that: + - result is changed + - vpc_info.vpcs | length == 0 + + # ============================================================ + + - name: create a VPC + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + ipv6_cidr: True + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the VPC was created successfully + assert: + that: + - result is successful + - result is changed + - vpc_info.vpcs | length == 1 + + - name: assert the output + assert: + that: + - '"cidr_block" in result.vpc' + - result.vpc.cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 1 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - '"classic_link_enabled" in result.vpc' + - result.vpc.dhcp_options_id.startswith("dopt-") + - result.vpc.id.startswith("vpc-") + - '"instance_tenancy" in result.vpc' + - result.vpc.ipv6_cidr_block_association_set | length == 1 + - result.vpc.ipv6_cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block | ansible.netcommon.ipv6 + - result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block_state.state in ["associated", "associating"] + - '"is_default" in result.vpc' + - '"state" in result.vpc' + - result.vpc.tags.keys() | length == 1 + - result.vpc.tags.Name == resource_prefix + + - name: set the first VPC's details as facts for comparison and cleanup + set_fact: + vpc_1_result: "{{ result }}" + vpc_1: "{{ result.vpc.id }}" + vpc_1_ipv6_cidr: "{{ result.vpc.ipv6_cidr_block_association_set.0.ipv6_cidr_block }}" + default_dhcp_options_id: "{{ result.vpc.dhcp_options_id }}" + + - name: create a VPC (retry) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + ipv6_cidr: True + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert nothing changed + assert: + that: + - result is successful + - result is not changed + - vpc_info.vpcs | length == 1 + - '"cidr_block" in result.vpc' + - result.vpc.cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 1 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - '"classic_link_enabled" in result.vpc' + - result.vpc.dhcp_options_id.startswith("dopt-") + - result.vpc.id.startswith("vpc-") + - '"instance_tenancy" in result.vpc' + - result.vpc.ipv6_cidr_block_association_set | length == 1 + - result.vpc.ipv6_cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block | ansible.netcommon.ipv6 + - result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block_state.state in ["associated", "associating"] + - '"is_default" in result.vpc' + - '"state" in result.vpc' + - result.vpc.tags.keys() | length == 1 + - result.vpc.tags.Name == resource_prefix + - result.vpc.id == vpc_1 + + # ============================================================ + + - name: VPC info (no filters) + ec2_vpc_net_info: + register: vpc_info + retries: 3 + delay: 3 + until: '"InvalidVpcID.NotFound" not in ( vpc_info.msg | default("") )' + + - name: Test that our new VPC shows up in the results + assert: + that: + - vpc_1 in ( vpc_info | community.general.json_query("vpcs[].vpc_id") | list ) + + - name: VPC info (Simple tag filter) + ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: Test vpc_info results + assert: + that: + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block_association_set | length == 1 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id == result.vpc.cidr_block_association_set[0].association_id + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block == result.vpc.cidr_block_association_set[0].cidr_block + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - '"classic_link_dns_supported" in vpc_info.vpcs[0]' + - '"classic_link_enabled" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].dhcp_options_id == result.vpc.dhcp_options_id + - ( vpc_info.vpcs[0].enable_dns_hostnames | bool ) == True + - ( vpc_info.vpcs[0].enable_dns_support | bool ) == True + - vpc_info.vpcs[0].id == result.vpc.id + - '"instance_tenancy" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].ipv6_cidr_block_association_set | length == 1 + - vpc_info.vpcs[0].ipv6_cidr_block_association_set[0].association_id == result.vpc.ipv6_cidr_block_association_set[0].association_id + - vpc_info.vpcs[0].ipv6_cidr_block_association_set[0].ipv6_cidr_block == result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block + - vpc_info.vpcs[0].ipv6_cidr_block_association_set[0].ipv6_cidr_block_state.state in ["associated", "associating"] + - '"is_default" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].owner_id == caller_facts.account + - '"state" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].vpc_id == result.vpc.id + + # ============================================================ + + - name: Try to add IPv6 CIDR when one already exists + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + ipv6_cidr: True + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: Assert no changes made + assert: + that: + - '"Only one IPv6 CIDR is permitted per VPC, {{ result.vpc.id }} already has CIDR {{ vpc_1_ipv6_cidr }}" in result.warnings' + - result is not changed + - vpc_info.vpcs | length == 1 + + # ============================================================ + + - name: test check mode creating an identical VPC (multi_ok) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + ipv6_cidr: True + multi_ok: yes + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change would be made + assert: + that: + - result is changed + - name: assert a change was not actually made + assert: + that: + - vpc_info.vpcs | length == 1 + + # ============================================================ + + - name: create a VPC with a dedicated tenancy using the same CIDR and name + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + ipv6_cidr: True + tenancy: dedicated + multi_ok: yes + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a new VPC was created + assert: + that: + - result is successful + - result is changed + - result.vpc.instance_tenancy == "dedicated" + - result.vpc.id != vpc_1 + - vpc_info.vpcs | length == 2 + + - name: set the second VPC's details as facts for comparison and cleanup + set_fact: + vpc_2_result: "{{ result }}" + vpc_2: "{{ result.vpc.id }}" + + # ============================================================ + + - name: VPC info (Simple VPC-ID filter) + ec2_vpc_net_info: + filters: + "vpc-id": "{{ vpc_2 }}" + register: vpc_info + + - name: Test vpc_info results + assert: + that: + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block_association_set | length == 1 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id == result.vpc.cidr_block_association_set[0].association_id + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block == result.vpc.cidr_block_association_set[0].cidr_block + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - '"classic_link_dns_supported" in vpc_info.vpcs[0]' + - '"classic_link_enabled" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].dhcp_options_id == result.vpc.dhcp_options_id + - ( vpc_info.vpcs[0].enable_dns_hostnames | bool ) == True + - ( vpc_info.vpcs[0].enable_dns_support | bool ) == True + - vpc_info.vpcs[0].id == vpc_2 + - '"instance_tenancy" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].ipv6_cidr_block_association_set | length == 1 + - vpc_info.vpcs[0].ipv6_cidr_block_association_set[0].association_id == result.vpc.ipv6_cidr_block_association_set[0].association_id + - vpc_info.vpcs[0].ipv6_cidr_block_association_set[0].ipv6_cidr_block == result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block + - vpc_info.vpcs[0].ipv6_cidr_block_association_set[0].ipv6_cidr_block_state.state in ["associated", "associating"] + - '"is_default" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].owner_id == caller_facts.account + - '"state" in vpc_info.vpcs[0]' + - vpc_info.vpcs[0].vpc_id == vpc_2 + + # ============================================================ + + # This will only fail if there are already *2* vpcs otherwise ec2_vpc_net + # assumes you want to update your existing VPC... + - name: attempt to create another VPC with the same CIDR and name without multi_ok + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + ipv6_cidr: True + tenancy: dedicated + multi_ok: no + register: new_result + ignore_errors: yes + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert failure + assert: + that: + - new_result is failed + - '"If you would like to create the VPC anyway please pass True to the multi_ok param" in new_result.msg' + - vpc_info.vpcs | length == 2 + + # ============================================================ + + # FIXME: right now if there are multiple matching VPCs they cannot be removed, + # as there is no vpc_id option for idempotence. A workaround is to retag the VPC. + - name: remove Name tag on new VPC + ec2_tag: + state: absent + resource: "{{ vpc_2 }}" + tags: + Name: "{{ resource_prefix }}" + + - name: add a unique name tag + ec2_tag: + state: present + resource: "{{ vpc_2 }}" + tags: + Name: "{{ resource_prefix }}-changed" + + - name: delete one of the VPCs + ec2_vpc_net: + state: absent + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}-changed" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert success + assert: + that: + - result is changed + - not result.vpc + - vpc_info.vpcs | length == 1 + + # ============================================================ + + - name: attempt to delete a VPC that doesn't exist + ec2_vpc_net: + state: absent + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}-changed" + register: result + + - name: assert no changes were made + assert: + that: + - result is not changed + - not result.vpc + + # ============================================================ + + - name: create a DHCP option set to use in next test + ec2_vpc_dhcp_option: + dns_servers: + - 4.4.4.4 + - 8.8.8.8 + tags: + Name: "{{ resource_prefix }}" + register: new_dhcp + - name: assert the DHCP option set was successfully created + assert: + that: + - new_dhcp is changed + + - name: modify the DHCP options set for a VPC (check_mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dhcp_opts_id: "{{ new_dhcp.dhcp_options_id }}" + register: result + check_mode: True + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the DHCP option set changed but didn't update + assert: + that: + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].dhcp_options_id == default_dhcp_options_id + + - name: modify the DHCP options set for a VPC + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dhcp_opts_id: "{{ new_dhcp.dhcp_options_id }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the DHCP option set changed + assert: + that: + - result is changed + - result.vpc.id == vpc_1 + - default_dhcp_options_id != result.vpc.dhcp_options_id + - result.vpc.dhcp_options_id == new_dhcp.dhcp_options_id + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].dhcp_options_id == new_dhcp.dhcp_options_id + + - name: modify the DHCP options set for a VPC (retry) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dhcp_opts_id: "{{ new_dhcp.dhcp_options_id }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the DHCP option set changed + assert: + that: + - result is not changed + - result.vpc.id == vpc_1 + - result.vpc.dhcp_options_id == new_dhcp.dhcp_options_id + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].dhcp_options_id == new_dhcp.dhcp_options_id + + # ============================================================ + + # XXX #62677 + #- name: disable dns_hostnames (check mode) + # ec2_vpc_net: + # state: present + # cidr_block: "{{ vpc_cidr }}" + # name: "{{ resource_prefix }}" + # dns_hostnames: False + # register: result + # check_mode: True + #- ec2_vpc_net_info: + # filters: + # "tag:Name": "{{ resource_prefix }}" + # register: vpc_info + + #- name: assert changed was set but not made + # assert: + # that: + # - result is successful + # - result is changed + # - vpc_info.vpcs | length == 1 + # - vpc_info.vpcs[0].enable_dns_hostnames | bool == True + # - vpc_info.vpcs[0].enable_dns_support | bool == True + + - name: disable dns_hostnames + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dns_hostnames: False + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change was made + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + - vpc_info.vpcs[0].enable_dns_support | bool == True + + - name: disable dns_hostnames (retry) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dns_hostnames: False + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change was made + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + - vpc_info.vpcs[0].enable_dns_support | bool == True + + # XXX #62677 + #- name: disable dns_support (check mode) + # ec2_vpc_net: + # state: present + # cidr_block: "{{ vpc_cidr }}" + # name: "{{ resource_prefix }}" + # dns_hostnames: False + # dns_support: False + # check_mode: True + # register: result + #- ec2_vpc_net_info: + # filters: + # "tag:Name": "{{ resource_prefix }}" + # register: vpc_info + + #- name: assert changed was set but not made + # assert: + # that: + # - result is successful + # - result is changed + # - result.vpc.id == vpc_1 + # - vpc_info.vpcs | length == 1 + # - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + # - vpc_info.vpcs[0].enable_dns_support | bool == True + + - name: disable dns_support + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dns_hostnames: False + dns_support: False + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change was made + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + - vpc_info.vpcs[0].enable_dns_support | bool == False + + - name: disable dns_support (retry) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dns_hostnames: False + dns_support: False + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change was not made + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == False + - vpc_info.vpcs[0].enable_dns_support | bool == False + + # XXX #62677 + #- name: re-enable dns_support (check mode) + # ec2_vpc_net: + # state: present + # cidr_block: "{{ vpc_cidr }}" + # name: "{{ resource_prefix }}" + # register: result + # check_mode: True + #- ec2_vpc_net_info: + # filters: + # "tag:Name": "{{ resource_prefix }}" + # register: vpc_info + + #- name: assert a change was made + # assert: + # that: + # - result is successful + # - result is changed + # - result.vpc.id == vpc_1 + # - vpc_info.vpcs | length == 1 + # - vpc_info.vpcs[0].enable_dns_hostnames | bool == True + # - vpc_info.vpcs[0].enable_dns_support | bool == True + + - name: re-enable dns_support + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change was made + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == True + - vpc_info.vpcs[0].enable_dns_support | bool == True + + - name: re-enable dns_support (retry) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert a change was not made + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].enable_dns_hostnames | bool == True + - vpc_info.vpcs[0].enable_dns_support | bool == True + + # ============================================================ + + - name: modify tags (check mode) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + tags: + Ansible: Test + check_mode: true + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the VPC has Name but not Ansible tag + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - result.vpc.tags | length == 1 + - result.vpc.tags.Name == resource_prefix + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags | length == 1 + - vpc_info.vpcs[0].tags.Name == resource_prefix + + - name: modify tags + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + tags: + Ansible: Test + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + until: + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags | length == 2 + retries: 5 + delay: 5 + + - name: assert the VPC has Name and Ansible tags + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - result.vpc.tags | length == 2 + - result.vpc.tags.Ansible == "Test" + - result.vpc.tags.Name == resource_prefix + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags | length == 2 + - vpc_info.vpcs[0].tags.Ansible == "Test" + - vpc_info.vpcs[0].tags.Name == resource_prefix + + - name: modify tags (no change) + ec2_vpc_net: + state: present + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + dns_support: True + dns_hostnames: True + tags: + Ansible: Test + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the VPC has Name and Ansible tags + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - result.vpc.tags|length == 2 + - result.vpc.tags.Ansible == "Test" + - result.vpc.tags.Name == resource_prefix + - vpc_info.vpcs | length == 1 + - vpc_info.vpcs[0].tags|length == 2 + - vpc_info.vpcs[0].tags.Ansible == "Test" + - vpc_info.vpcs[0].tags.Name == resource_prefix + + # ============================================================ + + # #62678 + #- name: modify CIDR (check mode) + # ec2_vpc_net: + # state: present + # cidr_block: + # - "{{ vpc_cidr }}" + # - "{{ vpc_cidr_a }}" + # name: "{{ resource_prefix }}" + # check_mode: true + # register: result + #- ec2_vpc_net_info: + # filters: + # "tag:Name": "{{ resource_prefix }}" + # register: vpc_info + + #- name: Check the CIDRs weren't changed + # assert: + # that: + # - result is successful + # - result is changed + # - result.vpc.id == vpc_1 + # - vpc_info.vpcs | length == 1 + # - vpc_info.vpcs[0].cidr_block == vpc_cidr + # - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_a not in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_b not in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_info.vpcs[0].cidr_block_association_set | length == 1 + # - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + # - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + # - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_a not in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_b not in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_a }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs changed + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 2 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b not in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 2 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b not in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR (no change) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_a }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 2 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b not in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 2 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b not in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + # #62678 + #- name: modify CIDR - no purge (check mode) + # ec2_vpc_net: + # state: present + # cidr_block: + # - "{{ vpc_cidr }}" + # - "{{ vpc_cidr_b }}" + # name: "{{ resource_prefix }}" + # check_mode: true + # register: result + #- ec2_vpc_net_info: + # filters: + # "tag:Name": "{{ resource_prefix }}" + # register: vpc_info + + #- name: Check the CIDRs weren't changed + # assert: + # that: + # - result is successful + # - result is changed + # - vpc_info.vpcs | length == 1 + # - vpc_info.vpcs[0].cidr_block == vpc_cidr + # - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_b not in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_info.vpcs[0].cidr_block_association_set | length == 2 + # - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + # - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + # - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_b not in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - no purge + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs changed + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 3 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - no purge (no change) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + assert: + that: + - result is successful + - result is not changed + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 3 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - no purge (no change - list all - check mode) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_a }}" + - "{{ vpc_cidr_b }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + assert: + that: + - result is successful + - result is not changed + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 3 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - no purge (no change - list all) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_a }}" + - "{{ vpc_cidr_b }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + assert: + that: + - result is successful + - result is not changed + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 3 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - no purge (no change - different order - check mode) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + - "{{ vpc_cidr_a }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + assert: + that: + - result is successful + - result is not changed + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 3 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - no purge (no change - different order) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + - "{{ vpc_cidr_a }}" + name: "{{ resource_prefix }}" + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + assert: + that: + - result is successful + - result is not changed + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc.cidr_block_association_set | length == 3 + - result.vpc.cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - result.vpc.cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - result.vpc.cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (result.vpc | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + # #62678 + #- name: modify CIDR - purge (check mode) + # ec2_vpc_net: + # state: present + # cidr_block: + # - "{{ vpc_cidr }}" + # - "{{ vpc_cidr_b }}" + # name: "{{ resource_prefix }}" + # purge_cidrs: yes + # check_mode: true + # register: result + #- ec2_vpc_net_info: + # filters: + # "tag:Name": "{{ resource_prefix }}" + # register: vpc_info + + #- name: Check the CIDRs weren't changed + # assert: + # that: + # - result is successful + # - result is changed + # - vpc_info.vpcs | length == 1 + # - vpc_info.vpcs[0].cidr_block == vpc_cidr + # - vpc_info.vpcs[0].cidr_block_association_set | length == 3 + # - vpc_info.vpcs[0].cidr_block_association_set[0].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[1].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[2].association_id.startswith("vpc-cidr-assoc-") + # - vpc_info.vpcs[0].cidr_block_association_set[0].cidr_block_state.state in ["associated", "associating"] + # - vpc_info.vpcs[0].cidr_block_association_set[1].cidr_block_state.state in ["associated", "associating"] + # - vpc_info.vpcs[0].cidr_block_association_set[2].cidr_block_state.state in ["associated", "associating"] + # - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_a in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + # - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query("cidr_block_association_set[*].cidr_block") | list) + + - name: modify CIDR - purge + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + name: "{{ resource_prefix }}" + purge_cidrs: yes + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs changed + vars: + cidr_query: 'cidr_block_association_set[?cidr_block_state.state == `associated`].cidr_block' + assert: + that: + - result is successful + - result is changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc | community.general.json_query(cidr_query) | list | length == 2 + - vpc_cidr in (result.vpc | community.general.json_query(cidr_query) | list) + - vpc_cidr_a not in (result.vpc | community.general.json_query(cidr_query) | list) + - vpc_cidr_b in (result.vpc | community.general.json_query(cidr_query) | list) + - vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list | length == 2 + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list) + - vpc_cidr_a not in (vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list) + + - name: modify CIDR - purge (no change) + ec2_vpc_net: + state: present + cidr_block: + - "{{ vpc_cidr }}" + - "{{ vpc_cidr_b }}" + name: "{{ resource_prefix }}" + purge_cidrs: yes + register: result + - ec2_vpc_net_info: + filters: + "tag:Name": "{{ resource_prefix }}" + register: vpc_info + + - name: assert the CIDRs didn't change + vars: + cidr_query: 'cidr_block_association_set[?cidr_block_state.state == `associated`].cidr_block' + assert: + that: + - result is successful + - result is not changed + - result.vpc.id == vpc_1 + - vpc_info.vpcs | length == 1 + - result.vpc.cidr_block == vpc_cidr + - vpc_info.vpcs[0].cidr_block == vpc_cidr + - result.vpc | community.general.json_query(cidr_query) | list | length == 2 + - vpc_cidr in (result.vpc | community.general.json_query(cidr_query) | list) + - vpc_cidr_a not in (result.vpc | community.general.json_query(cidr_query) | list) + - vpc_cidr_b in (result.vpc | community.general.json_query(cidr_query) | list) + - vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list | length == 2 + - vpc_cidr in (vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list) + - vpc_cidr_a not in (vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list) + - vpc_cidr_b in (vpc_info.vpcs[0] | community.general.json_query(cidr_query) | list) + + # ============================================================ + + - name: test check mode to delete a VPC + ec2_vpc_net: + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + state: absent + check_mode: true + register: result + + - name: assert that a change would have been made + assert: + that: + - result is changed + + # ============================================================ + + always: + + - name: replace the DHCP options set so the new one can be deleted + ec2_vpc_net: + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + state: present + multi_ok: no + dhcp_opts_id: "{{ default_dhcp_options_id }}" + ignore_errors: true + + - name: remove the DHCP option set + ec2_vpc_dhcp_option: + dhcp_options_id: "{{ new_dhcp.dhcp_options_id }}" + state: absent + ignore_errors: true + + - name: Describe VPCs before deleting them (for debugging) + ec2_vpc_net_info: + ignore_errors: true + + - name: remove the VPC + ec2_vpc_net: + cidr_block: "{{ vpc_cidr }}" + name: "{{ resource_prefix }}" + state: absent + ignore_errors: true diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/aliases new file mode 100644 index 00000000..4654de3a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/aliases @@ -0,0 +1,3 @@ +cloud/aws +shippable/aws/group2 +ec2_vpc_subnet_info
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/defaults/main.yml new file mode 100644 index 00000000..20e440c9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# defaults file for ec2_vpc_subnet +ec2_vpc_subnet_name: '{{resource_prefix}}' +ec2_vpc_subnet_description: 'Created by ansible integration tests' +vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' +subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.1.0/24' +subnet_cidr_b: '10.{{ 256 | random(seed=resource_prefix) }}.2.0/24' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/tasks/main.yml new file mode 100644 index 00000000..6dc24d61 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/ec2_vpc_subnet/tasks/main.yml @@ -0,0 +1,692 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + - name: list available AZs + aws_az_info: + register: region_azs + + - name: pick an AZ for testing + set_fact: + subnet_az: "{{ region_azs.availability_zones[0].zone_name }}" + + # ============================================================ + - name: create a VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: present + cidr_block: "{{ vpc_cidr }}" + ipv6_cidr: True + tags: + Name: "{{ resource_prefix }}-vpc" + Description: "Created by ansible-test" + register: vpc_result + + - set_fact: + vpc_ipv6_cidr: "{{ vpc_result.vpc.ipv6_cidr_block_association_set[0].ipv6_cidr_block }}" + + - set_fact: + subnet_ipv6_cidr: "{{ vpc_ipv6_cidr | regex_replace('::/.*', '::/64') }}" + + # ============================================================ + - name: check subnet does not exist + ec2_vpc_subnet_info: + filters: + "tag:Name": '{{ec2_vpc_subnet_name}}' + register: vpc_subnet_info + + - name: Assert info result is zero + assert: + that: + - (vpc_subnet_info.subnets|length) == 0 + + - name: create subnet (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + check_mode: true + register: vpc_subnet_create + + - name: assert creation would happen + assert: + that: + - vpc_subnet_create is changed + + - name: create subnet (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + register: vpc_subnet_create + + - name: assert creation happened (expected changed=true) + assert: + that: + - 'vpc_subnet_create' + - 'vpc_subnet_create.subnet.id.startswith("subnet-")' + - '"Name" in vpc_subnet_create.subnet.tags and vpc_subnet_create.subnet.tags["Name"] == ec2_vpc_subnet_name' + - '"Description" in vpc_subnet_create.subnet.tags and vpc_subnet_create.subnet.tags["Description"] == ec2_vpc_subnet_description' + + - name: get info about the subnet + ec2_vpc_subnet_info: + subnet_ids: '{{ vpc_subnet_create.subnet.id }}' + register: vpc_subnet_info + + - name: Assert info result matches create result + assert: + that: + - 'vpc_subnet_info.subnets | length == 1' + - '"assign_ipv6_address_on_creation" in subnet_info' + - 'subnet_info.assign_ipv6_address_on_creation == False' + - '"availability_zone" in subnet_info' + - 'subnet_info.availability_zone == subnet_az' + - '"available_ip_address_count" in subnet_info' + - '"cidr_block" in subnet_info' + - 'subnet_info.cidr_block == subnet_cidr' + - '"default_for_az" in subnet_info' + - '"id" in subnet_info' + - 'subnet_info.id == vpc_subnet_create.subnet.id' + - '"map_public_ip_on_launch" in subnet_info' + - 'subnet_info.map_public_ip_on_launch == False' + - '"state" in subnet_info' + - '"subnet_id" in subnet_info' + - 'subnet_info.subnet_id == vpc_subnet_create.subnet.id' + - '"tags" in subnet_info' + - 'subnet_info.tags["Description"] == ec2_vpc_subnet_description' + - 'subnet_info.tags["Name"] == vpc_subnet_create.subnet.tags["Name"]' + - '"vpc_id" in subnet_info' + - 'subnet_info.vpc_id == vpc_result.vpc.id' + vars: + subnet_info: '{{ vpc_subnet_info.subnets[0] }}' + + # ============================================================ + - name: recreate subnet (expected changed=false) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + check_mode: true + register: vpc_subnet_recreate + + - name: assert recreation changed nothing (expected changed=false) + assert: + that: + - vpc_subnet_recreate is not changed + + - name: recreate subnet (expected changed=false) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + register: vpc_subnet_recreate + + - name: assert recreation changed nothing (expected changed=false) + assert: + that: + - vpc_subnet_recreate is not changed + - 'vpc_subnet_recreate.subnet == vpc_subnet_create.subnet' + + # ============================================================ + - name: update subnet so instances launched in it are assigned an IP (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + map_public: true + check_mode: true + register: vpc_subnet_modify + + - name: assert subnet changed + assert: + that: + - vpc_subnet_modify is changed + + - name: update subnet so instances launched in it are assigned an IP + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + map_public: true + register: vpc_subnet_modify + + - name: assert subnet changed + assert: + that: + - vpc_subnet_modify is changed + - vpc_subnet_modify.subnet.map_public_ip_on_launch + + # ============================================================ + - name: add invalid ipv6 block to subnet (expected failed) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: 2001:db8::/64 + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + register: vpc_subnet_ipv6_failed + ignore_errors: yes + + - name: assert failure happened (expected failed) + assert: + that: + - vpc_subnet_ipv6_failed is failed + - "'Couldn\\'t associate ipv6 cidr' in vpc_subnet_ipv6_failed.msg" + + # ============================================================ + - name: add a tag (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + AnotherTag: SomeValue + state: present + check_mode: true + register: vpc_subnet_add_a_tag + + - name: assert tag addition happened (expected changed=true) + assert: + that: + - vpc_subnet_add_a_tag is changed + + - name: add a tag (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + AnotherTag: SomeValue + state: present + register: vpc_subnet_add_a_tag + + - name: assert tag addition happened (expected changed=true) + assert: + that: + - vpc_subnet_add_a_tag is changed + - '"Name" in vpc_subnet_add_a_tag.subnet.tags and vpc_subnet_add_a_tag.subnet.tags["Name"] == ec2_vpc_subnet_name' + - '"Description" in vpc_subnet_add_a_tag.subnet.tags and vpc_subnet_add_a_tag.subnet.tags["Description"] == ec2_vpc_subnet_description' + - '"AnotherTag" in vpc_subnet_add_a_tag.subnet.tags and vpc_subnet_add_a_tag.subnet.tags["AnotherTag"] == "SomeValue"' + + - name: Get info by tag + ec2_vpc_subnet_info: + filters: + "tag:Name": '{{ec2_vpc_subnet_name}}' + register: vpc_subnet_info_by_tag + + - name: assert info matches expected output + assert: + that: + - 'vpc_subnet_info_by_tag.subnets[0].id == vpc_subnet_add_a_tag.subnet.id' + - (vpc_subnet_info_by_tag.subnets[0].tags|length) == 3 + - '"Description" in vpc_subnet_info_by_tag.subnets[0].tags and vpc_subnet_info_by_tag.subnets[0].tags["Description"] == ec2_vpc_subnet_description' + - '"AnotherTag" in vpc_subnet_info_by_tag.subnets[0].tags and vpc_subnet_info_by_tag.subnets[0].tags["AnotherTag"] == "SomeValue"' + + # ============================================================ + - name: remove tags with default purge_tags=true (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + AnotherTag: SomeValue + state: present + check_mode: true + register: vpc_subnet_remove_tags + + - name: assert tag removal happened (expected changed=true) + assert: + that: + - vpc_subnet_remove_tags is changed + + - name: remove tags with default purge_tags=true (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + AnotherTag: SomeValue + state: present + register: vpc_subnet_remove_tags + + - name: assert tag removal happened (expected changed=true) + assert: + that: + - vpc_subnet_remove_tags is changed + - '"Name" not in vpc_subnet_remove_tags.subnet.tags' + - '"Description" not in vpc_subnet_remove_tags.subnet.tags' + - '"AnotherTag" in vpc_subnet_remove_tags.subnet.tags and vpc_subnet_remove_tags.subnet.tags["AnotherTag"] == "SomeValue"' + + - name: Check tags by info + ec2_vpc_subnet_info: + subnet_id: '{{ vpc_subnet_remove_tags.subnet.id }}' + register: vpc_subnet_info_removed_tags + + - name: assert info matches expected output + assert: + that: + - '"Name" not in vpc_subnet_info_removed_tags.subnets[0].tags' + - '"Description" not in vpc_subnet_info_removed_tags.subnets[0].tags' + - '"AnotherTag" in vpc_subnet_info_removed_tags.subnets[0].tags and vpc_subnet_info_removed_tags.subnets[0].tags["AnotherTag"] == "SomeValue"' + + + # ============================================================ + - name: change tags with purge_tags=false (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + purge_tags: false + check_mode: true + register: vpc_subnet_change_tags + + - name: assert tag addition happened (expected changed=true) + assert: + that: + - vpc_subnet_change_tags is changed + + - name: change tags with purge_tags=false (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + az: "{{ subnet_az }}" + vpc_id: "{{ vpc_result.vpc.id }}" + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + state: present + purge_tags: false + register: vpc_subnet_change_tags + + - name: assert tag addition happened (expected changed=true) + assert: + that: + - vpc_subnet_change_tags is changed + - '"Name" in vpc_subnet_change_tags.subnet.tags and vpc_subnet_change_tags.subnet.tags["Name"] == ec2_vpc_subnet_name' + - '"Description" in vpc_subnet_change_tags.subnet.tags and vpc_subnet_change_tags.subnet.tags["Description"] == ec2_vpc_subnet_description' + - '"AnotherTag" in vpc_subnet_change_tags.subnet.tags and vpc_subnet_change_tags.subnet.tags["AnotherTag"] == "SomeValue"' + + # ============================================================ + - name: test state=absent (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + check_mode: true + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - result is changed + + - name: test state=absent (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - result is changed + + # ============================================================ + - name: test state=absent (expected changed=false) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + check_mode: true + register: result + + - name: assert state=absent (expected changed=false) + assert: + that: + - result is not changed + + - name: test state=absent (expected changed=false) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + register: result + + - name: assert state=absent (expected changed=false) + assert: + that: + - result is not changed + + # ============================================================ + - name: create subnet without AZ (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + check_mode: true + register: subnet_without_az + + - name: check that subnet without AZ works fine + assert: + that: + - subnet_without_az is changed + + - name: create subnet without AZ + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + register: subnet_without_az + + - name: check that subnet without AZ works fine + assert: + that: + - subnet_without_az is changed + + # ============================================================ + - name: remove subnet without AZ (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + check_mode: true + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - result is changed + + - name: remove subnet without AZ + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + register: result + + - name: assert state=absent (expected changed=true) + assert: + that: + - result is changed + + + # ============================================================ + - name: create subnet with IPv6 (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + assign_instances_ipv6: true + state: present + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + check_mode: true + register: vpc_subnet_ipv6_create + + - name: assert creation with IPv6 happened (expected changed=true) + assert: + that: + - vpc_subnet_ipv6_create is changed + + - name: create subnet with IPv6 (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + assign_instances_ipv6: true + state: present + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + register: vpc_subnet_ipv6_create + + - name: assert creation with IPv6 happened (expected changed=true) + assert: + that: + - vpc_subnet_ipv6_create is changed + - 'vpc_subnet_ipv6_create.subnet.id.startswith("subnet-")' + - "vpc_subnet_ipv6_create.subnet.ipv6_cidr_block == subnet_ipv6_cidr" + - '"Name" in vpc_subnet_ipv6_create.subnet.tags and vpc_subnet_ipv6_create.subnet.tags["Name"] == ec2_vpc_subnet_name' + - '"Description" in vpc_subnet_ipv6_create.subnet.tags and vpc_subnet_ipv6_create.subnet.tags["Description"] == ec2_vpc_subnet_description' + - 'vpc_subnet_ipv6_create.subnet.assign_ipv6_address_on_creation' + + # ============================================================ + - name: recreate subnet (expected changed=false) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + assign_instances_ipv6: true + state: present + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + check_mode: true + register: vpc_subnet_ipv6_recreate + + - name: assert recreation changed nothing (expected changed=false) + assert: + that: + - vpc_subnet_ipv6_recreate is not changed + + - name: recreate subnet (expected changed=false) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + assign_instances_ipv6: true + state: present + tags: + Name: '{{ec2_vpc_subnet_name}}' + Description: '{{ec2_vpc_subnet_description}}' + register: vpc_subnet_ipv6_recreate + + - name: assert recreation changed nothing (expected changed=false) + assert: + that: + - vpc_subnet_ipv6_recreate is not changed + - 'vpc_subnet_ipv6_recreate.subnet == vpc_subnet_ipv6_create.subnet' + + # ============================================================ + - name: change subnet assign_instances_ipv6 attribute (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + assign_instances_ipv6: false + state: present + purge_tags: false + check_mode: true + register: vpc_change_attribute + + - name: assert assign_instances_ipv6 attribute changed (expected changed=true) + assert: + that: + - vpc_change_attribute is changed + + - name: change subnet assign_instances_ipv6 attribute (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + assign_instances_ipv6: false + state: present + purge_tags: false + register: vpc_change_attribute + + - name: assert assign_instances_ipv6 attribute changed (expected changed=true) + assert: + that: + - vpc_change_attribute is changed + - 'not vpc_change_attribute.subnet.assign_ipv6_address_on_creation' + + # ============================================================ + - name: add second subnet with duplicate ipv6 cidr (expected failure) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr_b }}" + vpc_id: "{{ vpc_result.vpc.id }}" + ipv6_cidr: "{{ subnet_ipv6_cidr }}" + state: present + purge_tags: false + register: vpc_add_duplicate_ipv6 + ignore_errors: true + + - name: assert graceful failure (expected failed) + assert: + that: + - vpc_add_duplicate_ipv6 is failed + - "'The IPv6 CIDR \\'{{ subnet_ipv6_cidr }}\\' conflicts with another subnet' in vpc_add_duplicate_ipv6.msg" + + # ============================================================ + - name: remove subnet ipv6 cidr (expected changed=true) (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + purge_tags: false + check_mode: true + register: vpc_remove_ipv6_cidr + + - name: assert subnet ipv6 cidr removed (expected changed=true) + assert: + that: + - vpc_remove_ipv6_cidr is changed + + - name: remove subnet ipv6 cidr (expected changed=true) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + purge_tags: false + register: vpc_remove_ipv6_cidr + + - name: assert subnet ipv6 cidr removed (expected changed=true) + assert: + that: + - vpc_remove_ipv6_cidr is changed + - "vpc_remove_ipv6_cidr.subnet.ipv6_cidr_block == ''" + - 'not vpc_remove_ipv6_cidr.subnet.assign_ipv6_address_on_creation' + + # ============================================================ + - name: test adding a tag that looks like a boolean to the subnet (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + purge_tags: false + tags: + looks_like_boolean: true + check_mode: true + register: vpc_subnet_info + + - name: assert a tag was added + assert: + that: + - vpc_subnet_info is changed + + - name: test adding a tag that looks like a boolean to the subnet + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + purge_tags: false + tags: + looks_like_boolean: true + register: vpc_subnet_info + + - name: assert a tag was added + assert: + that: + - vpc_subnet_info is changed + - 'vpc_subnet_info.subnet.tags.looks_like_boolean == "True"' + + # ============================================================ + - name: test idempotence adding a tag that looks like a boolean (CHECK MODE) + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + purge_tags: false + tags: + looks_like_boolean: true + check_mode: true + register: vpc_subnet_info + + - name: assert tags haven't changed + assert: + that: + - vpc_subnet_info is not changed + + - name: test idempotence adding a tag that looks like a boolean + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: present + purge_tags: false + tags: + looks_like_boolean: true + register: vpc_subnet_info + + - name: assert tags haven't changed + assert: + that: + - vpc_subnet_info is not changed + + always: + + ################################################ + # TEARDOWN STARTS HERE + ################################################ + + - name: tidy up subnet + ec2_vpc_subnet: + cidr: "{{ subnet_cidr }}" + vpc_id: "{{ vpc_result.vpc.id }}" + state: absent + + - name: tidy up VPC + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + state: absent + cidr_block: "{{ vpc_cidr }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/aliases new file mode 100644 index 00000000..a112c3d1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group1 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml new file mode 100644 index 00000000..7e4c3106 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml @@ -0,0 +1,11 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + vars: + template_name: "../templates/{{ template | default('inventory.yml.j2') }}" + tasks: + - name: write inventory config file + copy: + dest: ../test.aws_ec2.yml + content: "{{ lookup('template', template_name) }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/empty_inventory_config.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/empty_inventory_config.yml new file mode 100644 index 00000000..f67fff1a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/empty_inventory_config.yml @@ -0,0 +1,9 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + tasks: + - name: write inventory config file + copy: + dest: ../test.aws_ec2.yml + content: "" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/populate_cache.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/populate_cache.yml new file mode 100644 index 00000000..64e8da4c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/populate_cache.yml @@ -0,0 +1,63 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + collections: + - community.general + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # Create VPC, subnet, security group, and find image_id to create instance + + - include_tasks: setup.yml +# - pause: +# seconds: 240 + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_ec2' in groups" + - "not groups.aws_ec2" + + # Create new host, add it to inventory and then terminate it without updating the cache + + - name: create a new host + ec2: + image: '{{ image_id }}' + exact_count: 1 + count_tag: + Name: '{{ resource_prefix }}' + instance_tags: + Name: '{{ resource_prefix }}' + instance_type: t2.micro + wait: yes + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + register: setup_instance + + - meta: refresh_inventory + + always: + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + ignore_errors: yes + when: setup_instance is defined + + - include_tasks: tear_down.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/setup.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/setup.yml new file mode 100644 index 00000000..8af11b1c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/setup.yml @@ -0,0 +1,51 @@ +- name: get image ID to create an instance + ec2_ami_info: + filters: + architecture: x86_64 + owner-id: '125523088429' + virtualization-type: hvm + root-device-type: ebs + name: 'Fedora-Atomic-27*' + register: fedora_images + +- set_fact: + image_id: '{{ fedora_images.images.0.image_id }}' + vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' + subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/24' + +- name: create a VPC to work in + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: present + name: '{{ resource_prefix }}_setup' + resource_tags: + Name: '{{ resource_prefix }}_setup' + register: setup_vpc + +- set_fact: + vpc_id: '{{ setup_vpc.vpc.id }}' + +- name: create a subnet to use for creating an ec2 instance + ec2_vpc_subnet: + az: '{{ aws_region }}a' + tags: '{{ resource_prefix }}_setup' + vpc_id: '{{ setup_vpc.vpc.id }}' + cidr: '{{ subnet_cidr }}' + state: present + resource_tags: + Name: '{{ resource_prefix }}_setup' + register: setup_subnet + +- set_fact: + subnet_id: '{{ setup_subnet.subnet.id }}' + +- name: create a security group to use for creating an ec2 instance + ec2_group: + name: '{{ resource_prefix }}_setup' + description: 'created by Ansible integration tests' + state: present + vpc_id: '{{ setup_vpc.vpc.id }}' + register: setup_sg + +- set_fact: + sg_id: '{{ setup_sg.group_id }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/tear_down.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/tear_down.yml new file mode 100644 index 00000000..c782421d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/tear_down.yml @@ -0,0 +1,31 @@ +- set_fact: + vpc_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/16' + subnet_cidr: '10.{{ 256 | random(seed=resource_prefix) }}.0.0/24' + +- name: remove setup security group + ec2_group: + name: '{{ resource_prefix }}_setup' + description: 'created by Ansible integration tests' + state: absent + vpc_id: '{{ vpc_id }}' + ignore_errors: yes + +- name: remove setup subnet + ec2_vpc_subnet: + az: '{{ aws_region }}a' + tags: '{{ resource_prefix }}_setup' + vpc_id: '{{ vpc_id }}' + cidr: '{{ subnet_cidr }}' + state: absent + resource_tags: + Name: '{{ resource_prefix }}_setup' + ignore_errors: yes + +- name: remove setup VPC + ec2_vpc_net: + cidr_block: '{{ vpc_cidr }}' + state: absent + name: '{{ resource_prefix }}_setup' + resource_tags: + Name: '{{ resource_prefix }}_setup' + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_invalid_aws_ec2_inventory_config.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_invalid_aws_ec2_inventory_config.yml new file mode 100644 index 00000000..cc1b9a5a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_invalid_aws_ec2_inventory_config.yml @@ -0,0 +1,9 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + tasks: + - name: assert inventory was not populated by aws_ec2 inventory plugin + assert: + that: + - "'aws_ec2' not in groups" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_inventory_cache.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_inventory_cache.yml new file mode 100644 index 00000000..d83cb0bf --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_inventory_cache.yml @@ -0,0 +1,18 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + tasks: + - name: assert cache was used to populate inventory + assert: + that: + - "'aws_ec2' in groups" + - "groups.aws_ec2 | length == 1" + + - meta: refresh_inventory + + - name: assert refresh_inventory updated the cache + assert: + that: + - "'aws_ec2' in groups" + - "not groups.aws_ec2" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory.yml new file mode 100644 index 00000000..80f4f023 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory.yml @@ -0,0 +1,86 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + + block: + + # Create VPC, subnet, security group, and find image_id to create instance + + - include_tasks: setup.yml + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_ec2' in groups" + - "not groups.aws_ec2" + + # Create new host, refresh inventory, remove host, refresh inventory + + - name: create a new host + ec2: + image: '{{ image_id }}' + exact_count: 1 + count_tag: + Name: '{{ resource_prefix }}' + instance_tags: + Name: '{{ resource_prefix }}' + instance_type: t2.micro + wait: yes + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + register: setup_instance + + - meta: refresh_inventory + + - name: assert group was populated with inventory and is no longer empty + assert: + that: + - "'aws_ec2' in groups" + - "groups.aws_ec2 | length == 1" + - "groups.aws_ec2.0 == '{{ resource_prefix }}'" + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + + - meta: refresh_inventory + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_ec2' in groups" + - "not groups.aws_ec2" + + always: + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + ignore_errors: yes + when: setup_instance is defined + + - include_tasks: tear_down.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_concatenation.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_concatenation.yml new file mode 100644 index 00000000..bd4aaed3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_concatenation.yml @@ -0,0 +1,61 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # Create VPC, subnet, security group, and find image_id to create instance + - include_tasks: setup.yml + + # Create new host, refresh inventory + - name: create a new host + ec2: + image: '{{ image_id }}' + exact_count: 1 + count_tag: + Name: '{{ resource_prefix }}' + instance_tags: + Name: '{{ resource_prefix }}' + OtherTag: value + instance_type: t2.micro + wait: yes + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + register: setup_instance + + - meta: refresh_inventory + + - name: register the current hostname + set_fact: + expected_hostname: "value_{{ resource_prefix }}" + + - name: "Ensure we've got a hostvars entry for the new host" + assert: + that: + - expected_hostname in hostvars + + always: + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: "{{ sg_id }}" + vpc_subnet_id: "{{ subnet_id }}" + ignore_errors: yes + when: setup_instance is defined + + - include_tasks: tear_down.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_constructed.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_constructed.yml new file mode 100644 index 00000000..2085bed9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_populating_inventory_with_constructed.yml @@ -0,0 +1,74 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + # Create VPC, subnet, security group, and find image_id to create instance + + - include_tasks: setup.yml + + # Create new host, refresh inventory + + - name: create a new host + ec2: + image: '{{ image_id }}' + exact_count: 1 + count_tag: + Name: '{{ resource_prefix }}' + instance_tags: + Name: '{{ resource_prefix }}' + tag1: value1 + tag2: value2 + instance_type: t2.micro + wait: yes + group_id: '{{ sg_id }}' + vpc_subnet_id: '{{ subnet_id }}' + register: setup_instance + + - meta: refresh_inventory + + - name: register the keyed sg group name + set_fact: + sg_group_name: "security_groups_{{ sg_id | replace('-', '_') }}" + + - name: register one of the keyed tag groups name + set_fact: + tag_group_name: "tag_Name_{{ resource_prefix | replace('-', '_') }}" + + - name: assert the keyed groups and groups from constructed config were added to inventory and composite var added to hostvars + assert: + that: + # There are 9 groups: all, ungrouped, aws_ec2, sg keyed group, 3 tag keyed group (one per tag), arch keyed group, constructed group + - "groups | length == 9" + - "groups[tag_group_name] | length == 1" + - "groups[sg_group_name] | length == 1" + - "groups.arch_x86_64 | length == 1" + - "groups.tag_with_name_key | length == 1" + - vars.hostvars[groups.aws_ec2.0]['test_compose_var_sum'] == 'value1value2' + + always: + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: "{{ sg_id }}" + vpc_subnet_id: "{{ subnet_id }}" + ignore_errors: yes + when: setup_instance is defined + + - include_tasks: tear_down.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_refresh_inventory.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_refresh_inventory.yml new file mode 100644 index 00000000..b1c14543 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/test_refresh_inventory.yml @@ -0,0 +1,68 @@ +- name: Test updating inventory + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_ec2' in groups" + - "not groups.aws_ec2" + + - name: create a new host + ec2: + image: "{{ images[aws_region] }}" + exact_count: 1 + count_tag: + Name: '{{ resource_prefix }}' + instance_tags: + Name: '{{ resource_prefix }}' + instance_type: t2.micro + wait: yes + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + register: setup_instance + + - meta: refresh_inventory + + - name: assert group was populated with inventory and is no longer empty + assert: + that: + - "'aws_ec2' in groups" + - "groups.aws_ec2 | length == 1" + - "groups.aws_ec2.0 == '{{ resource_prefix }}'" + + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + + - meta: refresh_inventory + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_ec2' in groups" + - "not groups.aws_ec2" + + always: + - name: remove setup ec2 instance + ec2: + instance_type: t2.micro + instance_ids: '{{ setup_instance.instance_ids }}' + state: absent + wait: yes + instance_tags: + Name: '{{ resource_prefix }}' + group_id: '{{ setup_sg.group_id }}' + vpc_subnet_id: '{{ setup_subnet.subnet.id }}' + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/runme.sh new file mode 100755 index 00000000..d28e6970 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/runme.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +set -eux + +# ensure test config is empty +ansible-playbook playbooks/empty_inventory_config.yml "$@" + +export ANSIBLE_INVENTORY_ENABLED="amazon.aws.aws_ec2" + +# test with default inventory file +#ansible-playbook playbooks/test_invalid_aws_ec2_inventory_config.yml "$@" + +export ANSIBLE_INVENTORY=test.aws_ec2.yml + +# test empty inventory config +#ansible-playbook playbooks/test_invalid_aws_ec2_inventory_config.yml "$@" + +# generate inventory config and test using it +#ansible-playbook playbooks/create_inventory_config.yml "$@" +#ansible-playbook playbooks/test_populating_inventory.yml "$@" + +# generate inventory config with caching and test using it +ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_cache.yml.j2'" "$@" +ansible-playbook playbooks/populate_cache.yml "$@" +ansible-playbook playbooks/test_inventory_cache.yml "$@" + +# remove inventory cache +rm -r aws_ec2_cache_dir/ + +# generate inventory config with constructed features and test using it +#ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_constructed.yml.j2'" "$@" +#ansible-playbook playbooks/test_populating_inventory_with_constructed.yml "$@" +ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_concatenation.yml.j2'" "$@" +ansible-playbook playbooks/test_populating_inventory_with_concatenation.yml "$@" + +# cleanup inventory config +ansible-playbook playbooks/empty_inventory_config.yml "$@" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory.yml.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory.yml.j2 new file mode 100644 index 00000000..baac15be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory.yml.j2 @@ -0,0 +1,14 @@ +plugin: amazon.aws.aws_ec2 +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: +- '{{ aws_region }}' +filters: + tag:Name: + - '{{ resource_prefix }}' +hostnames: +- tag:Name +- dns-name diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_cache.yml.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_cache.yml.j2 new file mode 100644 index 00000000..8fe4e33f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_cache.yml.j2 @@ -0,0 +1,14 @@ +plugin: amazon.aws.aws_ec2 +cache: True +cache_plugin: jsonfile +cache_connection: aws_ec2_cache_dir +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: +- '{{ aws_region }}' +filters: + tag:Name: + - '{{ resource_prefix }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_concatenation.yml.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_concatenation.yml.j2 new file mode 100644 index 00000000..035b1d7c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_concatenation.yml.j2 @@ -0,0 +1,15 @@ +plugin: amazon.aws.aws_ec2 +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: +- '{{ aws_region }}' +filters: + tag:Name: + - '{{ resource_prefix }}' +hostnames: + - name: 'tag:Name' + separator: '_' + prefix: 'tag:OtherTag' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_constructed.yml.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_constructed.yml.j2 new file mode 100644 index 00000000..c0ebcbfc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_constructed.yml.j2 @@ -0,0 +1,22 @@ +plugin: amazon.aws.aws_ec2 +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: +- '{{ aws_region }}' +filters: + tag:Name: + - '{{ resource_prefix }}' +keyed_groups: +- key: 'security_groups|community.general.json_query("[].group_id")' + prefix: security_groups +- key: tags + prefix: tag +- prefix: arch + key: architecture +compose: + test_compose_var_sum: tags.tag1 + tags.tag2 +groups: + tag_with_name_key: '''Name'' in (tags | list)' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/test.aws_ec2.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/test.aws_ec2.yml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/test.aws_ec2.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/aliases new file mode 100644 index 00000000..56927195 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/aliases @@ -0,0 +1,2 @@ +cloud/aws +unsupported diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/create_inventory_config.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/create_inventory_config.yml new file mode 100644 index 00000000..f0a9030a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/create_inventory_config.yml @@ -0,0 +1,11 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + vars: + template_name: "../templates/{{ template | default('inventory.j2') }}" + tasks: + - name: write inventory config file + copy: + dest: ../test.aws_rds.yml + content: "{{ lookup('template', template_name) }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/empty_inventory_config.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/empty_inventory_config.yml new file mode 100644 index 00000000..d7e2cda3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/empty_inventory_config.yml @@ -0,0 +1,9 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + tasks: + - name: write inventory config file + copy: + dest: ../test.aws_rds.yml + content: "" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/populate_cache.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/populate_cache.yml new file mode 100644 index 00000000..aa757410 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/populate_cache.yml @@ -0,0 +1,56 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + collections: + - community.aws + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + - set_fact: + instance_id: '{{ resource_prefix }}-mariadb' + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_rds' in groups" + - "not groups.aws_rds" + + # Create new host, add it to inventory and then terminate it without updating the cache + + - name: create minimal mariadb instance in default VPC and default subnet group + rds_instance: + state: present + engine: mariadb + db_instance_class: db.t2.micro + allocated_storage: 20 + instance_id: '{{ instance_id }}' + master_username: 'ansibletestuser' + master_user_password: 'password-{{ resource_prefix | regex_findall(".{8}$") | first }}' + tags: + workload_type: other + register: setup_instance + + - meta: refresh_inventory + + - assert: + that: + - groups.aws_rds + + always: + + - name: remove mariadb instance + rds_instance: + state: absent + engine: mariadb + skip_final_snapshot: yes + instance_id: '{{ instance_id }}' + ignore_errors: yes + when: setup_instance is defined diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_invalid_aws_rds_inventory_config.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_invalid_aws_rds_inventory_config.yml new file mode 100644 index 00000000..49951357 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_invalid_aws_rds_inventory_config.yml @@ -0,0 +1,9 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + tasks: + - name: assert inventory was not populated by aws_rds inventory plugin + assert: + that: + - "'aws_rds' not in groups" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_inventory_cache.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_inventory_cache.yml new file mode 100644 index 00000000..7eadbad8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_inventory_cache.yml @@ -0,0 +1,18 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + tasks: + - name: assert cache was used to populate inventory + assert: + that: + - "'aws_rds' in groups" + - "groups.aws_rds | length == 1" + + - meta: refresh_inventory + + - name: assert refresh_inventory updated the cache + assert: + that: + - "'aws_rds' in groups" + - "not groups.aws_rds" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory.yml new file mode 100644 index 00000000..a34dd503 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory.yml @@ -0,0 +1,76 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + collections: + - community.aws + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - set_fact: + instance_id: "{{ resource_prefix }}-mariadb" + + - debug: var=groups + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_rds' in groups" + - "not groups.aws_rds" + + # Create new host, refresh inventory, remove host, refresh inventory + + - name: create minimal mariadb instance in default VPC and default subnet group + rds_instance: + state: present + engine: mariadb + db_instance_class: db.t2.micro + allocated_storage: 20 + instance_id: '{{ instance_id }}' + master_username: 'ansibletestuser' + master_user_password: 'password-{{ resource_prefix | regex_findall(".{8}$") | first }}' + tags: + workload_type: other + register: setup_instance + + - meta: refresh_inventory + + - name: assert group was populated with inventory and is no longer empty + assert: + that: + - "'aws_rds' in groups" + - "groups.aws_rds | length == 1" + - "groups.aws_rds.0 == '{{ instance_id }}'" + + - name: remove mariadb instance + rds_instance: + state: absent + engine: mariadb + skip_final_snapshot: yes + instance_id: '{{ instance_id }}' + + - meta: refresh_inventory + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_rds' in groups" + - "not groups.aws_rds" + + always: + + - name: remove mariadb instance + rds_instance: + state: absent + engine: mariadb + skip_final_snapshot: yes + instance_id: '{{ instance_id }}' + ignore_errors: yes + when: setup_instance is defined diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory_with_constructed.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory_with_constructed.yml new file mode 100644 index 00000000..d0b1ea36 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_populating_inventory_with_constructed.yml @@ -0,0 +1,64 @@ +--- +- hosts: 127.0.0.1 + connection: local + gather_facts: no + environment: "{{ ansible_test.environment }}" + collections: + - community.aws + tasks: + + - module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + block: + + - set_fact: + instance_id: "{{ resource_prefix }}-mariadb" + + - name: create minimal mariadb instance in default VPC and default subnet group + rds_instance: + state: present + engine: mariadb + db_instance_class: db.t2.micro + allocated_storage: 20 + instance_id: '{{ resource_prefix }}-mariadb' + master_username: 'ansibletestuser' + master_user_password: 'password-{{ resource_prefix | regex_findall(".{8}$") | first }}' + tags: + workload_type: other + register: setup_instance + + - meta: refresh_inventory + - debug: var=groups + + - name: 'generate expected group name based off the db parameter groups' + vars: + parameter_group_name: '{{ setup_instance.db_parameter_groups[0].db_parameter_group_name }}' + set_fact: + parameter_group_key: 'rds_parameter_group_{{ parameter_group_name | replace(".", "_") }}' + + - name: assert the keyed groups from constructed config were added to inventory + assert: + that: + # There are 6 groups: all, ungrouped, aws_rds, tag keyed group, engine keyed group, parameter group keyed group + - "groups | length == 6" + - '"all" in groups' + - '"ungrouped" in groups' + - '"aws_rds" in groups' + - '"tag_workload_type_other" in groups' + - '"rds_mariadb" in groups' + - 'parameter_group_key in groups' + + always: + + - name: remove mariadb instance + rds_instance: + state: absent + engine: mariadb + skip_final_snapshot: yes + instance_id: '{{ instance_id }}' + ignore_errors: yes + when: setup_instance is defined diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_refresh_inventory.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_refresh_inventory.yml new file mode 100644 index 00000000..f14e4861 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/playbooks/test_refresh_inventory.yml @@ -0,0 +1,66 @@ +- name: test updating inventory + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + collections: + - community.aws + block: + - set_fact: + instance_id: "{{ resource_prefix }}update" + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_rds' in groups" + - "not groups.aws_rds" + + - name: create minimal mariadb instance in default VPC and default subnet group + rds_instance: + state: present + engine: mariadb + db_instance_class: db.t2.micro + allocated_storage: 20 + instance_id: 'rds-mariadb-{{ resource_prefix }}' + master_username: 'ansibletestuser' + master_user_password: 'password-{{ resource_prefix | regex_findall(".{8}$") | first }}' + tags: + workload_type: other + register: setup_instance + + - meta: refresh_inventory + + - name: assert group was populated with inventory and is no longer empty + assert: + that: + - "'aws_rds' in groups" + - "groups.aws_rds | length == 1" + - "groups.aws_rds.0 == '{{ resource_prefix }}'" + + - name: remove mariadb instance + rds_instance: + state: absent + engine: mariadb + skip_final_snapshot: yes + instance_id: ansible-rds-mariadb-example + + - meta: refresh_inventory + + - name: assert group was populated with inventory but is empty + assert: + that: + - "'aws_rds' in groups" + - "not groups.aws_rds" + + always: + + - name: remove mariadb instance + rds_instance: + state: absent + engine: mariadb + skip_final_snapshot: yes + instance_id: ansible-rds-mariadb-example + ignore_errors: yes + when: setup_instance is defined diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/runme.sh new file mode 100755 index 00000000..7697fbb8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/runme.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -eux + +# ensure test config is empty +ansible-playbook playbooks/empty_inventory_config.yml "$@" + +export ANSIBLE_INVENTORY_ENABLED="amazon.aws.aws_rds" + +# test with default inventory file +ansible-playbook playbooks/test_invalid_aws_rds_inventory_config.yml "$@" + +export ANSIBLE_INVENTORY=test.aws_rds.yml + +# test empty inventory config +ansible-playbook playbooks/test_invalid_aws_rds_inventory_config.yml "$@" + +# generate inventory config and test using it +ansible-playbook playbooks/create_inventory_config.yml "$@" +ansible-playbook playbooks/test_populating_inventory.yml "$@" + +# generate inventory config with caching and test using it +ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_cache.j2'" "$@" +ansible-playbook playbooks/populate_cache.yml "$@" +ansible-playbook playbooks/test_inventory_cache.yml "$@" + +# remove inventory cache +rm -r aws_rds_cache_dir/ + +# generate inventory config with constructed features and test using it +ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_constructed.j2'" "$@" +ansible-playbook playbooks/test_populating_inventory_with_constructed.yml "$@" + +# cleanup inventory config +ansible-playbook playbooks/empty_inventory_config.yml "$@" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory.j2 new file mode 100644 index 00000000..61a659ea --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory.j2 @@ -0,0 +1,10 @@ +plugin: amazon.aws.aws_rds +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: + - '{{ aws_region }}' +filters: + db-instance-id: "{{ resource_prefix }}-mariadb" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory_with_cache.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory_with_cache.j2 new file mode 100644 index 00000000..6e9c40e9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory_with_cache.j2 @@ -0,0 +1,13 @@ +plugin: amazon.aws.aws_rds +cache: True +cache_plugin: jsonfile +cache_connection: aws_rds_cache_dir +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: + - '{{ aws_region }}' +filters: + db-instance-id: "{{ resource_prefix }}-mariadb" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory_with_constructed.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory_with_constructed.j2 new file mode 100644 index 00000000..a0636a97 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/templates/inventory_with_constructed.j2 @@ -0,0 +1,17 @@ +plugin: amazon.aws.aws_rds +aws_access_key_id: '{{ aws_access_key }}' +aws_secret_access_key: '{{ aws_secret_key }}' +{% if security_token | default(false) %} +aws_security_token: '{{ security_token }}' +{% endif %} +regions: + - '{{ aws_region }}' +keyed_groups: + - key: 'db_parameter_groups|community.general.json_query("[].db_parameter_group_name")' + prefix: rds_parameter_group + - key: tags + prefix: tag + - key: engine + prefix: rds +filters: + db-instance-id: "{{ resource_prefix }}-mariadb" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/test.aws_rds.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/test.aws_rds.yml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_rds/test.aws_rds.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/aliases new file mode 100644 index 00000000..6e3860be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/inventory b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/inventory new file mode 100644 index 00000000..5093e858 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/inventory @@ -0,0 +1,6 @@ +[tests] +localhost + +[all:vars] +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/main.yml new file mode 100644 index 00000000..139250a6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/main.yml @@ -0,0 +1,8 @@ +- hosts: all + gather_facts: no + collections: + - community.aws + - amazon.aws + roles: + # Test the behaviour of module_utils.core.AnsibleAWSModule.client (boto3) + - 'ansibleawsmodule.client' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/amazonroot.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/amazonroot.pem new file mode 100644 index 00000000..a6f3e92a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/amazonroot.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/isrg-x1.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/isrg-x1.pem new file mode 100644 index 00000000..b85c8037 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/files/isrg-x1.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/library/example_module.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/library/example_module.py new file mode 100644 index 00000000..5e2c8e3e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/library/example_module.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# A bare-minimum Ansible Module based on AnsibleAWSModule used for testing some +# of the core behaviour around AWS/Boto3 connection details + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # Handled by AnsibleAWSModule + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict + + +def main(): + module = AnsibleAWSModule( + argument_spec={}, + supports_check_mode=True, + ) + + decorator = AWSRetry.jittered_backoff() + client = module.client('ec2', retry_decorator=decorator) + + filters = ansible_dict_to_boto3_filter_list({'name': 'amzn2-ami-hvm-2.0.202006*-x86_64-gp2'}) + + try: + images = client.describe_images(aws_retry=True, ImageIds=[], Filters=filters, Owners=['amazon'], ExecutableUsers=[]) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Fail JSON AWS') + + # Return something, just because we can. + module.exit_json( + changed=False, + **camel_dict_to_snake_dict(images)) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/meta/main.yml new file mode 100644 index 00000000..77589cc2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/meta/main.yml @@ -0,0 +1,5 @@ +dependencies: + - prepare_tests + - setup_ec2 +collections: + - amazon.aws diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/ca_bundle.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/ca_bundle.yml new file mode 100644 index 00000000..7ad4e7a3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/ca_bundle.yml @@ -0,0 +1,202 @@ +--- +- name: 'Create temporary location for CA files' + tempfile: + state: directory + suffix: 'test-CAs' + register: ca_tmp + +- name: 'Ensure we have Amazons root CA available to us' + copy: + src: 'amazonroot.pem' + dest: '{{ ca_tmp.path }}/amazonroot.pem' + mode: 0644 + +- name: 'Ensure we have a another CA (ISRG-X1) bundle available to us' + copy: + src: 'isrg-x1.pem' + dest: '{{ ca_tmp.path }}/isrg-x1.pem' + mode: 0644 + +################################################################################## +# Test disabling cert validation (make sure we don't error) + +- name: 'Test basic operation using default CA bundle (no validation) - parameter' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + validate_certs: False + register: default_bundle_result + +- assert: + that: + - default_bundle_result is successful + +################################################################################## +# Tests using Amazon's CA (the one the endpoint certs should be signed with) + +- name: 'Test basic operation using Amazons root CA - parameter' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + aws_ca_bundle: '{{ ca_tmp.path }}/amazonroot.pem' + register: amazon_ca_result + +- assert: + that: + - amazon_ca_result is successful + +- name: 'Test basic operation using Amazons root CA - environment' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + environment: + AWS_CA_BUNDLE: '{{ ca_tmp.path }}/amazonroot.pem' + register: amazon_ca_result + +- assert: + that: + - amazon_ca_result is successful + +- name: 'Test basic operation using Amazons root CA (no validation) - parameter' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + aws_ca_bundle: '{{ ca_tmp.path }}/amazonroot.pem' + validate_certs: False + register: amazon_ca_result + +- assert: + that: + - amazon_ca_result is successful + +- name: 'Test basic operation using Amazons root CA (no validation) - environment' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + validate_certs: False + environment: + AWS_CA_BUNDLE: '{{ ca_tmp.path }}/amazonroot.pem' + register: amazon_ca_result + +- assert: + that: + - amazon_ca_result is successful + +################################################################################## +# Tests using ISRG's CA (one that the endpoint certs *aren't* signed with) + +- name: 'Test basic operation using a different CA - parameter' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + aws_ca_bundle: '{{ ca_tmp.path }}/isrg-x1.pem' + register: isrg_ca_result + ignore_errors: yes + +- assert: + that: + - isrg_ca_result is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"CERTIFICATE_VERIFY_FAILED" in isrg_ca_result.msg' + - '"Fail JSON AWS" in isrg_ca_result.msg' + +- name: 'Test basic operation using a different CA - environment' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + environment: + AWS_CA_BUNDLE: '{{ ca_tmp.path }}/isrg-x1.pem' + register: isrg_ca_result + ignore_errors: yes + +- assert: + that: + - isrg_ca_result is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"CERTIFICATE_VERIFY_FAILED" in isrg_ca_result.msg' + - '"Fail JSON AWS" in isrg_ca_result.msg' + +- name: 'Test basic operation using a different CA (no validation) - parameter' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + aws_ca_bundle: '{{ ca_tmp.path }}/isrg-x1.pem' + validate_certs: False + register: isrg_ca_result + +- assert: + that: + - isrg_ca_result is successful + +- name: 'Test basic operation using a different CA (no validation) - environment' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + validate_certs: False + environment: + AWS_CA_BUNDLE: '{{ ca_tmp.path }}/isrg-x1.pem' + register: isrg_ca_result + +- assert: + that: + - isrg_ca_result is successful + +################################################################################## +# https://github.com/ansible-collections/amazon.aws/issues/129 +- name: 'Test CA bundle is used when authenticating with a profile - implied validation' + example_module: + profile: 'test_profile' + aws_ca_bundle: '{{ ca_tmp.path }}/isrg-x1.pem' + register: isrg_ca_result + ignore_errors: yes + +- assert: + that: + - isrg_ca_result is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"CERTIFICATE_VERIFY_FAILED" in isrg_ca_result.msg' + - '"Fail JSON AWS" in isrg_ca_result.msg' + +- name: 'Test CA bundle is used when authenticating with a profile - explicit validation' + example_module: + profile: 'test_profile' + aws_ca_bundle: '{{ ca_tmp.path }}/isrg-x1.pem' + validate_certs: True + register: isrg_ca_result + ignore_errors: yes + +- assert: + that: + - isrg_ca_result is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"CERTIFICATE_VERIFY_FAILED" in isrg_ca_result.msg' + - '"Fail JSON AWS" in isrg_ca_result.msg' + +- name: 'Test CA bundle is used when authenticating with a profile - explicitly disable validation' + example_module: + profile: 'test_profile' + aws_ca_bundle: '{{ ca_tmp.path }}/isrg-x1.pem' + validate_certs: False + register: isrg_ca_result + +- assert: + that: + - isrg_ca_result is success diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/credentials.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/credentials.yml new file mode 100644 index 00000000..94925829 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/credentials.yml @@ -0,0 +1,281 @@ +--- +################################################################################## +# Tests using standard credential parameters + +- name: 'Test basic operation using simple credentials (simple-parameters)' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (aws-parameters)' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (ec2-parameters)' + example_module: + ec2_region: '{{ aws_region }}' + ec2_access_key: '{{ aws_access_key }}' + ec2_secret_key: '{{ aws_secret_key }}' + access_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +################################################################################## +# Tests using standard credentials from environment variables + +- name: 'Test basic operation using simple credentials (aws-environment)' + example_module: + environment: + AWS_REGION: '{{ aws_region }}' + AWS_ACCESS_KEY_ID: '{{ aws_access_key }}' + AWS_SECRET_ACCESS_KEY: '{{ aws_secret_key }}' + AWS_SECURITY_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (aws2-environment)' + example_module: + environment: + AWS_DEFAULT_REGION: '{{ aws_region }}' + AWS_ACCESS_KEY: '{{ aws_access_key }}' + AWS_SECRET_KEY: '{{ aws_secret_key }}' + AWS_SESSION_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (ec2-environment)' + example_module: + environment: + EC2_REGION: '{{ aws_region }}' + EC2_ACCESS_KEY: '{{ aws_access_key }}' + EC2_SECRET_KEY: '{{ aws_secret_key }}' + EC2_SECURITY_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +################################################################################## +# Tests for missing parameters + +- name: 'Test with missing region' + example_module: + region: '{{ omit }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: missing_region + ignore_errors: True + +- assert: + that: + - missing_region is failed + - '"requires a region" in missing_region.msg' + +- name: 'Test with missing access key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ omit }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: missing_access + ignore_errors: True + +- assert: + that: + - missing_access is failed + - '"Partial credentials found" in missing_access.msg' + - '"aws_access_key_id" in missing_access.msg' + +- name: 'Test with missing secret key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ omit }}' + security_token: '{{ security_token }}' + register: missing_secret + ignore_errors: True + +- assert: + that: + - missing_secret is failed + - '"Partial credentials found" in missing_secret.msg' + - '"aws_secret_access_key" in missing_secret.msg' + +- name: 'Test with missing security token' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ omit }}' + register: missing_token + ignore_errors: True + +- assert: + that: + - missing_token is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"AuthFailure" in missing_token.msg' + - '"Fail JSON AWS" in missing_token.msg' + - '"error" in missing_token' + - '"code" in missing_token.error' + - missing_token.error.code == 'AuthFailure' + - '"message" in missing_token.error' + +################################################################################## +# Run an additional authentication request to ensure that we're out of any +# deny-lists caused by bad requests +- name: 'Perform valid authentication to avoid deny-listing' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: anti_denylist + until: anti_denylist is success + retries: 5 + delay: 5 + +################################################################################## +# Tests for bad parameters + +- name: 'Test with bad region' + example_module: + region: 'junk-example' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_region + ignore_errors: True + +- assert: + that: + - bad_region is failed + - '"msg" in bad_region' + - '"Could not connect to the endpoint URL" in bad_region.msg' + - '"Fail JSON AWS" in bad_region.msg' + - '"ec2.junk-example" in bad_region.msg' + +- name: 'Test with bad access key' + example_module: + region: '{{ aws_region }}' + access_key: 'junk-example' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_access + ignore_errors: True + +- assert: + that: + - bad_access is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"AuthFailure" in bad_access.msg' + - '"Fail JSON AWS" in bad_access.msg' + - '"error" in bad_access' + - '"code" in bad_access.error' + - bad_access.error.code == 'AuthFailure' + - '"message" in bad_access.error' + +# Run an additional authentication request to ensure that we're out of any +# deny-lists caused by bad requests +- name: 'Perform valid authentication to avoid deny-listing' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: anti_denylist + until: anti_denylist is success + retries: 5 + delay: 5 + +- name: 'Test with bad secret key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: 'junk-example' + security_token: '{{ security_token }}' + register: bad_secret + ignore_errors: True + +- assert: + that: + - bad_secret is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"AuthFailure" in bad_secret.msg' + - '"Fail JSON AWS" in bad_secret.msg' + - '"error" in bad_secret' + - '"code" in bad_secret.error' + - bad_secret.error.code == 'AuthFailure' + - '"message" in bad_secret.error' + +# Run an additional authentication request to ensure that we're out of any +# deny-lists caused by bad requests +- name: 'Perform valid authentication to avoid deny-listing' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: anti_denylist + until: anti_denylist is success + retries: 5 + delay: 5 + +- name: 'Test with bad security token' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: 'junk-example' + register: bad_token + ignore_errors: True + +- assert: + that: + - bad_token is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"AuthFailure" in bad_token.msg' + - '"Fail JSON AWS" in bad_token.msg' + - '"error" in bad_token' + - '"code" in bad_token.error' + - bad_token.error.code == 'AuthFailure' + - '"message" in bad_token.error' + +# Run an additional authentication request to ensure that we're out of any +# deny-lists caused by bad requests +- name: 'Perform valid authentication to avoid deny-listing' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: anti_denylist + until: anti_denylist is success + retries: 5 + delay: 5 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/endpoints.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/endpoints.yml new file mode 100644 index 00000000..590af913 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/endpoints.yml @@ -0,0 +1,123 @@ +--- +################################################################################## +# Tests using Endpoints + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + - '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +# The FIPS endpoints aren't available in every region, this will trigger errors +# outside of: [ us-east-1, us-east-2, us-west-1, us-west-2 ] + +- name: 'Test basic operation using FIPS endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + aws_endpoint_url: 'https://ec2-fips.us-east-1.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: fips_endpoint_result + +- name: 'Check that we connected to the FIPS endpoint' + assert: + that: + - fips_endpoint_result is successful + - '"ec2-fips:DescribeImages" in fips_endpoint_result.resource_actions' + +- name: 'Test basic operation using FIPS endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + endpoint_url: 'https://ec2-fips.us-east-1.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: fips_endpoint_result + +- name: 'Check that we connected to the FIPS endpoint' + assert: + that: + - fips_endpoint_result is successful + - '"ec2-fips:DescribeImages" in fips_endpoint_result.resource_actions' + +- name: 'Test basic operation using FIPS endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + ec2_url: 'https://ec2-fips.us-east-1.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: fips_endpoint_result + +- name: 'Check that we connected to the FIPS endpoint' + assert: + that: + - fips_endpoint_result is successful + - '"ec2-fips:DescribeImages" in fips_endpoint_result.resource_actions' + +################################################################################## +# Tests using environment variables + +- name: 'Test basic operation using FIPS endpoint (aws-environment)' + example_module: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + environment: + AWS_URL: 'https://ec2-fips.us-east-1.amazonaws.com' + register: fips_endpoint_result + +- name: 'Check that we connected to the FIPS endpoint' + assert: + that: + - fips_endpoint_result is successful + - '"ec2-fips:DescribeImages" in fips_endpoint_result.resource_actions' + +- name: 'Test basic operation using FIPS endpoint (ec2-environment)' + example_module: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + environment: + EC2_URL: 'https://ec2-fips.us-east-1.amazonaws.com' + register: fips_endpoint_result + +- name: 'Check that we connected to the FIPS endpoint' + assert: + that: + - fips_endpoint_result is successful + - '"ec2-fips:DescribeImages" in fips_endpoint_result.resource_actions' + +################################################################################## +# Tests using a bad endpoint URL +# - This demonstrates that endpoint_url overrode region + +- name: 'Test with bad endpoint URL' + example_module: + region: '{{ aws_region }}' + endpoint_url: 'https://junk.{{ aws_region }}.amazonaws.com' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_endpoint + ignore_errors: True + +- assert: + that: + - bad_endpoint is failed + - '"msg" in bad_endpoint' + - '"Could not connect to the endpoint URL" in bad_endpoint.msg' + - '"Fail JSON AWS" in bad_endpoint.msg' + - '"junk.{{ aws_region }}.amazonaws.com" in bad_endpoint.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/main.yml new file mode 100644 index 00000000..dc61fad6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: 'Tests around standard credentials' + include_tasks: 'credentials.yml' + +- name: 'Tests around profiles' + include_tasks: 'profiles.yml' + +- name: 'Tests around endpoints' + include_tasks: 'endpoints.yml' + +- name: 'Tests around CA Bundles' + include_tasks: 'ca_bundle.yml' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/profiles.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/profiles.yml new file mode 100644 index 00000000..17b85038 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/roles/ansibleawsmodule.client/tasks/profiles.yml @@ -0,0 +1,57 @@ +--- +################################################################################## +# Tests using profiles instead of directly consuming credentials + +- name: 'Test basic operation using profile (simple-parameters)' + example_module: + profile: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws-parameters)' + example_module: + aws_profile: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws-environment)' + example_module: + environment: + AWS_PROFILE: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws2-environment)' + example_module: + environment: + AWS_DEFAULT_PROFILE: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +################################################################################## +# Tests with bad profile + +- name: 'Test with bad profile' + example_module: + profile: 'junk-profile' + register: bad_profile + ignore_errors: True + +- assert: + that: + - bad_profile is failed + - '"msg" in bad_profile' + - '"junk-profile" in bad_profile.msg' + - '"could not be found" in bad_profile.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/runme.sh new file mode 100755 index 00000000..9b0536d2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/runme.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -eux + +ANSIBLE_ROLES_PATH="../" +# Boto3 +AWS_CONFIG_FILE="$( pwd )/boto3_config" +# Boto2 +BOTO_CONFIG="$( pwd )/boto3_config" + +export ANSIBLE_ROLES_PATH +export AWS_CONFIG_FILE +export BOTO_CONFIG + +ansible-playbook setup.yml -i localhost "$@" +ansible-playbook main.yml -i inventory "$@" -e "@session_credentials.yml" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/setup.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/setup.yml new file mode 100644 index 00000000..9b219eb2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/setup.yml @@ -0,0 +1,40 @@ +--- +- hosts: localhost + connection: local + gather_facts: no + tasks: + # =========================================================== + # While CI uses a dedicated session, the easiest way to run + # tests outside of CI is with a simple access/secret key pair. + # + # For consistency, use sts_session_token to grab session + # credentials if we're not already using a session + # Note: this can't be done within a session, hence the slightly + # strange dance + - name: 'Get a session token if we are using a basic key' + when: + - security_token is not defined + block: + - name: 'Get a session token' + sts_session_token: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + register: session_token + no_log: true + - name: 'Override initial tokens' + set_fact: + session_access_key: '{{ session_token.sts_creds.access_key }}' + session_secret_key: '{{ session_token.sts_creds.secret_key }}' + session_security_token: '{{ session_token.sts_creds.session_token }}' + no_log: true + + - name: 'Write out credentials' + template: + dest: './session_credentials.yml' + src: 'session_credentials.yml.j2' + + - name: 'Write out boto config file' + template: + dest: './boto3_config' + src: 'boto_config.j2' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/templates/boto_config.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/templates/boto_config.j2 new file mode 100644 index 00000000..f8668f05 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/templates/boto_config.j2 @@ -0,0 +1,5 @@ +[profile test_profile] +region = {{ aws_region }} +aws_access_key_id = {{ session_access_key | default(aws_access_key) }} +aws_secret_access_key = {{ session_secret_key | default(aws_secret_key) }} +aws_security_token = {{ session_security_token | default(security_token) }} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/templates/session_credentials.yml.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/templates/session_credentials.yml.j2 new file mode 100644 index 00000000..bb030439 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_core/templates/session_credentials.yml.j2 @@ -0,0 +1,3 @@ +aws_access_key: {{ session_access_key | default(aws_access_key) }} +aws_secret_key: {{ session_secret_key | default(aws_secret_key) }} +security_token: {{ session_security_token | default(security_token) }} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/aliases new file mode 100644 index 00000000..72a9fb4f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group4 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/connect_to_aws.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/connect_to_aws.yml new file mode 100644 index 00000000..f0adfc72 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/connect_to_aws.yml @@ -0,0 +1,8 @@ +- hosts: all + gather_facts: no + collections: + - community.aws + - amazon.aws + roles: + # Test the behaviour of module_utils.connect_to_aws + - 'connect_to_aws' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/ec2_connect.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/ec2_connect.yml new file mode 100644 index 00000000..75ecd297 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/ec2_connect.yml @@ -0,0 +1,8 @@ +- hosts: all + gather_facts: no + collections: + - community.aws + - amazon.aws + roles: + # Test the behaviour of module_utils.ec2.ec2_connect + - 'ec2_connect' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/inventory b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/inventory new file mode 100644 index 00000000..5093e858 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/inventory @@ -0,0 +1,6 @@ +[tests] +localhost + +[all:vars] +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem new file mode 100644 index 00000000..a6f3e92a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/amazonroot.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem new file mode 100644 index 00000000..b85c8037 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/files/isrg-x1.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py new file mode 100644 index 00000000..543776a0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/library/example_module.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# A bare-minimum Ansible Module based on AnsibleAWSModule used for testing some +# of the core behaviour around AWS/Boto3 connection details + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +try: + import boto.ec2 +except ImportError: + pass + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AnsibleAWSError +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import connect_to_aws +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info + + +def main(): + module = AnsibleAWSModule( + argument_spec={}, + supports_check_mode=True, + check_boto3=False, + ) + + region, ec2_url, aws_connect_params = get_aws_connection_info(module) + if not region: + module.fail_json(msg="Fail JSON: No Region") + + try: + client = connect_to_aws(boto.ec2, region, **aws_connect_params) + except boto.exception.NoAuthHandlerFound as e: + module.fail_json_aws(e, msg='No Authentication Handler Found') + except AnsibleAWSError as e: + module.fail_json_aws(e, msg='Fail JSON AWS') + + filters = {'name': 'amzn2-ami-hvm-2.0.202006*-x86_64-gp2'} + + try: + images = client.get_all_images(image_ids=[], filters=filters, owners=['amazon'], executable_by=[]) + except (boto.exception.BotoServerError, AnsibleAWSError) as e: + module.fail_json_aws(e, msg='Fail JSON AWS') + + images_out = [] + for image in images: + images_out.append(image.id) + + # Return something, just because we can. + module.exit_json( + changed=False, + images=images_out) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml new file mode 100644 index 00000000..77589cc2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/meta/main.yml @@ -0,0 +1,5 @@ +dependencies: + - prepare_tests + - setup_ec2 +collections: + - amazon.aws diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml new file mode 100644 index 00000000..573532bb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/credentials.yml @@ -0,0 +1,212 @@ +--- +################################################################################## +# Tests using standard credential parameters + +- name: 'Test basic operation using simple credentials (simple-parameters)' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (aws-parameters)' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (ec2-parameters)' + example_module: + ec2_region: '{{ aws_region }}' + ec2_access_key: '{{ aws_access_key }}' + ec2_secret_key: '{{ aws_secret_key }}' + access_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +################################################################################## +# Tests using standard credentials from environment variables + +- name: 'Test basic operation using simple credentials (aws-environment)' + example_module: + environment: + AWS_REGION: '{{ aws_region }}' + AWS_ACCESS_KEY_ID: '{{ aws_access_key }}' + AWS_SECRET_ACCESS_KEY: '{{ aws_secret_key }}' + AWS_SECURITY_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (aws2-environment)' + example_module: + environment: + AWS_DEFAULT_REGION: '{{ aws_region }}' + AWS_ACCESS_KEY: '{{ aws_access_key }}' + AWS_SECRET_KEY: '{{ aws_secret_key }}' + AWS_SESSION_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (ec2-environment)' + example_module: + environment: + EC2_REGION: '{{ aws_region }}' + EC2_ACCESS_KEY: '{{ aws_access_key }}' + EC2_SECRET_KEY: '{{ aws_secret_key }}' + EC2_SECURITY_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +################################################################################## +# Tests for missing parameters + +- name: 'Test with missing region' + example_module: + region: '{{ omit }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: missing_region + ignore_errors: True + +- assert: + that: + - missing_region is failed + - '"Fail JSON: No Region" in missing_region.msg' + +- name: 'Test with missing access key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ omit }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: missing_access + ignore_errors: True + +- assert: + that: + - missing_access is failed + - '"No handler was ready to authenticate." in missing_access.msg' + #- '"aws_access_key_id" in missing_access.msg' + +- name: 'Test with missing secret key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ omit }}' + security_token: '{{ security_token }}' + register: missing_secret + ignore_errors: True + +- assert: + that: + - missing_secret is failed + - '"No handler was ready to authenticate." in missing_secret.msg' + #- '"aws_secret_access_key" in missing_secret.msg' + +- name: 'Test with missing security token' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ omit }}' + register: missing_token + ignore_errors: True + +- assert: + that: + - missing_token is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' + + +################################################################################## +# Tests for bad parameters + +- name: 'Test with bad region' + example_module: + region: 'junk-example' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_region + ignore_errors: True + +- assert: + that: + - bad_region is failed + - '"msg" in bad_region' + - '"does not seem to be available" in bad_region.msg' + - '"If the region definitely exists, you may need to upgrade boto or extend with endpoints_path" in bad_region.msg' + +- name: 'Test with bad access key' + example_module: + region: '{{ aws_region }}' + access_key: 'junk-example' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_access + ignore_errors: True + +- assert: + that: + - bad_access is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' + +- name: 'Test with bad secret key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: 'junk-example' + security_token: '{{ security_token }}' + register: bad_secret + ignore_errors: True + +- assert: + that: + - bad_secret is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' + +- name: 'Test with bad security token' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: 'junk-example' + register: bad_token + ignore_errors: True + +- assert: + that: + - bad_token is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml new file mode 100644 index 00000000..a2531e96 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/endpoints.yml @@ -0,0 +1,105 @@ +--- +# Note: connect_to_aws currently *ignores* aws_endpoint_url +# +################################################################################## +# Tests using Endpoints + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + endpoint_url: 'https://ec2.us-east-1.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + ec2_url: 'https://ec2.us-east-1.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +################################################################################## +# Tests using environment variables + +- name: 'Test basic operation using standard endpoint (aws-environment)' + example_module: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + environment: + AWS_URL: 'https://ec2.us-east-1.amazonaws.com' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint (ec2-environment)' + example_module: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + environment: + EC2_URL: 'https://ec2.us-east-1.amazonaws.com' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +################################################################################## +# Tests using a bad endpoint URL +# - This demonstrates that endpoint_url overrode region + +- name: 'Test with bad endpoint URL' + example_module: + region: '{{ aws_region }}' + endpoint_url: 'https://junk.{{ aws_region }}.amazonaws.com' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_endpoint + ignore_errors: True + +- assert: + that: + # endpoint_url is ignored by connect_to_aws + - bad_endpoint is successful + #- bad_endpoint is failed diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml new file mode 100644 index 00000000..9e81b308 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: 'Tests around standard credentials' + include_tasks: 'credentials.yml' + +- name: 'Tests around profiles' + include_tasks: 'profiles.yml' + +- name: 'Tests around endpoints' + include_tasks: 'endpoints.yml' + +#- name: 'Tests around CA Bundles' +# include_tasks: 'boto2_ec2/ca_bundle.yml' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml new file mode 100644 index 00000000..bab095e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/connect_to_aws/tasks/profiles.yml @@ -0,0 +1,63 @@ +--- +# Note: unlike boto3 modules, boto2 modules can't read region from the profile +# +################################################################################## +# Tests using profiles instead of directly consuming credentials + +- name: 'Test basic operation using profile (simple-parameters)' + example_module: + region: '{{ aws_region }}' + profile: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws-parameters)' + example_module: + region: '{{ aws_region }}' + aws_profile: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws-environment)' + example_module: + region: '{{ aws_region }}' + environment: + AWS_PROFILE: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws2-environment)' + example_module: + region: '{{ aws_region }}' + environment: + AWS_DEFAULT_PROFILE: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +################################################################################## +# Tests with bad profile + +- name: 'Test with bad profile' + example_module: + region: '{{ aws_region }}' + profile: 'junk-profile' + register: bad_profile + ignore_errors: True + +- assert: + that: + - bad_profile is failed + - '"msg" in bad_profile' + - '"Profile given for AWS was not found." in bad_profile.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem new file mode 100644 index 00000000..a6f3e92a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/amazonroot.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem new file mode 100644 index 00000000..b85c8037 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/files/isrg-x1.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py new file mode 100644 index 00000000..6bbc1a4a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/library/example_module.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# A bare-minimum Ansible Module based on AnsibleAWSModule used for testing some +# of the core behaviour around AWS/Boto3 connection details + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +try: + import boto.ec2 +except ImportError: + pass + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ec2_connect + + +def main(): + module = AnsibleAWSModule( + argument_spec={}, + supports_check_mode=True, + check_boto3=False, + ) + + try: + client = ec2_connect(module) + except boto.exception.NoAuthHandlerFound as e: + module.fail_json_aws(e, msg='Failed to get connection') + + filters = {'name': 'amzn2-ami-hvm-2.0.202006*-x86_64-gp2'} + + try: + images = client.get_all_images(image_ids=[], filters=filters, owners=['amazon'], executable_by=[]) + except boto.exception.BotoServerError as e: + module.fail_json_aws(e, msg='Fail JSON AWS') + + images_out = [] + for image in images: + images_out.append(image.id) + + # Return something, just because we can. + module.exit_json( + changed=False, + images=images_out) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml new file mode 100644 index 00000000..77589cc2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/meta/main.yml @@ -0,0 +1,5 @@ +dependencies: + - prepare_tests + - setup_ec2 +collections: + - amazon.aws diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml new file mode 100644 index 00000000..1843a497 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/credentials.yml @@ -0,0 +1,212 @@ +--- +################################################################################## +# Tests using standard credential parameters + +- name: 'Test basic operation using simple credentials (simple-parameters)' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (aws-parameters)' + example_module: + aws_region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (ec2-parameters)' + example_module: + ec2_region: '{{ aws_region }}' + ec2_access_key: '{{ aws_access_key }}' + ec2_secret_key: '{{ aws_secret_key }}' + access_token: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +################################################################################## +# Tests using standard credentials from environment variables + +- name: 'Test basic operation using simple credentials (aws-environment)' + example_module: + environment: + AWS_REGION: '{{ aws_region }}' + AWS_ACCESS_KEY_ID: '{{ aws_access_key }}' + AWS_SECRET_ACCESS_KEY: '{{ aws_secret_key }}' + AWS_SECURITY_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (aws2-environment)' + example_module: + environment: + AWS_DEFAULT_REGION: '{{ aws_region }}' + AWS_ACCESS_KEY: '{{ aws_access_key }}' + AWS_SECRET_KEY: '{{ aws_secret_key }}' + AWS_SESSION_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +- name: 'Test basic operation using simple credentials (ec2-environment)' + example_module: + environment: + EC2_REGION: '{{ aws_region }}' + EC2_ACCESS_KEY: '{{ aws_access_key }}' + EC2_SECRET_KEY: '{{ aws_secret_key }}' + EC2_SECURITY_TOKEN: '{{ security_token }}' + register: credential_result + +- assert: + that: + - credential_result is successful + +################################################################################## +# Tests for missing parameters + +- name: 'Test with missing region' + example_module: + region: '{{ omit }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: missing_region + ignore_errors: True + +- assert: + that: + - missing_region is failed + - '"Either region or ec2_url must be specified" in missing_region.msg' + +- name: 'Test with missing access key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ omit }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: missing_access + ignore_errors: True + +- assert: + that: + - missing_access is failed + - '"No handler was ready to authenticate." in missing_access.msg' + #- '"aws_access_key_id" in missing_access.msg' + +- name: 'Test with missing secret key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ omit }}' + security_token: '{{ security_token }}' + register: missing_secret + ignore_errors: True + +- assert: + that: + - missing_secret is failed + - '"No handler was ready to authenticate." in missing_secret.msg' + #- '"aws_secret_access_key" in missing_secret.msg' + +- name: 'Test with missing security token' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ omit }}' + register: missing_token + ignore_errors: True + +- assert: + that: + - missing_token is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' + + +################################################################################## +# Tests for bad parameters + +- name: 'Test with bad region' + example_module: + region: 'junk-example' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_region + ignore_errors: True + +- assert: + that: + - bad_region is failed + - '"msg" in bad_region' + - '"does not seem to be available" in bad_region.msg' + - '"If the region definitely exists, you may need to upgrade boto or extend with endpoints_path" in bad_region.msg' + +- name: 'Test with bad access key' + example_module: + region: '{{ aws_region }}' + access_key: 'junk-example' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_access + ignore_errors: True + +- assert: + that: + - bad_access is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' + +- name: 'Test with bad secret key' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: 'junk-example' + security_token: '{{ security_token }}' + register: bad_secret + ignore_errors: True + +- assert: + that: + - bad_secret is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' + +- name: 'Test with bad security token' + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: 'junk-example' + register: bad_token + ignore_errors: True + +- assert: + that: + - bad_token is failed + # Caught when we try to do something, and passed to fail_json_aws + - '"Fail JSON AWS" in missing_token.msg' + - '"AWS was not able to validate the provided access credentials" in missing_token.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml new file mode 100644 index 00000000..a8a6ba20 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/endpoints.yml @@ -0,0 +1,119 @@ +--- +# Note 1: With boto3 we can use the FIPS endpoints as a minimal proxy for testing that +# we're using something different. With boto2 the authentication fails. +# +################################################################################## +# Tests using Endpoints + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint - no region (aws-parameters)' + example_module: + region: '{{ omit }}' + aws_endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + endpoint_url: 'https://ec2.{{ aws_region }}.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint (aws-parameters)' + example_module: + region: '{{ aws_region }}' + ec2_url: 'https://ec2.{{ aws_region }}.amazonaws.com' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +################################################################################## +# Tests using environment variables + +- name: 'Test basic operation using standard endpoint (aws-environment)' + example_module: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + environment: + AWS_URL: 'https://ec2.{{ aws_region }}.amazonaws.com' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +- name: 'Test basic operation using standard endpoint (ec2-environment)' + example_module: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + aws_security_token: '{{ security_token }}' + environment: + EC2_URL: 'https://ec2.{{ aws_region }}.amazonaws.com' + register: standard_endpoint_result + +- name: 'Check that we connected to the standard endpoint' + assert: + that: + - standard_endpoint_result is successful + #- '"ec2:DescribeImages" in standard_endpoint_result.resource_actions' + +################################################################################## +# Tests using a bad endpoint URL +# - This demonstrates that endpoint_url overrode region + +- name: 'Test with bad endpoint URL' + example_module: + region: '{{ aws_region }}' + endpoint_url: 'https://junk.{{ aws_region }}.amazonaws.com' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token }}' + register: bad_endpoint + ignore_errors: True + +- assert: + that: + - bad_endpoint is failed diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml new file mode 100644 index 00000000..9e81b308 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: 'Tests around standard credentials' + include_tasks: 'credentials.yml' + +- name: 'Tests around profiles' + include_tasks: 'profiles.yml' + +- name: 'Tests around endpoints' + include_tasks: 'endpoints.yml' + +#- name: 'Tests around CA Bundles' +# include_tasks: 'boto2_ec2/ca_bundle.yml' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml new file mode 100644 index 00000000..bab095e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/roles/ec2_connect/tasks/profiles.yml @@ -0,0 +1,63 @@ +--- +# Note: unlike boto3 modules, boto2 modules can't read region from the profile +# +################################################################################## +# Tests using profiles instead of directly consuming credentials + +- name: 'Test basic operation using profile (simple-parameters)' + example_module: + region: '{{ aws_region }}' + profile: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws-parameters)' + example_module: + region: '{{ aws_region }}' + aws_profile: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws-environment)' + example_module: + region: '{{ aws_region }}' + environment: + AWS_PROFILE: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +- name: 'Test basic operation using profile (aws2-environment)' + example_module: + region: '{{ aws_region }}' + environment: + AWS_DEFAULT_PROFILE: 'test_profile' + register: profile_result + +- assert: + that: + - profile_result is successful + +################################################################################## +# Tests with bad profile + +- name: 'Test with bad profile' + example_module: + region: '{{ aws_region }}' + profile: 'junk-profile' + register: bad_profile + ignore_errors: True + +- assert: + that: + - bad_profile is failed + - '"msg" in bad_profile' + - '"Profile given for AWS was not found." in bad_profile.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/runme.sh new file mode 100755 index 00000000..cbf09a54 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/runme.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -eux + +ANSIBLE_ROLES_PATH="../" +# Boto3 +AWS_CONFIG_FILE="$( pwd )/boto3_config" +# Boto2 +BOTO_CONFIG="$( pwd )/boto3_config" + +export ANSIBLE_ROLES_PATH +export AWS_CONFIG_FILE +export BOTO_CONFIG + +ansible-playbook setup.yml -i localhost "$@" +ansible-playbook ec2_connect.yml -i inventory "$@" -e "@session_credentials.yml" +ansible-playbook connect_to_aws.yml -i inventory "$@" -e "@session_credentials.yml" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/setup.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/setup.yml new file mode 100644 index 00000000..9b219eb2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/setup.yml @@ -0,0 +1,40 @@ +--- +- hosts: localhost + connection: local + gather_facts: no + tasks: + # =========================================================== + # While CI uses a dedicated session, the easiest way to run + # tests outside of CI is with a simple access/secret key pair. + # + # For consistency, use sts_session_token to grab session + # credentials if we're not already using a session + # Note: this can't be done within a session, hence the slightly + # strange dance + - name: 'Get a session token if we are using a basic key' + when: + - security_token is not defined + block: + - name: 'Get a session token' + sts_session_token: + region: '{{ aws_region }}' + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + register: session_token + no_log: true + - name: 'Override initial tokens' + set_fact: + session_access_key: '{{ session_token.sts_creds.access_key }}' + session_secret_key: '{{ session_token.sts_creds.secret_key }}' + session_security_token: '{{ session_token.sts_creds.session_token }}' + no_log: true + + - name: 'Write out credentials' + template: + dest: './session_credentials.yml' + src: 'session_credentials.yml.j2' + + - name: 'Write out boto config file' + template: + dest: './boto3_config' + src: 'boto_config.j2' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/templates/boto_config.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/templates/boto_config.j2 new file mode 100644 index 00000000..f8668f05 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/templates/boto_config.j2 @@ -0,0 +1,5 @@ +[profile test_profile] +region = {{ aws_region }} +aws_access_key_id = {{ session_access_key | default(aws_access_key) }} +aws_secret_access_key = {{ session_secret_key | default(aws_secret_key) }} +aws_security_token = {{ session_security_token | default(security_token) }} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2 b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2 new file mode 100644 index 00000000..bb030439 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_ec2/templates/session_credentials.yml.j2 @@ -0,0 +1,3 @@ +aws_access_key: {{ session_access_key | default(aws_access_key) }} +aws_secret_key: {{ session_secret_key | default(aws_secret_key) }} +security_token: {{ session_security_token | default(security_token) }} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/aliases new file mode 100644 index 00000000..6e3860be --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/inventory b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/inventory new file mode 100644 index 00000000..5093e858 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/inventory @@ -0,0 +1,6 @@ +[tests] +localhost + +[all:vars] +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/main.yml new file mode 100644 index 00000000..4edc3637 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/main.yml @@ -0,0 +1,7 @@ +- hosts: all + gather_facts: no + collections: + - amazon.aws + roles: + # Test the behaviour of module_utils.core.AnsibleAWSModule.client (boto3) + - 'get_waiter' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/meta/main.yml new file mode 100644 index 00000000..1f64f116 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/library/example_module.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/library/example_module.py new file mode 100644 index 00000000..4e16fb1b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/library/example_module.py @@ -0,0 +1,39 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# A bare-minimum Ansible Module based on AnsibleAWSModule used for testing some +# of the core behaviour around AWS/Boto3 connection details + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter + + +def main(): + argument_spec = dict( + client=dict(required=True, type='str'), + waiter_name=dict(required=True, type='str'), + with_decorator=dict(required=False, type='bool', default=False), + ) + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + decorator = None + if module.params.get('with_decorator'): + decorator = AWSRetry.jittered_backoff() + + client = module.client(module.params.get('client'), retry_decorator=decorator) + waiter = get_waiter(client, module.params.get('waiter_name')) + + module.exit_json(changed=False, waiter_attributes=dir(waiter)) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/meta/main.yml new file mode 100644 index 00000000..77589cc2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/meta/main.yml @@ -0,0 +1,5 @@ +dependencies: + - prepare_tests + - setup_ec2 +collections: + - amazon.aws diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/tasks/main.yml new file mode 100644 index 00000000..466d9584 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/roles/get_waiter/tasks/main.yml @@ -0,0 +1,36 @@ +--- +- module_defaults: + example_module: + region: '{{ aws_region }}' + access_key: '{{ aws_access_key }}' + secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + block: + - name: 'Attempt to get a waiter (no retry decorator)' + example_module: + client: 'ec2' + waiter_name: 'internet_gateway_exists' + register: test_no_decorator + + - assert: + that: + - test_no_decorator is succeeded + # Standard methods on a boto3 wrapper + - '"wait" in test_no_decorator.waiter_attributes' + - '"name" in test_no_decorator.waiter_attributes' + - '"config" in test_no_decorator.waiter_attributes' + + - name: 'Attempt to get a waiter (with decorator)' + example_module: + client: 'ec2' + waiter_name: 'internet_gateway_exists' + with_decorator: True + register: test_with_decorator + + - assert: + that: + - test_with_decorator is succeeded + # Standard methods on a boto3 wrapper + - '"wait" in test_with_decorator.waiter_attributes' + - '"name" in test_with_decorator.waiter_attributes' + - '"config" in test_with_decorator.waiter_attributes' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/runme.sh new file mode 100755 index 00000000..78a6f6db --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/module_utils_waiter/runme.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -eux + +ANSIBLE_ROLES_PATH="../" +export ANSIBLE_ROLES_PATH + +ansible-playbook main.yml -i inventory "$@" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/prepare_tests/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/prepare_tests/tasks/main.yml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/prepare_tests/tasks/main.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/aliases b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/aliases new file mode 100644 index 00000000..a112c3d1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group1 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/inventory b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/inventory new file mode 100644 index 00000000..59a2423a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/inventory @@ -0,0 +1,13 @@ +[tests] +missing +simple +complex +dotted +tags +encryption_kms +encryption_sse +public_access + +[all:vars] +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/main.yml new file mode 100644 index 00000000..22fc0d64 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/main.yml @@ -0,0 +1,12 @@ +--- +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/s3_bucket/tasks/ + +# VPC should get cleaned up once all hosts have run +- hosts: all + gather_facts: no + strategy: free + #serial: 10 + roles: + - s3_bucket diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/meta/main.yml new file mode 100644 index 00000000..38b31be0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - prepare_tests + - setup_ec2 + - setup_remote_tmp_dir diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/defaults/main.yml new file mode 100644 index 00000000..b4fd58ad --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/defaults/main.yml @@ -0,0 +1,2 @@ +--- +bucket_name: '{{ resource_prefix }}-{{ inventory_hostname | regex_replace("_","-") }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/meta/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/meta/main.yml new file mode 100644 index 00000000..38b31be0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - prepare_tests + - setup_ec2 + - setup_remote_tmp_dir diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/complex.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/complex.yml new file mode 100644 index 00000000..19736356 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/complex.yml @@ -0,0 +1,146 @@ +--- +- block: + - name: 'Create more complex s3_bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + policy: "{{ lookup('template','policy.json') }}" + requester_pays: yes + versioning: yes + tags: + example: tag1 + another: tag2 + register: output + + - assert: + that: + - output is changed + - output.name == '{{ bucket_name }}' + - output.requester_pays + - output.versioning.MfaDelete == 'Disabled' + - output.versioning.Versioning == 'Enabled' + - output.tags.example == 'tag1' + - output.tags.another == 'tag2' + - output.policy.Statement[0].Action == 's3:GetObject' + - output.policy.Statement[0].Effect == 'Allow' + - output.policy.Statement[0].Principal == '*' + - output.policy.Statement[0].Resource == 'arn:aws:s3:::{{ bucket_name }}/*' + - output.policy.Statement[0].Sid == 'AddPerm' + + # ============================================================ + + - name: 'Pause to help with s3 bucket eventual consistency' + wait_for: + timeout: 10 + delegate_to: localhost + + - name: 'Try to update the same complex s3_bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + policy: "{{ lookup('template','policy.json') }}" + requester_pays: yes + versioning: yes + tags: + example: tag1 + another: tag2 + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.requester_pays + - output.versioning.MfaDelete == 'Disabled' + - output.versioning.Versioning == 'Enabled' + - output.tags.example == 'tag1' + - output.tags.another == 'tag2' + - output.policy.Statement[0].Action == 's3:GetObject' + - output.policy.Statement[0].Effect == 'Allow' + - output.policy.Statement[0].Principal == '*' + - output.policy.Statement[0].Resource == 'arn:aws:s3:::{{ bucket_name }}/*' + - output.policy.Statement[0].Sid == 'AddPerm' + + # ============================================================ + - name: 'Update bucket policy on complex bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + policy: "{{ lookup('template','policy-updated.json') }}" + requester_pays: yes + versioning: yes + tags: + example: tag1 + another: tag2 + register: output + + - assert: + that: + - output is changed + - output.policy.Statement[0].Action == 's3:GetObject' + - output.policy.Statement[0].Effect == 'Deny' + - output.policy.Statement[0].Principal.AWS == '*' + - output.policy.Statement[0].Resource == 'arn:aws:s3:::{{ bucket_name }}/*' + - output.policy.Statement[0].Sid == 'AddPerm' + + # ============================================================ + + - name: 'Pause to help with s3 bucket eventual consistency' + wait_for: + timeout: 10 + delegate_to: localhost + + - name: Update attributes for s3_bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + policy: "{{ lookup('template','policy.json') }}" + requester_pays: no + versioning: no + tags: + example: tag1-udpated + another: tag2 + register: output + + - assert: + that: + - output is changed + - output.name == '{{ bucket_name }}' + - not output.requester_pays + - output.versioning.MfaDelete == 'Disabled' + - output.versioning.Versioning in ['Suspended', 'Disabled'] + - output.tags.example == 'tag1-udpated' + - output.tags.another == 'tag2' + - output.policy.Statement[0].Action == 's3:GetObject' + - output.policy.Statement[0].Effect == 'Allow' + - output.policy.Statement[0].Principal == '*' + - output.policy.Statement[0].Resource == 'arn:aws:s3:::{{ bucket_name }}/*' + - output.policy.Statement[0].Sid == 'AddPerm' + + - name: 'Delete complex test bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is changed + + - name: 'Re-delete complex test bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is not changed + + # ============================================================ + always: + - name: 'Ensure all buckets are deleted' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/dotted.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/dotted.yml new file mode 100644 index 00000000..7d4e0ae9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/dotted.yml @@ -0,0 +1,54 @@ +--- +- block: + - name: 'Ensure bucket_name contains a .' + set_fact: + bucket_name: '{{ bucket_name }}.something' + + # ============================================================ + # + - name: 'Create bucket with dot in name' + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - assert: + that: + - output is changed + - output.name == '{{ bucket_name }}' + + + # ============================================================ + + - name: 'Pause to help with s3 bucket eventual consistency' + wait_for: + timeout: 10 + delegate_to: localhost + + - name: 'Delete s3_bucket with dot in name' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is changed + + - name: 'Re-delete s3_bucket with dot in name' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is not changed + + # ============================================================ + always: + - name: 'Ensure all buckets are deleted' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_kms.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_kms.yml new file mode 100644 index 00000000..869dd402 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_kms.yml @@ -0,0 +1,88 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + # ============================================================ + + - name: 'Create a simple bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - name: 'Enable aws:kms encryption with KMS master key' + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: "aws:kms" + register: output + + - assert: + that: + - output.changed + - output.encryption + - output.encryption.SSEAlgorithm == 'aws:kms' + + - name: 'Re-enable aws:kms encryption with KMS master key (idempotent)' + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: "aws:kms" + register: output + + - assert: + that: + - not output.changed + - output.encryption + - output.encryption.SSEAlgorithm == 'aws:kms' + + # ============================================================ + + - name: Disable encryption from bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: "none" + register: output + + - assert: + that: + - output.changed + - not output.encryption + + - name: Disable encryption from bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: "none" + register: output + + - assert: + that: + - output is not changed + - not output.encryption + + # ============================================================ + + - name: Delete encryption test s3 bucket + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output.changed + + # ============================================================ + always: + - name: Ensure all buckets are deleted + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_sse.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_sse.yml new file mode 100644 index 00000000..699e8ae4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/encryption_sse.yml @@ -0,0 +1,88 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + # ============================================================ + + - name: 'Create a simple bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - name: 'Enable AES256 encryption' + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: 'AES256' + register: output + + - assert: + that: + - output.changed + - output.encryption + - output.encryption.SSEAlgorithm == 'AES256' + + - name: 'Re-enable AES256 encryption (idempotency)' + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: 'AES256' + register: output + + - assert: + that: + - not output.changed + - output.encryption + - output.encryption.SSEAlgorithm == 'AES256' + + # ============================================================ + + - name: Disable encryption from bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: "none" + register: output + + - assert: + that: + - output.changed + - not output.encryption + + - name: Disable encryption from bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + encryption: "none" + register: output + + - assert: + that: + - output is not changed + - not output.encryption + + # ============================================================ + + - name: Delete encryption test s3 bucket + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output.changed + + # ============================================================ + always: + - name: Ensure all buckets are deleted + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/main.yml new file mode 100644 index 00000000..8eba03ba --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/main.yml @@ -0,0 +1,20 @@ +--- +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/ec2_roles/tasks/ +# +# ############################################################################### + +- name: "Wrap up all tests and setup AWS credentials" + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + - debug: + msg: "{{ inventory_hostname }} start: {{ lookup('pipe','date') }}" + - include_tasks: '{{ inventory_hostname }}.yml' + - debug: + msg: "{{ inventory_hostname }} finish: {{ lookup('pipe','date') }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/missing.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/missing.yml new file mode 100644 index 00000000..4d827680 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/missing.yml @@ -0,0 +1,26 @@ +--- +- name: 'Attempt to delete non-existent buckets' + block: + # ============================================================ + # + # While in theory the 'simple' test case covers this there are + # ways in which eventual-consistency could catch us out. + # + - name: 'Delete non-existstent s3_bucket (never created)' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is success + - output is not changed + + # ============================================================ + always: + - name: 'Ensure all buckets are deleted' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/public_access.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/public_access.yml new file mode 100644 index 00000000..f7bc1984 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/public_access.yml @@ -0,0 +1,114 @@ +--- +- module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + block: + + # ============================================================ + + - name: 'Create a simple bucket with public access block configuration' + s3_bucket: + name: '{{ bucket_name }}' + state: present + public_access: + block_public_acls: true + block_public_policy: true + ignore_public_acls: true + restrict_public_buckets: true + register: output + + - assert: + that: + - output.changed + - output.public_access_block + - output.public_access_block.BlockPublicAcls + - output.public_access_block.BlockPublicPolicy + - output.public_access_block.IgnorePublicAcls + - output.public_access_block.RestrictPublicBuckets + + - name: 'Re-configure public access block configuration' + s3_bucket: + name: '{{ bucket_name }}' + state: present + public_access: + block_public_acls: true + block_public_policy: false + ignore_public_acls: true + restrict_public_buckets: false + register: output + + - assert: + that: + - output.changed + - output.public_access_block + - output.public_access_block.BlockPublicAcls + - not output.public_access_block.BlockPublicPolicy + - output.public_access_block.IgnorePublicAcls + - not output.public_access_block.RestrictPublicBuckets + + - name: 'Re-configure public access block configuration (idempotency)' + s3_bucket: + name: '{{ bucket_name }}' + state: present + public_access: + block_public_acls: true + block_public_policy: false + ignore_public_acls: true + restrict_public_buckets: false + register: output + + - assert: + that: + - output is not changed + - output.public_access_block + - output.public_access_block.BlockPublicAcls + - not output.public_access_block.BlockPublicPolicy + - output.public_access_block.IgnorePublicAcls + - not output.public_access_block.RestrictPublicBuckets + + - name: 'Delete public access block configuration' + s3_bucket: + name: '{{ bucket_name }}' + state: present + delete_public_access: true + register: output + + - assert: + that: + - output is changed + - not output.public_access_block|bool + + - name: 'Delete public access block configuration (idempotency)' + s3_bucket: + name: '{{ bucket_name }}' + state: present + delete_public_access: true + register: output + + - assert: + that: + - output is not changed + - not output.public_access_block|bool + + # ============================================================ + + - name: Delete testing s3 bucket + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output.changed + + # ============================================================ + always: + - name: Ensure all buckets are deleted + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/simple.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/simple.yml new file mode 100644 index 00000000..5b445bd5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/simple.yml @@ -0,0 +1,65 @@ +--- +- name: 'Run simple tests' + block: + # Note: s3_bucket doesn't support check_mode + + # ============================================================ + - name: 'Create a simple s3_bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - assert: + that: + - output is success + - output is changed + - output.name == '{{ bucket_name }}' + - not output.requester_pays + - output.public_access is undefined + + # ============================================================ + - name: 'Try to update the simple bucket with the same values' + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - assert: + that: + - output is success + - output is not changed + - output.name == '{{ bucket_name }}' + - not output.requester_pays + + # ============================================================ + - name: 'Delete the simple s3_bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is success + - output is changed + + # ============================================================ + - name: 'Re-delete the simple s3_bucket (idempotency)' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output is success + - output is not changed + + # ============================================================ + always: + - name: 'Ensure all buckets are deleted' + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/tags.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/tags.yml new file mode 100644 index 00000000..437dd2ca --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/tasks/tags.yml @@ -0,0 +1,256 @@ +--- +- name: 'Run tagging tests' + block: + + # ============================================================ + - name: 'Create simple s3_bucket for testing tagging' + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - assert: + that: + - output.changed + - output.name == '{{ bucket_name }}' + + # ============================================================ + + - name: 'Add tags to s3 bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + tags: + example: tag1 + another: tag2 + register: output + + - assert: + that: + - output.changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.another == 'tag2' + + - name: 'Re-Add tags to s3 bucket' + s3_bucket: + name: '{{ bucket_name }}' + state: present + tags: + example: tag1 + another: tag2 + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.another == 'tag2' + + # ============================================================ + + - name: Remove a tag from an s3_bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + tags: + example: tag1 + register: output + + - assert: + that: + - output.changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - "'another' not in output.tags" + + - name: Re-remove the tag from an s3_bucket + s3_bucket: + name: '{{ bucket_name }}' + state: present + tags: + example: tag1 + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - "'another' not in output.tags" + + ## ============================================================ + + #- name: 'Pause to help with s3 bucket eventual consistency' + # wait_for: + # timeout: 10 + # delegate_to: localhost + + ## ============================================================ + + - name: 'Add a tag for s3_bucket with purge_tags False' + s3_bucket: + name: '{{ bucket_name }}' + state: present + purge_tags: no + tags: + anewtag: here + register: output + + - assert: + that: + - output.changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.anewtag == 'here' + + - name: 'Re-add a tag for s3_bucket with purge_tags False' + s3_bucket: + name: '{{ bucket_name }}' + state: present + purge_tags: no + tags: + anewtag: here + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.anewtag == 'here' + + ## ============================================================ + + #- name: 'Pause to help with s3 bucket eventual consistency' + # wait_for: + # timeout: 10 + # delegate_to: localhost + + ## ============================================================ + + - name: Update a tag for s3_bucket with purge_tags False + s3_bucket: + name: '{{ bucket_name }}' + state: present + purge_tags: no + tags: + anewtag: next + register: output + + - assert: + that: + - output.changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.anewtag == 'next' + + - name: Re-update a tag for s3_bucket with purge_tags False + s3_bucket: + name: '{{ bucket_name }}' + state: present + purge_tags: no + tags: + anewtag: next + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.anewtag == 'next' + + ## ============================================================ + + #- name: 'Pause to help with s3 bucket eventual consistency' + # wait_for: + # timeout: 10 + # delegate_to: localhost + + ## ============================================================ + + - name: Pass empty tags dict for s3_bucket with purge_tags False + s3_bucket: + name: '{{ bucket_name }}' + state: present + purge_tags: no + tags: {} + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + - output.tags.anewtag == 'next' + + ## ============================================================ + + #- name: 'Pause to help with s3 bucket eventual consistency' + # wait_for: + # timeout: 10 + # delegate_to: localhost + + ## ============================================================ + + - name: Do not specify any tag to ensure previous tags are not removed + s3_bucket: + name: '{{ bucket_name }}' + state: present + register: output + + - assert: + that: + - not output.changed + - output.name == '{{ bucket_name }}' + - output.tags.example == 'tag1' + + # ============================================================ + + - name: Remove all tags + s3_bucket: + name: '{{ bucket_name }}' + state: present + tags: {} + register: output + + - assert: + that: + - output.changed + - output.name == '{{ bucket_name }}' + - output.tags == {} + + - name: Re-remove all tags + s3_bucket: + name: '{{ bucket_name }}' + state: present + tags: {} + register: output + + - assert: + that: + - output is not changed + - output.name == '{{ bucket_name }}' + - output.tags == {} + + # ============================================================ + + - name: Delete bucket + s3_bucket: + name: '{{ bucket_name }}' + state: absent + register: output + + - assert: + that: + - output.changed + + # ============================================================ + always: + - name: Ensure all buckets are deleted + s3_bucket: + name: '{{ bucket_name }}' + state: absent + ignore_errors: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy-updated.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy-updated.json new file mode 100644 index 00000000..23aec6fb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy-updated.json @@ -0,0 +1,12 @@ +{ + "Version":"2012-10-17", + "Statement":[ + { + "Sid":"AddPerm", + "Effect":"Deny", + "Principal": {"AWS": "*"}, + "Action":["s3:GetObject"], + "Resource":["arn:aws:s3:::{{bucket_name}}/*"] + } + ] +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy.json new file mode 100644 index 00000000..a2720aed --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/roles/s3_bucket/templates/policy.json @@ -0,0 +1,12 @@ +{ + "Version":"2012-10-17", + "Statement":[ + { + "Sid":"AddPerm", + "Effect":"Allow", + "Principal": "*", + "Action":["s3:GetObject"], + "Resource":["arn:aws:s3:::{{bucket_name}}/*"] + } + ] +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/runme.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/runme.sh new file mode 100755 index 00000000..aa324772 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/s3_bucket/runme.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/ec2_instance/tasks/ + + +set -eux + +export ANSIBLE_ROLES_PATH=../ + +ansible-playbook main.yml -i inventory "$@" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/defaults/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/defaults/main.yml new file mode 100644 index 00000000..fb1f88b1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/defaults/main.yml @@ -0,0 +1,2 @@ +--- +resource_prefix: 'ansible-testing-' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/tasks/common.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/tasks/common.yml new file mode 100644 index 00000000..bf23f539 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/tasks/common.yml @@ -0,0 +1,119 @@ +--- + +# ============================================================ +- name: test with no parameters + action: "{{module_name}}" + register: result + ignore_errors: true + +- name: assert failure when called with no parameters + assert: + that: + - 'result.failed' + - 'result.msg == "missing required arguments: name"' + +# ============================================================ +- name: test with only name + action: "{{module_name}} name={{ec2_key_name}}" + register: result + ignore_errors: true + +- name: assert failure when called with only 'name' + assert: + that: + - 'result.failed' + - 'result.msg == "Either region or ec2_url must be specified"' + +# ============================================================ +- name: test invalid region parameter + action: "{{module_name}} name='{{ec2_key_name}}' region='asdf querty 1234'" + register: result + ignore_errors: true + +- name: assert invalid region parameter + assert: + that: + - 'result.failed' + - 'result.msg.startswith("value of region must be one of:")' + +# ============================================================ +- name: test valid region parameter + action: "{{module_name}} name='{{ec2_key_name}}' region='{{ec2_region}}'" + register: result + ignore_errors: true + +- name: assert valid region parameter + assert: + that: + - 'result.failed' + - 'result.msg.startswith("No handler was ready to authenticate.")' + +# ============================================================ +- name: test environment variable EC2_REGION + action: "{{module_name}} name='{{ec2_key_name}}'" + environment: + EC2_REGION: '{{ec2_region}}' + register: result + ignore_errors: true + +- name: assert environment variable EC2_REGION + assert: + that: + - 'result.failed' + - 'result.msg.startswith("No handler was ready to authenticate.")' + +# ============================================================ +- name: test invalid ec2_url parameter + action: "{{module_name}} name='{{ec2_key_name}}'" + environment: + EC2_URL: bogus.example.com + register: result + ignore_errors: true + +- name: assert invalid ec2_url parameter + assert: + that: + - 'result.failed' + - 'result.msg.startswith("No handler was ready to authenticate.")' + +# ============================================================ +- name: test valid ec2_url parameter + action: "{{module_name}} name='{{ec2_key_name}}'" + environment: + EC2_URL: '{{ec2_url}}' + register: result + ignore_errors: true + +- name: assert valid ec2_url parameter + assert: + that: + - 'result.failed' + - 'result.msg.startswith("No handler was ready to authenticate.")' + +# ============================================================ +- name: test credentials from environment + action: "{{module_name}} name='{{ec2_key_name}}'" + environment: + EC2_REGION: '{{ec2_region}}' + EC2_ACCESS_KEY: bogus_access_key + EC2_SECRET_KEY: bogus_secret_key + register: result + ignore_errors: true + +- name: assert ec2_key with valid ec2_url + assert: + that: + - 'result.failed' + - '"EC2ResponseError: 401 Unauthorized" in result.msg' + +# ============================================================ +- name: test credential parameters + action: "{{module_name}} name='{{ec2_key_name}}' ec2_region='{{ec2_region}}' ec2_access_key=bogus_access_key ec2_secret_key=bogus_secret_key" + register: result + ignore_errors: true + +- name: assert credential parameters + assert: + that: + - 'result.failed' + - '"EC2ResponseError: 401 Unauthorized" in result.msg' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/vars/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/vars/main.yml new file mode 100644 index 00000000..3d7209ef --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_ec2/vars/main.yml @@ -0,0 +1,3 @@ +--- +ec2_url: ec2.amazonaws.com +ec2_region: us-east-1 diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml new file mode 100644 index 00000000..229037c8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml @@ -0,0 +1,5 @@ +- name: delete temporary directory + include_tasks: default-cleanup.yml + +- name: delete temporary directory (windows) + include_tasks: windows-cleanup.yml diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml new file mode 100644 index 00000000..39872d74 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml @@ -0,0 +1,5 @@ +- name: delete temporary directory + file: + path: "{{ remote_tmp_dir }}" + state: absent + no_log: yes diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml new file mode 100644 index 00000000..1e0f51b8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml @@ -0,0 +1,11 @@ +- name: create temporary directory + tempfile: + state: directory + suffix: .test + register: remote_tmp_dir + notify: + - delete temporary directory + +- name: record temporary directory + set_fact: + remote_tmp_dir: "{{ remote_tmp_dir.path }}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml new file mode 100644 index 00000000..f8df391b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml @@ -0,0 +1,10 @@ +- name: make sure we have the ansible_os_family and ansible_distribution_version facts + setup: + gather_subset: distribution + when: ansible_facts == {} + +- include_tasks: "{{ lookup('first_found', files)}}" + vars: + files: + - "{{ ansible_os_family | lower }}.yml" + - "default.yml" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml new file mode 100644 index 00000000..32f372d0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows-cleanup.yml @@ -0,0 +1,4 @@ +- name: delete temporary directory (windows) + ansible.windows.win_file: + path: '{{ remote_tmp_dir }}' + state: absent diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml new file mode 100644 index 00000000..317c146d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_remote_tmp_dir/tasks/windows.yml @@ -0,0 +1,10 @@ +- name: create temporary directory + register: remote_tmp_dir + notify: + - delete temporary directory (windows) + ansible.windows.win_tempfile: + state: directory + suffix: .test +- name: record temporary directory + set_fact: + remote_tmp_dir: '{{ remote_tmp_dir.path }}' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_sshkey/tasks/main.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_sshkey/tasks/main.yml new file mode 100644 index 00000000..18c571b6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/integration/targets/setup_sshkey/tasks/main.yml @@ -0,0 +1,55 @@ +# (c) 2014, James Laska <jlaska@ansible.com> + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +- name: create a temp file + tempfile: + state: file + register: sshkey_file + tags: + - prepare + +- name: generate sshkey + shell: echo 'y' | ssh-keygen -P '' -f {{ sshkey_file.path }} + tags: + - prepare + +- name: create another temp file + tempfile: + state: file + register: another_sshkey_file + tags: + - prepare + +- name: generate another_sshkey + shell: echo 'y' | ssh-keygen -P '' -f {{ another_sshkey_file.path }} + tags: + - prepare + +- name: record fingerprint + shell: openssl rsa -in {{ sshkey_file.path }} -pubout -outform DER 2>/dev/null | openssl md5 -c + register: fingerprint + tags: + - prepare + +- name: set facts for future roles + set_fact: + sshkey: '{{ sshkey_file.path }}' + key_material: "{{ lookup('file', sshkey_file.path ~ '.pub') }}" + another_key_material: "{{ lookup('file', another_sshkey_file.path ~ '.pub') }}" + fingerprint: '{{ fingerprint.stdout.split()[1] }}' + tags: + - prepare diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/requirements.yml b/collections-debian-merged/ansible_collections/amazon/aws/tests/requirements.yml new file mode 100644 index 00000000..3e967b19 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/requirements.yml @@ -0,0 +1,4 @@ +integration_tests_dependencies: +- ansible.windows +- community.general +unit_tests_dependencies: [] diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.10.txt b/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.10.txt new file mode 100644 index 00000000..6536b1ab --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.10.txt @@ -0,0 +1,4 @@ +plugins/modules/ec2_tag.py validate-modules:parameter-state-invalid-choice +plugins/modules/ec2_vol.py validate-modules:parameter-state-invalid-choice +tests/utils/shippable/check_matrix.py replace-urlopen +tests/utils/shippable/timing.py shebang
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.11.txt b/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.11.txt new file mode 100644 index 00000000..6536b1ab --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.11.txt @@ -0,0 +1,4 @@ +plugins/modules/ec2_tag.py validate-modules:parameter-state-invalid-choice +plugins/modules/ec2_vol.py validate-modules:parameter-state-invalid-choice +tests/utils/shippable/check_matrix.py replace-urlopen +tests/utils/shippable/timing.py shebang
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.9.txt b/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.9.txt new file mode 100644 index 00000000..b7cfe373 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/sanity/ignore-2.9.txt @@ -0,0 +1,17 @@ +plugins/modules/aws_az_info.py pylint:ansible-deprecated-no-version +plugins/modules/aws_caller_info.py pylint:ansible-deprecated-no-version +plugins/modules/cloudformation_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_ami_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_eni_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_group_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_snapshot_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_tag.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_vol.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_vol_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_vpc_dhcp_option_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_vpc_net_info.py pylint:ansible-deprecated-no-version +plugins/modules/ec2_vpc_subnet_info.py pylint:ansible-deprecated-no-version +plugins/module_utils/ec2.py pylint:ansible-deprecated-no-version +tests/utils/shippable/check_matrix.py replace-urlopen +tests/utils/shippable/timing.py shebang diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/builtins.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/builtins.py new file mode 100644 index 00000000..349d310e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/builtins.py @@ -0,0 +1,33 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +# +# Compat for python2.7 +# + +# One unittest needs to import builtins via __import__() so we need to have +# the string that represents it +try: + import __builtin__ # pylint: disable=unused-import +except ImportError: + BUILTINS = 'builtins' +else: + BUILTINS = '__builtin__' diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/mock.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/mock.py new file mode 100644 index 00000000..0972cd2e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/mock.py @@ -0,0 +1,122 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python3.x's unittest.mock module +''' +import sys + +# Python 2.7 + +# Note: Could use the pypi mock library on python3.x as well as python2.x. It +# is the same as the python3 stdlib mock library + +try: + # Allow wildcard import because we really do want to import all of mock's + # symbols into this compat shim + # pylint: disable=wildcard-import,unused-wildcard-import + from unittest.mock import * +except ImportError: + # Python 2 + # pylint: disable=wildcard-import,unused-wildcard-import + try: + from mock import * + except ImportError: + print('You need the mock library installed on python2.x to run tests') + + +# Prior to 3.4.4, mock_open cannot handle binary read_data +if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): + file_spec = None + + def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] + else: + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + + for line in data_as_list: + yield line + + def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` methoddline`, and `readlines` of the + file handle to return. This is an empty string by default. + """ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return type(read_data)().join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + global file_spec + if file_spec is None: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + + mock.return_value = handle + return mock diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/unittest.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/unittest.py new file mode 100644 index 00000000..98f08ad6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/compat/unittest.py @@ -0,0 +1,38 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python2.7's unittest module +''' + +import sys + +# Allow wildcard import because we really do want to import all of +# unittests's symbols into this compat shim +# pylint: disable=wildcard-import,unused-wildcard-import +if sys.version_info < (2, 7): + try: + # Need unittest2 on python2.6 + from unittest2 import * + except ImportError: + print('You need unittest2 installed on python2.6.x to run tests') +else: + from unittest import * diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/loader.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/loader.py new file mode 100644 index 00000000..0ee47fbb --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/loader.py @@ -0,0 +1,116 @@ +# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +from ansible.errors import AnsibleParserError +from ansible.parsing.dataloader import DataLoader +from ansible.module_utils._text import to_bytes, to_text + + +class DictDataLoader(DataLoader): + + def __init__(self, file_mapping=None): + file_mapping = {} if file_mapping is None else file_mapping + assert type(file_mapping) == dict + + super(DictDataLoader, self).__init__() + + self._file_mapping = file_mapping + self._build_known_directories() + self._vault_secrets = None + + def load_from_file(self, path, cache=True, unsafe=False): + path = to_text(path) + if path in self._file_mapping: + return self.load(self._file_mapping[path], path) + return None + + # TODO: the real _get_file_contents returns a bytestring, so we actually convert the + # unicode/text it's created with to utf-8 + def _get_file_contents(self, path): + path = to_text(path) + if path in self._file_mapping: + return (to_bytes(self._file_mapping[path]), False) + else: + raise AnsibleParserError("file not found: %s" % path) + + def path_exists(self, path): + path = to_text(path) + return path in self._file_mapping or path in self._known_directories + + def is_file(self, path): + path = to_text(path) + return path in self._file_mapping + + def is_directory(self, path): + path = to_text(path) + return path in self._known_directories + + def list_directory(self, path): + ret = [] + path = to_text(path) + for x in (list(self._file_mapping.keys()) + self._known_directories): + if x.startswith(path): + if os.path.dirname(x) == path: + ret.append(os.path.basename(x)) + return ret + + def is_executable(self, path): + # FIXME: figure out a way to make paths return true for this + return False + + def _add_known_directory(self, directory): + if directory not in self._known_directories: + self._known_directories.append(directory) + + def _build_known_directories(self): + self._known_directories = [] + for path in self._file_mapping: + dirname = os.path.dirname(path) + while dirname not in ('/', ''): + self._add_known_directory(dirname) + dirname = os.path.dirname(dirname) + + def push(self, path, content): + rebuild_dirs = False + if path not in self._file_mapping: + rebuild_dirs = True + + self._file_mapping[path] = content + + if rebuild_dirs: + self._build_known_directories() + + def pop(self, path): + if path in self._file_mapping: + del self._file_mapping[path] + self._build_known_directories() + + def clear(self): + self._file_mapping = dict() + self._known_directories = [] + + def get_basedir(self): + return os.getcwd() + + def set_vault_secrets(self, vault_secrets): + self._vault_secrets = vault_secrets diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/path.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/path.py new file mode 100644 index 00000000..8de2aec2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/path.py @@ -0,0 +1,8 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.amazon.aws.tests.unit.compat.mock import MagicMock +from ansible.utils.path import unfrackpath + + +mock_unfrackpath_noop = MagicMock(spec_set=unfrackpath, side_effect=lambda x, *args, **kwargs: x) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/procenv.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/procenv.py new file mode 100644 index 00000000..273959e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/procenv.py @@ -0,0 +1,90 @@ +# (c) 2016, Matt Davis <mdavis@ansible.com> +# (c) 2016, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys +import json + +from contextlib import contextmanager +from io import BytesIO, StringIO +from ansible_collections.amazon.aws.tests.unit.compat import unittest +from ansible.module_utils.six import PY3 +from ansible.module_utils._text import to_bytes + + +@contextmanager +def swap_stdin_and_argv(stdin_data='', argv_data=tuple()): + """ + context manager that temporarily masks the test runner's values for stdin and argv + """ + real_stdin = sys.stdin + real_argv = sys.argv + + if PY3: + fake_stream = StringIO(stdin_data) + fake_stream.buffer = BytesIO(to_bytes(stdin_data)) + else: + fake_stream = BytesIO(to_bytes(stdin_data)) + + try: + sys.stdin = fake_stream + sys.argv = argv_data + + yield + finally: + sys.stdin = real_stdin + sys.argv = real_argv + + +@contextmanager +def swap_stdout(): + """ + context manager that temporarily replaces stdout for tests that need to verify output + """ + old_stdout = sys.stdout + + if PY3: + fake_stream = StringIO() + else: + fake_stream = BytesIO() + + try: + sys.stdout = fake_stream + + yield fake_stream + finally: + sys.stdout = old_stdout + + +class ModuleTestCase(unittest.TestCase): + def setUp(self, module_args=None): + if module_args is None: + module_args = {'_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False} + + args = json.dumps(dict(ANSIBLE_MODULE_ARGS=module_args)) + + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap = swap_stdin_and_argv(stdin_data=args) + self.stdin_swap.__enter__() + + def tearDown(self): + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap.__exit__(None, None, None) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/vault_helper.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/vault_helper.py new file mode 100644 index 00000000..dcce9c78 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/vault_helper.py @@ -0,0 +1,39 @@ +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.module_utils._text import to_bytes + +from ansible.parsing.vault import VaultSecret + + +class TextVaultSecret(VaultSecret): + '''A secret piece of text. ie, a password. Tracks text encoding. + + The text encoding of the text may not be the default text encoding so + we keep track of the encoding so we encode it to the same bytes.''' + + def __init__(self, text, encoding=None, errors=None, _bytes=None): + super(TextVaultSecret, self).__init__() + self.text = text + self.encoding = encoding or 'utf-8' + self._bytes = _bytes + self.errors = errors or 'strict' + + @property + def bytes(self): + '''The text encoded with encoding, unless we specifically set _bytes.''' + return self._bytes or to_bytes(self.text, encoding=self.encoding, errors=self.errors) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/yaml_helper.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/yaml_helper.py new file mode 100644 index 00000000..1ef17215 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/mock/yaml_helper.py @@ -0,0 +1,124 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import io +import yaml + +from ansible.module_utils.six import PY3 +from ansible.parsing.yaml.loader import AnsibleLoader +from ansible.parsing.yaml.dumper import AnsibleDumper + + +class YamlTestUtils(object): + """Mixin class to combine with a unittest.TestCase subclass.""" + def _loader(self, stream): + """Vault related tests will want to override this. + + Vault cases should setup a AnsibleLoader that has the vault password.""" + return AnsibleLoader(stream) + + def _dump_stream(self, obj, stream, dumper=None): + """Dump to a py2-unicode or py3-string stream.""" + if PY3: + return yaml.dump(obj, stream, Dumper=dumper) + else: + return yaml.dump(obj, stream, Dumper=dumper, encoding=None) + + def _dump_string(self, obj, dumper=None): + """Dump to a py2-unicode or py3-string""" + if PY3: + return yaml.dump(obj, Dumper=dumper) + else: + return yaml.dump(obj, Dumper=dumper, encoding=None) + + def _dump_load_cycle(self, obj): + # Each pass though a dump or load revs the 'generation' + # obj to yaml string + string_from_object_dump = self._dump_string(obj, dumper=AnsibleDumper) + + # wrap a stream/file like StringIO around that yaml + stream_from_object_dump = io.StringIO(string_from_object_dump) + loader = self._loader(stream_from_object_dump) + # load the yaml stream to create a new instance of the object (gen 2) + obj_2 = loader.get_data() + + # dump the gen 2 objects directory to strings + string_from_object_dump_2 = self._dump_string(obj_2, + dumper=AnsibleDumper) + + # The gen 1 and gen 2 yaml strings + self.assertEqual(string_from_object_dump, string_from_object_dump_2) + # the gen 1 (orig) and gen 2 py object + self.assertEqual(obj, obj_2) + + # again! gen 3... load strings into py objects + stream_3 = io.StringIO(string_from_object_dump_2) + loader_3 = self._loader(stream_3) + obj_3 = loader_3.get_data() + + string_from_object_dump_3 = self._dump_string(obj_3, dumper=AnsibleDumper) + + self.assertEqual(obj, obj_3) + # should be transitive, but... + self.assertEqual(obj_2, obj_3) + self.assertEqual(string_from_object_dump, string_from_object_dump_3) + + def _old_dump_load_cycle(self, obj): + '''Dump the passed in object to yaml, load it back up, dump again, compare.''' + stream = io.StringIO() + + yaml_string = self._dump_string(obj, dumper=AnsibleDumper) + self._dump_stream(obj, stream, dumper=AnsibleDumper) + + yaml_string_from_stream = stream.getvalue() + + # reset stream + stream.seek(0) + + loader = self._loader(stream) + # loader = AnsibleLoader(stream, vault_password=self.vault_password) + obj_from_stream = loader.get_data() + + stream_from_string = io.StringIO(yaml_string) + loader2 = self._loader(stream_from_string) + # loader2 = AnsibleLoader(stream_from_string, vault_password=self.vault_password) + obj_from_string = loader2.get_data() + + stream_obj_from_stream = io.StringIO() + stream_obj_from_string = io.StringIO() + + if PY3: + yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper) + yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper) + else: + yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper, encoding=None) + yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper, encoding=None) + + yaml_string_stream_obj_from_stream = stream_obj_from_stream.getvalue() + yaml_string_stream_obj_from_string = stream_obj_from_string.getvalue() + + stream_obj_from_stream.seek(0) + stream_obj_from_string.seek(0) + + if PY3: + yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper) + yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper) + else: + yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper, encoding=None) + yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper, encoding=None) + + assert yaml_string == yaml_string_obj_from_stream + assert yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + assert (yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string == yaml_string_stream_obj_from_stream == + yaml_string_stream_obj_from_string) + assert obj == obj_from_stream + assert obj == obj_from_string + assert obj == yaml_string_obj_from_stream + assert obj == yaml_string_obj_from_string + assert obj == obj_from_stream == obj_from_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + return {'obj': obj, + 'yaml_string': yaml_string, + 'yaml_string_from_stream': yaml_string_from_stream, + 'obj_from_stream': obj_from_stream, + 'obj_from_string': obj_from_string, + 'yaml_string_obj_from_string': yaml_string_obj_from_string} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/conftest.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/conftest.py new file mode 100644 index 00000000..8bc13c4d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/conftest.py @@ -0,0 +1,72 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import sys +from io import BytesIO + +import pytest + +import ansible.module_utils.basic +from ansible.module_utils.six import PY3, string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def stdin(mocker, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the stdin pytest fixture') + + fake_stdin = BytesIO(to_bytes(args, errors='surrogate_or_strict')) + if PY3: + mocker.patch('ansible.module_utils.basic.sys.stdin', mocker.MagicMock()) + mocker.patch('ansible.module_utils.basic.sys.stdin.buffer', fake_stdin) + else: + mocker.patch('ansible.module_utils.basic.sys.stdin', fake_stdin) + + yield fake_stdin + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv + + +@pytest.fixture +def am(stdin, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + argspec = {} + if hasattr(request, 'param'): + if isinstance(request.param, dict): + argspec = request.param + + am = ansible.module_utils.basic.AnsibleModule( + argument_spec=argspec, + ) + am._name = 'ansible_unittest' + + yield am + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/ansible_aws_module/test_fail_json_aws.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/ansible_aws_module/test_fail_json_aws.py new file mode 100644 index 00000000..c7e53afc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/ansible_aws_module/test_fail_json_aws.py @@ -0,0 +1,321 @@ +# (c) 2020 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +import botocore +import boto3 +import json + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule + + +class TestFailJsonAws(object): + # ======================================================== + # Prepare some data for use in our testing + # ======================================================== + def setup_method(self): + # Basic information that ClientError needs to spawn off an error + self.EXAMPLE_EXCEPTION_DATA = { + "Error": { + "Code": "InvalidParameterValue", + "Message": "The filter 'exampleFilter' is invalid" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef", + "HTTPStatusCode": 400, + "HTTPHeaders": { + "transfer-encoding": "chunked", + "date": "Fri, 13 Nov 2020 00:00:00 GMT", + "connection": "close", + "server": "AmazonEC2" + }, + "RetryAttempts": 0 + } + } + self.CAMEL_RESPONSE = camel_dict_to_snake_dict(self.EXAMPLE_EXCEPTION_DATA.get("ResponseMetadata")) + self.CAMEL_ERROR = camel_dict_to_snake_dict(self.EXAMPLE_EXCEPTION_DATA.get("Error")) + # ClientError(EXAMPLE_EXCEPTION_DATA, "testCall") will generate this + self.EXAMPLE_MSG = "An error occurred (InvalidParameterValue) when calling the testCall operation: The filter 'exampleFilter' is invalid" + self.DEFAULT_CORE_MSG = "An unspecified error occurred" + self.FAIL_MSG = "I Failed!" + + # ======================================================== + # Passing fail_json_aws nothing more than a ClientError + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_client_minimal(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall") + except botocore.exceptions.ClientError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.EXAMPLE_MSG + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert return_val.get("response_metadata") == self.CAMEL_RESPONSE + assert return_val.get("error") == self.CAMEL_ERROR + + # ======================================================== + # Passing fail_json_aws a ClientError and a message + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_client_msg(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall") + except botocore.exceptions.ClientError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, msg=self.FAIL_MSG) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.FAIL_MSG + ": " + self.EXAMPLE_MSG + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert return_val.get("response_metadata") == self.CAMEL_RESPONSE + assert return_val.get("error") == self.CAMEL_ERROR + + # ======================================================== + # Passing fail_json_aws a ClientError and a message as a positional argument + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_client_positional_msg(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall") + except botocore.exceptions.ClientError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, self.FAIL_MSG) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.FAIL_MSG + ": " + self.EXAMPLE_MSG + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert return_val.get("response_metadata") == self.CAMEL_RESPONSE + assert return_val.get("error") == self.CAMEL_ERROR + + # ======================================================== + # Passing fail_json_aws a ClientError and an arbitrary key + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_client_key(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall") + except botocore.exceptions.ClientError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, extra_key="Some Value") + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.EXAMPLE_MSG + assert return_val.get("extra_key") == "Some Value" + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert return_val.get("response_metadata") == self.CAMEL_RESPONSE + assert return_val.get("error") == self.CAMEL_ERROR + + # ======================================================== + # Passing fail_json_aws a ClientError, and arbitraty key and a message + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_client_msg_and_key(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall") + except botocore.exceptions.ClientError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, extra_key="Some Value", msg=self.FAIL_MSG) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.FAIL_MSG + ": " + self.EXAMPLE_MSG + assert return_val.get("extra_key") == "Some Value" + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert return_val.get("response_metadata") == self.CAMEL_RESPONSE + assert return_val.get("error") == self.CAMEL_ERROR + + # ======================================================== + # Passing fail_json_aws nothing more than a BotoCoreError + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_botocore_minimal(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.BotoCoreError() + except botocore.exceptions.BotoCoreError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.DEFAULT_CORE_MSG + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert "response_metadata" not in return_val + assert "error" not in return_val + + # ======================================================== + # Passing fail_json_aws BotoCoreError and a message + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_botocore_msg(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.BotoCoreError() + except botocore.exceptions.BotoCoreError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, msg=self.FAIL_MSG) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.FAIL_MSG + ": " + self.DEFAULT_CORE_MSG + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert "response_metadata" not in return_val + assert "error" not in return_val + + # ======================================================== + # Passing fail_json_aws BotoCoreError and a message as a positional + # argument + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_botocore_positional_msg(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.BotoCoreError() + except botocore.exceptions.BotoCoreError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, self.FAIL_MSG) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.FAIL_MSG + ": " + self.DEFAULT_CORE_MSG + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert "response_metadata" not in return_val + assert "error" not in return_val + + # ======================================================== + # Passing fail_json_aws a BotoCoreError and an arbitrary key + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_botocore_key(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.BotoCoreError() + except botocore.exceptions.BotoCoreError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, extra_key="Some Value") + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.DEFAULT_CORE_MSG + assert return_val.get("extra_key") == "Some Value" + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert "response_metadata" not in return_val + assert "error" not in return_val + + # ======================================================== + # Passing fail_json_aws BotoCoreError, an arbitry key and a message + # ======================================================== + @pytest.mark.parametrize("stdin", [{}], indirect=["stdin"]) + def test_fail_botocore_msg_and_key(self, monkeypatch, stdin, capfd): + monkeypatch.setattr(botocore, "__version__", "1.2.3") + monkeypatch.setattr(boto3, "__version__", "1.2.4") + + # Create a minimal module that we can call + module = AnsibleAWSModule(argument_spec=dict()) + try: + raise botocore.exceptions.BotoCoreError() + except botocore.exceptions.BotoCoreError as e: + with pytest.raises(SystemExit) as ctx: + module.fail_json_aws(e, extra_key="Some Value", msg=self.FAIL_MSG) + assert ctx.value.code == 1 + out, err = capfd.readouterr() + return_val = json.loads(out) + + assert return_val.get("msg") == self.FAIL_MSG + ": " + self.DEFAULT_CORE_MSG + assert return_val.get("extra_key") == "Some Value" + assert return_val.get("boto3_version") == "1.2.4" + assert return_val.get("botocore_version") == "1.2.3" + assert return_val.get("exception") is not None + assert return_val.get("failed") + assert "response_metadata" not in return_val + assert "error" not in return_val diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_is_boto3_error_code.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_is_boto3_error_code.py new file mode 100644 index 00000000..1b1a70e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_is_boto3_error_code.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- +# (c) 2020 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +import botocore + +from ansible_collections.amazon.aws.tests.unit.compat import unittest + +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 + +if not HAS_BOTO3: + pytestmark = pytest.mark.skip("test_iam.py requires the python modules 'boto3' and 'botocore'") + + +class Boto3ErrorCodeTestSuite(unittest.TestCase): + + def _make_denied_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "AccessDenied", + "Message": "User: arn:aws:iam::123456789012:user/ExampleUser " + + "is not authorized to perform: iam:GetUser on resource: user ExampleUser" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'getUser') + + def _make_unexpected_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "SomeThingWentWrong", + "Message": "Boom!" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'someCall') + + def _make_encoded_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "PermissionDenied", + "Message": "You are not authorized to perform this operation. Encoded authorization failure message: " + + "fEwXX6llx3cClm9J4pURgz1XPnJPrYexEbrJcLhFkwygMdOgx_-aEsj0LqRM6Kxt2HVI6prUhDwbJqBo9U2V7iRKZ" + + "T6ZdJvHH02cXmD0Jwl5vrTsf0PhBcWYlH5wl2qME7xTfdolEUr4CzumCiti7ETiO-RDdHqWlasBOW5bWsZ4GSpPdU" + + "06YAX0TfwVBs48uU5RpCHfz1uhSzez-3elbtp9CmTOHLt5pzJodiovccO55BQKYLPtmJcs6S9YLEEogmpI4Cb1D26" + + "fYahDh51jEmaohPnW5pb1nQe2yPEtuIhtRzNjhFCOOMwY5DBzNsymK-Gj6eJLm7FSGHee4AHLU_XmZMe_6bcLAiOx" + + "6Zdl65Kdd0hLcpwVxyZMi27HnYjAdqRlV3wuCW2PkhAW14qZQLfiuHZDEwnPe2PBGSlFcCmkQvJvX-YLoA7Uyc2wf" + + "NX5RJm38STwfiJSkQaNDhHKTWKiLOsgY4Gze6uZoG7zOcFXFRyaA4cbMmI76uyBO7j-9uQUCtBYqYto8x_9CUJcxI" + + "VC5SPG_C1mk-WoDMew01f0qy-bNaCgmJ9TOQGd08FyuT1SaMpCC0gX6mHuOnEgkFw3veBIowMpp9XcM-yc42fmIOp" + + "FOdvQO6uE9p55Qc-uXvsDTTvT3A7EeFU8a_YoAIt9UgNYM6VTvoprLz7dBI_P6C-bdPPZCY2amm-dJNVZelT6TbJB" + + "H_Vxh0fzeiSUBersy_QzB0moc-vPWgnB-IkgnYLV-4L3K0L2" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'someCall') + + def _make_botocore_exception(self): + return botocore.exceptions.EndpointConnectionError(endpoint_url='junk.endpoint') + + def setUp(self): + pass + + def test_is_boto3_error_code_single__raise__client(self): + thrown_exception = self._make_denied_exception() + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code('AccessDenied') as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'Code') + + def test_is_boto3_error_code_single__raise__unexpected(self): + thrown_exception = self._make_unexpected_exception() + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code('AccessDenied') as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'ClientError') + + def test_is_boto3_error_code_single__raise__botocore(self): + thrown_exception = self._make_botocore_exception() + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code('AccessDenied') as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'BotoCoreError') + + def test_is_boto3_error_code_multiple__raise__client(self): + thrown_exception = self._make_denied_exception() + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code(['NotAccessDenied', 'AccessDenied']) as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'Code') + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code(['AccessDenied', 'NotAccessDenied']) as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'Code') + + def test_is_boto3_error_code_multiple__raise__unexpected(self): + thrown_exception = self._make_unexpected_exception() + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code(['NotAccessDenied', 'AccessDenied']) as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'ClientError') + + def test_is_boto3_error_code_multiple__raise__botocore(self): + thrown_exception = self._make_botocore_exception() + caught_exception = None + try: + raise thrown_exception + except is_boto3_error_code(['NotAccessDenied', 'AccessDenied']) as e: + caught_exception = e + caught = 'Code' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'BotoCoreError') + + def test_is_boto3_error_code_single__pass__client(self): + passed_exception = self._make_denied_exception() + returned_exception = is_boto3_error_code('AccessDenied', e=passed_exception) + self.assertTrue(isinstance(passed_exception, returned_exception)) + self.assertTrue(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertNotEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_code_single__pass__unexpected(self): + passed_exception = self._make_unexpected_exception() + returned_exception = is_boto3_error_code('AccessDenied', e=passed_exception) + self.assertFalse(isinstance(passed_exception, returned_exception)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_code_single__pass__botocore(self): + passed_exception = self._make_botocore_exception() + returned_exception = is_boto3_error_code('AccessDenied', e=passed_exception) + self.assertFalse(isinstance(passed_exception, returned_exception)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_code_multiple__pass__client(self): + passed_exception = self._make_denied_exception() + returned_exception = is_boto3_error_code(['NotAccessDenied', 'AccessDenied'], e=passed_exception) + self.assertTrue(isinstance(passed_exception, returned_exception)) + self.assertTrue(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertNotEqual(returned_exception.__name__, "NeverEverRaisedException") + returned_exception = is_boto3_error_code(['AccessDenied', 'NotAccessDenied'], e=passed_exception) + self.assertTrue(isinstance(passed_exception, returned_exception)) + self.assertTrue(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertNotEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_code_multiple__pass__unexpected(self): + passed_exception = self._make_unexpected_exception() + returned_exception = is_boto3_error_code(['NotAccessDenied', 'AccessDenied'], e=passed_exception) + self.assertFalse(isinstance(passed_exception, returned_exception)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_code_multiple__pass__botocore(self): + passed_exception = self._make_botocore_exception() + returned_exception = is_boto3_error_code(['NotAccessDenied', 'AccessDenied'], e=passed_exception) + self.assertFalse(isinstance(passed_exception, returned_exception)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertEqual(returned_exception.__name__, "NeverEverRaisedException") diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_is_boto3_error_message.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_is_boto3_error_message.py new file mode 100644 index 00000000..550e89ef --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_is_boto3_error_message.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +# (c) 2020 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +import botocore + +from ansible_collections.amazon.aws.tests.unit.compat import unittest + +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 + +if not HAS_BOTO3: + pytestmark = pytest.mark.skip("test_iam.py requires the python modules 'boto3' and 'botocore'") + + +class Boto3ErrorTestSuite(unittest.TestCase): + + def _make_denied_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "AccessDenied", + "Message": "User: arn:aws:iam::123456789012:user/ExampleUser " + + "is not authorized to perform: iam:GetUser on resource: user ExampleUser" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'getUser') + + def _make_unexpected_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "SomeThingWentWrong", + "Message": "Boom!" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'someCall') + + def _make_encoded_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "AccessDenied", + "Message": "You are not authorized to perform this operation. Encoded authorization failure message: " + + "fEwXX6llx3cClm9J4pURgz1XPnJPrYexEbrJcLhFkwygMdOgx_-aEsj0LqRM6Kxt2HVI6prUhDwbJqBo9U2V7iRKZ" + + "T6ZdJvHH02cXmD0Jwl5vrTsf0PhBcWYlH5wl2qME7xTfdolEUr4CzumCiti7ETiO-RDdHqWlasBOW5bWsZ4GSpPdU" + + "06YAX0TfwVBs48uU5RpCHfz1uhSzez-3elbtp9CmTOHLt5pzJodiovccO55BQKYLPtmJcs6S9YLEEogmpI4Cb1D26" + + "fYahDh51jEmaohPnW5pb1nQe2yPEtuIhtRzNjhFCOOMwY5DBzNsymK-Gj6eJLm7FSGHee4AHLU_XmZMe_6bcLAiOx" + + "6Zdl65Kdd0hLcpwVxyZMi27HnYjAdqRlV3wuCW2PkhAW14qZQLfiuHZDEwnPe2PBGSlFcCmkQvJvX-YLoA7Uyc2wf" + + "NX5RJm38STwfiJSkQaNDhHKTWKiLOsgY4Gze6uZoG7zOcFXFRyaA4cbMmI76uyBO7j-9uQUCtBYqYto8x_9CUJcxI" + + "VC5SPG_C1mk-WoDMew01f0qy-bNaCgmJ9TOQGd08FyuT1SaMpCC0gX6mHuOnEgkFw3veBIowMpp9XcM-yc42fmIOp" + + "FOdvQO6uE9p55Qc-uXvsDTTvT3A7EeFU8a_YoAIt9UgNYM6VTvoprLz7dBI_P6C-bdPPZCY2amm-dJNVZelT6TbJB" + + "H_Vxh0fzeiSUBersy_QzB0moc-vPWgnB-IkgnYLV-4L3K0L2" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'someCall') + + def _make_botocore_exception(self): + return botocore.exceptions.EndpointConnectionError(endpoint_url='junk.endpoint') + + def setUp(self): + pass + + def test_is_boto3_error_message_single__raise__client(self): + caught_exception = None + thrown_exception = self._make_denied_exception() + # Test that we don't catch BotoCoreError + try: + raise thrown_exception + except is_boto3_error_message('is not authorized to perform') as e: + caught_exception = e + caught = 'Message' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'Message') + + def test_is_boto3_error_message_single__raise__unexpected(self): + caught_exception = None + thrown_exception = self._make_unexpected_exception() + # Test that we don't catch BotoCoreError + try: + raise thrown_exception + except is_boto3_error_message('is not authorized to perform') as e: + caught_exception = e + caught = 'Message' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'ClientError') + + def test_is_boto3_error_message_single__raise__botocore(self): + caught_exception = None + thrown_exception = self._make_botocore_exception() + # Test that we don't catch BotoCoreError + try: + raise thrown_exception + except is_boto3_error_message('is not authorized to perform') as e: + caught_exception = e + caught = 'Message' + except botocore.exceptions.ClientError as e: # pylint: disable=duplicate-except + caught_exception = e + caught = 'ClientError' + except botocore.exceptions.BotoCoreError as e: + caught_exception = e + caught = 'BotoCoreError' + except Exception as e: + caught_exception = e + caught = 'Exception' + self.assertEqual(caught_exception, thrown_exception) + self.assertEqual(caught, 'BotoCoreError') + + def test_is_boto3_error_message_single__pass__client(self): + passed_exception = self._make_denied_exception() + returned_exception = is_boto3_error_message('is not authorized to perform', e=passed_exception) + self.assertTrue(isinstance(passed_exception, returned_exception)) + self.assertTrue(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertNotEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_message_single__pass__unexpected(self): + passed_exception = self._make_unexpected_exception() + returned_exception = is_boto3_error_message('is not authorized to perform', e=passed_exception) + self.assertFalse(isinstance(passed_exception, returned_exception)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertEqual(returned_exception.__name__, "NeverEverRaisedException") + + def test_is_boto3_error_message_single__pass__botocore(self): + passed_exception = self._make_botocore_exception() + returned_exception = is_boto3_error_message('is not authorized to perform', e=passed_exception) + self.assertFalse(isinstance(passed_exception, returned_exception)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.ClientError)) + self.assertFalse(issubclass(returned_exception, botocore.exceptions.BotoCoreError)) + self.assertTrue(issubclass(returned_exception, Exception)) + self.assertEqual(returned_exception.__name__, "NeverEverRaisedException") diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_scrub_none_parameters.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_scrub_none_parameters.py new file mode 100644 index 00000000..084c3102 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/core/test_scrub_none_parameters.py @@ -0,0 +1,56 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.amazon.aws.plugins.module_utils.core import scrub_none_parameters +import pytest + +scrub_none_test_data = [ + (dict(), + dict() + ), + (dict(param1='something'), + dict(param1='something') + ), + (dict(param1=False), + dict(param1=False) + ), + (dict(param1='something', param2='something_else'), + dict(param1='something', param2='something_else') + ), + (dict(param1='something', param2=dict()), + dict(param1='something', param2=dict()) + ), + (dict(param1='something', param2=None), + dict(param1='something') + ), + (dict(param1='something', param2=None, param3=None), + dict(param1='something') + ), + (dict(param1='something', param2=None, param3=None, param4='something_else'), + dict(param1='something', param4='something_else') + ), + (dict(param1=dict(sub_param1='something', sub_param2=None), param2=None, param3=None, param4='something_else'), + dict(param1=dict(sub_param1='something'), param4='something_else') + ), + (dict(param1=dict(sub_param1='something', sub_param2=dict(sub_sub_param1='another_thing')), param2=None, param3=None, param4='something_else'), + dict(param1=dict(sub_param1='something', sub_param2=dict(sub_sub_param1='another_thing')), param4='something_else') + ), + (dict(param1=dict(sub_param1='something', sub_param2=dict()), param2=None, param3=None, param4='something_else'), + dict(param1=dict(sub_param1='something', sub_param2=dict()), param4='something_else') + ), + (dict(param1=dict(sub_param1='something', sub_param2=False), param2=None, param3=None, param4='something_else'), + dict(param1=dict(sub_param1='something', sub_param2=False), param4='something_else') + ), + (dict(param1=None, param2=None), + dict() + ), + (dict(param1=None, param2=[]), + dict(param2=[]) + ) +] + + +@pytest.mark.parametrize("input_params, output_params", scrub_none_test_data) +def test_scrub_none_parameters(input_params, output_params): + + assert scrub_none_parameters(input_params) == output_params diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/test_aws.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/test_aws.py new file mode 100644 index 00000000..91170b9e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/test_aws.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# (c) 2015, Allen Sanabria <asanabria@linuxdynasty.org> +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + # We explicitly want to know if boto3/botocore are available, they're used + # by the code we're testing even if we don't directly use them. + import boto3 # pylint: disable=unused-import + import botocore + HAS_BOTO3 = True +except Exception: + HAS_BOTO3 = False + +import pytest + +from ansible_collections.amazon.aws.tests.unit.compat import unittest +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + +if not HAS_BOTO3: + pytestmark = pytest.mark.skip("test_aws.py requires the python modules 'boto3' and 'botocore'") + + +class RetryTestCase(unittest.TestCase): + + def test_no_failures(self): + self.counter = 0 + + @AWSRetry.backoff(tries=2, delay=0.1) + def no_failures(): + self.counter += 1 + + r = no_failures() + self.assertEqual(self.counter, 1) + + def test_extend_boto3_failures(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'MalformedPolicyDocument'}} + + @AWSRetry.backoff(tries=2, delay=0.1, catch_extra_error_codes=['MalformedPolicyDocument']) + def extend_failures(): + self.counter += 1 + if self.counter < 2: + raise botocore.exceptions.ClientError(err_msg, 'You did something wrong.') + else: + return 'success' + + r = extend_failures() + self.assertEqual(r, 'success') + self.assertEqual(self.counter, 2) + + def test_retry_once(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'InternalFailure'}} + + @AWSRetry.backoff(tries=2, delay=0.1) + def retry_once(): + self.counter += 1 + if self.counter < 2: + raise botocore.exceptions.ClientError(err_msg, 'Something went wrong!') + else: + return 'success' + + r = retry_once() + self.assertEqual(r, 'success') + self.assertEqual(self.counter, 2) + + def test_reached_limit(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'RequestLimitExceeded'}} + + @AWSRetry.backoff(tries=4, delay=0.1) + def fail(): + self.counter += 1 + raise botocore.exceptions.ClientError(err_msg, 'toooo fast!!') + + # with self.assertRaises(botocore.exceptions.ClientError): + try: + fail() + except Exception as e: + self.assertEqual(e.response['Error']['Code'], 'RequestLimitExceeded') + self.assertEqual(self.counter, 4) + + def test_unexpected_exception_does_not_retry(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'AuthFailure'}} + + @AWSRetry.backoff(tries=4, delay=0.1) + def raise_unexpected_error(): + self.counter += 1 + raise botocore.exceptions.ClientError(err_msg, 'unexpected error') + + # with self.assertRaises(botocore.exceptions.ClientError): + try: + raise_unexpected_error() + except Exception as e: + self.assertEqual(e.response['Error']['Code'], 'AuthFailure') + + self.assertEqual(self.counter, 1) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/test_compare_policies.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/test_compare_policies.py new file mode 100644 index 00000000..c821f7a4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/ec2/test_compare_policies.py @@ -0,0 +1,341 @@ +# (c) 2017 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import unittest + +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_policies + + +class Ec2UtilsComparePolicies(unittest.TestCase): + + # ======================================================== + # Setup some initial data that we can use within our tests + # ======================================================== + def setUp(self): + # A pair of simple IAM Trust relationships using bools, the first a + # native bool the second a quoted string + self.bool_policy_bool = { + 'Version': '2012-10-17', + 'Statement': [ + { + "Action": "sts:AssumeRole", + "Condition": { + "Bool": {"aws:MultiFactorAuthPresent": True} + }, + "Effect": "Allow", + "Principal": {"AWS": "arn:aws:iam::XXXXXXXXXXXX:root"}, + "Sid": "AssumeRoleWithBoolean" + } + ] + } + + self.bool_policy_string = { + 'Version': '2012-10-17', + 'Statement': [ + { + "Action": "sts:AssumeRole", + "Condition": { + "Bool": {"aws:MultiFactorAuthPresent": "true"} + }, + "Effect": "Allow", + "Principal": {"AWS": "arn:aws:iam::XXXXXXXXXXXX:root"}, + "Sid": "AssumeRoleWithBoolean" + } + ] + } + + # A pair of simple bucket policies using numbers, the first a + # native int the second a quoted string + self.numeric_policy_number = { + 'Version': '2012-10-17', + 'Statement': [ + { + "Action": "s3:ListBucket", + "Condition": { + "NumericLessThanEquals": {"s3:max-keys": 15} + }, + "Effect": "Allow", + "Resource": "arn:aws:s3:::examplebucket", + "Sid": "s3ListBucketWithNumericLimit" + } + ] + } + + self.numeric_policy_string = { + 'Version': '2012-10-17', + 'Statement': [ + { + "Action": "s3:ListBucket", + "Condition": { + "NumericLessThanEquals": {"s3:max-keys": "15"} + }, + "Effect": "Allow", + "Resource": "arn:aws:s3:::examplebucket", + "Sid": "s3ListBucketWithNumericLimit" + } + ] + } + + self.small_policy_one = { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': 's3:PutObjectAcl', + 'Sid': 'AddCannedAcl2', + 'Resource': 'arn:aws:s3:::test_policy/*', + 'Effect': 'Allow', + 'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']} + } + ] + } + + # The same as small_policy_one, except the single resource is in a list and the contents of Statement are jumbled + self.small_policy_two = { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Effect': 'Allow', + 'Action': 's3:PutObjectAcl', + 'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']}, + 'Resource': ['arn:aws:s3:::test_policy/*'], + 'Sid': 'AddCannedAcl2' + } + ] + } + + self.version_policy_missing = { + 'Statement': [ + { + 'Action': 's3:PutObjectAcl', + 'Sid': 'AddCannedAcl2', + 'Resource': 'arn:aws:s3:::test_policy/*', + 'Effect': 'Allow', + 'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']} + } + ] + } + + self.version_policy_old = { + 'Version': '2008-10-17', + 'Statement': [ + { + 'Action': 's3:PutObjectAcl', + 'Sid': 'AddCannedAcl2', + 'Resource': 'arn:aws:s3:::test_policy/*', + 'Effect': 'Allow', + 'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']} + } + ] + } + + self.version_policy_new = { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Action': 's3:PutObjectAcl', + 'Sid': 'AddCannedAcl2', + 'Resource': 'arn:aws:s3:::test_policy/*', + 'Effect': 'Allow', + 'Principal': {'AWS': ['arn:aws:iam::XXXXXXXXXXXX:user/username1', 'arn:aws:iam::XXXXXXXXXXXX:user/username2']} + } + ] + } + + self.larger_policy_one = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Test", + "Effect": "Allow", + "Principal": { + "AWS": [ + "arn:aws:iam::XXXXXXXXXXXX:user/testuser1", + "arn:aws:iam::XXXXXXXXXXXX:user/testuser2" + ] + }, + "Action": "s3:PutObjectAcl", + "Resource": "arn:aws:s3:::test_policy/*" + }, + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::XXXXXXXXXXXX:user/testuser2" + }, + "Action": [ + "s3:PutObject", + "s3:PutObjectAcl" + ], + "Resource": "arn:aws:s3:::test_policy/*" + } + ] + } + + # The same as larger_policy_one, except having a list of length 1 and jumbled contents + self.larger_policy_two = { + "Version": "2012-10-17", + "Statement": [ + { + "Principal": { + "AWS": ["arn:aws:iam::XXXXXXXXXXXX:user/testuser2"] + }, + "Effect": "Allow", + "Resource": "arn:aws:s3:::test_policy/*", + "Action": [ + "s3:PutObject", + "s3:PutObjectAcl" + ] + }, + { + "Action": "s3:PutObjectAcl", + "Principal": { + "AWS": [ + "arn:aws:iam::XXXXXXXXXXXX:user/testuser1", + "arn:aws:iam::XXXXXXXXXXXX:user/testuser2" + ] + }, + "Sid": "Test", + "Resource": "arn:aws:s3:::test_policy/*", + "Effect": "Allow" + } + ] + } + + # Different than larger_policy_two: a different principal is given + self.larger_policy_three = { + "Version": "2012-10-17", + "Statement": [ + { + "Principal": { + "AWS": ["arn:aws:iam::XXXXXXXXXXXX:user/testuser2"] + }, + "Effect": "Allow", + "Resource": "arn:aws:s3:::test_policy/*", + "Action": [ + "s3:PutObject", + "s3:PutObjectAcl"] + }, + { + "Action": "s3:PutObjectAcl", + "Principal": { + "AWS": [ + "arn:aws:iam::XXXXXXXXXXXX:user/testuser1", + "arn:aws:iam::XXXXXXXXXXXX:user/testuser3" + ] + }, + "Sid": "Test", + "Resource": "arn:aws:s3:::test_policy/*", + "Effect": "Allow" + } + ] + } + + # Minimal policy using wildcarded Principal + self.wildcard_policy_one = { + "Version": "2012-10-17", + "Statement": [ + { + "Principal": { + "AWS": ["*"] + }, + "Effect": "Allow", + "Resource": "arn:aws:s3:::test_policy/*", + "Action": [ + "s3:PutObject", + "s3:PutObjectAcl"] + } + ] + } + + # Minimal policy using wildcarded Principal + self.wildcard_policy_two = { + "Version": "2012-10-17", + "Statement": [ + { + "Principal": "*", + "Effect": "Allow", + "Resource": "arn:aws:s3:::test_policy/*", + "Action": [ + "s3:PutObject", + "s3:PutObjectAcl"] + } + ] + } + + # ======================================================== + # ec2.compare_policies + # ======================================================== + + def test_compare_small_policies_without_differences(self): + """ Testing two small policies which are identical except for: + * The contents of the statement are in different orders + * The second policy contains a list of length one whereas in the first it is a string + """ + self.assertFalse(compare_policies(self.small_policy_one, self.small_policy_two)) + + def test_compare_large_policies_without_differences(self): + """ Testing two larger policies which are identical except for: + * The statements are in different orders + * The contents of the statements are also in different orders + * The second contains a list of length one for the Principal whereas in the first it is a string + """ + self.assertFalse(compare_policies(self.larger_policy_one, self.larger_policy_two)) + + def test_compare_larger_policies_with_difference(self): + """ Testing two larger policies which are identical except for: + * one different principal + """ + self.assertTrue(compare_policies(self.larger_policy_two, self.larger_policy_three)) + + def test_compare_smaller_policy_with_larger(self): + """ Testing two policies of different sizes """ + self.assertTrue(compare_policies(self.larger_policy_one, self.small_policy_one)) + + def test_compare_boolean_policy_bool_and_string_are_equal(self): + """ Testing two policies one using a quoted boolean, the other a bool """ + self.assertFalse(compare_policies(self.bool_policy_string, self.bool_policy_bool)) + + def test_compare_numeric_policy_number_and_string_are_equal(self): + """ Testing two policies one using a quoted number, the other an int """ + self.assertFalse(compare_policies(self.numeric_policy_string, self.numeric_policy_number)) + + def test_compare_version_policies_defaults_old(self): + """ Testing that a policy without Version is considered identical to one + with the 'old' Version (by default) + """ + self.assertFalse(compare_policies(self.version_policy_old, self.version_policy_missing)) + self.assertTrue(compare_policies(self.version_policy_new, self.version_policy_missing)) + + def test_compare_version_policies_default_disabled(self): + """ Testing that a policy without Version not considered identical when default_version=None + """ + self.assertFalse(compare_policies(self.version_policy_missing, self.version_policy_missing, default_version=None)) + self.assertTrue(compare_policies(self.version_policy_old, self.version_policy_missing, default_version=None)) + self.assertTrue(compare_policies(self.version_policy_new, self.version_policy_missing, default_version=None)) + + def test_compare_version_policies_default_set(self): + """ Testing that a policy without Version is only considered identical + when default_version="2008-10-17" + """ + self.assertFalse(compare_policies(self.version_policy_missing, self.version_policy_missing, default_version="2012-10-17")) + self.assertTrue(compare_policies(self.version_policy_old, self.version_policy_missing, default_version="2012-10-17")) + self.assertFalse(compare_policies(self.version_policy_old, self.version_policy_missing, default_version="2008-10-17")) + self.assertFalse(compare_policies(self.version_policy_new, self.version_policy_missing, default_version="2012-10-17")) + self.assertTrue(compare_policies(self.version_policy_new, self.version_policy_missing, default_version="2008-10-17")) + + def test_compare_version_policies_with_none(self): + """ Testing that comparing with no policy works + """ + self.assertTrue(compare_policies(self.small_policy_one, None)) + self.assertTrue(compare_policies(None, self.small_policy_one)) + self.assertFalse(compare_policies(None, None)) + + def test_compare_wildcard_policies_without_differences(self): + """ Testing two small wildcard policies which are identical except for: + * Principal: "*" vs Principal: ["AWS": "*"] + """ + self.assertFalse(compare_policies(self.wildcard_policy_one, self.wildcard_policy_two)) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_ec2.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_ec2.py new file mode 100644 index 00000000..dbba5be4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_ec2.py @@ -0,0 +1,191 @@ +# (c) 2017 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import unittest + +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import map_complex_type + + +class Ec2Utils(unittest.TestCase): + + # ======================================================== + # Setup some initial data that we can use within our tests + # ======================================================== + def setUp(self): + + self.tag_example_boto3_list = [ + {'Key': 'lowerCamel', 'Value': 'lowerCamelValue'}, + {'Key': 'UpperCamel', 'Value': 'upperCamelValue'}, + {'Key': 'Normal case', 'Value': 'Normal Value'}, + {'Key': 'lower case', 'Value': 'lower case value'} + ] + + self.tag_example_dict = { + 'lowerCamel': 'lowerCamelValue', + 'UpperCamel': 'upperCamelValue', + 'Normal case': 'Normal Value', + 'lower case': 'lower case value' + } + + # ======================================================== + # ec2.map_complex_type + # ======================================================== + def test_map_complex_type_over_dict(self): + complex_type = {'minimum_healthy_percent': "75", 'maximum_percent': "150"} + type_map = {'minimum_healthy_percent': 'int', 'maximum_percent': 'int'} + complex_type_mapped = map_complex_type(complex_type, type_map) + complex_type_expected = {'minimum_healthy_percent': 75, 'maximum_percent': 150} + self.assertEqual(complex_type_mapped, complex_type_expected) + + # ======================================================== + # ec2.ansible_dict_to_boto3_filter_list + # ======================================================== + + def test_ansible_dict_with_string_to_boto3_filter_list(self): + filters = {'some-aws-id': 'i-01234567'} + filter_list_string = [ + { + 'Name': 'some-aws-id', + 'Values': [ + 'i-01234567', + ] + } + ] + + converted_filters_list = ansible_dict_to_boto3_filter_list(filters) + self.assertEqual(converted_filters_list, filter_list_string) + + def test_ansible_dict_with_boolean_to_boto3_filter_list(self): + filters = {'enabled': True} + filter_list_boolean = [ + { + 'Name': 'enabled', + 'Values': [ + 'true', + ] + } + ] + + converted_filters_bool = ansible_dict_to_boto3_filter_list(filters) + self.assertEqual(converted_filters_bool, filter_list_boolean) + + def test_ansible_dict_with_integer_to_boto3_filter_list(self): + filters = {'version': 1} + filter_list_integer = [ + { + 'Name': 'version', + 'Values': [ + '1', + ] + } + ] + + converted_filters_int = ansible_dict_to_boto3_filter_list(filters) + self.assertEqual(converted_filters_int, filter_list_integer) + + # ======================================================== + # ec2.ansible_dict_to_boto3_tag_list + # ======================================================== + + def test_ansible_dict_to_boto3_tag_list(self): + converted_list = ansible_dict_to_boto3_tag_list(self.tag_example_dict) + sorted_converted_list = sorted(converted_list, key=lambda i: (i['Key'])) + sorted_list = sorted(self.tag_example_boto3_list, key=lambda i: (i['Key'])) + self.assertEqual(sorted_converted_list, sorted_list) + + # ======================================================== + # ec2.boto3_tag_list_to_ansible_dict + # ======================================================== + + def test_boto3_tag_list_to_ansible_dict(self): + converted_dict = boto3_tag_list_to_ansible_dict(self.tag_example_boto3_list) + self.assertEqual(converted_dict, self.tag_example_dict) + + def test_boto3_tag_list_to_ansible_dict_empty(self): + # AWS returns [] when there are no tags + self.assertEqual(boto3_tag_list_to_ansible_dict([]), {}) + # Minio returns [{}] when there are no tags + self.assertEqual(boto3_tag_list_to_ansible_dict([{}]), {}) + + # ======================================================== + # ec2.compare_aws_tags + # ======================================================== + + def test_compare_aws_tags_equal(self): + new_dict = dict(self.tag_example_dict) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict) + self.assertEqual({}, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=False) + self.assertEqual({}, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=True) + self.assertEqual({}, keys_to_set) + self.assertEqual([], keys_to_unset) + + def test_compare_aws_tags_removed(self): + new_dict = dict(self.tag_example_dict) + del new_dict['lowerCamel'] + del new_dict['Normal case'] + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict) + self.assertEqual({}, keys_to_set) + self.assertEqual(set(['lowerCamel', 'Normal case']), set(keys_to_unset)) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=False) + self.assertEqual({}, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=True) + self.assertEqual({}, keys_to_set) + self.assertEqual(set(['lowerCamel', 'Normal case']), set(keys_to_unset)) + + def test_compare_aws_tags_added(self): + new_dict = dict(self.tag_example_dict) + new_keys = {'add_me': 'lower case', 'Me too!': 'Contributing'} + new_dict.update(new_keys) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=False) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=True) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + + def test_compare_aws_tags_changed(self): + new_dict = dict(self.tag_example_dict) + new_keys = {'UpperCamel': 'anotherCamelValue', 'Normal case': 'normal value'} + new_dict.update(new_keys) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=False) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=True) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + + def test_compare_aws_tags_complex_update(self): + # Adds 'Me too!', Changes 'UpperCamel' and removes 'Normal case' + new_dict = dict(self.tag_example_dict) + new_keys = {'UpperCamel': 'anotherCamelValue', 'Me too!': 'Contributing'} + new_dict.update(new_keys) + del new_dict['Normal case'] + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual(['Normal case'], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=False) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual([], keys_to_unset) + keys_to_set, keys_to_unset = compare_aws_tags(self.tag_example_dict, new_dict, purge_tags=True) + self.assertEqual(new_keys, keys_to_set) + self.assertEqual(['Normal case'], keys_to_unset) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py new file mode 100644 index 00000000..dba2c129 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_elbv2.py @@ -0,0 +1,43 @@ +# +# (c) 2021 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import ansible_collections.amazon.aws.plugins.module_utils.elbv2 as elbv2 + + +one_action = [ + { + "ForwardConfig": { + "TargetGroupStickinessConfig": {"Enabled": False}, + "TargetGroups": [ + { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:966509639900:targetgroup/my-tg-58045486/5b231e04f663ae21", + "Weight": 1, + } + ], + }, + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:966509639900:targetgroup/my-tg-58045486/5b231e04f663ae21", + "Type": "forward", + } +] + + +def test__prune_ForwardConfig(): + expectation = { + "TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:966509639900:targetgroup/my-tg-58045486/5b231e04f663ae21", + "Type": "forward", + } + assert elbv2._prune_ForwardConfig(one_action[0]) == expectation + + +def _prune_secret(): + assert elbv2._prune_secret(one_action[0]) == one_action[0] + + +def _sort_actions_one_entry(): + assert elbv2._sort_actions(one_action) == one_action diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_iam.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_iam.py new file mode 100644 index 00000000..0bfa7484 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/module_utils/test_iam.py @@ -0,0 +1,296 @@ +# +# (c) 2020 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +import botocore + +from ansible_collections.amazon.aws.tests.unit.compat.mock import MagicMock +from ansible_collections.amazon.aws.tests.unit.compat import unittest + +import ansible_collections.amazon.aws.plugins.module_utils.iam as utils_iam +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 + +if not HAS_BOTO3: + pytestmark = pytest.mark.skip("test_iam.py requires the python modules 'boto3' and 'botocore'") + + +class IamUtilsTestSuite(unittest.TestCase): + + def _make_denied_exception(self, partition): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "AccessDenied", + "Message": "User: arn:" + partition + ":iam::123456789012:user/ExampleUser " + + "is not authorized to perform: iam:GetUser on resource: user ExampleUser" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'getUser') + + def _make_unexpected_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "SomeThingWentWrong", + "Message": "Boom!" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'someCall') + + def _make_encoded_exception(self): + return botocore.exceptions.ClientError( + { + "Error": { + "Code": "AccessDenied", + "Message": "You are not authorized to perform this operation. Encoded authorization failure message: " + + "fEwXX6llx3cClm9J4pURgz1XPnJPrYexEbrJcLhFkwygMdOgx_-aEsj0LqRM6Kxt2HVI6prUhDwbJqBo9U2V7iRKZ" + + "T6ZdJvHH02cXmD0Jwl5vrTsf0PhBcWYlH5wl2qME7xTfdolEUr4CzumCiti7ETiO-RDdHqWlasBOW5bWsZ4GSpPdU" + + "06YAX0TfwVBs48uU5RpCHfz1uhSzez-3elbtp9CmTOHLt5pzJodiovccO55BQKYLPtmJcs6S9YLEEogmpI4Cb1D26" + + "fYahDh51jEmaohPnW5pb1nQe2yPEtuIhtRzNjhFCOOMwY5DBzNsymK-Gj6eJLm7FSGHee4AHLU_XmZMe_6bcLAiOx" + + "6Zdl65Kdd0hLcpwVxyZMi27HnYjAdqRlV3wuCW2PkhAW14qZQLfiuHZDEwnPe2PBGSlFcCmkQvJvX-YLoA7Uyc2wf" + + "NX5RJm38STwfiJSkQaNDhHKTWKiLOsgY4Gze6uZoG7zOcFXFRyaA4cbMmI76uyBO7j-9uQUCtBYqYto8x_9CUJcxI" + + "VC5SPG_C1mk-WoDMew01f0qy-bNaCgmJ9TOQGd08FyuT1SaMpCC0gX6mHuOnEgkFw3veBIowMpp9XcM-yc42fmIOp" + + "FOdvQO6uE9p55Qc-uXvsDTTvT3A7EeFU8a_YoAIt9UgNYM6VTvoprLz7dBI_P6C-bdPPZCY2amm-dJNVZelT6TbJB" + + "H_Vxh0fzeiSUBersy_QzB0moc-vPWgnB-IkgnYLV-4L3K0L2" + }, + "ResponseMetadata": { + "RequestId": "01234567-89ab-cdef-0123-456789abcdef" + } + }, 'someCall') + + def _make_botocore_exception(self): + return botocore.exceptions.EndpointConnectionError(endpoint_url='junk.endpoint') + + def setUp(self): + self.sts_client = MagicMock() + self.iam_client = MagicMock() + self.module = MagicMock() + clients = {'sts': self.sts_client, 'iam': self.iam_client} + + def get_client(*args, **kwargs): + return clients[args[0]] + + self.module.client.side_effect = get_client + self.module.fail_json_aws.side_effect = SystemExit(1) + self.module.fail_json.side_effect = SystemExit(2) + + # ========== get_aws_account_id ============ + # This is just a minimal (compatability) wrapper around get_aws_account_info + # Perform some basic testing and call it a day. + + # Test the simplest case - We're permitted to call GetCallerIdentity + def test_get_aws_account_id__caller_success(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [{'UserId': 'AIDA1234567890ABCDEFG', + 'Account': '123456789012', + 'Arn': 'arn:aws:iam::123456789012:user/ExampleUser'}] + # Run module + return_value = utils_iam.get_aws_account_id(self.module) + # Check we only saw the calls we mocked out + self.module.client.assert_called_once() + self.sts_client.get_caller_identity.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, '123456789012') + + # Test the simplest case - We're permitted to call GetCallerIdentity + # (China partition) + def test_get_aws_account_id__caller_success_cn(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [{'UserId': 'AIDA1234567890ABCDEFG', + 'Account': '123456789012', + 'Arn': 'arn:aws-cn:iam::123456789012:user/ExampleUser'}] + # Run module + return_value = utils_iam.get_aws_account_id(self.module) + # Check we only saw the calls we mocked out + self.module.client.assert_called_once() + self.sts_client.get_caller_identity.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, '123456789012') + + # ========== get_aws_account_info ============ + # Test the simplest case - We're permitted to call GetCallerIdentity + def test_get_aws_account_info__caller_success(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [{'UserId': 'AIDA1234567890ABCDEFG', + 'Account': '123456789012', + 'Arn': 'arn:aws:iam::123456789012:user/ExampleUser'}] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.module.client.assert_called_once() + self.sts_client.get_caller_identity.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws',)) + + # (China partition) + def test_get_aws_account_info__caller_success_cn(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [{'UserId': 'AIDA1234567890ABCDEFG', + 'Account': '123456789012', + 'Arn': 'arn:aws-cn:iam::123456789012:user/ExampleUser'}] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.module.client.assert_called_once() + self.sts_client.get_caller_identity.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws-cn',)) + + # (US-Gov partition) + def test_get_aws_account_info__caller_success_gov(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [{'UserId': 'AIDA1234567890ABCDEFG', + 'Account': '123456789012', + 'Arn': 'arn:aws-us-gov:iam::123456789012:user/ExampleUser'}] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.module.client.assert_called_once() + self.sts_client.get_caller_identity.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws-us-gov',)) + + # If sts:get_caller_identity fails (most likely something wierd on the + # client side), then try a few extra options. + # Test response if STS fails and we need to fall back to GetUser + def test_get_aws_account_info__user_success(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [{"User": {"Path": "/", "UserName": "ExampleUser", "UserId": "AIDA1234567890ABCDEFG", + "Arn": "arn:aws:iam::123456789012:user/ExampleUser", "CreateDate": "2020-09-08T14:04:32Z"}}] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws',)) + + # (China partition) + def test_get_aws_account_info__user_success_cn(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [{"User": {"Path": "/", "UserName": "ExampleUser", "UserId": "AIDA1234567890ABCDEFG", + "Arn": "arn:aws-cn:iam::123456789012:user/ExampleUser", "CreateDate": "2020-09-08T14:04:32Z"}}] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws-cn',)) + + # (US-Gov partition) + def test_get_aws_account_info__user_success_gov(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [{"User": {"Path": "/", "UserName": "ExampleUser", "UserId": "AIDA1234567890ABCDEFG", + "Arn": "arn:aws-us-gov:iam::123456789012:user/ExampleUser", "CreateDate": "2020-09-08T14:04:32Z"}}] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws-us-gov',)) + + # Test response if STS and IAM fails and we need to fall back to the denial message + def test_get_aws_account_info__user_denied(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [self._make_denied_exception('aws')] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws',)) + + # (China partition) + def test_get_aws_account_info__user_denied_cn(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [self._make_denied_exception('aws-cn')] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws-cn',)) + + # (US-Gov partition) + def test_get_aws_account_info__user_denied_gov(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [self._make_denied_exception('aws-us-gov')] + # Run module + return_value = utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + self.assertEqual(return_value, ('123456789012', 'aws-us-gov',)) + + # Test that we fail gracefully if Boto throws exceptions at us... + def test_get_aws_account_info__boto_failures(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_botocore_exception()] + self.iam_client.get_user.side_effect = [self._make_botocore_exception()] + # Run module + with pytest.raises(SystemExit) as e: + utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + assert e.type == SystemExit + assert e.value.code == 1 # 1 == fail_json_aws + + def test_get_aws_account_info__client_failures(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_unexpected_exception()] + self.iam_client.get_user.side_effect = [self._make_unexpected_exception()] + # Run module + with pytest.raises(SystemExit) as e: + utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + assert e.type == SystemExit + assert e.value.code == 1 # 1 == fail_json_aws + + def test_get_aws_account_info__encoded_failures(self): + # Prepare + self.sts_client.get_caller_identity.side_effect = [self._make_encoded_exception()] + self.iam_client.get_user.side_effect = [self._make_encoded_exception()] + # Run module + with pytest.raises(SystemExit) as e: + utils_iam.get_aws_account_info(self.module) + # Check we only saw the calls we mocked out + self.assertEqual(self.module.client.call_count, 2) + self.sts_client.get_caller_identity.assert_called_once() + self.iam_client.get_user.assert_called_once() + # Check we got the values back we expected. + assert e.type == SystemExit + assert e.value.code == 1 # 1 == fail_json (we couldn't parse the AccessDenied errors) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/inventory/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/inventory/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/inventory/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py new file mode 100644 index 00000000..12ef2f6c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- + +# Copyright 2017 Sloane Hertel <shertel@redhat.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +import datetime + +# Just to test that we have the prerequisite for InventoryModule and instance_data_filter_to_boto_attr +boto3 = pytest.importorskip('boto3') +botocore = pytest.importorskip('botocore') + +from ansible.errors import AnsibleError +from ansible_collections.amazon.aws.plugins.inventory.aws_ec2 import InventoryModule, instance_data_filter_to_boto_attr + +instances = { + u'Instances': [ + {u'Monitoring': {u'State': 'disabled'}, + u'PublicDnsName': 'ec2-12-345-67-890.compute-1.amazonaws.com', + u'State': {u'Code': 16, u'Name': 'running'}, + u'EbsOptimized': False, + u'LaunchTime': datetime.datetime(2017, 10, 31, 12, 59, 25), + u'PublicIpAddress': '12.345.67.890', + u'PrivateIpAddress': '098.76.54.321', + u'ProductCodes': [], + u'VpcId': 'vpc-12345678', + u'StateTransitionReason': '', + u'InstanceId': 'i-00000000000000000', + u'EnaSupport': True, + u'ImageId': 'ami-12345678', + u'PrivateDnsName': 'ip-098-76-54-321.ec2.internal', + u'KeyName': 'testkey', + u'SecurityGroups': [{u'GroupName': 'default', u'GroupId': 'sg-12345678'}], + u'ClientToken': '', + u'SubnetId': 'subnet-12345678', + u'InstanceType': 't2.micro', + u'NetworkInterfaces': [ + {u'Status': 'in-use', + u'MacAddress': '12:a0:50:42:3d:a4', + u'SourceDestCheck': True, + u'VpcId': 'vpc-12345678', + u'Description': '', + u'NetworkInterfaceId': 'eni-12345678', + u'PrivateIpAddresses': [ + {u'PrivateDnsName': 'ip-098-76-54-321.ec2.internal', + u'PrivateIpAddress': '098.76.54.321', + u'Primary': True, + u'Association': + {u'PublicIp': '12.345.67.890', + u'PublicDnsName': 'ec2-12-345-67-890.compute-1.amazonaws.com', + u'IpOwnerId': 'amazon'}}], + u'PrivateDnsName': 'ip-098-76-54-321.ec2.internal', + u'Attachment': + {u'Status': 'attached', + u'DeviceIndex': 0, + u'DeleteOnTermination': True, + u'AttachmentId': 'eni-attach-12345678', + u'AttachTime': datetime.datetime(2017, 10, 31, 12, 59, 25)}, + u'Groups': [ + {u'GroupName': 'default', + u'GroupId': 'sg-12345678'}], + u'Ipv6Addresses': [], + u'OwnerId': '123456789000', + u'PrivateIpAddress': '098.76.54.321', + u'SubnetId': 'subnet-12345678', + u'Association': + {u'PublicIp': '12.345.67.890', + u'PublicDnsName': 'ec2-12-345-67-890.compute-1.amazonaws.com', + u'IpOwnerId': 'amazon'}}], + u'SourceDestCheck': True, + u'Placement': + {u'Tenancy': 'default', + u'GroupName': '', + u'AvailabilityZone': 'us-east-1c'}, + u'Hypervisor': 'xen', + u'BlockDeviceMappings': [ + {u'DeviceName': '/dev/xvda', + u'Ebs': + {u'Status': 'attached', + u'DeleteOnTermination': True, + u'VolumeId': 'vol-01234567890000000', + u'AttachTime': datetime.datetime(2017, 10, 31, 12, 59, 26)}}], + u'Architecture': 'x86_64', + u'RootDeviceType': 'ebs', + u'RootDeviceName': '/dev/xvda', + u'VirtualizationType': 'hvm', + u'Tags': [{u'Value': 'test', u'Key': 'ansible'}, {u'Value': 'aws_ec2', u'Key': 'Name'}], + u'AmiLaunchIndex': 0}], + u'ReservationId': 'r-01234567890000000', + u'Groups': [], + u'OwnerId': '123456789000' +} + + +@pytest.fixture(scope="module") +def inventory(): + return InventoryModule() + + +def test_compile_values(inventory): + found_value = instances['Instances'][0] + chain_of_keys = instance_data_filter_to_boto_attr['instance.group-id'] + for attr in chain_of_keys: + found_value = inventory._compile_values(found_value, attr) + assert found_value == "sg-12345678" + + +def test_get_boto_attr_chain(inventory): + instance = instances['Instances'][0] + assert inventory._get_boto_attr_chain('network-interface.addresses.private-ip-address', instance) == "098.76.54.321" + + +def test_boto3_conn(inventory): + inventory._options = {"aws_profile": "first_precedence", + "aws_access_key": "test_access_key", + "aws_secret_key": "test_secret_key", + "aws_security_token": "test_security_token", + "iam_role_arn": None} + inventory._set_credentials() + with pytest.raises(AnsibleError) as error_message: + for connection, region in inventory._boto3_conn(regions=['us-east-1']): + assert "Insufficient credentials found" in error_message + + +def test_get_hostname_default(inventory): + instance = instances['Instances'][0] + assert inventory._get_hostname(instance, hostnames=None) == "ec2-12-345-67-890.compute-1.amazonaws.com" + + +def test_get_hostname(inventory): + hostnames = ['ip-address', 'dns-name'] + instance = instances['Instances'][0] + assert inventory._get_hostname(instance, hostnames) == "12.345.67.890" + + +def test_get_hostname_dict(inventory): + hostnames = [{'name': 'private-ip-address', 'separator': '_', 'prefix': 'tag:Name'}] + instance = instances['Instances'][0] + assert inventory._get_hostname(instance, hostnames) == "aws_ec2_098.76.54.321" + + +def test_set_credentials(inventory): + inventory._options = {'aws_access_key': 'test_access_key', + 'aws_secret_key': 'test_secret_key', + 'aws_security_token': 'test_security_token', + 'aws_profile': 'test_profile', + 'iam_role_arn': 'arn:aws:iam::112233445566:role/test-role'} + inventory._set_credentials() + + assert inventory.boto_profile == "test_profile" + assert inventory.aws_access_key_id == "test_access_key" + assert inventory.aws_secret_access_key == "test_secret_key" + assert inventory.aws_security_token == "test_security_token" + assert inventory.iam_role_arn == "arn:aws:iam::112233445566:role/test-role" + + +def test_insufficient_credentials(inventory): + inventory._options = { + 'aws_access_key': None, + 'aws_secret_key': None, + 'aws_security_token': None, + 'aws_profile': None, + 'iam_role_arn': None + } + with pytest.raises(AnsibleError) as error_message: + inventory._set_credentials() + assert "Insufficient credentials found" in error_message + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('not_aws_config.yml') is False diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/fixtures/avi.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/fixtures/avi.json new file mode 100644 index 00000000..ae89ca68 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/fixtures/avi.json @@ -0,0 +1,104 @@ +{ + "mock_single_obj": { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "PG-123", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-2084-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-2084-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-2084-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + }, + "mock_multiple_obj": { + "results": [ + { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "J-PG-0682", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-2084-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-2084-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-2084-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + }, + { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "J-PG-0231", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-1627-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-1627-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-1627-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + }, + { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "J-PG-0535", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-1934-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-1934-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-1934-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + }, + { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "J-PG-0094", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-1458-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-1458-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-1458-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + }, + { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "J-PG-0437", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-1836-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-1836-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-1836-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + }, + { + "_last_modified": "", + "cloud_ref": "https://192.0.2.132/api/cloud/cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "dhcp_enabled": true, + "exclude_discovered_subnets": false, + "name": "J-PG-0673", + "synced_from_se": true, + "tenant_ref": "https://192.0.2.132/api/tenant/admin", + "url": "https://192.0.2.132/api/network/dvportgroup-2075-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "uuid": "dvportgroup-2075-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vcenter_dvs": true, + "vimgrnw_ref": "https://192.0.2.132/api/vimgrnwruntime/dvportgroup-2075-cloud-4d063be1-99c2-44cf-8b28-977bd970524c", + "vrf_context_ref": "https://192.0.2.132/api/vrfcontext/vrfcontext-31f1b55f-319c-44eb-862f-69d79ffdf295" + } + ] + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/test_aws_secret.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/test_aws_secret.py new file mode 100644 index 00000000..3ac92824 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/test_aws_secret.py @@ -0,0 +1,218 @@ +# (c) 2019 Robert Williams +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +import datetime +from copy import copy + +from ansible.errors import AnsibleError +from ansible.plugins.loader import lookup_loader + +from ansible_collections.amazon.aws.plugins.lookup import aws_secret + +try: + import boto3 + from botocore.exceptions import ClientError +except ImportError: + pytestmark = pytest.mark.skip("This test requires the boto3 and botocore Python libraries") + + +@pytest.fixture +def dummy_credentials(): + dummy_credentials = {} + dummy_credentials['boto_profile'] = None + dummy_credentials['aws_secret_key'] = "notasecret" + dummy_credentials['aws_access_key'] = "notakey" + dummy_credentials['aws_security_token'] = None + dummy_credentials['region'] = 'eu-west-1' + return dummy_credentials + + +simple_variable_success_response = { + 'Name': 'secret', + 'VersionId': 'cafe8168-e6ce-4e59-8830-5b143faf6c52', + 'SecretString': '{"secret":"simplesecret"}', + 'VersionStages': ['AWSCURRENT'], + 'ResponseMetadata': { + 'RequestId': '21099462-597c-490a-800f-8b7a41e5151c', + 'HTTPStatusCode': 200, + 'HTTPHeaders': { + 'date': 'Thu, 04 Apr 2019 10:43:12 GMT', + 'content-type': 'application/x-amz-json-1.1', + 'content-length': '252', + 'connection': 'keep-alive', + 'x-amzn-requestid': '21099462-597c-490a-800f-8b7a41e5151c' + }, + 'RetryAttempts': 0 + } +} + + +def test_lookup_variable(mocker, dummy_credentials): + dateutil_tz = pytest.importorskip("dateutil.tz") + lookup = lookup_loader.get('amazon.aws.aws_secret') + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_secret_value.return_value = copy( + simple_variable_success_response) + boto3_client_double = boto3_double.Session.return_value.client + + mocker.patch.object(boto3, 'session', boto3_double) + retval = lookup.run(["simple_variable"], None, **dummy_credentials) + assert (retval[0] == '{"secret":"simplesecret"}') + boto3_client_double.assert_called_with('secretsmanager', 'eu-west-1', aws_access_key_id='notakey', + aws_secret_access_key="notasecret", aws_session_token=None) + + +error_response_missing = {'Error': {'Code': 'ResourceNotFoundException', 'Message': 'Fake Not Found Error'}} +error_response_denied = {'Error': {'Code': 'AccessDeniedException', 'Message': 'Fake Denied Error'}} +operation_name = 'FakeOperation' + + +def test_on_missing_option(mocker, dummy_credentials): + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_secret_value.side_effect = ClientError(error_response_missing, operation_name) + + with pytest.raises(AnsibleError, match="ResourceNotFound"): + mocker.patch.object(boto3, 'session', boto3_double) + lookup_loader.get('amazon.aws.aws_secret').run(["missing_secret"], None, **dummy_credentials) + + mocker.patch.object(boto3, 'session', boto3_double) + args = copy(dummy_credentials) + args["on_missing"] = 'skip' + retval = lookup_loader.get('amazon.aws.aws_secret').run(["missing_secret"], None, **args) + assert(retval == []) + + mocker.patch.object(boto3, 'session', boto3_double) + args = copy(dummy_credentials) + args["on_missing"] = 'warn' + retval = lookup_loader.get('amazon.aws.aws_secret').run(["missing_secret"], None, **args) + assert(retval == []) + + +def test_on_denied_option(mocker, dummy_credentials): + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_secret_value.side_effect = ClientError(error_response_denied, operation_name) + + with pytest.raises(AnsibleError, match="AccessDenied"): + mocker.patch.object(boto3, 'session', boto3_double) + lookup_loader.get('amazon.aws.aws_secret').run(["denied_secret"], None, **dummy_credentials) + + mocker.patch.object(boto3, 'session', boto3_double) + args = copy(dummy_credentials) + args["on_denied"] = 'skip' + retval = lookup_loader.get('amazon.aws.aws_secret').run(["denied_secret"], None, **args) + assert(retval == []) + + mocker.patch.object(boto3, 'session', boto3_double) + args = copy(dummy_credentials) + args["on_denied"] = 'warn' + retval = lookup_loader.get('amazon.aws.aws_secret').run(["denied_secret"], None, **args) + assert(retval == []) + + +def test_nested_lookup_variable(mocker, dummy_credentials): + dateutil_tz = pytest.importorskip("dateutil.tz") + simple_variable_success_response = { + 'Name': 'simple_variable', + 'VersionId': 'cafe8168-e6ce-4e59-8830-5b143faf6c52', + 'SecretString': '{"key1": {"key2": {"key3": 1 } } }', + 'VersionStages': ['AWSCURRENT'], + 'CreatedDate': datetime.datetime(2019, 4, 4, 11, 41, 0, 878000, tzinfo=dateutil_tz.tzlocal()), + 'ResponseMetadata': { + 'RequestId': '21099462-597c-490a-800f-8b7a41e5151c', + 'HTTPStatusCode': 200, + 'HTTPHeaders': { + 'date': 'Thu, 04 Apr 2019 10:43:12 GMT', + 'content-type': 'application/x-amz-json-1.1', + 'content-length': '252', + 'connection': 'keep-alive', + 'x-amzn-requestid': '21099462-597c-490a-800f-8b7a41e5151c' + }, + 'RetryAttempts': 0 + } + } + lookup = lookup_loader.get('amazon.aws.aws_secret') + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_secret_value.return_value = simple_variable_success_response + boto3_client_double = boto3_double.Session.return_value.client + + mocker.patch.object(boto3, 'session', boto3_double) + dummy_credentials["nested"] = 'true' + retval = lookup.run(["simple_variable.key1.key2.key3"], None, **dummy_credentials) + assert(retval[0] == '1') + boto3_client_double.assert_called_with('secretsmanager', 'eu-west-1', aws_access_key_id='notakey', + aws_secret_access_key="notasecret", aws_session_token=None) + + +def test_path_lookup_variable(mocker, dummy_credentials): + lookup = aws_secret.LookupModule() + lookup._load_name = "aws_secret" + + path_list_secrets_success_response = { + 'SecretList': [ + { + 'Name': '/testpath/too', + }, + { + 'Name': '/testpath/won', + } + ], + 'ResponseMetadata': { + 'RequestId': '21099462-597c-490a-800f-8b7a41e5151c', + 'HTTPStatusCode': 200, + 'HTTPHeaders': { + 'date': 'Thu, 04 Apr 2019 10:43:12 GMT', + 'content-type': 'application/x-amz-json-1.1', + 'content-length': '252', + 'connection': 'keep-alive', + 'x-amzn-requestid': '21099462-597c-490a-800f-8b7a41e5151c' + }, + 'RetryAttempts': 0 + } + } + + boto3_double = mocker.MagicMock() + list_secrets_fn = boto3_double.Session.return_value.client.return_value.list_secrets + list_secrets_fn.return_value = path_list_secrets_success_response + + get_secret_value_fn = boto3_double.Session.return_value.client.return_value.get_secret_value + first_path = copy(simple_variable_success_response) + first_path['SecretString'] = 'simple_value_too' + second_path = copy(simple_variable_success_response) + second_path['SecretString'] = 'simple_value_won' + get_secret_value_fn.side_effect = [ + first_path, + second_path + ] + + boto3_client_double = boto3_double.Session.return_value.client + + mocker.patch.object(boto3, 'session', boto3_double) + dummy_credentials["bypath"] = 'true' + dummy_credentials["boto_profile"] = 'test' + dummy_credentials["aws_profile"] = 'test' + retval = lookup.run(["/testpath"], {}, **dummy_credentials) + assert (retval[0]["/testpath/won"] == "simple_value_won") + assert (retval[0]["/testpath/too"] == "simple_value_too") + boto3_client_double.assert_called_with('secretsmanager', 'eu-west-1', aws_access_key_id='notakey', + aws_secret_access_key="notasecret", aws_session_token=None) + list_secrets_fn.assert_called_with(Filters=[{'Key': 'name', 'Values': ['/testpath']}]) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/test_aws_ssm.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/test_aws_ssm.py new file mode 100644 index 00000000..c94850b9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/lookup/test_aws_ssm.py @@ -0,0 +1,166 @@ +# +# (c) 2017 Michael De La Rue +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +from copy import copy + +from ansible.errors import AnsibleError + +from ansible_collections.amazon.aws.plugins.lookup import aws_ssm + +try: + import boto3 + from botocore.exceptions import ClientError +except ImportError: + pytestmark = pytest.mark.skip("This test requires the boto3 and botocore Python libraries") + +simple_variable_success_response = { + 'Parameters': [ + { + 'Name': 'simple_variable', + 'Type': 'String', + 'Value': 'simplevalue', + 'Version': 1 + } + ], + 'InvalidParameters': [], + 'ResponseMetadata': { + 'RequestId': '12121212-3434-5656-7878-9a9a9a9a9a9a', + 'HTTPStatusCode': 200, + 'HTTPHeaders': { + 'x-amzn-requestid': '12121212-3434-5656-7878-9a9a9a9a9a9a', + 'content-type': 'application/x-amz-json-1.1', + 'content-length': '116', + 'date': 'Tue, 23 Jan 2018 11:04:27 GMT' + }, + 'RetryAttempts': 0 + } +} + +path_success_response = copy(simple_variable_success_response) +path_success_response['Parameters'] = [ + {'Name': '/testpath/too', 'Type': 'String', 'Value': 'simple_value_too', 'Version': 1}, + {'Name': '/testpath/won', 'Type': 'String', 'Value': 'simple_value_won', 'Version': 1} +] + +missing_variable_response = copy(simple_variable_success_response) +missing_variable_response['Parameters'] = [] +missing_variable_response['InvalidParameters'] = ['missing_variable'] + +some_missing_variable_response = copy(simple_variable_success_response) +some_missing_variable_response['Parameters'] = [ + {'Name': 'simple', 'Type': 'String', 'Value': 'simple_value', 'Version': 1}, + {'Name': '/testpath/won', 'Type': 'String', 'Value': 'simple_value_won', 'Version': 1} +] +some_missing_variable_response['InvalidParameters'] = ['missing_variable'] + + +dummy_credentials = {} +dummy_credentials['boto_profile'] = None +dummy_credentials['aws_secret_key'] = "notasecret" +dummy_credentials['aws_access_key'] = "notakey" +dummy_credentials['aws_security_token'] = None +dummy_credentials['region'] = 'eu-west-1' + + +def test_lookup_variable(mocker): + lookup = aws_ssm.LookupModule() + lookup._load_name = "aws_ssm" + + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_parameters.return_value = simple_variable_success_response + boto3_client_double = boto3_double.Session.return_value.client + + mocker.patch.object(boto3, 'session', boto3_double) + retval = lookup.run(["simple_variable"], {}, **dummy_credentials) + assert(retval[0] == "simplevalue") + boto3_client_double.assert_called_with('ssm', 'eu-west-1', aws_access_key_id='notakey', + aws_secret_access_key="notasecret", aws_session_token=None) + + +def test_path_lookup_variable(mocker): + lookup = aws_ssm.LookupModule() + lookup._load_name = "aws_ssm" + + boto3_double = mocker.MagicMock() + get_path_fn = boto3_double.Session.return_value.client.return_value.get_parameters_by_path + get_path_fn.return_value = path_success_response + boto3_client_double = boto3_double.Session.return_value.client + + mocker.patch.object(boto3, 'session', boto3_double) + args = copy(dummy_credentials) + args["bypath"] = 'true' + retval = lookup.run(["/testpath"], {}, **args) + assert(retval[0]["/testpath/won"] == "simple_value_won") + assert(retval[0]["/testpath/too"] == "simple_value_too") + boto3_client_double.assert_called_with('ssm', 'eu-west-1', aws_access_key_id='notakey', + aws_secret_access_key="notasecret", aws_session_token=None) + get_path_fn.assert_called_with(Path="/testpath", Recursive=False, WithDecryption=True) + + +def test_return_none_for_missing_variable(mocker): + """ + during jinja2 templates, we can't shouldn't normally raise exceptions since this blocks the ability to use defaults. + + for this reason we return ```None``` for missing variables + """ + lookup = aws_ssm.LookupModule() + lookup._load_name = "aws_ssm" + + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_parameters.return_value = missing_variable_response + + mocker.patch.object(boto3, 'session', boto3_double) + retval = lookup.run(["missing_variable"], {}, **dummy_credentials) + assert(retval[0] is None) + + +def test_match_retvals_to_call_params_even_with_some_missing_variables(mocker): + """ + If we get a complex list of variables with some missing and some not, we still have to return a + list which matches with the original variable list. + """ + lookup = aws_ssm.LookupModule() + lookup._load_name = "aws_ssm" + + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_parameters.return_value = some_missing_variable_response + + mocker.patch.object(boto3, 'session', boto3_double) + retval = lookup.run(["simple", "missing_variable", "/testpath/won", "simple"], {}, **dummy_credentials) + assert(retval == ["simple_value", None, "simple_value_won", "simple_value"]) + + +error_response = {'Error': {'Code': 'ResourceNotFoundException', 'Message': 'Fake Testing Error'}} +operation_name = 'FakeOperation' + + +def test_warn_denied_variable(mocker): + lookup = aws_ssm.LookupModule() + lookup._load_name = "aws_ssm" + + boto3_double = mocker.MagicMock() + boto3_double.Session.return_value.client.return_value.get_parameters.side_effect = ClientError(error_response, operation_name) + + with pytest.raises(AnsibleError): + mocker.patch.object(boto3, 'session', boto3_double) + lookup.run(["denied_variable"], {}, **dummy_credentials) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py new file mode 100644 index 00000000..a7d1e047 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/conftest.py @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +import pytest + +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def patch_ansible_module(request, mocker): + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the patch_ansible_module pytest fixture') + + mocker.patch('ansible.module_utils.basic._ANSIBLE_ARGS', to_bytes(args)) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem new file mode 100644 index 00000000..4412f325 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/a.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFVTCCBD2gAwIBAgISAx4pnfwvGxYrrQhr/UXiN7HCMA0GCSqGSIb3DQEBCwUA +MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD +ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA3MjUwMDI4NTdaFw0x +OTEwMjMwMDI4NTdaMBoxGDAWBgNVBAMTD2NyeXB0b2dyYXBoeS5pbzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJDpCL99DVo83587MrVp6gunmKRoUfY +vcgk5u2v0tB9OmZkcIY37z6AunHWr18Yj55zHmm6G8Nf35hmu3ql2A26WThCbmOe +WXbxhgarkningZI9opUWnI2dIllguVIsq99GzhpNnDdCb26s5+SRhJI4cr4hYaKC +XGDKooKWyXUX09SJTq7nW/1+pq3y9ZMvldRKjJALeAdwnC7kmUB6pK7q8J2VlpfQ +wqGu6q/WHVdgnhWARw3GEFJWDn9wkxBAF08CpzhVaEj+iK+Ut/1HBgNYwqI47h7S +q+qv0G2qklRVUtEM0zYRsp+y/6vivdbFLlPw8VaerbpJN3gLtpVNcGECAwEAAaOC +AmMwggJfMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB +BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjbe0bE1aZ8HiqtwqUfCe15bF +V8UwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEE +YzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQu +b3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQu +b3JnLzAaBgNVHREEEzARgg9jcnlwdG9ncmFwaHkuaW8wTAYDVR0gBEUwQzAIBgZn +gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s +ZXRzZW5jcnlwdC5vcmcwggEDBgorBgEEAdZ5AgQCBIH0BIHxAO8AdgB0ftqDMa0z +EJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAAAWwmvtnXAAAEAwBHMEUCIFXHYX/E +xtbYCvjjQ3dN0HOLW1d8+aduktmax4mu3KszAiEAvTpxuSVVXJnVGA4tU2GOnI60 +sqTh/IK6hvrFN1k1HBUAdQApPFGWVMg5ZbqqUPxYB9S3b79Yeily3KTDDPTlRUf0 +eAAAAWwmvtm9AAAEAwBGMEQCIDn7sgzD+7JzR+XTvjKf7VyLWwX37O8uwCfCTKo7 ++tEhAiB05bHiICU5wkfRBrwcvqXf4bPF7NT5LVlRQYzJ/hbpvzANBgkqhkiG9w0B +AQsFAAOCAQEAcMU8E6D+5WC07QSeTppRTboC++7YgQg5NiSWm7OE2FlyiRZXnu0Y +uBoaqAkZIqj7dom9wy1c1UauxOfM9lUZKhYnDTBu9tIhBAvCS0J0avv1j1KQygQ1 +qV+urJsunUwqV/vPWo1GfWophvyXVN6MAycv34ZXZvAjtG7oDcoQVLLvK1SIo2vu +4/dNkOQzaeZez8q6Ij9762TbBWaK5C789VMdUWZCADWoToPIK533cWbDEp4IhBU/ +K73d7lGGl7S59SjT2V/XE6eJS9Zlj0M+A8pf/8tjM/ImHAjlOHB02sM/VfZ7HAuZ +61TPxohL+e+X1FYeqIXYGXJmCEuB8WEmBg== +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem new file mode 100644 index 00000000..2be4bca5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/b.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIIUjCCB/egAwIBAgIRALiJR3zQjp0MevT/Hk89sfAwCgYIKoZIzj0EAwIwgZIx +CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV +BAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYDVQQD +Ey9DT01PRE8gRUNDIERvbWFpbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0Eg +MjAeFw0xOTA3MzEwMDAwMDBaFw0yMDAyMDYyMzU5NTlaMGwxITAfBgNVBAsTGERv +bWFpbiBDb250cm9sIFZhbGlkYXRlZDEhMB8GA1UECxMYUG9zaXRpdmVTU0wgTXVs +dGktRG9tYWluMSQwIgYDVQQDExtzc2wzODczMzcuY2xvdWRmbGFyZXNzbC5jb20w +WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARPFdjdnBIJRPnHCPsCBJ/MmPytXnZX +KV6lD2bbG5EVNuUQln4Na8heCY+sfpV+SPuuiNzZxgDA46GvyzdRYFhxo4IGUTCC +Bk0wHwYDVR0jBBgwFoAUQAlhZ/C8g3FP3hIILG/U1Ct2PZYwHQYDVR0OBBYEFGLh +bHk1KAYIRfVwXA3L+yDf0CxjMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA +MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysG +AQQBsjEBAgIHMCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5j +b20vQ1BTMAgGBmeBDAECATBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLmNv +bW9kb2NhNC5jb20vQ09NT0RPRUNDRG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZl +ckNBMi5jcmwwgYgGCCsGAQUFBwEBBHwwejBRBggrBgEFBQcwAoZFaHR0cDovL2Ny +dC5jb21vZG9jYTQuY29tL0NPTU9ET0VDQ0RvbWFpblZhbGlkYXRpb25TZWN1cmVT +ZXJ2ZXJDQTIuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC5jb21vZG9jYTQu +Y29tMIIDkAYDVR0RBIIDhzCCA4OCG3NzbDM4NzMzNy5jbG91ZGZsYXJlc3NsLmNv +bYIMKi5hanJ0Y3QuY29tghMqLmFrcmVwYnVyY3UuZ2VuLnRyghUqLmFuZHJlYXNr +YW5lbGxvcy5jb22CDSouYW5zaWJsZS5jb22CGSouYXJ0b2Z0b3VjaC1raW5nd29v +ZC5jb22CFyouYm91bGRlcnN3YXRlcmhvbGUuY29tghcqLmJyb2Nrc3RlY2hzdXBw +b3J0LmNvbYIQKi5idXJjbGFyLndlYi50coIcKi5ob3Blc29uZ2ZyZW5jaGJ1bGxk +b2dzLm5ldIIMKi5odXJyZW0uY29tghAqLmh5dmVsaWNvbnMuY29tghAqLmthcm1h +Zml0LmNvLnVrghUqLmxvd3J5c3lzdGVtc2luYy5jb22CDioubWFuaWNydW4uY29t +ghUqLm11dHVvZmluYW5jaWVyYS5jb22CDyoucGlsZ3JpbWFnZS5waIINKi5wa2dh +bWVzLm9yZ4IbKi5ybHBjb25zdWx0aW5nc2VydmljZXMuY29tghYqLnJ1eWF0YWJp +cmxlcmkuZ2VuLnRyghQqLnJ5YW5hcHBoeXNpY3NjLmNvbYIVKi53ZWFyaXRiYWNr +d2FyZHMub3Jngg8qLnlldGlzbmFjay5jb22CCmFqcnRjdC5jb22CEWFrcmVwYnVy +Y3UuZ2VuLnRyghNhbmRyZWFza2FuZWxsb3MuY29tggthbnNpYmxlLmNvbYIXYXJ0 +b2Z0b3VjaC1raW5nd29vZC5jb22CFWJvdWxkZXJzd2F0ZXJob2xlLmNvbYIVYnJv +Y2tzdGVjaHN1cHBvcnQuY29tgg5idXJjbGFyLndlYi50coIaaG9wZXNvbmdmcmVu +Y2hidWxsZG9ncy5uZXSCCmh1cnJlbS5jb22CDmh5dmVsaWNvbnMuY29tgg5rYXJt +YWZpdC5jby51a4ITbG93cnlzeXN0ZW1zaW5jLmNvbYIMbWFuaWNydW4uY29tghNt +dXR1b2ZpbmFuY2llcmEuY29tgg1waWxncmltYWdlLnBoggtwa2dhbWVzLm9yZ4IZ +cmxwY29uc3VsdGluZ3NlcnZpY2VzLmNvbYIUcnV5YXRhYmlybGVyaS5nZW4udHKC +EnJ5YW5hcHBoeXNpY3NjLmNvbYITd2Vhcml0YmFja3dhcmRzLm9yZ4INeWV0aXNu +YWNrLmNvbTCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALIeBcyLos2KIE6HZvkr +uYolIGdr2vpw57JJUy3vi5BeAAABbEVw8SgAAAQDAEcwRQIgE2YeTfb/d4BBUwpZ +ihWXSR+vRyNNUg8GlOak2MFMHv0CIQCLBvtU401m5/Psg9KirQZs321BSxgUKgSQ +m9M691d3eQB2AF6nc/nfVsDntTZIfdBJ4DJ6kZoMhKESEoQYdZaBcUVYAAABbEVw +8VgAAAQDAEcwRQIgGYsGfr3/mekjzMS9+ALAjx1ryfIfhXB/+UghTcw4Y8ICIQDS +K2L18WX3+Oh4TjJhjh5tV1iYyZVYivcwwbr7mtmOqjAKBggqhkjOPQQDAgNJADBG +AiEAjNt7LF78GV7snky9jwFcBsLH55ndzduvsrkJ7Ne1SgYCIQDsMJsTr9VP6kar +4Kv8V9zNBmpGrGNuE7A1GixBvzNaHA== +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert new file mode 100644 index 00000000..6997766a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.0.cert @@ -0,0 +1,121 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert new file mode 100644 index 00000000..51f64f08 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.1.cert @@ -0,0 +1,69 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIyOFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBvcmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuzKhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsUgdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9iYWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdvc3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2dsb2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5jb20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkhbxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFpCJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LFrJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zozA20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVace2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0yMjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vKEymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPanH05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jzkE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5BxxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uDJGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNVHSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4EFgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/AVn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTsY4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMbSXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert new file mode 100644 index 00000000..ce299241 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.2.cert @@ -0,0 +1,113 @@ +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert new file mode 100644 index 00000000..0c947b17 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.3.cert @@ -0,0 +1,124 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + + + + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert new file mode 100644 index 00000000..adbb8edc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-1.4.cert @@ -0,0 +1,86 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIHTCCBgWgAwIBAgIUCqrrzSfjzaoyB3DOxst2kMxFp/MwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyMTIyMjIy +OFoXDTIxMDgyMTIyMzIwMFowgZsxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MSYwJAYDVQQD +DB1kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMPAPH2y206qios2NMzlCNJv1mrwC1/8tH2HOqJGiYZB +O7QOBRSvJsV++IozCB8ap99e8B64OOAQPOyykrdXd2axhftmMb1SFMF56eukHSuz +KhKWRUgHs0UFRU51lDcBcOvphwJ+5SOgqrqKFFFBgJ0ZpcP54JpFwKIdh3ac10x2 +mBaW5ccqdv5X9oEMu1D/yivBmy34tsbLYyfttCjP76iVT7UVYHjHWynnIhsEyMsU +gdM90NzrTlrvTSi/EcCD1W3+8b0f+G1TI5rhHbKwR0n/mv5QLFm7EABoYPhxS8bX +B+9tE67yb0RyWbgvUiHySRynQLNMRpRx8Y9bA8uC8n8CAwEAAaOCA6QwggOgMAkG +A1UdEwQCMAAwHwYDVR0jBBgwFoAUsxKJtalLNbwVAPCA6dh4h/ETfHYwcwYIKwYB +BQUHAQEEZzBlMDcGCCsGAQUFBzAChitodHRwOi8vdHJ1c3QucXVvdmFkaXNnbG9i +YWwuY29tL3F2c3NsZzMuY3J0MCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5xdW92 +YWRpc2dsb2JhbC5jb20wgZ8GA1UdEQSBlzCBlIIdZGV2LmVuZXJneS5pbnNpZGUu +dGVsc3RyYS5jb22CJXJlcG9ydHMuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5j +b22CJ2dyZWVuc3luYy5kZXYuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbYIjbmdv +c3MuZGV2LmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wUQYDVR0gBEowSDBGBgwr +BgEEAb5YAAJkAQEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5xdW92YWRpc2ds +b2JhbC5jb20vcmVwb3NpdG9yeTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5xdW92YWRpc2dsb2JhbC5j +b20vcXZzc2xnMy5jcmwwHQYDVR0OBBYEFEoJQRpPC/V5ZK3mMkszZE2v6vh+MA4G +A1UdDwEB/wQEAwIFoDCCAXwGCisGAQQB1nkCBAIEggFsBIIBaAFmAHUAVhQGmi/X +wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFstk9Y+gAABAMARjBEAiBFMZa6 +O9iXVjy2kqQa54vgNFdU7shgFJJhm//fSAQZUAIgBIL/yPdh+XiuQS2xPhCzNYkh +bxf7BbN4qUISESgiZpsAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZ +EwAAAWy2T1nKAAAEAwBHMEUCIG0tp63jLsDsfCTDlcvV5ItjRkbUJBnkxlPdP2PH +88sTAiEApgaPofVdn2hdI12iDDex72ta+9wpwQ1MxoaJn2nt+qEAdQDuS723dc5g +uuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWy2T1iJAAAEAwBGMEQCIE/mzEFp +CJUc71jvwJa4Px86R3ZYK4mHmUlQAUZqd0ZkAiBdEmT8xxTuleSUlYHEkKCK/FZX +L+vsYJpPrA9TsO5IsTANBgkqhkiG9w0BAQsFAAOCAgEApE9WLz3S8tqA9Dk3r9LF +rJy8km9cBt1O9SQZwFsduGKGdF3Fd+/Y0V7UrFDzrX+NIzqcmgBHKxaIXorMBF70 +ajMaaROP2ymkpEXnruEwoR47fbW+JRAWDRm2xnouQveQX9ZcgCLbBvAWBqpndQj2 +DGmLJhNz5GlFBjh3PQZlU1w8hU7TrDxa7M1GMtVnk8X+o3l/MX9iPeEs+PiC4dHD +hpj84RY1VQJz8+10rql47SB5YgbwcqaizTG4ax/OAv1JHNWtfAodIMX8Y8X00zoz +A20LQv880jCCNANVNbrXJ3h4X3xwW/C1X9vYk0shymZJbT5u17JbPD1cy39bA7kT +F4L7scdQRxvcqazYN4/IdgvgMji9OltiYufP88Ti8KB2tcl2accpiC5St/zllGD1 +hqEeYLMzjyvUKR/1uvURQQtc0DPvBRmvkB+aI4g+sLkTTFWj5bsA1vKU8SDCyMuB +RQV11DId5+RNNCmWnskORUZJQssvY49pnfCxCES2nt3l/XzTzVtLYmd6G9uAqVac +e2ibnmDrFVlmlyRsCiMfZl5/OTJzt7Cj3az59m5Syfw/lnS9YP82t/r/ufuKkO5Q +q5a9aI8DuNNmAjR4lpIJNqIpX/y+dG2aGmx4XTc31MR9szWtiTgOHe0MkMupOAL0 +qkHrBgwo1zjuTMf3QOg6Z5Q= +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert new file mode 100644 index 00000000..2b82edf6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/chain-4.cert @@ -0,0 +1,121 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=prod.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +MIIIJDCCBgygAwIBAgIUP9S/56XvOFzWk1vp1+7JJT17brEwDQYJKoZIhvcNAQEL +BQAwTTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxIzAh +BgNVBAMTGlF1b1ZhZGlzIEdsb2JhbCBTU0wgSUNBIEczMB4XDTE5MDgyNzAzMTU1 +NFoXDTIxMDgyNzAzMjUwMFowgZwxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhWaWN0 +b3JpYTESMBAGA1UEBwwJTWVsYm91cm5lMSQwIgYDVQQKDBtUZWxzdHJhIENvcnBv +cmF0aW9uIExpbWl0ZWQxFzAVBgNVBAsMDlRlbHN0cmEgRW5lcmd5MScwJQYDVQQD +DB5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCrRouNZFOZwM1qyAU6v6ag9fzSx3y8zz36nR8HuqbA +/wqrbMmnpofwdx/9u1bilsHfJzIODv0hm7aGk+neTK3DIapiII3m0HKW0v+GLsl7 +JkDuc2o3XlakcXlA45qDKCZXbXZtY4/kdxKG0OSUZi7oQqohhYl/c/ojrTiey+4G +KhEVqWwOuQ1OC1DRw4qMH54d0koFxxSLPJ8JiiztLlK/e9n8BoJikj5fBqWy5R1F +bGXCdzjcfmPV6iSOzJShpUgj4ga91mO6j3S6LLfK5ibbTlY+pmUxUT+m9nKMon3h +mFptTYo9t9vUF/a/owjRxNLg01fJLNjYn8QV2vQvODGfAgMBAAGjggOqMIIDpjAJ +BgNVHRMEAjAAMB8GA1UdIwQYMBaAFLMSibWpSzW8FQDwgOnYeIfxE3x2MHMGCCsG +AQUFBwEBBGcwZTA3BggrBgEFBQcwAoYraHR0cDovL3RydXN0LnF1b3ZhZGlzZ2xv +YmFsLmNvbS9xdnNzbGczLmNydDAqBggrBgEFBQcwAYYeaHR0cDovL29jc3AucXVv +dmFkaXNnbG9iYWwuY29tMIGjBgNVHREEgZswgZiCHnByb2QuZW5lcmd5Lmluc2lk +ZS50ZWxzdHJhLmNvbYImcmVwb3J0cy5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3Ry +YS5jb22CKGdyZWVuc3luYy5wcm9kLmVuZXJneS5pbnNpZGUudGVsc3RyYS5jb22C +JG5nb3NzLnByb2QuZW5lcmd5Lmluc2lkZS50ZWxzdHJhLmNvbTBRBgNVHSAESjBI +MEYGDCsGAQQBvlgAAmQBATA2MDQGCCsGAQUFBwIBFihodHRwOi8vd3d3LnF1b3Zh +ZGlzZ2xvYmFsLmNvbS9yZXBvc2l0b3J5MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr +BgEFBQcDATA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3JsLnF1b3ZhZGlzZ2xv +YmFsLmNvbS9xdnNzbGczLmNybDAdBgNVHQ4EFgQUoIME5TykVAI8VF5g0zeh0xdv +i3owDgYDVR0PAQH/BAQDAgWgMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgBW +FAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ3QAAAWzRG8r0AAAEAwBHMEUC +IQDShuQyYMiy7KKxWOzffolVIcPRgWD7ClNEbIcUATHKyQIgXnTZBXcpcbXBQXLs +tFuvY36TbKIYc2ql2nmdydGQ9wcAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jj +d80OyA3cEAAAAWzRG8sAAAAEAwBHMEUCIGsLEoA9S7pNE3VoNZHxl2IAdeP3Dy2Q +Mk0rM46hp6CRAiEA08rOjswSdcn7qgDEoiyvlcrOTIFJAEcMlxSY65yLVUwAdgBV +gdTCFpA2AUrqC5tXPFPwwOQ4eHAlCBcvo6odBxPTDAAAAWzRG8q7AAAEAwBHMEUC +IAkVCcTFG8MBDI58JKIhMlPbzkdrKnYY3Kp9KqWuTAvMAiEAipeI7RCLBk8+T/p+ +gY7+vtFZxKDthcJMUpZz7qmica0wDQYJKoZIhvcNAQELBQADggIBAESe0U1qArxL +F2uk65q6x6HBcZuSocpceokzcUBv07Kxs6UJU9ybTbl8VYPuC+OUdpvut1kOJCJm +1TRrr5KMh+9as42xkbKRZnh5TQt7aHmVcLHLfA4x0UrELfNX3fVTDxwDAPAhE5oM +0w+d1foLakh7dXKKSxobEI3KRwFp19iuZeIqwI8XMWMr9ajhTC0T7D2QvKotpNBS +sNDHiIE3IXoa9o7UiOG8IfW0wAt7CEygv0F7ctHRTcQSP/SJIGYOUZ7uotULVL5i +elG31Y83Jx3sPNCy4IZfCip6Gw7MgsN2CZGApqi49edSqDWyRIfmCeXtMc7XI7Md +kqqWxbqGGTdYJCucoGqahqRR+BI9anEqTD9T5Gy0TpCi2pgp1i7czza71nfz0PcN +R0pw/1lqb9AqmJ2XELpBpo82B9XGple9thpincai7jPk3ezY5eEvDTmkHRlUFCp8 +8M66Ga19hZTgnHPWDKZYZzuZ7Lcl2WbapFOYYHJggSpBRy4GkH6eTSkUB9G9k8vU +gbvtS7sR5ggecbCBu0M4TWYmnUojR8UXtr0oOTlXysTHVGs5Tx9ChhOLyUqhX8tM +1zSDT8JJvbbw4RqpGzBKTNaO5nxRLgKVQOQdM8f1kjMr9/U58Lc4UiaTkJM14VfK +8GfV8+K/vRCBtME53ILvm1l18jtakG3c +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIGFzCCA/+gAwIBAgIUftbnnMmtgcTIGT75XUQodw40ExcwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjExMDYxNDUwMThaFw0y +MjExMDYxNDUwMThaME0xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMSMwIQYDVQQDExpRdW9WYWRpcyBHbG9iYWwgU1NMIElDQSBHMzCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANf8Od17be6c6lTGJDhEXpmkTs4y +Q39Rr5VJyBeWCg06nSS71s6xF3sZvKcV0MbXlXCYM2ZX7cNTbJ81gs7uDsKFp+vK +EymiKyEiI2SImOtECNnSg+RVR4np/xz/UlC0yFUisH75cZsJ8T1pkGMfiEouR0EM +7O0uFgoboRfUP582TTWy0F7ynSA6YfGKnKj0OFwZJmGHVkLs1VevWjhj3R1fsPan +H05P5moePFnpQdj1FofoSxUHZ0c7VB+sUimboHm/uHNY1LOsk77qiSuVC5/yrdg3 +2EEfP/mxJYT4r/5UiD7VahySzeZHzZ2OibQm2AfgfMN3l57lCM3/WPQBhMAPS1jz +kE+7MjajM2f0aZctimW4Hasrj8AQnfAdHqZehbhtXaAlffNEzCdpNK584oCTVR7N +UR9iZFx83ruTqpo+GcLP/iSYqhM4g7fy45sNhU+IS+ca03zbxTl3TTlkofXunI5B +xxE30eGSQpDZ5+iUJcEOAuVKrlYocFbB3KF45hwcbzPWQ1DcO2jFAapOtQzeS+MZ +yZzT2YseJ8hQHKu8YrXZWwKaNfyl8kFkHUBDICowNEoZvBwRCQp8sgqL6YRZy0uD +JGxmnC2e0BVKSjcIvmq/CRWH7yiTk9eWm73xrsg9iIyD/kwJEnLyIk8tR5V8p/hc +1H2AjDrZH12PsZ45AgMBAAGjgfMwgfAwEgYDVR0TAQH/BAgwBgEB/wIBATARBgNV +HSAECjAIMAYGBFUdIAAwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRw +Oi8vb2NzcC5xdW92YWRpc2dsb2JhbC5jb20wDgYDVR0PAQH/BAQDAgEGMB8GA1Ud +IwQYMBaAFO3nb3Zav2DsSVvGpXe7chZxm8Q9MDsGA1UdHwQ0MDIwMKAuoCyGKmh0 +dHA6Ly9jcmwucXVvdmFkaXNnbG9iYWwuY29tL3F2cmNhMmczLmNybDAdBgNVHQ4E +FgQUsxKJtalLNbwVAPCA6dh4h/ETfHYwDQYJKoZIhvcNAQELBQADggIBAFGm1Fqp +RMiKr7a6h707M+km36PVXZnX1NZocCn36MrfRvphotbOCDm+GmRkar9ZMGhc8c/A +Vn7JSCjwF9jNOFIOUyNLq0w4luk+Pt2YFDbgF8IDdx53xIo8Gv05e9xpTvQYaIto +qeHbQjGXfSGc91olfX6JUwZlxxbhdJH+rxTFAg0jcbqToJoScWTfXSr1QRcNbSTs +Y4CPG6oULsnhVvrzgldGSK+DxFi2OKcDsOKkV7W4IGg8Do2L/M588AfBnV8ERzpl +qgMBBQxC2+0N6RdFHbmZt0HQE/NIg1s0xcjGx1XW3YTOfje31rmAXKHOehm4Bu48 +gr8gePq5cdQ2W9tA0Dnytb9wzH2SyPPIXRI7yNxaX9H8wYeDeeiKSSmQtfh1v5cV +7RXvm8F6hLJkkco/HOW3dAUwZFcKsUH+1eUJKLN18eDGwB8yGawjHvOKqcfg5Lf/ +TvC7hgcx7pDYaCCaqHaekgUwXbB2Enzqr1fdwoU1c01W5YuQAtAx5wk1bf34Yq/J +ph7wNXGvo88N0/EfP9AdVGmJzy7VuRXeVAOyjKAIeADMlwpjBRhcbs9m3dkqvoMb +SXKJxv/hFmNgEOvOlaFsXX1dbKg1v+C1AzKAFdiuAIa62JzASiEhigqNSdqdTsOh +8W8hdONuKKpe9zKedhBFAvuxhDgKmnySglYc +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert new file mode 100644 index 00000000..1d9bbe21 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-a.cert @@ -0,0 +1,18 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +aaa +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +bbb +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +ccc +-----END CERTIFICATE----- + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert new file mode 100644 index 00000000..1d9bbe21 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/certs/simple-chain-b.cert @@ -0,0 +1,18 @@ +subject=/C=AU/ST=Victoria/L=Melbourne/O=Telstra Corporation Limited/OU=Telstra Energy/CN=dev.energy.inside.telstra.com +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +-----BEGIN CERTIFICATE----- +aaa +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Global SSL ICA G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +bbb +-----END CERTIFICATE----- + +subject=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +issuer=/C=BM/O=QuoVadis Limited/CN=QuoVadis Root CA 2 G3 +-----BEGIN CERTIFICATE----- +ccc +-----END CERTIFICATE----- + diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zip b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zip Binary files differnew file mode 100644 index 00000000..6eaefdd5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/fixtures/thezip.zip diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/.gitkeep diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json new file mode 100644 index 00000000..36f1489b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.CreateStack_1.json @@ -0,0 +1,17 @@ +{ + "status_code": 200, + "data": { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:07 GMT", + "content-length": "393", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json new file mode 100644 index 00000000..d526155a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DeleteStack_1.json @@ -0,0 +1,16 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "170d1e02-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "170d1e02-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:39 GMT", + "content-length": "212", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json new file mode 100644 index 00000000..3758c77b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_1.json @@ -0,0 +1,38 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "043d4a05-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "043d4a05-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:08 GMT", + "content-length": "1183", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json new file mode 100644 index 00000000..2c5a7655 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_2.json @@ -0,0 +1,80 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "075d9d71-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "075d9d71-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:13 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json new file mode 100644 index 00000000..cf2c2450 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_3.json @@ -0,0 +1,80 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:19 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json new file mode 100644 index 00000000..32ee9c1c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_4.json @@ -0,0 +1,80 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:24 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json new file mode 100644 index 00000000..b547cd4d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_5.json @@ -0,0 +1,80 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:29 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json new file mode 100644 index 00000000..15bd043a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_6.json @@ -0,0 +1,100 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z", + "ResourceStatus": "CREATE_COMPLETE", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 33, + "microsecond": 200000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "3490", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:34 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json new file mode 100644 index 00000000..87db7c59 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStackEvents_7.json @@ -0,0 +1,119 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "140d7220-b5d0-11e7-933f-50a686be7356", + "ResourceStatus": "CREATE_COMPLETE", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 35, + "microsecond": 121000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z", + "ResourceStatus": "CREATE_COMPLETE", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 33, + "microsecond": 200000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-basic-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "LogicalResourceId": "ansible-test-basic-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "16faf590-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "16faf590-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "4276", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:39 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..7acdb3ac --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_1.json @@ -0,0 +1,40 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EnableTerminationProtection": false, + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "StackStatusReason": "User Initiated", + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "042974db-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "042974db-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:08 GMT", + "content-length": "975", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json new file mode 100644 index 00000000..0ed674b2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_2.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "074b26dc-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "074b26dc-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:13 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json new file mode 100644 index 00000000..633c5e15 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_3.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:18 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json new file mode 100644 index 00000000..e5ca69dd --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_4.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:23 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json new file mode 100644 index 00000000..31a3057c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_5.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:28 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json new file mode 100644 index 00000000..90ca7467 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_6.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:33 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json new file mode 100644 index 00000000..905c04f4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/basic_s3_stack/cloudformation.DescribeStacks_7.json @@ -0,0 +1,45 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-basic-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "Outputs": [ + { + "OutputKey": "TheName", + "OutputValue": "ansible-test-basic-yaml-mybucket-13m2y4v8bptj4" + } + ], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-basic-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_COMPLETE", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:39 GMT", + "content-length": "1115", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json new file mode 100644 index 00000000..9084936a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.CreateStack_1.json @@ -0,0 +1,17 @@ +{ + "status_code": 200, + "data": { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "03fbfc36-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:07 GMT", + "content-length": "393", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json new file mode 100644 index 00000000..d526155a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DeleteStack_1.json @@ -0,0 +1,16 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "170d1e02-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "170d1e02-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:39 GMT", + "content-length": "212", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json new file mode 100644 index 00000000..399eab49 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_1.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "043d4a05-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "043d4a05-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:08 GMT", + "content-length": "1183", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json new file mode 100644 index 00000000..f57dbf53 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_2.json @@ -0,0 +1,83 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "075d9d71-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "075d9d71-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:13 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json new file mode 100644 index 00000000..c8b4d694 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_3.json @@ -0,0 +1,83 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0a7eb31b-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:19 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json new file mode 100644 index 00000000..8bb03ede --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_4.json @@ -0,0 +1,83 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0d9e1c06-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:24 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json new file mode 100644 index 00000000..311949d0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_5.json @@ -0,0 +1,83 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "10bd84ca-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "2730", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:29 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json new file mode 100644 index 00000000..ddab94a5 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_6.json @@ -0,0 +1,104 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z", + "ResourceStatus": "CREATE_COMPLETE", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 33, + "microsecond": 200000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "13dbb3fd-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "3490", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:34 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json new file mode 100644 index 00000000..86da5fb4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStackEvents_7.json @@ -0,0 +1,124 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "140d7220-b5d0-11e7-933f-50a686be7356", + "ResourceStatus": "CREATE_COMPLETE", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 35, + "microsecond": 121000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_COMPLETE-2017-10-20T19:51:33.200Z", + "ResourceStatus": "CREATE_COMPLETE", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 33, + "microsecond": 200000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:12.754Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 12, + "microsecond": 754000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "Resource creation Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "MyBucket-CREATE_IN_PROGRESS-2017-10-20T19:51:11.159Z", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::S3::Bucket", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 11, + "microsecond": 159000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "ResourceProperties": "{}\n", + "PhysicalResourceId": "", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "MyBucket" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EventId": "04032730-b5d0-11e7-86b8-503ac93168c5", + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "ResourceStatusReason": "User Initiated", + "StackName": "ansible-test-client-request-token-yaml", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "ClientRequestToken": "3faf3fb5-b289-41fc-b940-44151828f6cf", + "LogicalResourceId": "ansible-test-client-request-token-yaml" + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "16faf590-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "16faf590-b5d0-11e7-ae09-550cfe4b2358", + "vary": "Accept-Encoding", + "content-length": "4276", + "content-type": "text/xml", + "date": "Fri, 20 Oct 2017 19:51:39 GMT" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..7734b0ca --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_1.json @@ -0,0 +1,40 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "EnableTerminationProtection": false, + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "StackStatusReason": "User Initiated", + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "042974db-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "042974db-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:08 GMT", + "content-length": "975", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json new file mode 100644 index 00000000..0a1e74d7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_2.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "074b26dc-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "074b26dc-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:13 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json new file mode 100644 index 00000000..12d5839f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_3.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0a6cb1b3-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:18 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json new file mode 100644 index 00000000..a3cb0a8c --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_4.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "0d8cddf1-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:23 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json new file mode 100644 index 00000000..251d71fa --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_5.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "10ac94d5-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:28 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json new file mode 100644 index 00000000..2251125f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_6.json @@ -0,0 +1,39 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "13caeb1b-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:33 GMT", + "content-length": "913", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json new file mode 100644 index 00000000..aa8c7fd0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/client_request_token_s3_stack/cloudformation.DescribeStacks_7.json @@ -0,0 +1,45 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-client-request-token-yaml/04023cd0-b5d0-11e7-86b8-503ac93168c5", + "Description": "Basic template that creates an S3 bucket", + "Tags": [], + "Outputs": [ + { + "OutputKey": "TheName", + "OutputValue": "ansible-test-client-request-token-yaml-mybucket-13m2y4v8bptj4" + } + ], + "EnableTerminationProtection": false, + "CreationTime": { + "hour": 19, + "__class__": "datetime", + "month": 10, + "second": 8, + "microsecond": 324000, + "year": 2017, + "day": 20, + "minute": 51 + }, + "StackName": "ansible-test-client-request-token-yaml", + "NotificationARNs": [], + "StackStatus": "CREATE_COMPLETE", + "DisableRollback": false, + "RollbackConfiguration": {} + } + ], + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 200, + "RequestId": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "16ea53bb-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:39 GMT", + "content-length": "1115", + "content-type": "text/xml" + } + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json new file mode 100644 index 00000000..109feacd --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_1.json @@ -0,0 +1,22 @@ +{ + "status_code": 400, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "RequestId": "179d9e46-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "179d9e46-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:40 GMT", + "content-length": "301", + "content-type": "text/xml", + "connection": "close" + } + }, + "Error": { + "Message": "Stack [ansible-test-nonexist] does not exist", + "Code": "ValidationError", + "Type": "Sender" + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json new file mode 100644 index 00000000..589f92cc --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStackEvents_2.json @@ -0,0 +1,22 @@ +{ + "status_code": 400, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "RequestId": "17d80f44-b5d0-11e7-80c4-9f499f779cdb", + "HTTPHeaders": { + "x-amzn-requestid": "17d80f44-b5d0-11e7-80c4-9f499f779cdb", + "date": "Fri, 20 Oct 2017 19:51:40 GMT", + "content-length": "301", + "content-type": "text/xml", + "connection": "close" + } + }, + "Error": { + "Message": "Stack [ansible-test-nonexist] does not exist", + "Code": "ValidationError", + "Type": "Sender" + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..ea227415 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/delete_nonexistent_stack/cloudformation.DescribeStacks_1.json @@ -0,0 +1,22 @@ +{ + "status_code": 400, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "RequestId": "175fab26-b5d0-11e7-9d9b-45815c77100a", + "HTTPHeaders": { + "x-amzn-requestid": "175fab26-b5d0-11e7-9d9b-45815c77100a", + "date": "Fri, 20 Oct 2017 19:51:40 GMT", + "content-length": "307", + "content-type": "text/xml", + "connection": "close" + } + }, + "Error": { + "Message": "Stack with id ansible-test-nonexist does not exist", + "Code": "ValidationError", + "Type": "Sender" + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..cf29c6c7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/get_nonexistent_stack/cloudformation.DescribeStacks_1.json @@ -0,0 +1,22 @@ +{ + "status_code": 400, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "RequestId": "181566c8-b5d0-11e7-9d9b-45815c77100a", + "HTTPHeaders": { + "x-amzn-requestid": "181566c8-b5d0-11e7-9d9b-45815c77100a", + "date": "Fri, 20 Oct 2017 19:51:41 GMT", + "content-length": "307", + "content-type": "text/xml", + "connection": "close" + } + }, + "Error": { + "Message": "Stack with id ansible-test-nonexist does not exist", + "Code": "ValidationError", + "Type": "Sender" + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json new file mode 100644 index 00000000..7ad6cac9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/invalid_template_json/cloudformation.CreateStack_1.json @@ -0,0 +1,22 @@ +{ + "status_code": 400, + "data": { + "ResponseMetadata": { + "RetryAttempts": 0, + "HTTPStatusCode": 400, + "RequestId": "03b1107f-b5d0-11e7-ae09-550cfe4b2358", + "HTTPHeaders": { + "x-amzn-requestid": "03b1107f-b5d0-11e7-ae09-550cfe4b2358", + "date": "Fri, 20 Oct 2017 19:51:07 GMT", + "content-length": "320", + "content-type": "text/xml", + "connection": "close" + } + }, + "Error": { + "Message": "Template format error: JSON not well-formed. (line 4, column 4)", + "Code": "ValidationError", + "Type": "Sender" + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json new file mode 100644 index 00000000..64c8e1f2 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.CreateStack_1.json @@ -0,0 +1,17 @@ +{ + "status_code": 200, + "data": { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResponseMetadata": { + "RequestId": "c741ebcd-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "c741ebcd-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "407", + "date": "Tue, 26 Feb 2019 21:37:55 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json new file mode 100644 index 00000000..7a6a4964 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_1.json @@ -0,0 +1,38 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "c74b1310-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "c7b0b337-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "c7b0b337-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "1153", + "date": "Tue, 26 Feb 2019 21:37:56 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json new file mode 100644 index 00000000..6218ed8b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_2.json @@ -0,0 +1,101 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 1, + "microsecond": 107000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 657000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 221000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "c74b1310-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "caf667e9-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "caf667e9-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "4312", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:38:01 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json new file mode 100644 index 00000000..cde6beb8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_3.json @@ -0,0 +1,121 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "cafc8250-3a0e-11e9-86c5-02035744c0fa", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 2, + "microsecond": 76000 + }, + "ResourceStatus": "DELETE_IN_PROGRESS", + "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user." + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 1, + "microsecond": 107000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 657000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 221000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "c74b1310-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "ce498af1-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "ce498af1-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "5207", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:38:06 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json new file mode 100644 index 00000000..4f35d6dd --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_4.json @@ -0,0 +1,180 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "d19c8600-3a0e-11e9-a4ba-0a3524ef8042", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 13, + "microsecond": 177000 + }, + "ResourceStatus": "DELETE_COMPLETE" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-DELETE_COMPLETE-2019-02-26T21:38:12.486Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 12, + "microsecond": 486000 + }, + "ResourceStatus": "DELETE_COMPLETE", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-DELETE_IN_PROGRESS-2019-02-26T21:38:12.139Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 12, + "microsecond": 139000 + }, + "ResourceStatus": "DELETE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "cafc8250-3a0e-11e9-86c5-02035744c0fa", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 2, + "microsecond": 76000 + }, + "ResourceStatus": "DELETE_IN_PROGRESS", + "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user." + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 1, + "microsecond": 107000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 657000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 221000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "c74b1310-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "d19fbb1b-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "d19fbb1b-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "7857", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:38:12 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json new file mode 100644 index 00000000..68a743f8 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStackEvents_5.json @@ -0,0 +1,180 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "d19c8600-3a0e-11e9-a4ba-0a3524ef8042", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 13, + "microsecond": 177000 + }, + "ResourceStatus": "DELETE_COMPLETE" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-DELETE_COMPLETE-2019-02-26T21:38:12.486Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 12, + "microsecond": 486000 + }, + "ResourceStatus": "DELETE_COMPLETE", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-DELETE_IN_PROGRESS-2019-02-26T21:38:12.139Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 12, + "microsecond": 139000 + }, + "ResourceStatus": "DELETE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "cafc8250-3a0e-11e9-86c5-02035744c0fa", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 2, + "microsecond": 76000 + }, + "ResourceStatus": "DELETE_IN_PROGRESS", + "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user." + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:38:01.107Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 1, + "microsecond": 107000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: ca5769ae-3a0e-11e9-a183-3f277586a4cb)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.657Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-8jlpw72yz5x8", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 657000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:38:00.221Z", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 0, + "microsecond": 221000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "EventId": "c74b1310-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "LogicalResourceId": "ansible-test-on-create-failure-delete", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "d4fbddab-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "d4fbddab-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "7857", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:38:18 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..cf5f86ac --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_1.json @@ -0,0 +1,42 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "RollbackConfiguration": {}, + "StackStatus": "CREATE_IN_PROGRESS", + "StackStatusReason": "User Initiated", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "c77fb823-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "c77fb823-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "1041", + "date": "Tue, 26 Feb 2019 21:37:56 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json new file mode 100644 index 00000000..71a9f54b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_2.json @@ -0,0 +1,41 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "RollbackConfiguration": {}, + "StackStatus": "CREATE_IN_PROGRESS", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "cad153b2-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "cad153b2-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "979", + "date": "Tue, 26 Feb 2019 21:38:01 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json new file mode 100644 index 00000000..c2028183 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_3.json @@ -0,0 +1,52 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "DeletionTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 2, + "microsecond": 76000 + }, + "RollbackConfiguration": {}, + "StackStatus": "DELETE_IN_PROGRESS", + "StackStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Delete requested by user.", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "ce24289a-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "ce24289a-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "1171", + "date": "Tue, 26 Feb 2019 21:38:06 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json new file mode 100644 index 00000000..89f83553 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_4.json @@ -0,0 +1,51 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "DeletionTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 2, + "microsecond": 76000 + }, + "RollbackConfiguration": {}, + "StackStatus": "DELETE_IN_PROGRESS", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "d16c27f2-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "d16c27f2-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "1041", + "date": "Tue, 26 Feb 2019 21:38:12 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json new file mode 100644 index 00000000..739c8293 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_delete/cloudformation.DescribeStacks_5.json @@ -0,0 +1,50 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-delete/c74a4fc0-3a0e-11e9-9a48-067794494828", + "StackName": "ansible-test-on-create-failure-delete", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 37, + "second": 55, + "microsecond": 909000 + }, + "DeletionTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 38, + "second": 2, + "microsecond": 76000 + }, + "RollbackConfiguration": {}, + "StackStatus": "DELETE_COMPLETE", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "d4c90dd6-3a0e-11e9-b25f-d1217e6893bf", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "d4c90dd6-3a0e-11e9-b25f-d1217e6893bf", + "content-type": "text/xml", + "content-length": "965", + "date": "Tue, 26 Feb 2019 21:38:18 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json new file mode 100644 index 00000000..86f1945f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.CreateStack_1.json @@ -0,0 +1,17 @@ +{ + "status_code": 200, + "data": { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "ResponseMetadata": { + "RequestId": "a396a58a-3a0f-11e9-b7db-3fe3824c73cb", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "a396a58a-3a0f-11e9-b7db-3fe3824c73cb", + "content-type": "text/xml", + "content-length": "411", + "date": "Tue, 26 Feb 2019 21:44:05 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json new file mode 100644 index 00000000..1a3a67c6 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DeleteStack_1.json @@ -0,0 +1,16 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "RequestId": "a78f0832-3a0f-11e9-b7db-3fe3824c73cb", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "a78f0832-3a0f-11e9-b7db-3fe3824c73cb", + "content-type": "text/xml", + "content-length": "212", + "date": "Tue, 26 Feb 2019 21:44:11 GMT" + }, + "RetryAttempts": 0 + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json new file mode 100644 index 00000000..58d7a89e --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_1.json @@ -0,0 +1,38 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "EventId": "a39e6ce0-3a0f-11e9-96ca-02f46dd00950", + "StackName": "ansible-test-on-create-failure-do-nothing", + "LogicalResourceId": "ansible-test-on-create-failure-do-nothing", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 5, + "microsecond": 553000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "a406cc84-3a0f-11e9-b7db-3fe3824c73cb", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "a406cc84-3a0f-11e9-b7db-3fe3824c73cb", + "content-type": "text/xml", + "content-length": "1169", + "date": "Tue, 26 Feb 2019 21:44:06 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json new file mode 100644 index 00000000..0a7e32e4 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStackEvents_2.json @@ -0,0 +1,121 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "EventId": "a6c32c80-3a0f-11e9-ac5e-06deb474fa52", + "StackName": "ansible-test-on-create-failure-do-nothing", + "LogicalResourceId": "ansible-test-on-create-failure-do-nothing", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 10, + "microsecond": 804000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. " + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:44:09.905Z", + "StackName": "ansible-test-on-create-failure-do-nothing", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-a8g0mh5il4t5", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 9, + "microsecond": 905000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: a62a6f71-3a0f-11e9-9164-457e0a3a5e1b)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:44:09.497Z", + "StackName": "ansible-test-on-create-failure-do-nothing", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-a8g0mh5il4t5", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 9, + "microsecond": 497000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:44:09.076Z", + "StackName": "ansible-test-on-create-failure-do-nothing", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 9, + "microsecond": 76000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "EventId": "a39e6ce0-3a0f-11e9-96ca-02f46dd00950", + "StackName": "ansible-test-on-create-failure-do-nothing", + "LogicalResourceId": "ansible-test-on-create-failure-do-nothing", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 5, + "microsecond": 553000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "a75fbad0-3a0f-11e9-b7db-3fe3824c73cb", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "a75fbad0-3a0f-11e9-b7db-3fe3824c73cb", + "content-type": "text/xml", + "content-length": "5231", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:44:11 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..53214331 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_1.json @@ -0,0 +1,42 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "StackName": "ansible-test-on-create-failure-do-nothing", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 5, + "microsecond": 553000 + }, + "RollbackConfiguration": {}, + "StackStatus": "CREATE_IN_PROGRESS", + "StackStatusReason": "User Initiated", + "DisableRollback": true, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "a3d44acf-3a0f-11e9-b7db-3fe3824c73cb", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "a3d44acf-3a0f-11e9-b7db-3fe3824c73cb", + "content-type": "text/xml", + "content-length": "1048", + "date": "Tue, 26 Feb 2019 21:44:05 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json new file mode 100644 index 00000000..df17f5a7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_do_nothing/cloudformation.DescribeStacks_2.json @@ -0,0 +1,42 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-do-nothing/a39dd0a0-3a0f-11e9-96ca-02f46dd00950", + "StackName": "ansible-test-on-create-failure-do-nothing", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 44, + "second": 5, + "microsecond": 553000 + }, + "RollbackConfiguration": {}, + "StackStatus": "CREATE_FAILED", + "StackStatusReason": "The following resource(s) failed to create: [ECRRepo]. ", + "DisableRollback": true, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "a7301f4a-3a0f-11e9-b7db-3fe3824c73cb", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "a7301f4a-3a0f-11e9-b7db-3fe3824c73cb", + "content-type": "text/xml", + "content-length": "1084", + "date": "Tue, 26 Feb 2019 21:44:11 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json new file mode 100644 index 00000000..f71422b9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.CreateStack_1.json @@ -0,0 +1,17 @@ +{ + "status_code": 200, + "data": { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResponseMetadata": { + "RequestId": "9139de54-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "9139de54-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "409", + "date": "Tue, 26 Feb 2019 21:43:34 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json new file mode 100644 index 00000000..111dc90d --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DeleteStack_1.json @@ -0,0 +1,16 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "RequestId": "988b3097-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "988b3097-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "212", + "date": "Tue, 26 Feb 2019 21:43:46 GMT" + }, + "RetryAttempts": 0 + } + } +}
\ No newline at end of file diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json new file mode 100644 index 00000000..2bcac7f0 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_1.json @@ -0,0 +1,38 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "9140bc10-3a0f-11e9-94bf-0a9edf17d014", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ansible-test-on-create-failure-rollback", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 34, + "microsecond": 740000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "9199b1a7-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "9199b1a7-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "1161", + "date": "Tue, 26 Feb 2019 21:43:35 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json new file mode 100644 index 00000000..3992fd39 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_2.json @@ -0,0 +1,121 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "945b90a0-3a0f-11e9-adaf-0211d8bec7e2", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ansible-test-on-create-failure-rollback", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 39, + "microsecond": 920000 + }, + "ResourceStatus": "ROLLBACK_IN_PROGRESS", + "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Rollback requested by user." + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:43:39.210Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 39, + "microsecond": 210000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: 93e0bb60-3a0f-11e9-a53c-7162bb423e4d)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.793Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 38, + "microsecond": 793000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.266Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 38, + "microsecond": 266000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "9140bc10-3a0f-11e9-94bf-0a9edf17d014", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ansible-test-on-create-failure-rollback", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 34, + "microsecond": 740000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "94e16307-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "94e16307-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "5241", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:43:40 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json new file mode 100644 index 00000000..e272c734 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStackEvents_3.json @@ -0,0 +1,180 @@ +{ + "status_code": 200, + "data": { + "StackEvents": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "9743bc70-3a0f-11e9-b335-0ade61d04ee6", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ansible-test-on-create-failure-rollback", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 44, + "microsecond": 797000 + }, + "ResourceStatus": "ROLLBACK_COMPLETE" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-DELETE_COMPLETE-2019-02-26T21:43:43.908Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 43, + "microsecond": 908000 + }, + "ResourceStatus": "DELETE_COMPLETE", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-DELETE_IN_PROGRESS-2019-02-26T21:43:43.478Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 43, + "microsecond": 478000 + }, + "ResourceStatus": "DELETE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "945b90a0-3a0f-11e9-adaf-0211d8bec7e2", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ansible-test-on-create-failure-rollback", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 39, + "microsecond": 920000 + }, + "ResourceStatus": "ROLLBACK_IN_PROGRESS", + "ResourceStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Rollback requested by user." + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-CREATE_FAILED-2019-02-26T21:43:39.210Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 39, + "microsecond": 210000 + }, + "ResourceStatus": "CREATE_FAILED", + "ResourceStatusReason": "Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided' (Service: AmazonECR; Status Code: 400; Error Code: InvalidParameterException; Request ID: 93e0bb60-3a0f-11e9-a53c-7162bb423e4d)", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.793Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "ansib-ecrre-1lsnxu2zpb20l", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 38, + "microsecond": 793000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "Resource creation Initiated", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "ECRRepo-CREATE_IN_PROGRESS-2019-02-26T21:43:38.266Z", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ECRRepo", + "PhysicalResourceId": "", + "ResourceType": "AWS::ECR::Repository", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 38, + "microsecond": 266000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceProperties": "{\"RepositoryPolicyText\":{\"Version\":\"3000-10-17\",\"Statement\":[{\"Action\":[\"ecr:*\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::123456789012:root\"}}]}}" + }, + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "EventId": "9140bc10-3a0f-11e9-94bf-0a9edf17d014", + "StackName": "ansible-test-on-create-failure-rollback", + "LogicalResourceId": "ansible-test-on-create-failure-rollback", + "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "ResourceType": "AWS::CloudFormation::Stack", + "Timestamp": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 34, + "microsecond": 740000 + }, + "ResourceStatus": "CREATE_IN_PROGRESS", + "ResourceStatusReason": "User Initiated" + } + ], + "ResponseMetadata": { + "RequestId": "982d0bff-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "982d0bff-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "7911", + "vary": "Accept-Encoding", + "date": "Tue, 26 Feb 2019 21:43:45 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json new file mode 100644 index 00000000..25facea1 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_1.json @@ -0,0 +1,42 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "StackName": "ansible-test-on-create-failure-rollback", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 34, + "microsecond": 740000 + }, + "RollbackConfiguration": {}, + "StackStatus": "CREATE_IN_PROGRESS", + "StackStatusReason": "User Initiated", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "91725383-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "91725383-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "1045", + "date": "Tue, 26 Feb 2019 21:43:35 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json new file mode 100644 index 00000000..55a80d8a --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_2.json @@ -0,0 +1,52 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "StackName": "ansible-test-on-create-failure-rollback", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 34, + "microsecond": 740000 + }, + "DeletionTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 39, + "microsecond": 920000 + }, + "RollbackConfiguration": {}, + "StackStatus": "ROLLBACK_IN_PROGRESS", + "StackStatusReason": "The following resource(s) failed to create: [ECRRepo]. . Rollback requested by user.", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "94bb1651-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "94bb1651-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "1179", + "date": "Tue, 26 Feb 2019 21:43:40 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json new file mode 100644 index 00000000..7c00a836 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/placebo_recordings/cloudformation/on_create_failure_rollback/cloudformation.DescribeStacks_3.json @@ -0,0 +1,51 @@ +{ + "status_code": 200, + "data": { + "Stacks": [ + { + "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/ansible-test-on-create-failure-rollback/914046e0-3a0f-11e9-94bf-0a9edf17d014", + "StackName": "ansible-test-on-create-failure-rollback", + "CreationTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 34, + "microsecond": 740000 + }, + "DeletionTime": { + "__class__": "datetime", + "year": 2019, + "month": 2, + "day": 26, + "hour": 21, + "minute": 43, + "second": 39, + "microsecond": 920000 + }, + "RollbackConfiguration": {}, + "StackStatus": "ROLLBACK_COMPLETE", + "DisableRollback": false, + "NotificationARNs": [], + "Tags": [], + "EnableTerminationProtection": false, + "DriftInformation": { + "StackDriftStatus": "NOT_CHECKED" + } + } + ], + "ResponseMetadata": { + "RequestId": "98016814-3a0f-11e9-b938-97983b40cabe", + "HTTPStatusCode": 200, + "HTTPHeaders": { + "x-amzn-requestid": "98016814-3a0f-11e9-b938-97983b40cabe", + "content-type": "text/xml", + "content-length": "1044", + "date": "Tue, 26 Feb 2019 21:43:45 GMT" + }, + "RetryAttempts": 0 + } + } +} diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_aws_s3.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_aws_s3.py new file mode 100644 index 00000000..7d34a84f --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_aws_s3.py @@ -0,0 +1,38 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +import unittest + +try: + import ansible_collections.amazon.aws.plugins.modules.aws_s3 as s3 +except ImportError: + pytestmark = pytest.mark.skip("This test requires the s3 Python libraries") + +from ansible.module_utils.six.moves.urllib.parse import urlparse + +boto3 = pytest.importorskip("boto3") + + +class TestUrlparse(unittest.TestCase): + + def test_urlparse(self): + actual = urlparse("http://test.com/here") + self.assertEqual("http", actual.scheme) + self.assertEqual("test.com", actual.netloc) + self.assertEqual("/here", actual.path) + + def test_is_fakes3(self): + actual = s3.is_fakes3("fakes3://bla.blubb") + self.assertEqual(True, actual) + + def test_get_s3_connection(self): + aws_connect_kwargs = dict(aws_access_key_id="access_key", + aws_secret_access_key="secret_key") + location = None + rgw = True + s3_url = "http://bla.blubb" + actual = s3.get_s3_connection(None, aws_connect_kwargs, location, rgw, s3_url) + self.assertEqual(bool("bla.blubb" in str(actual._endpoint)), True) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py new file mode 100644 index 00000000..6ee1fcf9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_cloudformation.py @@ -0,0 +1,213 @@ +# (c) 2017 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +# Magic... +from ansible_collections.amazon.aws.tests.unit.utils.amazon_placebo_fixtures import maybe_sleep, placeboify # pylint: disable=unused-import + +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto_exception +from ansible_collections.amazon.aws.plugins.modules import cloudformation as cfn_module + +basic_yaml_tpl = """ +--- +AWSTemplateFormatVersion: '2010-09-09' +Description: 'Basic template that creates an S3 bucket' +Resources: + MyBucket: + Type: "AWS::S3::Bucket" +Outputs: + TheName: + Value: + !Ref MyBucket +""" + +bad_json_tpl = """{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Broken template, no comma here ->" + "Resources": { + "MyBucket": { + "Type": "AWS::S3::Bucket" + } + } +}""" + +failing_yaml_tpl = """ +--- +AWSTemplateFormatVersion: 2010-09-09 +Resources: + ECRRepo: + Type: AWS::ECR::Repository + Properties: + RepositoryPolicyText: + Version: 3000-10-17 # <--- invalid version + Statement: + - Effect: Allow + Action: + - 'ecr:*' + Principal: + AWS: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:root +""" + +default_events_limit = 10 + + +class FakeModule(object): + def __init__(self, **kwargs): + self.params = kwargs + + def fail_json(self, *args, **kwargs): + self.exit_args = args + self.exit_kwargs = kwargs + raise Exception('FAIL') + + def fail_json_aws(self, *args, **kwargs): + self.exit_args = args + self.exit_kwargs = kwargs + raise Exception('FAIL') + + def exit_json(self, *args, **kwargs): + self.exit_args = args + self.exit_kwargs = kwargs + raise Exception('EXIT') + + +def test_invalid_template_json(placeboify): + connection = placeboify.client('cloudformation') + params = { + 'StackName': 'ansible-test-wrong-json', + 'TemplateBody': bad_json_tpl, + } + m = FakeModule(disable_rollback=False) + with pytest.raises(Exception) as exc_info: + cfn_module.create_stack(m, params, connection, default_events_limit) + pytest.fail('Expected malformed JSON to have caused the call to fail') + + assert exc_info.match('FAIL') + assert "ValidationError" in boto_exception(m.exit_args[0]) + + +def test_client_request_token_s3_stack(maybe_sleep, placeboify): + connection = placeboify.client('cloudformation') + params = { + 'StackName': 'ansible-test-client-request-token-yaml', + 'TemplateBody': basic_yaml_tpl, + 'ClientRequestToken': '3faf3fb5-b289-41fc-b940-44151828f6cf', + } + m = FakeModule(disable_rollback=False) + result = cfn_module.create_stack(m, params, connection, default_events_limit) + assert result['changed'] + assert len(result['events']) > 1 + # require that the final recorded stack state was CREATE_COMPLETE + # events are retrieved newest-first, so 0 is the latest + assert 'CREATE_COMPLETE' in result['events'][0] + connection.delete_stack(StackName='ansible-test-client-request-token-yaml') + + +def test_basic_s3_stack(maybe_sleep, placeboify): + connection = placeboify.client('cloudformation') + params = { + 'StackName': 'ansible-test-basic-yaml', + 'TemplateBody': basic_yaml_tpl + } + m = FakeModule(disable_rollback=False) + result = cfn_module.create_stack(m, params, connection, default_events_limit) + assert result['changed'] + assert len(result['events']) > 1 + # require that the final recorded stack state was CREATE_COMPLETE + # events are retrieved newest-first, so 0 is the latest + assert 'CREATE_COMPLETE' in result['events'][0] + connection.delete_stack(StackName='ansible-test-basic-yaml') + + +def test_delete_nonexistent_stack(maybe_sleep, placeboify): + connection = placeboify.client('cloudformation') + result = cfn_module.stack_operation(connection, 'ansible-test-nonexist', 'DELETE', default_events_limit) + assert result['changed'] + assert 'Stack does not exist.' in result['log'] + + +def test_get_nonexistent_stack(placeboify): + connection = placeboify.client('cloudformation') + assert cfn_module.get_stack_facts(connection, 'ansible-test-nonexist') is None + + +def test_missing_template_body(): + m = FakeModule() + with pytest.raises(Exception) as exc_info: + cfn_module.create_stack( + module=m, + stack_params={}, + cfn=None, + events_limit=default_events_limit + ) + pytest.fail('Expected module to have failed with no template') + + assert exc_info.match('FAIL') + assert not m.exit_args + assert "Either 'template', 'template_body' or 'template_url' is required when the stack does not exist." == m.exit_kwargs['msg'] + + +def test_on_create_failure_delete(maybe_sleep, placeboify): + m = FakeModule( + on_create_failure='DELETE', + disable_rollback=False, + ) + connection = placeboify.client('cloudformation') + params = { + 'StackName': 'ansible-test-on-create-failure-delete', + 'TemplateBody': failing_yaml_tpl + } + result = cfn_module.create_stack(m, params, connection, default_events_limit) + assert result['changed'] + assert result['failed'] + assert len(result['events']) > 1 + # require that the final recorded stack state was DELETE_COMPLETE + # events are retrieved newest-first, so 0 is the latest + assert 'DELETE_COMPLETE' in result['events'][0] + + +def test_on_create_failure_rollback(maybe_sleep, placeboify): + m = FakeModule( + on_create_failure='ROLLBACK', + disable_rollback=False, + ) + connection = placeboify.client('cloudformation') + params = { + 'StackName': 'ansible-test-on-create-failure-rollback', + 'TemplateBody': failing_yaml_tpl + } + result = cfn_module.create_stack(m, params, connection, default_events_limit) + assert result['changed'] + assert result['failed'] + assert len(result['events']) > 1 + # require that the final recorded stack state was ROLLBACK_COMPLETE + # events are retrieved newest-first, so 0 is the latest + assert 'ROLLBACK_COMPLETE' in result['events'][0] + connection.delete_stack(StackName=params['StackName']) + + +def test_on_create_failure_do_nothing(maybe_sleep, placeboify): + m = FakeModule( + on_create_failure='DO_NOTHING', + disable_rollback=False, + ) + connection = placeboify.client('cloudformation') + params = { + 'StackName': 'ansible-test-on-create-failure-do-nothing', + 'TemplateBody': failing_yaml_tpl + } + result = cfn_module.create_stack(m, params, connection, default_events_limit) + assert result['changed'] + assert result['failed'] + assert len(result['events']) > 1 + # require that the final recorded stack state was CREATE_FAILED + # events are retrieved newest-first, so 0 is the latest + assert 'CREATE_FAILED' in result['events'][0] + connection.delete_stack(StackName=params['StackName']) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_group.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_group.py new file mode 100644 index 00000000..9b3a14ea --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/test_ec2_group.py @@ -0,0 +1,83 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.amazon.aws.plugins.modules import ec2_group as group_module + + +def test_from_permission(): + internal_http = { + u'FromPort': 80, + u'IpProtocol': 'tcp', + u'IpRanges': [ + { + u'CidrIp': '10.0.0.0/8', + u'Description': 'Foo Bar Baz' + }, + ], + u'Ipv6Ranges': [ + {u'CidrIpv6': 'fe80::94cc:8aff:fef6:9cc/64'}, + ], + u'PrefixListIds': [], + u'ToPort': 80, + u'UserIdGroupPairs': [], + } + perms = list(group_module.rule_from_group_permission(internal_http)) + assert len(perms) == 2 + assert perms[0].target == '10.0.0.0/8' + assert perms[0].target_type == 'ipv4' + assert perms[0].description == 'Foo Bar Baz' + assert perms[1].target == 'fe80::94cc:8aff:fef6:9cc/64' + + global_egress = { + 'IpProtocol': '-1', + 'IpRanges': [{'CidrIp': '0.0.0.0/0'}], + 'Ipv6Ranges': [], + 'PrefixListIds': [], + 'UserIdGroupPairs': [] + } + perms = list(group_module.rule_from_group_permission(global_egress)) + assert len(perms) == 1 + assert perms[0].target == '0.0.0.0/0' + assert perms[0].port_range == (None, None) + + internal_prefix_http = { + u'FromPort': 80, + u'IpProtocol': 'tcp', + u'PrefixListIds': [ + {'PrefixListId': 'p-1234'} + ], + u'ToPort': 80, + u'UserIdGroupPairs': [], + } + perms = list(group_module.rule_from_group_permission(internal_prefix_http)) + assert len(perms) == 1 + assert perms[0].target == 'p-1234' + + +def test_rule_to_permission(): + tests = [ + group_module.Rule((22, 22), 'udp', 'sg-1234567890', 'group', None), + group_module.Rule((1, 65535), 'tcp', '0.0.0.0/0', 'ipv4', "All TCP from everywhere"), + group_module.Rule((443, 443), 'tcp', 'ip-123456', 'ip_prefix', "Traffic to privatelink IPs"), + group_module.Rule((443, 443), 'tcp', 'feed:dead:::beef/64', 'ipv6', None), + ] + for test in tests: + perm = group_module.to_permission(test) + assert perm['FromPort'], perm['ToPort'] == test.port_range + assert perm['IpProtocol'] == test.protocol + + +def test_validate_ip(): + class Warner(object): + def warn(self, msg): + return + ips = [ + ('10.1.1.1/24', '10.1.1.0/24'), + ('192.168.56.101/16', '192.168.0.0/16'), + # Don't modify IPv6 CIDRs, AWS supports /128 and device ranges + ('fc00:8fe0:fe80:b897:8990:8a7c:99bf:323d/128', 'fc00:8fe0:fe80:b897:8990:8a7c:99bf:323d/128'), + ] + + for ip, net in ips: + assert group_module.validate_ip(Warner(), ip) == net diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py new file mode 100644 index 00000000..058a5b60 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/plugins/modules/utils.py @@ -0,0 +1,50 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible_collections.amazon.aws.tests.unit.compat import unittest +from ansible_collections.amazon.aws.tests.unit.compat.mock import patch +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class ModuleTestCase(unittest.TestCase): + + def setUp(self): + self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json) + self.mock_module.start() + self.mock_sleep = patch('time.sleep') + self.mock_sleep.start() + set_module_args({}) + self.addCleanup(self.mock_module.stop) + self.addCleanup(self.mock_sleep.stop) diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/requirements.txt b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/requirements.txt new file mode 100644 index 00000000..917ee278 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/requirements.txt @@ -0,0 +1,2 @@ +boto3 +placebo diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/utils/__init__.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/utils/__init__.py diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/utils/amazon_placebo_fixtures.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/utils/amazon_placebo_fixtures.py new file mode 100644 index 00000000..6912c2e3 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/unit/utils/amazon_placebo_fixtures.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import errno +import os +import time +import mock +import pytest + +boto3 = pytest.importorskip("boto3") +botocore = pytest.importorskip("botocore") +placebo = pytest.importorskip("placebo") + +""" +Using Placebo to test modules using boto3: + +This is an example test, using the placeboify fixture to test that a module +will fail if resources it depends on don't exist. + +> from placebo_fixtures import placeboify, scratch_vpc +> +> def test_create_with_nonexistent_launch_config(placeboify): +> connection = placeboify.client('autoscaling') +> module = FakeModule('test-asg-created', None, min_size=0, max_size=0, desired_capacity=0) +> with pytest.raises(FailJSON) as excinfo: +> asg_module.create_autoscaling_group(connection, module) +> .... asserts based on module state/exceptions .... + +In more advanced cases, use unrecorded resource fixtures to fill in ARNs/IDs of +things modules depend on, such as: + +> def test_create_in_vpc(placeboify, scratch_vpc): +> connection = placeboify.client('autoscaling') +> module = FakeModule(name='test-asg-created', +> min_size=0, max_size=0, desired_capacity=0, +> availability_zones=[s['az'] for s in scratch_vpc['subnets']], +> vpc_zone_identifier=[s['id'] for s in scratch_vpc['subnets']], +> ) +> ..... so on and so forth .... +""" + + +@pytest.fixture +def placeboify(request, monkeypatch): + """This fixture puts a recording/replaying harness around `boto3_conn` + + Placeboify patches the `boto3_conn` function in ec2 module_utils to return + a boto3 session that in recording or replaying mode, depending on the + PLACEBO_RECORD environment variable. Unset PLACEBO_RECORD (the common case + for just running tests) will put placebo in replay mode, set PLACEBO_RECORD + to any value to turn off replay & operate on real AWS resources. + + The recorded sessions are stored in the test file's directory, under the + namespace `placebo_recordings/{testfile name}/{test function name}` to + distinguish them. + """ + session = boto3.Session(region_name='us-west-2') + + recordings_path = os.path.join( + request.fspath.dirname, + 'placebo_recordings', + request.fspath.basename.replace('.py', ''), + request.function.__name__ + # remove the test_ prefix from the function & file name + ).replace('test_', '') + + if not os.getenv('PLACEBO_RECORD'): + if not os.path.isdir(recordings_path): + raise NotImplementedError('Missing Placebo recordings in directory: %s' % recordings_path) + else: + try: + # make sure the directory for placebo test recordings is available + os.makedirs(recordings_path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + pill = placebo.attach(session, data_path=recordings_path) + if os.getenv('PLACEBO_RECORD'): + pill.record() + else: + pill.playback() + + def boto3_middleman_connection(module, conn_type, resource, region='us-west-2', **kwargs): + if conn_type != 'client': + # TODO support resource-based connections + raise ValueError('Mocker only supports client, not %s' % conn_type) + return session.client(resource, region_name=region) + + import ansible_collections.amazon.aws.plugins.module_utils.ec2 + monkeypatch.setattr( + ansible_collections.amazon.aws.plugins.module_utils.ec2, + 'boto3_conn', + boto3_middleman_connection, + ) + yield session + + # tear down + pill.stop() + + +@pytest.fixture(scope='module') +def basic_launch_config(): + """Create an EC2 launch config whose creation *is not* recorded and return its name + + This fixture is module-scoped, since launch configs are immutable and this + can be reused for many tests. + """ + if not os.getenv('PLACEBO_RECORD'): + yield 'pytest_basic_lc' + return + + # use a *non recording* session to make the launch config + # since that's a prereq of the ec2_asg module, and isn't what + # we're testing. + asg = boto3.client('autoscaling') + asg.create_launch_configuration( + LaunchConfigurationName='pytest_basic_lc', + ImageId='ami-9be6f38c', # Amazon Linux 2016.09 us-east-1 AMI, can be any valid AMI + SecurityGroups=[], + UserData='#!/bin/bash\necho hello world', + InstanceType='t2.micro', + InstanceMonitoring={'Enabled': False}, + AssociatePublicIpAddress=True + ) + + yield 'pytest_basic_lc' + + try: + asg.delete_launch_configuration(LaunchConfigurationName='pytest_basic_lc') + except botocore.exceptions.ClientError as e: + if 'not found' in e.message: + return + raise + + +@pytest.fixture(scope='module') +def scratch_vpc(): + if not os.getenv('PLACEBO_RECORD'): + yield { + 'vpc_id': 'vpc-123456', + 'cidr_range': '10.0.0.0/16', + 'subnets': [ + { + 'id': 'subnet-123456', + 'az': 'us-east-1d', + }, + { + 'id': 'subnet-654321', + 'az': 'us-east-1e', + }, + ] + } + return + + # use a *non recording* session to make the base VPC and subnets + ec2 = boto3.client('ec2') + vpc_resp = ec2.create_vpc( + CidrBlock='10.0.0.0/16', + AmazonProvidedIpv6CidrBlock=False, + ) + subnets = ( + ec2.create_subnet( + VpcId=vpc_resp['Vpc']['VpcId'], + CidrBlock='10.0.0.0/24', + ), + ec2.create_subnet( + VpcId=vpc_resp['Vpc']['VpcId'], + CidrBlock='10.0.1.0/24', + ) + ) + time.sleep(3) + + yield { + 'vpc_id': vpc_resp['Vpc']['VpcId'], + 'cidr_range': '10.0.0.0/16', + 'subnets': [ + { + 'id': s['Subnet']['SubnetId'], + 'az': s['Subnet']['AvailabilityZone'], + } for s in subnets + ] + } + + try: + for s in subnets: + try: + ec2.delete_subnet(SubnetId=s['Subnet']['SubnetId']) + except botocore.exceptions.ClientError as e: + if 'not found' in e.message: + continue + raise + ec2.delete_vpc(VpcId=vpc_resp['Vpc']['VpcId']) + except botocore.exceptions.ClientError as e: + if 'not found' in e.message: + return + raise + + +@pytest.fixture(scope='module') +def maybe_sleep(): + """If placebo is reading saved sessions, make sleep always take 0 seconds. + + AWS modules often perform polling or retries, but when using recorded + sessions there's no reason to wait. We can still exercise retry and other + code paths without waiting for wall-clock time to pass.""" + if not os.getenv('PLACEBO_RECORD'): + p = mock.patch('time.sleep', return_value=None) + p.start() + yield + p.stop() + else: + yield diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/aws.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/aws.sh new file mode 100755 index 00000000..d76c3228 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/aws.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +cloud="${args[0]}" +python="${args[1]}" +group="${args[2]}" + +target="shippable/${cloud}/group${group}/" + +stage="${S:-prod}" + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote-terminate always --remote-stage "${stage}" \ + --docker --python "${python}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/check_matrix.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/check_matrix.py new file mode 100755 index 00000000..2ec5cc36 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/check_matrix.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +"""Verify the currently executing Shippable test matrix matches the one defined in the "shippable.yml" file.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import datetime +import json +import os +import re +import sys +import time + +try: + from typing import NoReturn +except ImportError: + NoReturn = None + +try: + # noinspection PyCompatibility + from urllib2 import urlopen # pylint: disable=ansible-bad-import-from +except ImportError: + # noinspection PyCompatibility + from urllib.request import urlopen + + +def main(): # type: () -> None + """Main entry point.""" + repo_full_name = os.environ['REPO_FULL_NAME'] + required_repo_full_name = 'ansible-collections/amazon.aws' + + if repo_full_name != required_repo_full_name: + sys.stderr.write('Skipping matrix check on repo "%s" which is not "%s".\n' % (repo_full_name, required_repo_full_name)) + return + + with open('shippable.yml', 'rb') as yaml_file: + yaml = yaml_file.read().decode('utf-8').splitlines() + + defined_matrix = [match.group(1) for match in [re.search(r'^ *- env: T=(.*)$', line) for line in yaml] if match and match.group(1) != 'none'] + + if not defined_matrix: + fail('No matrix entries found in the "shippable.yml" file.', + 'Did you modify the "shippable.yml" file?') + + run_id = os.environ['SHIPPABLE_BUILD_ID'] + sleep = 1 + jobs = [] + + for attempts_remaining in range(4, -1, -1): + try: + jobs = json.loads(urlopen('https://api.shippable.com/jobs?runIds=%s' % run_id).read()) + + if not isinstance(jobs, list): + raise Exception('Shippable run %s data is not a list.' % run_id) + + break + except Exception as ex: + if not attempts_remaining: + fail('Unable to retrieve Shippable run %s matrix.' % run_id, + str(ex)) + + sys.stderr.write('Unable to retrieve Shippable run %s matrix: %s\n' % (run_id, ex)) + sys.stderr.write('Trying again in %d seconds...\n' % sleep) + time.sleep(sleep) + sleep *= 2 + + if len(jobs) != len(defined_matrix): + if len(jobs) == 1: + hint = '\n\nMake sure you do not use the "Rebuild with SSH" option.' + else: + hint = '' + + fail('Shippable run %s has %d jobs instead of the expected %d jobs.' % (run_id, len(jobs), len(defined_matrix)), + 'Try re-running the entire matrix.%s' % hint) + + actual_matrix = dict((job.get('jobNumber'), dict(tuple(line.split('=', 1)) for line in job.get('env', [])).get('T', '')) for job in jobs) + errors = [(job_number, test, actual_matrix.get(job_number)) for job_number, test in enumerate(defined_matrix, 1) if actual_matrix.get(job_number) != test] + + if len(errors): + error_summary = '\n'.join('Job %s expected "%s" but found "%s" instead.' % (job_number, expected, actual) for job_number, expected, actual in errors) + + fail('Shippable run %s has a job matrix mismatch.' % run_id, + 'Try re-running the entire matrix.\n\n%s' % error_summary) + + +def fail(message, output): # type: (str, str) -> NoReturn + # Include a leading newline to improve readability on Shippable "Tests" tab. + # Without this, the first line becomes indented. + output = '\n' + output.strip() + + timestamp = datetime.datetime.utcnow().replace(microsecond=0).isoformat() + + # hack to avoid requiring junit-xml, which isn't pre-installed on Shippable outside our test containers + xml = ''' +<?xml version="1.0" encoding="utf-8"?> +<testsuites disabled="0" errors="1" failures="0" tests="1" time="0.0"> +\t<testsuite disabled="0" errors="1" failures="0" file="None" log="None" name="ansible-test" skipped="0" tests="1" time="0" timestamp="%s" url="None"> +\t\t<testcase classname="timeout" name="timeout"> +\t\t\t<error message="%s" type="error">%s</error> +\t\t</testcase> +\t</testsuite> +</testsuites> +''' % (timestamp, message, output) + + path = 'shippable/testresults/check-matrix.xml' + dir_path = os.path.dirname(path) + + if not os.path.exists(dir_path): + os.makedirs(dir_path) + + with open(path, 'w') as junit_fd: + junit_fd.write(xml.lstrip()) + + sys.stderr.write(message + '\n') + sys.stderr.write(output + '\n') + + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/sanity.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/sanity.sh new file mode 100755 index 00000000..dd1e68b9 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/sanity.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -o pipefail -eux + +if [ "${BASE_BRANCH:-}" ]; then + base_branch="origin/${BASE_BRANCH}" +else + base_branch="" +fi + +# shellcheck disable=SC2086 +ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \ + --docker --base-branch "${base_branch}" \ + --allow-disabled diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/shippable.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/shippable.sh new file mode 100755 index 00000000..dd52e040 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/shippable.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash + +set -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +script="${args[0]}" + +test="$1" + +docker images ansible/ansible +docker images quay.io/ansible/* +docker ps + +for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v '^drydock/' | sed 's/^.* //'); do + docker rm -f "${container}" || true # ignore errors +done + +docker ps + +if [ -d /home/shippable/cache/ ]; then + ls -la /home/shippable/cache/ +fi + +command -v python +python -V + +command -v pip +pip --version +pip list --disable-pip-version-check + +export PATH="${PWD}/bin:${PATH}" +export PYTHONIOENCODING='utf-8' + +if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then + COVERAGE=yes + COMPLETE=yes +fi + +if [ -n "${COVERAGE:-}" ]; then + # on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value + export COVERAGE="--coverage" +elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then + # on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message + export COVERAGE="--coverage" +else + # on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled) + export COVERAGE="--coverage-check" +fi + +if [ -n "${COMPLETE:-}" ]; then + # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value + export CHANGED="" +elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then + # disable change detection triggered by having 'ci_complete' in the latest commit message + export CHANGED="" +else + # enable change detection (default behavior) + export CHANGED="--changed" +fi + +if [ "${IS_PULL_REQUEST:-}" == "true" ]; then + # run unstable tests which are targeted by focused changes on PRs + export UNSTABLE="--allow-unstable-changed" +else + # do not run unstable tests outside PRs + export UNSTABLE="" +fi + +virtualenv --python /usr/bin/python3.7 ~/ansible-venv +set +ux +. ~/ansible-venv/bin/activate +set -ux + +pip install setuptools==44.1.0 + +pip install https://github.com/ansible/ansible/archive/"${A_REV:-devel}".tar.gz --disable-pip-version-check + +#ansible-galaxy collection install community.general +mkdir -p "${HOME}/.ansible/collections/ansible_collections/community" +mkdir -p "${HOME}/.ansible/collections/ansible_collections/google" +mkdir -p "${HOME}/.ansible/collections/ansible_collections/openstack" +cwd=$(pwd) +cd "${HOME}/.ansible/collections/ansible_collections/" +git clone https://github.com/ansible-collections/community.general community/general +git clone https://github.com/ansible-collections/community.aws community/aws +# community.general requires a lot of things we need to manual pull in +# once community.general is published this will be handled by galaxy cli +git clone https://github.com/ansible-collections/ansible_collections_google google/cloud +git clone https://opendev.org/openstack/ansible-collections-openstack openstack/cloud +ansible-galaxy collection install ansible.netcommon +cd "${cwd}" + +export ANSIBLE_COLLECTIONS_PATHS="${HOME}/.ansible/" +SHIPPABLE_RESULT_DIR="$(pwd)/shippable" +TEST_DIR="${HOME}/.ansible/collections/ansible_collections/amazon/aws/" +mkdir -p "${TEST_DIR}" +cp -aT "${SHIPPABLE_BUILD_DIR}" "${TEST_DIR}" +cd "${TEST_DIR}" + +function cleanup +{ + if [ -d tests/output/coverage/ ]; then + if find tests/output/coverage/ -mindepth 1 -name '.*' -prune -o -print -quit | grep -q .; then + # for complete on-demand coverage generate a report for all files with no coverage on the "other" job so we only have one copy + if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ] && [ "${test}" == "sanity/1" ]; then + stub="--stub" + else + stub="" + fi + + # shellcheck disable=SC2086 + ansible-test coverage xml --color --requirements --group-by command --group-by version ${stub:+"$stub"} + cp -a tests/output/reports/coverage=*.xml "$SHIPPABLE_RESULT_DIR/codecoverage/" + + # analyze and capture code coverage aggregated by integration test target if not on 2.9, default if unspecified is devel + if [ -z "${A_REV:-}" ] || [ "${A_REV:-}" != "stable-2.9" ]; then + ansible-test coverage analyze targets generate -v "$SHIPPABLE_RESULT_DIR/testresults/coverage-analyze-targets.json" + fi + + # upload coverage report to codecov.io only when using complete on-demand coverage + if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ]; then + for file in tests/output/reports/coverage=*.xml; do + flags="${file##*/coverage=}" + flags="${flags%-powershell.xml}" + flags="${flags%.xml}" + # remove numbered component from stub files when converting to tags + flags="${flags//stub-[0-9]*/stub}" + flags="${flags//=/,}" + flags="${flags//[^a-zA-Z0-9_,]/_}" + + bash <(curl -s https://codecov.io/bash) \ + -f "${file}" \ + -F "${flags}" \ + -n "${test}" \ + -t bc371da7-e5d2-4743-93b5-309f81d457a4 \ + -X coveragepy \ + -X gcov \ + -X fix \ + -X search \ + -X xcode \ + || echo "Failed to upload code coverage report to codecov.io: ${file}" + done + fi + fi + fi + if [ -d tests/output/junit/ ]; then + cp -aT tests/output/junit/ "$SHIPPABLE_RESULT_DIR/testresults/" + fi + + if [ -d tests/output/data/ ]; then + cp -a tests/output/data/ "$SHIPPABLE_RESULT_DIR/testresults/" + fi + + if [ -d tests/output/bot/ ]; then + cp -aT tests/output/bot/ "$SHIPPABLE_RESULT_DIR/testresults/" + fi +} + +trap cleanup EXIT + +if [[ "${COVERAGE:-}" == "--coverage" ]]; then + timeout=60 +else + timeout=45 +fi + +ansible-test env --dump --show --timeout "${timeout}" --color -v + +"tests/utils/shippable/check_matrix.py" +"tests/utils/shippable/${script}.sh" "${test}" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/timing.py b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/timing.py new file mode 100755 index 00000000..fb538271 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/timing.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3.7 +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys +import time + +start = time.time() + +sys.stdin.reconfigure(errors='surrogateescape') +sys.stdout.reconfigure(errors='surrogateescape') + +for line in sys.stdin: + seconds = time.time() - start + sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line)) + sys.stdout.flush() diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/timing.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/timing.sh new file mode 100755 index 00000000..77e25783 --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/timing.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -o pipefail -eu + +"$@" 2>&1 | "$(dirname "$0")/timing.py" diff --git a/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/units.sh b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/units.sh new file mode 100755 index 00000000..dc115dec --- /dev/null +++ b/collections-debian-merged/ansible_collections/amazon/aws/tests/utils/shippable/units.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +version="${args[1]}" + +# shellcheck disable=SC2086 +ansible-test units --color -v --docker default --python "${version}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \ |